mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
Error and Audit logger webhooks (#1855)
Similar to MinIO now it's possible to configure webhooks to log all triggered errors and incomming requests via env variables: ``` CONSOLE_LOGGER_WEBHOOK_ENABLE_<ID> CONSOLE_LOGGER_WEBHOOK_ENDPOINT_<ID> CONSOLE_LOGGER_WEBHOOK_AUTH_TOKEN_<ID> CONSOLE_LOGGER_WEBHOOK_CLIENT_CERT_<ID> CONSOLE_LOGGER_WEBHOOK_CLIENT_KEY_<ID> CONSOLE_LOGGER_WEBHOOK_QUEUE_SIZE_<ID> CONSOLE_AUDIT_WEBHOOK_ENABLE_<ID> CONSOLE_AUDIT_WEBHOOK_ENDPOINT_<ID> CONSOLE_AUDIT_WEBHOOK_AUTH_TOKEN_<ID> CONSOLE_AUDIT_WEBHOOK_CLIENT_CERT_<ID> CONSOLE_AUDIT_WEBHOOK_QUEUE_SIZE_<ID> ``` Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -19,6 +19,7 @@ vendor/
|
||||
|
||||
# Ignore executables
|
||||
target/
|
||||
!pkg/logger/target/
|
||||
console
|
||||
!console/
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/pkg/env"
|
||||
@@ -68,7 +70,7 @@ func GetMinioImage() (*string, error) {
|
||||
return &image, nil
|
||||
}
|
||||
latestMinIOImage, errLatestMinIOImage := utils.GetLatestMinIOImage(
|
||||
&utils.HTTPClient{
|
||||
&xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
},
|
||||
|
||||
@@ -20,10 +20,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/console/restapi"
|
||||
)
|
||||
@@ -36,15 +40,28 @@ var appCmds = []cli.Command{
|
||||
|
||||
// StartServer starts the console service
|
||||
func StartServer(ctx *cli.Context) error {
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
return startOperatorServer(ctx)
|
||||
}
|
||||
|
||||
// Load all certificates
|
||||
if err := loadAllCerts(ctx); err != nil {
|
||||
// Log this as a warning and continue running console without TLS certificates
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
return startOperatorServer(ctx)
|
||||
}
|
||||
|
||||
var rctx restapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
|
||||
@@ -20,9 +20,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/cli"
|
||||
"github.com/minio/console/restapi"
|
||||
)
|
||||
@@ -39,6 +43,17 @@ func StartServer(ctx *cli.Context) error {
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
var rctx restapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// +build operator
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -20,6 +20,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
@@ -27,6 +28,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/loads"
|
||||
@@ -106,7 +109,7 @@ func buildOperatorServer() (*operatorapi.Server, error) {
|
||||
}
|
||||
|
||||
api := operations.NewOperatorAPI(swaggerSpec)
|
||||
api.Logger = operatorapi.LogInfo
|
||||
api.Logger = restapi.LogInfo
|
||||
server := operatorapi.NewServer(api)
|
||||
|
||||
parser := flags.NewParser(server, flags.Default)
|
||||
@@ -147,7 +150,7 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// load the certificates and the CAs
|
||||
operatorapi.GlobalRootCAs, operatorapi.GlobalPublicCerts, operatorapi.GlobalTLSCertsManager, err = certs.GetAllCertificatesAndCAs()
|
||||
restapi.GlobalRootCAs, restapi.GlobalPublicCerts, restapi.GlobalTLSCertsManager, err = certs.GetAllCertificatesAndCAs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load certificates at %s: failed with %w", certs.GlobalCertsDir.Get(), err)
|
||||
}
|
||||
@@ -159,12 +162,12 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
swaggerServerCACertificate := ctx.String("tls-ca")
|
||||
// load tls cert and key from swagger server tls-certificate and tls-key flags
|
||||
if swaggerServerCertificate != "" && swaggerServerCertificateKey != "" {
|
||||
if err = operatorapi.GlobalTLSCertsManager.AddCertificate(swaggerServerCertificate, swaggerServerCertificateKey); err != nil {
|
||||
if err = restapi.GlobalTLSCertsManager.AddCertificate(swaggerServerCertificate, swaggerServerCertificateKey); err != nil {
|
||||
return err
|
||||
}
|
||||
x509Certs, err := certs.ParsePublicCertFile(swaggerServerCertificate)
|
||||
if err == nil {
|
||||
operatorapi.GlobalPublicCerts = append(operatorapi.GlobalPublicCerts, x509Certs...)
|
||||
restapi.GlobalPublicCerts = append(restapi.GlobalPublicCerts, x509Certs...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +175,7 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
if swaggerServerCACertificate != "" {
|
||||
caCert, caCertErr := ioutil.ReadFile(swaggerServerCACertificate)
|
||||
if caCertErr == nil {
|
||||
operatorapi.GlobalRootCAs.AppendCertsFromPEM(caCert)
|
||||
restapi.GlobalRootCAs.AppendCertsFromPEM(caCert)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,20 +189,32 @@ func loadOperatorAllCerts(ctx *cli.Context) error {
|
||||
|
||||
// StartServer starts the console service
|
||||
func startOperatorServer(ctx *cli.Context) error {
|
||||
if err := loadOperatorAllCerts(ctx); err != nil {
|
||||
|
||||
if err := loadAllCerts(ctx); err != nil {
|
||||
// Log this as a warning and continue running console without TLS certificates
|
||||
operatorapi.LogError("Unable to load certs: %v", err)
|
||||
restapi.LogError("Unable to load certs: %v", err)
|
||||
}
|
||||
|
||||
xctx := context.Background()
|
||||
transport := restapi.PrepareSTSClientTransport(false)
|
||||
if err := logger.InitializeLogger(xctx, transport); err != nil {
|
||||
fmt.Println("error InitializeLogger", err)
|
||||
logger.CriticalIf(xctx, err)
|
||||
}
|
||||
// custom error configuration
|
||||
restapi.LogInfo = logger.Info
|
||||
restapi.LogError = logger.Error
|
||||
restapi.LogIf = logger.LogIf
|
||||
|
||||
var rctx operatorapi.Context
|
||||
if err := rctx.Load(ctx); err != nil {
|
||||
operatorapi.LogError("argument validation failed: %v", err)
|
||||
restapi.LogError("argument validation failed: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
server, err := buildOperatorServer()
|
||||
if err != nil {
|
||||
operatorapi.LogError("Unable to initialize console server: %v", err)
|
||||
restapi.LogError("Unable to initialize console server: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -212,7 +227,7 @@ func startOperatorServer(ctx *cli.Context) error {
|
||||
operatorapi.Port = strconv.Itoa(server.Port)
|
||||
operatorapi.Hostname = server.Host
|
||||
|
||||
if len(operatorapi.GlobalPublicCerts) > 0 {
|
||||
if len(restapi.GlobalPublicCerts) > 0 {
|
||||
// If TLS certificates are provided enforce the HTTPS schema, meaning console will redirect
|
||||
// plain HTTP connections to HTTPS server
|
||||
server.EnabledListeners = []string{"http", "https"}
|
||||
|
||||
9
go.mod
9
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/blang/semver/v4 v4.0.0
|
||||
github.com/cheggaaa/pb/v3 v3.0.8
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/go-openapi/errors v0.20.2
|
||||
github.com/go-openapi/loads v0.21.1
|
||||
github.com/go-openapi/runtime v0.23.3
|
||||
@@ -14,10 +15,12 @@ require (
|
||||
github.com/go-openapi/swag v0.21.1
|
||||
github.com/go-openapi/validate v0.21.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/jessevdk/go-flags v1.5.0
|
||||
github.com/klauspost/compress v1.15.1
|
||||
github.com/minio/cli v1.22.0
|
||||
github.com/minio/highwayhash v1.0.2
|
||||
github.com/minio/kes v0.19.2
|
||||
github.com/minio/madmin-go v1.3.12
|
||||
github.com/minio/mc v0.0.0-20220419155441-cc4ff3a0cc82
|
||||
@@ -57,7 +60,6 @@ require (
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fatih/structs v1.1.0 // indirect
|
||||
github.com/gdamore/encoding v1.0.0 // indirect
|
||||
github.com/gdamore/tcell/v2 v2.4.1-0.20210905002822-f057f0a857a1 // indirect
|
||||
@@ -74,7 +76,6 @@ require (
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -109,7 +110,7 @@ require (
|
||||
github.com/navidys/tvxwidgets v0.1.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/philhofer/fwd v1.1.1 // indirect
|
||||
github.com/philhofer/fwd v1.1.2-0.20210722190033-5c56ac6d0bb9 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/xattr v0.4.5 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
@@ -126,7 +127,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/tinylib/msgp v1.1.6 // indirect
|
||||
github.com/tinylib/msgp v1.1.7-0.20211026165309-e818a1881b0e // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/tklauser/numcpus v0.4.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@@ -470,6 +470,8 @@ github.com/minio/colorjson v1.0.2 h1:Em3IM68MTm3h+Oxa0nxrV9VQqDgbxvC5iq5A+pqzDeI
|
||||
github.com/minio/colorjson v1.0.2/go.mod h1:JWxcL2n8T8JVf+NY6awl6kn5nK49aAzHOeQEM33dL0k=
|
||||
github.com/minio/filepath v1.0.0 h1:fvkJu1+6X+ECRA6G3+JJETj4QeAYO9sV43I79H8ubDY=
|
||||
github.com/minio/filepath v1.0.0/go.mod h1:/nRZA2ldl5z6jT9/KQuvZcQlxZIMQoFFQPvEXx9T/Bw=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/minio/kes v0.19.2 h1:0kdMAgLMSkiDA33k8pMHC7d6erDuseuLrZF+N3017SM=
|
||||
github.com/minio/kes v0.19.2/go.mod h1:X2fMkDbAkjbSKDGOQZvyPkHxoG7nuzP6R78Jw+TzXtM=
|
||||
github.com/minio/madmin-go v1.3.5/go.mod h1:vGKGboQgGIWx4DuDUaXixjlIEZOCIp6ivJkQoiVaACc=
|
||||
@@ -556,8 +558,9 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
|
||||
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/philhofer/fwd v1.1.2-0.20210722190033-5c56ac6d0bb9 h1:6ob53CVz+ja2i7easAStApZJlh7sxyq3Cm7g1Di6iqA=
|
||||
github.com/philhofer/fwd v1.1.2-0.20210722190033-5c56ac6d0bb9/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -663,8 +666,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tinylib/msgp v1.1.3/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw=
|
||||
github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
|
||||
github.com/tinylib/msgp v1.1.7-0.20211026165309-e818a1881b0e h1:P5tyWbssToKowBPTA1/EzqPXwrZNc8ZeNPdjgpcDEoI=
|
||||
github.com/tinylib/msgp v1.1.7-0.20211026165309-e818a1881b0e/go.mod h1:g7jEyb18KPe65d9RRhGw+ThaJr5duyBH8eaFgBUor7Y=
|
||||
github.com/tklauser/go-sysconf v0.3.6/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
|
||||
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
|
||||
github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
|
||||
@@ -858,6 +861,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -18,15 +18,14 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
operatorClientset "github.com/minio/operator/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
var errInvalidCredentials = errors.New("invalid Login")
|
||||
|
||||
// operatorCredentialsProvider is an struct to hold the JWT (service account token)
|
||||
type operatorCredentialsProvider struct {
|
||||
serviceAccountJWT string
|
||||
@@ -86,7 +85,7 @@ func GetConsoleCredentialsForOperator(jwt string) (*credentials.Credentials, err
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err = checkServiceAccountTokenValid(ctx, opClient); err != nil {
|
||||
return nil, errInvalidCredentials
|
||||
return nil, errors.ErrInvalidLogin
|
||||
}
|
||||
return credentials.New(operatorCredentialsProvider{serviceAccountJWT: jwt}), nil
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
"github.com/unrolled/secure"
|
||||
|
||||
@@ -58,7 +57,7 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
api.KeyAuth = func(token string, scopes []string) (*models.Principal, error) {
|
||||
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
|
||||
// was generated and signed by us in the first place
|
||||
claims, err := auth.SessionTokenAuthenticate(token)
|
||||
claims, err := auth.ParseClaimsFromToken(token)
|
||||
if err != nil {
|
||||
api.Logger("Unable to validate the session token %s: %v", token, err)
|
||||
return nil, errors.New(401, "incorrect api key auth")
|
||||
@@ -101,8 +100,8 @@ func configureAPI(api *operations.OperatorAPI) http.Handler {
|
||||
|
||||
// The TLS configuration before HTTPS server starts.
|
||||
func configureTLS(tlsConfig *tls.Config) {
|
||||
tlsConfig.RootCAs = GlobalRootCAs
|
||||
tlsConfig.GetCertificate = GlobalTLSCertsManager.GetCertificate
|
||||
tlsConfig.RootCAs = restapi.GlobalRootCAs
|
||||
tlsConfig.GetCertificate = restapi.GlobalTLSCertsManager.GetCertificate
|
||||
}
|
||||
|
||||
// As soon as server is initialized but not run yet, this function will be called.
|
||||
@@ -118,24 +117,6 @@ func setupMiddlewares(handler http.Handler) http.Handler {
|
||||
return handler
|
||||
}
|
||||
|
||||
func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
token, err := auth.GetTokenFromRequest(r)
|
||||
if err != nil && err != auth.ErrNoAuthToken {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
// All handlers handle appropriately to return errors
|
||||
// based on their swagger rules, we do not need to
|
||||
// additionally return error here, let the next ServeHTTPs
|
||||
// handle it appropriately.
|
||||
if token != "" {
|
||||
r.Header.Add("Authorization", "Bearer "+token)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// proxyMiddleware adds the proxy capability
|
||||
func proxyMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -150,19 +131,23 @@ func proxyMiddleware(next http.Handler) http.Handler {
|
||||
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
|
||||
// So this is a good place to plug in a panic handling middleware, logging and metrics.
|
||||
func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
// handle cookie or authorization header for session
|
||||
next := AuthenticationMiddleware(handler)
|
||||
// proxy requests
|
||||
next = proxyMiddleware(next)
|
||||
next := proxyMiddleware(handler)
|
||||
// if audit-log is enabled console will log all incoming request
|
||||
next = restapi.AuditLogMiddleware(next)
|
||||
// serve static files
|
||||
next = restapi.FileServerMiddleware(next)
|
||||
// add information to request context
|
||||
next = restapi.ContextMiddleware(next)
|
||||
// handle cookie or authorization header for session
|
||||
next = restapi.AuthenticationMiddleware(next)
|
||||
// Secure middleware, this middleware wrap all the previous handlers and add
|
||||
// HTTP security headers
|
||||
secureOptions := secure.Options{
|
||||
AllowedHosts: restapi.GetSecureAllowedHosts(),
|
||||
AllowedHostsAreRegex: restapi.GetSecureAllowedHostsAreRegex(),
|
||||
HostsProxyHeaders: restapi.GetSecureHostsProxyHeaders(),
|
||||
SSLRedirect: restapi.GetTLSRedirect() == "on" && len(GlobalPublicCerts) > 0,
|
||||
SSLRedirect: restapi.GetTLSRedirect() == "on" && len(restapi.GlobalPublicCerts) > 0,
|
||||
SSLHost: restapi.GetSecureTLSHost(),
|
||||
STSSeconds: restapi.GetSecureSTSSeconds(),
|
||||
STSIncludeSubdomains: restapi.GetSecureSTSIncludeSubdomains(),
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// Generic error messages
|
||||
errorGeneric = errors.New("an error occurred, please try again")
|
||||
errInvalidCredentials = errors.New("invalid Login")
|
||||
errorGenericInvalidSession = errors.New("invalid session")
|
||||
errorGenericUnauthorized = errors.New("unauthorized")
|
||||
errorGenericForbidden = errors.New("forbidden")
|
||||
// ErrorGenericNotFound Generic error for not found
|
||||
ErrorGenericNotFound = errors.New("not found")
|
||||
// Explicit error messages
|
||||
errorInvalidErasureCodingValue = errors.New("invalid Erasure Coding Value")
|
||||
errorUnableToGetTenantUsage = errors.New("unable to get tenant usage")
|
||||
errorUnableToGetTenantLogs = errors.New("unable to get tenant logs")
|
||||
errorUnableToUpdateTenantCertificates = errors.New("unable to update tenant certificates")
|
||||
errorUpdatingEncryptionConfig = errors.New("unable to update encryption configuration")
|
||||
errorDeletingEncryptionConfig = errors.New("error disabling tenant encryption")
|
||||
errorEncryptionConfigNotFound = errors.New("encryption configuration not found")
|
||||
errBucketBodyNotInRequest = errors.New("error bucket body not in request")
|
||||
errBucketNameNotInRequest = errors.New("error bucket name not in request")
|
||||
errGroupBodyNotInRequest = errors.New("error group body not in request")
|
||||
errGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
errPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
errPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
errSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
errBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
errChangePassword = errors.New("error please check your current password")
|
||||
errInvalidLicense = errors.New("invalid license key")
|
||||
errLicenseNotFound = errors.New("license not found")
|
||||
errAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
|
||||
errAccessDenied = errors.New("access denied")
|
||||
errTooManyNodes = errors.New("cannot request more nodes than what is available in the cluster")
|
||||
errTooFewNodes = errors.New("there are not enough nodes in the cluster to support this tenant")
|
||||
errTooFewSchedulableNodes = errors.New("there is not enough schedulable nodes to satisfy this requirement")
|
||||
errFewerThanFourNodes = errors.New("at least 4 nodes are required for a tenant")
|
||||
)
|
||||
|
||||
// prepareError receives an error object and parse it against k8sErrors, returns the right error code paired with a generic error message
|
||||
func prepareError(err ...error) *models.Error {
|
||||
errorCode := int32(500)
|
||||
errorMessage := errorGeneric.Error()
|
||||
if len(err) > 0 {
|
||||
frame := getFrame(2)
|
||||
fileParts := strings.Split(frame.File, "/")
|
||||
LogError("original error -> (%s:%d: %v)", fileParts[len(fileParts)-1], frame.Line, err[0])
|
||||
if k8sErrors.IsUnauthorized(err[0]) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericUnauthorized.Error()
|
||||
}
|
||||
if k8sErrors.IsForbidden(err[0]) {
|
||||
errorCode = 403
|
||||
errorMessage = errorGenericForbidden.Error()
|
||||
}
|
||||
if k8sErrors.IsNotFound(err[0]) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrorGenericNotFound.Error()
|
||||
}
|
||||
if err[0] == ErrorGenericNotFound {
|
||||
errorCode = 404
|
||||
errorMessage = ErrorGenericNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidCredentials) {
|
||||
errorCode = 401
|
||||
errorMessage = errInvalidCredentials.Error()
|
||||
}
|
||||
// console invalid erasure coding value
|
||||
if errors.Is(err[0], errorInvalidErasureCodingValue) {
|
||||
errorCode = 400
|
||||
errorMessage = errorInvalidErasureCodingValue.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if errors.Is(err[0], errorGenericInvalidSession) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// Bucket life cycle not configured
|
||||
if errors.Is(err[0], errBucketLifeCycleNotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errBucketLifeCycleNotConfigured.Error()
|
||||
}
|
||||
// Encryption not configured
|
||||
if errors.Is(err[0], errSSENotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errSSENotConfigured.Error()
|
||||
}
|
||||
// account change password
|
||||
if madmin.ToErrorResponse(err[0]).Code == "SignatureDoesNotMatch" {
|
||||
errorCode = 403
|
||||
errorMessage = errChangePassword.Error()
|
||||
}
|
||||
if errors.Is(err[0], errLicenseNotFound) {
|
||||
errorCode = 404
|
||||
errorMessage = errLicenseNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidLicense) {
|
||||
errorCode = 404
|
||||
errorMessage = errInvalidLicense.Error()
|
||||
}
|
||||
if errors.Is(err[0], errAvoidSelfAccountDelete) {
|
||||
errorCode = 403
|
||||
errorMessage = errAvoidSelfAccountDelete.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "AccessDenied" {
|
||||
errorCode = 403
|
||||
errorMessage = errAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "InvalidAccessKeyId" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if madmin.ToErrorResponse(err[0]).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// if we received a second error take that as friendly message but dont override the code
|
||||
if len(err) > 1 && err[1] != nil {
|
||||
LogError("friendly error: %v", err[1].Error())
|
||||
errorMessage = err[1].Error()
|
||||
}
|
||||
// if we receive third error we just print that as debugging
|
||||
if len(err) > 2 && err[2] != nil {
|
||||
LogError("debugging error: %v", err[2].Error())
|
||||
}
|
||||
|
||||
errRemoteTierExists := errors.New("Specified remote tier already exists") //nolint
|
||||
if errors.Is(err[0], errRemoteTierExists) {
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if errors.Is(err[0], errTooFewNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errTooFewNodes.Error()
|
||||
}
|
||||
if errors.Is(err[0], errTooFewSchedulableNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errTooFewSchedulableNodes.Error()
|
||||
}
|
||||
if errors.Is(err[0], errFewerThanFourNodes) {
|
||||
errorCode = 507
|
||||
errorMessage = errFewerThanFourNodes.Error()
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err[0].Error())}
|
||||
}
|
||||
|
||||
func getFrame(skipFrames int) runtime.Frame {
|
||||
// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
|
||||
targetFrameIndex := skipFrames + 2
|
||||
|
||||
// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
|
||||
programCounters := make([]uintptr, targetFrameIndex+2)
|
||||
n := runtime.Callers(0, programCounters)
|
||||
|
||||
frame := runtime.Frame{Function: "unknown"}
|
||||
if n > 0 {
|
||||
frames := runtime.CallersFrames(programCounters[:n])
|
||||
for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
|
||||
var frameCandidate runtime.Frame
|
||||
frameCandidate, more = frames.Next()
|
||||
if frameIndex == targetFrameIndex {
|
||||
frame = frameCandidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frame
|
||||
}
|
||||
@@ -42,7 +42,7 @@ import (
|
||||
func registerLoginHandlers(api *operations.OperatorAPI) {
|
||||
// GET login strategy
|
||||
api.AuthLoginDetailHandler = authApi.LoginDetailHandlerFunc(func(params authApi.LoginDetailParams) middleware.Responder {
|
||||
loginDetails, err := getLoginDetailsResponse(params.HTTPRequest)
|
||||
loginDetails, err := getLoginDetailsResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginDetailDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
|
||||
})
|
||||
// POST login using k8s service account token
|
||||
api.AuthLoginOperatorHandler = authApi.LoginOperatorHandlerFunc(func(params authApi.LoginOperatorParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOperatorResponse(params.Body)
|
||||
loginResponse, err := getLoginOperatorResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginOperatorDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func registerLoginHandlers(api *operations.OperatorAPI) {
|
||||
})
|
||||
// POST login using external IDP
|
||||
api.AuthLoginOauth2AuthHandler = authApi.LoginOauth2AuthHandlerFunc(func(params authApi.LoginOauth2AuthParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params.HTTPRequest, params.Body)
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -87,14 +87,19 @@ func login(credentials restapi.ConsoleCredentialsI) (*string, error) {
|
||||
// if we made it here, the consoleCredentials work, generate a jwt with claims
|
||||
token, err := auth.NewEncryptedTokenForClient(&tokens, credentials.GetAccountAccessKey(), nil)
|
||||
if err != nil {
|
||||
LogError("error authenticating user: %v", err)
|
||||
return nil, errInvalidCredentials
|
||||
restapi.LogError("error authenticating user: %v", err)
|
||||
return nil, restapi.ErrInvalidLogin
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
|
||||
// getLoginDetailsResponse returns information regarding the Console authentication mechanism.
|
||||
func getLoginDetailsResponse(r *http.Request) (*models.LoginDetails, *models.Error) {
|
||||
func getLoginDetailsResponse(params authApi.LoginDetailParams) (*models.LoginDetails, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
r := params.HTTPRequest
|
||||
|
||||
loginStrategy := models.LoginDetailsLoginStrategyServiceDashAccount
|
||||
redirectURL := ""
|
||||
|
||||
@@ -103,7 +108,7 @@ func getLoginDetailsResponse(r *http.Request) (*models.LoginDetails, *models.Err
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// Validate user against IDP
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
@@ -126,31 +131,35 @@ func verifyUserAgainstIDP(ctx context.Context, provider auth.IdentityProviderI,
|
||||
return oauth2Token, nil
|
||||
}
|
||||
|
||||
func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
r := params.HTTPRequest
|
||||
lr := params.Body
|
||||
|
||||
if oauth2.IsIDPEnabled() {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, restapi.GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// initialize new identity provider
|
||||
identityProvider := auth.IdentityProvider{Client: oauth2Client}
|
||||
// Validate user against IDP
|
||||
_, err = verifyUserAgainstIDP(ctx, identityProvider, *lr.Code, *lr.State)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// If we pass here that means the IDP correctly authenticate the user with the operator resource
|
||||
// we proceed to use the service account token configured in the operator-console pod
|
||||
creds, err := newConsoleCredentials(getK8sSAToken())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
token, err := login(restapi.ConsoleCredentials{ConsoleCredentials: creds})
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -158,7 +167,7 @@ func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthReque
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
return nil, prepareError(errorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
|
||||
func newConsoleCredentials(secretKey string) (*credentials.Credentials, error) {
|
||||
@@ -170,17 +179,22 @@ func newConsoleCredentials(secretKey string) (*credentials.Credentials, error) {
|
||||
}
|
||||
|
||||
// getLoginOperatorResponse validate the provided service account token against k8s api
|
||||
func getLoginOperatorResponse(lmr *models.LoginOperatorRequest) (*models.LoginResponse, *models.Error) {
|
||||
func getLoginOperatorResponse(params authApi.LoginOperatorParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
lmr := params.Body
|
||||
|
||||
creds, err := newConsoleCredentials(*lmr.Jwt)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
consoleCreds := restapi.ConsoleCredentials{ConsoleCredentials: creds}
|
||||
// Set a random as access key as session identifier
|
||||
consoleCreds.AccountAccessKey = fmt.Sprintf("%d", rand.Intn(100000-10000)+10000)
|
||||
token, err := login(consoleCreds)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
|
||||
@@ -20,12 +20,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
@@ -44,12 +45,12 @@ func registerNamespaceHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
|
||||
func getNamespaceCreatedResponse(session *models.Principal, params operator_api.CreateNamespaceParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
namespace := *params.Body.Name
|
||||
@@ -57,7 +58,7 @@ func getNamespaceCreatedResponse(session *models.Principal, params operator_api.
|
||||
errCreation := getNamespaceCreated(ctx, clientset.CoreV1(), namespace)
|
||||
|
||||
if errCreation != nil {
|
||||
return prepareError(errCreation)
|
||||
return xerrors.ErrorWithContext(ctx, errCreation)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"errors"
|
||||
"sort"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
@@ -52,8 +54,8 @@ func registerNodesHandlers(api *operations.OperatorAPI) {
|
||||
return operator_api.NewListNodeLabelsOK().WithPayload(*resp)
|
||||
})
|
||||
|
||||
api.OperatorAPIGetAllocatableResourcesHandler = operator_api.GetAllocatableResourcesHandlerFunc(func(params operator_api.GetAllocatableResourcesParams, principal *models.Principal) middleware.Responder {
|
||||
resp, err := getAllocatableResourcesResponse(params.NumNodes, principal)
|
||||
api.OperatorAPIGetAllocatableResourcesHandler = operator_api.GetAllocatableResourcesHandlerFunc(func(params operator_api.GetAllocatableResourcesParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := getAllocatableResourcesResponse(session, params)
|
||||
if err != nil {
|
||||
return operator_api.NewGetAllocatableResourcesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -71,7 +73,7 @@ type NodeResourceInfo struct {
|
||||
func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface, numNodes int32) (*models.MaxAllocatableMemResponse, error) {
|
||||
// can't request less than 4 nodes
|
||||
if numNodes < 4 {
|
||||
return nil, errFewerThanFourNodes
|
||||
return nil, xerrors.ErrFewerThanFourNodes
|
||||
}
|
||||
|
||||
// get all nodes from cluster
|
||||
@@ -97,15 +99,15 @@ func getMaxAllocatableMemory(ctx context.Context, clientset v1.CoreV1Interface,
|
||||
}
|
||||
// requesting more nodes than schedulable and less than total number of workers
|
||||
if int(numNodes) > schedulableNodes && int(numNodes) < nonMasterNodes {
|
||||
return nil, errTooManyNodes
|
||||
return nil, xerrors.ErrTooManyNodes
|
||||
}
|
||||
if nonMasterNodes < int(numNodes) {
|
||||
return nil, errTooFewNodes
|
||||
return nil, xerrors.ErrTooFewNodes
|
||||
}
|
||||
|
||||
// not enough schedulable nodes
|
||||
if schedulableNodes < int(numNodes) {
|
||||
return nil, errTooFewSchedulableNodes
|
||||
return nil, xerrors.ErrTooFewAvailableNodes
|
||||
}
|
||||
|
||||
availableMemSizes := []int64{}
|
||||
@@ -177,12 +179,12 @@ func min(x, y int64) int64 {
|
||||
func getMaxAllocatableMemoryResponse(ctx context.Context, session *models.Principal, numNodes int32) (*models.MaxAllocatableMemResponse, *models.Error) {
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getMaxAllocatableMemory(ctx, client.CoreV1(), numNodes)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
@@ -217,12 +219,12 @@ func getNodeLabels(ctx context.Context, clientset v1.CoreV1Interface) (*models.N
|
||||
func getNodeLabelsResponse(ctx context.Context, session *models.Principal) (*models.NodeLabels, *models.Error) {
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getNodeLabels(ctx, client.CoreV1())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
@@ -357,17 +359,16 @@ OUTER:
|
||||
|
||||
// Get allocatable resources response
|
||||
|
||||
func getAllocatableResourcesResponse(numNodes int32, session *models.Principal) (*models.AllocatableResourcesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getAllocatableResourcesResponse(session *models.Principal, params operator_api.GetAllocatableResourcesParams) (*models.AllocatableResourcesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
clusterResources, err := getAllocatableResources(ctx, client.CoreV1(), numNodes)
|
||||
clusterResources, err := getAllocatableResources(ctx, client.CoreV1(), params.NumNodes)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return clusterResources, nil
|
||||
}
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
@@ -50,14 +51,14 @@ func GetParityInfo(nodes int64, disksPerNode int64) (models.ParityResponse, erro
|
||||
}
|
||||
|
||||
func getParityResponse(params operator_api.GetParityParams) (models.ParityResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
nodes := params.Nodes
|
||||
disksPerNode := params.DisksPerNode
|
||||
|
||||
parityValues, err := GetParityInfo(nodes, disksPerNode)
|
||||
if err != nil {
|
||||
restapi.LogError("error getting parity info: %v", err)
|
||||
return nil, prepareError(err)
|
||||
errors.LogError("error getting parity info: %v", err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return parityValues, nil
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
@@ -94,18 +96,18 @@ func getResourceQuota(ctx context.Context, client K8sClientI, namespace, resourc
|
||||
}
|
||||
|
||||
func getResourceQuotaResponse(session *models.Principal, params operator_api.GetResourceQuotaParams) (*models.ResourceQuota, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
k8sClient := &k8sClient{
|
||||
client: client,
|
||||
}
|
||||
resourceQuota, err := getResourceQuota(ctx, k8sClient, params.Namespace, params.ResourceQuotaName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return resourceQuota, nil
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
@@ -28,7 +31,7 @@ import (
|
||||
func registerSessionHandlers(api *operations.OperatorAPI) {
|
||||
// session check
|
||||
api.AuthSessionCheckHandler = authApi.SessionCheckHandlerFunc(func(params authApi.SessionCheckParams, session *models.Principal) middleware.Responder {
|
||||
sessionResp, err := getSessionResponse(session)
|
||||
sessionResp, err := getSessionResponse(session, params)
|
||||
if err != nil {
|
||||
return authApi.NewSessionCheckDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -37,10 +40,12 @@ func registerSessionHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getSessionResponse(session *models.Principal) (*models.OperatorSessionResponse, *models.Error) {
|
||||
func getSessionResponse(session *models.Principal, params authApi.SessionCheckParams) (*models.OperatorSessionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return nil, prepareError(errorGenericInvalidSession)
|
||||
return nil, errors.ErrorWithContext(ctx, errors.ErrInvalidSession)
|
||||
}
|
||||
sessionResp := &models.OperatorSessionResponse{
|
||||
Status: models.OperatorSessionResponseStatusOk,
|
||||
|
||||
@@ -41,7 +41,7 @@ import (
|
||||
func getTenantCreatedResponse(session *models.Principal, params operator_api.CreateTenantParams) (response *models.CreateTenantResponse, mError *models.Error) {
|
||||
tenantReq := params.Body
|
||||
minioImage := tenantReq.Image
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if minioImage == "" {
|
||||
minImg, err := cluster.GetMinioImage()
|
||||
@@ -56,7 +56,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
ns := *tenantReq.Namespace
|
||||
@@ -98,7 +98,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
|
||||
_, err = clientSet.CoreV1().Secrets(ns).Create(ctx, &instanceSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Enable/Disable console object browser for MinIO tenant (default is on)
|
||||
@@ -110,7 +110,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
tenantConfigurationENV["MINIO_ROOT_USER"] = accessKey
|
||||
tenantConfigurationENV["MINIO_ROOT_PASSWORD"] = secretKey
|
||||
|
||||
// delete secrets created if an error occurred during tenant creation,
|
||||
// delete secrets created if an errors occurred during tenant creation,
|
||||
defer func() {
|
||||
if mError != nil {
|
||||
restapi.LogError("deleting secrets created for failed tenant: %s if any: %v", tenantName, mError)
|
||||
@@ -127,7 +127,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
// Check the Erasure Coding Parity for validity and pass it to Tenant
|
||||
if tenantReq.ErasureCodingParity > 0 {
|
||||
if tenantReq.ErasureCodingParity < 2 || tenantReq.ErasureCodingParity > 8 {
|
||||
return nil, prepareError(errorInvalidErasureCodingValue)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrInvalidErasureCodingValue)
|
||||
}
|
||||
tenantConfigurationENV["MINIO_STORAGE_CLASS_STANDARD"] = fmt.Sprintf("EC:%d", tenantReq.ErasureCodingParity)
|
||||
}
|
||||
@@ -204,7 +204,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
// attach the users to the tenant
|
||||
@@ -248,7 +248,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
}
|
||||
_, err := clientSet.CoreV1().Secrets(ns).Create(ctx, &userSecret, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
// attach the users to the tenant
|
||||
@@ -274,7 +274,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
externalCertSecretName := fmt.Sprintf("%s-instance-external-certificates", secretName)
|
||||
externalCertSecret, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, tenantReq.TLS.Minio, externalCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.ExternalCertSecret = externalCertSecret
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
certificates := []*models.KeyPairConfiguration{tenantReq.Encryption.Client}
|
||||
certificateSecrets, err := createOrReplaceExternalCertSecrets(ctx, &k8sClient, ns, certificates, tenantExternalClientCertSecretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
if len(certificateSecrets) > 0 {
|
||||
minInst.Spec.ExternalClientCertSecret = certificateSecrets[0]
|
||||
@@ -296,7 +296,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
// KES configuration for Tenant instance
|
||||
minInst.Spec.KES, err = getKESConfiguration(ctx, &k8sClient, ns, tenantReq.Encryption, secretName, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault)
|
||||
}
|
||||
// Set Labels, Annotations and Node Selector for KES
|
||||
minInst.Spec.KES.Labels = tenantReq.Encryption.Labels
|
||||
@@ -306,7 +306,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.Encryption.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.Encryption.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.KES.SecurityContext = sc
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
for i, caCertificate := range tenantReq.TLS.CaCertificates {
|
||||
certificateContent, err := base64.StdEncoding.DecodeString(caCertificate)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
caCertificates = append(caCertificates, tenantSecret{
|
||||
Name: fmt.Sprintf("ca-certificate-%d", i),
|
||||
@@ -329,7 +329,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if len(caCertificates) > 0 {
|
||||
certificateSecrets, err := createOrReplaceSecrets(ctx, &k8sClient, ns, caCertificates, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
minInst.Spec.ExternalCaCertSecret = certificateSecrets
|
||||
}
|
||||
@@ -347,7 +347,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
pool, err := parseTenantPoolRequest(pool)
|
||||
if err != nil {
|
||||
restapi.LogError("parseTenantPoolRequest failed: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.Pools = append(minInst.Spec.Pools, *pool)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.ImagePullSecret != "" {
|
||||
imagePullSecret = tenantReq.ImagePullSecret
|
||||
} else if imagePullSecret, err = setImageRegistry(ctx, tenantReq.ImageRegistry, clientSet.CoreV1(), ns, tenantName); err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// pass the image pull secret to the Tenant
|
||||
if imagePullSecret != "" {
|
||||
@@ -410,7 +410,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.LogSearchConfiguration.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logSearchSecurityContext = sc
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.LogSearchConfiguration.PostgresSecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.LogSearchConfiguration.PostgresSecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logSearchPgSecurityContext = sc
|
||||
}
|
||||
@@ -514,7 +514,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
if tenantReq.PrometheusConfiguration != nil && tenantReq.PrometheusConfiguration.SecurityContext != nil {
|
||||
sc, err := convertModelSCToK8sSC(tenantReq.PrometheusConfiguration.SecurityContext)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
minInst.Spec.Prometheus.SecurityContext = sc
|
||||
}
|
||||
@@ -538,7 +538,7 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
},
|
||||
}, tenantName)
|
||||
if err != nil {
|
||||
return nil, prepareError(restapi.ErrorGeneric, nil, err)
|
||||
return nil, restapi.ErrorWithContext(ctx, restapi.ErrDefault, nil, err)
|
||||
}
|
||||
minInst.Spec.Configuration = &corev1.LocalObjectReference{Name: tenantConfigurationName}
|
||||
|
||||
@@ -562,20 +562,20 @@ func getTenantCreatedResponse(session *models.Principal, params operator_api.Cre
|
||||
|
||||
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
_, err = opClient.MinioV2().Tenants(ns).Create(context.Background(), &minInst, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
restapi.LogError("Creating new tenant failed with: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Integrations
|
||||
if os.Getenv("GKE_INTEGRATION") != "" {
|
||||
err := gkeIntegration(clientSet, tenantName, ns, session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
response = &models.CreateTenantResponse{
|
||||
|
||||
@@ -20,20 +20,21 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/cluster"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func getTenantDetailsResponse(session *models.Principal, params operator_api.TenantDetailsParams) (*models.Tenant, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -42,7 +43,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
info := getTenantInfo(minTenant)
|
||||
@@ -50,7 +51,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
k8sClient := k8sClient{
|
||||
@@ -59,7 +60,7 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
|
||||
tenantConfiguration, err := GetTenantConfiguration(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
restapi.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
errors.LogError("unable to fetch configuration for tenant %s: %v", minTenant.Name, err)
|
||||
}
|
||||
|
||||
// detect if AD/LDAP is enabled
|
||||
@@ -105,14 +106,14 @@ func getTenantDetailsResponse(session *models.Principal, params operator_api.Ten
|
||||
//minio service
|
||||
minSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.MinIOCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
// we can tolerate this errors
|
||||
errors.LogError("Unable to get MinIO service name: %v, continuing", err)
|
||||
}
|
||||
//console service
|
||||
conSvc, err := k8sClient.getService(ctx, minTenant.Namespace, minTenant.ConsoleCIServiceName(), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
restapi.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
// we can tolerate this errors
|
||||
errors.LogError("Unable to get MinIO console service name: %v, continuing", err)
|
||||
}
|
||||
|
||||
schema := "http"
|
||||
|
||||
@@ -22,9 +22,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
utils2 "github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/console/restapi"
|
||||
miniov2 "github.com/minio/operator/pkg/apis/minio.min.io/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
@@ -32,7 +35,7 @@ import (
|
||||
)
|
||||
|
||||
// updateTenantAction does an update on the minioTenant by patching the desired changes
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl utils2.HTTPClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
func updateTenantAction(ctx context.Context, operatorClient OperatorClientI, clientset v1.CoreV1Interface, httpCl http.ClientI, namespace string, params operator_api.UpdateTenantParams) error {
|
||||
imageToUpdate := params.Body.Image
|
||||
imageRegistryReq := params.Body.ImageRegistry
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
utils2 "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/minio/madmin-go"
|
||||
|
||||
utils2 "github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
@@ -354,14 +354,16 @@ func registerTenantHandlers(api *operations.OperatorAPI) {
|
||||
|
||||
// getDeleteTenantResponse gets the output of deleting a minio instance
|
||||
func getDeleteTenantResponse(session *models.Principal, params operator_api.DeleteTenantParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
@@ -373,12 +375,12 @@ func getDeleteTenantResponse(session *models.Principal, params operator_api.Dele
|
||||
|
||||
tenant, err := opClient.TenantGet(params.HTTPRequest.Context(), params.Namespace, params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
tenant.EnsureDefaults()
|
||||
|
||||
if err = deleteTenantAction(params.HTTPRequest.Context(), opClient, clientset.CoreV1(), tenant, deleteTenantPVCs); err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -396,7 +398,7 @@ func deleteTenantAction(
|
||||
err := operatorClient.TenantDelete(ctx, tenant.Namespace, tenant.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// try to delete pvc even if the tenant doesn't exist anymore but only if deletePvcs is set to true,
|
||||
// else, we return the error
|
||||
// else, we return the errors
|
||||
if (deletePvcs && !k8sErrors.IsNotFound(err)) || !deletePvcs {
|
||||
return err
|
||||
}
|
||||
@@ -440,19 +442,19 @@ func deleteTenantAction(
|
||||
|
||||
// getDeleteTenantResponse gets the output of deleting a minio instance
|
||||
func getDeletePodResponse(session *models.Principal, params operator_api.DeletePodParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("v1.min.io/tenant=%s", params.Tenant),
|
||||
FieldSelector: fmt.Sprintf("metadata.name=%s%s", params.Tenant, params.PodName[len(params.Tenant):]),
|
||||
}
|
||||
if err = clientset.CoreV1().Pods(params.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts); err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -494,12 +496,12 @@ func getTenantCreds(ctx context.Context, client K8sClientI, tenant *miniov2.Tena
|
||||
tenantAccessKey, ok := tenantConfiguration["accesskey"]
|
||||
if !ok {
|
||||
restapi.LogError("tenant's secret doesn't contain accesskey")
|
||||
return nil, restapi.ErrorGeneric
|
||||
return nil, restapi.ErrDefault
|
||||
}
|
||||
tenantSecretKey, ok := tenantConfiguration["secretkey"]
|
||||
if !ok {
|
||||
restapi.LogError("tenant's secret doesn't contain secretkey")
|
||||
return nil, restapi.ErrorGeneric
|
||||
return nil, restapi.ErrDefault
|
||||
}
|
||||
return &tenantKeys{accessKey: tenantAccessKey, secretKey: tenantSecretKey}, nil
|
||||
}
|
||||
@@ -842,19 +844,19 @@ func updateTenantIdentityProvider(ctx context.Context, operatorClient OperatorCl
|
||||
|
||||
func getTenantIdentityProviderResponse(session *models.Principal, params operator_api.TenantIdentityProviderParams) (*models.IdpConfiguration, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
@@ -862,27 +864,27 @@ func getTenantIdentityProviderResponse(session *models.Principal, params operato
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
info, err := getTenantIdentityProvider(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func getUpdateTenantIdentityProviderResponse(session *models.Principal, params operator_api.UpdateTenantIdentityProviderParams) *models.Error {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
@@ -891,25 +893,25 @@ func getUpdateTenantIdentityProviderResponse(session *models.Principal, params o
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := updateTenantIdentityProvider(ctx, opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errors.New("unable to update tenant"))
|
||||
return restapi.ErrorWithContext(ctx, err, errors.New("unable to update tenant"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTenantSecurityResponse(session *models.Principal, params operator_api.TenantSecurityParams) (*models.TenantSecurityResponse, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
@@ -917,27 +919,27 @@ func getTenantSecurityResponse(session *models.Principal, params operator_api.Te
|
||||
client: clientSet,
|
||||
}
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
info, err := getTenantSecurity(ctx, &k8sClient, minTenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func getUpdateTenantSecurityResponse(session *models.Principal, params operator_api.UpdateTenantSecurityParams) *models.Error {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
@@ -946,7 +948,7 @@ func getUpdateTenantSecurityResponse(session *models.Principal, params operator_
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := updateTenantSecurity(ctx, opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errors.New("unable to update tenant"))
|
||||
return restapi.ErrorWithContext(ctx, err, errors.New("unable to update tenant"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1117,36 +1119,36 @@ func listTenants(ctx context.Context, operatorClient OperatorClientI, namespace
|
||||
}
|
||||
|
||||
func getListAllTenantsResponse(session *models.Principal, params operator_api.ListAllTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
listT, err := listTenants(ctx, opClient, "", params.Limit)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return listT, nil
|
||||
}
|
||||
|
||||
// getListTenantsResponse list tenants by namespace
|
||||
func getListTenantsResponse(session *models.Principal, params operator_api.ListTenantsParams) (*models.ListTenantsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
listT, err := listTenants(ctx, opClient, params.Namespace, params.Limit)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return listT, nil
|
||||
}
|
||||
@@ -1233,27 +1235,27 @@ func removeAnnotations(annotationsOne, annotationsTwo map[string]string) map[str
|
||||
}
|
||||
|
||||
func getUpdateTenantResponse(session *models.Principal, params operator_api.UpdateTenantParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
httpC := &utils2.HTTPClient{
|
||||
httpC := &utils2.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 4 * time.Second,
|
||||
},
|
||||
}
|
||||
if err := updateTenantAction(ctx, opClient, clientSet.CoreV1(), httpC, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errors.New("unable to update tenant"))
|
||||
return restapi.ErrorWithContext(ctx, err, errors.New("unable to update tenant"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1284,17 +1286,17 @@ func addTenantPool(ctx context.Context, operatorClient OperatorClientI, params o
|
||||
}
|
||||
|
||||
func getTenantAddPoolResponse(session *models.Principal, params operator_api.TenantAddPoolParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
opClient := &operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := addTenantPool(ctx, opClient, params); err != nil {
|
||||
return prepareError(err, errors.New("unable to add pool"))
|
||||
return restapi.ErrorWithContext(ctx, err, errors.New("unable to add pool"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1302,16 +1304,16 @@ func getTenantAddPoolResponse(session *models.Principal, params operator_api.Ten
|
||||
// getTenantUsageResponse returns the usage of a tenant
|
||||
func getTenantUsageResponse(session *models.Principal, params operator_api.GetTenantUsageParams) (*models.TenantUsage, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1323,7 +1325,7 @@ func getTenantUsageResponse(session *models.Principal, params operator_api.GetTe
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
minTenant.EnsureDefaults()
|
||||
|
||||
@@ -1336,7 +1338,7 @@ func getTenantUsageResponse(session *models.Principal, params operator_api.GetTe
|
||||
svcURL,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -1344,7 +1346,7 @@ func getTenantUsageResponse(session *models.Principal, params operator_api.GetTe
|
||||
// serialize output
|
||||
adminInfo, err := restapi.GetAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
info := &models.TenantUsage{Used: adminInfo.Usage, DiskUsed: adminInfo.DisksUsage}
|
||||
return info, nil
|
||||
@@ -1353,12 +1355,12 @@ func getTenantUsageResponse(session *models.Principal, params operator_api.GetTe
|
||||
// getTenantLogsResponse returns the logs of a tenant
|
||||
func getTenantLogsResponse(session *models.Principal, params operator_api.GetTenantLogsParams) (*models.TenantLogs, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantLogs)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantLogs)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1367,7 +1369,7 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorUnableToGetTenantLogs)
|
||||
return nil, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantLogs)
|
||||
}
|
||||
if minTenant.Spec.Log == nil {
|
||||
retval := &models.TenantLogs{
|
||||
@@ -1450,12 +1452,12 @@ func getTenantLogsResponse(session *models.Principal, params operator_api.GetTen
|
||||
// setTenantLogsResponse returns the logs of a tenant
|
||||
func setTenantLogsResponse(session *models.Principal, params operator_api.SetTenantLogsParams) (bool, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1464,7 +1466,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
var labels = make(map[string]string)
|
||||
@@ -1493,7 +1495,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
if reflect.TypeOf(params.Data.LogCPURequest).Kind() == reflect.String && params.Data.LogCPURequest != "0Gi" && params.Data.LogCPURequest != "" {
|
||||
cpuQuantity, err := resource.ParseQuantity(params.Data.LogCPURequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logResourceRequest["cpu"] = cpuQuantity
|
||||
minTenant.Spec.Log.Resources.Requests = logResourceRequest
|
||||
@@ -1501,7 +1503,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
if reflect.TypeOf(params.Data.LogMemRequest).Kind() == reflect.String {
|
||||
memQuantity, err := resource.ParseQuantity(params.Data.LogMemRequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
logResourceRequest["memory"] = memQuantity
|
||||
@@ -1538,7 +1540,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
if reflect.TypeOf(params.Data.LogDBCPURequest).Kind() == reflect.String && params.Data.LogDBCPURequest != "0Gi" && params.Data.LogDBCPURequest != "" {
|
||||
dbCPUQuantity, err := resource.ParseQuantity(params.Data.LogDBCPURequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logDBResourceRequest["cpu"] = dbCPUQuantity
|
||||
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
|
||||
@@ -1546,7 +1548,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
if reflect.TypeOf(params.Data.LogDBMemRequest).Kind() == reflect.String {
|
||||
dbMemQuantity, err := resource.ParseQuantity(params.Data.LogDBMemRequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
logDBResourceRequest["memory"] = dbMemQuantity
|
||||
minTenant.Spec.Log.Db.Resources.Requests = logDBResourceRequest
|
||||
@@ -1609,7 +1611,7 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -1617,12 +1619,12 @@ func setTenantLogsResponse(session *models.Principal, params operator_api.SetTen
|
||||
// enableTenantLoggingResponse enables Tenant Logging
|
||||
func enableTenantLoggingResponse(session *models.Principal, params operator_api.EnableTenantLoggingParams) (bool, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1631,7 +1633,7 @@ func enableTenantLoggingResponse(session *models.Principal, params operator_api.
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
minTenant.EnsureDefaults()
|
||||
|
||||
@@ -1670,7 +1672,7 @@ func enableTenantLoggingResponse(session *models.Principal, params operator_api.
|
||||
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -1678,15 +1680,15 @@ func enableTenantLoggingResponse(session *models.Principal, params operator_api.
|
||||
// disableTenantLoggingResponse disables Tenant Logging
|
||||
func disableTenantLoggingResponse(session *models.Principal, params operator_api.DisableTenantLoggingParams) (bool, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1695,31 +1697,31 @@ func disableTenantLoggingResponse(session *models.Principal, params operator_api
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
minTenant.EnsureDefaults()
|
||||
minTenant.Spec.Log = nil
|
||||
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getTenantPodsResponse(session *models.Principal, params operator_api.GetTenantPodsParams) ([]*models.TenantPod, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("%s=%s", miniov2.TenantLabel, params.Tenant),
|
||||
}
|
||||
pods, err := clientset.CoreV1().Pods(params.Namespace).List(ctx, listOpts)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
retval := []*models.TenantPod{}
|
||||
for _, pod := range pods.Items {
|
||||
@@ -1743,35 +1745,35 @@ func getTenantPodsResponse(session *models.Principal, params operator_api.GetTen
|
||||
}
|
||||
|
||||
func getPodLogsResponse(session *models.Principal, params operator_api.GetPodLogsParams) (string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return "", prepareError(err)
|
||||
return "", restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
listOpts := &corev1.PodLogOptions{}
|
||||
logs := clientset.CoreV1().Pods(params.Namespace).GetLogs(params.PodName, listOpts)
|
||||
buff, err := logs.DoRaw(ctx)
|
||||
if err != nil {
|
||||
return "", prepareError(err)
|
||||
return "", restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return string(buff), nil
|
||||
}
|
||||
|
||||
func getPodEventsResponse(session *models.Principal, params operator_api.GetPodEventsParams) (models.EventListWrapper, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
pod, err := clientset.CoreV1().Pods(params.Namespace).Get(ctx, params.PodName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", pod.UID)})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
retval := models.EventListWrapper{}
|
||||
for i := 0; i < len(events.Items); i++ {
|
||||
@@ -1791,12 +1793,12 @@ func getPodEventsResponse(session *models.Principal, params operator_api.GetPodE
|
||||
|
||||
//get values for prometheus metrics
|
||||
func getTenantMonitoringResponse(session *models.Principal, params operator_api.GetTenantMonitoringParams) (*models.TenantMonitoringInfo, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1805,7 +1807,7 @@ func getTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
|
||||
minInst, err := opClient.TenantGet(ctx, params.Namespace, params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
monitoringInfo := &models.TenantMonitoringInfo{}
|
||||
@@ -1886,12 +1888,12 @@ func getTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
//sets tenant Prometheus monitoring cofiguration fields to values provided
|
||||
func setTenantMonitoringResponse(session *models.Principal, params operator_api.SetTenantMonitoringParams) (bool, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -1900,7 +1902,7 @@ func setTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
|
||||
minTenant, err := getTenant(ctx, opClient, params.Namespace, params.Tenant)
|
||||
if err != nil {
|
||||
return false, prepareError(err, errorUnableToGetTenantUsage)
|
||||
return false, restapi.ErrorWithContext(ctx, err, restapi.ErrUnableToGetTenantUsage)
|
||||
}
|
||||
|
||||
if params.Data.Toggle {
|
||||
@@ -1916,7 +1918,7 @@ func setTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
}
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
@@ -1944,7 +1946,7 @@ func setTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
if params.Data.MonitoringCPURequest != "" {
|
||||
cpuQuantity, err := resource.ParseQuantity(params.Data.MonitoringCPURequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
monitoringResourceRequest["cpu"] = cpuQuantity
|
||||
}
|
||||
@@ -1952,7 +1954,7 @@ func setTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
if params.Data.MonitoringMemRequest != "" {
|
||||
memQuantity, err := resource.ParseQuantity(params.Data.MonitoringMemRequest)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
monitoringResourceRequest["memory"] = memQuantity
|
||||
}
|
||||
@@ -1977,7 +1979,7 @@ func setTenantMonitoringResponse(session *models.Principal, params operator_api.
|
||||
minTenant.Spec.Prometheus.ServiceAccountName = params.Data.ServiceAccountName
|
||||
_, err = opClient.TenantUpdate(ctx, minTenant, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
@@ -2422,11 +2424,11 @@ func parseNodeSelectorTerm(term *corev1.NodeSelectorTerm) *models.NodeSelectorTe
|
||||
}
|
||||
|
||||
func getTenantUpdatePoolResponse(session *models.Principal, params operator_api.TenantUpdatePoolsParams) (*models.Tenant, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -2436,7 +2438,7 @@ func getTenantUpdatePoolResponse(session *models.Principal, params operator_api.
|
||||
t, err := updateTenantPools(ctx, opClient, params.Namespace, params.Tenant, params.Body.Pools)
|
||||
if err != nil {
|
||||
restapi.LogError("error updating Tenant's pools: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// parse it to models.Tenant
|
||||
@@ -2487,20 +2489,19 @@ func updateTenantPools(
|
||||
}
|
||||
|
||||
func getTenantYAML(session *models.Principal, params operator_api.GetTenantYAMLParams) (*models.TenantYAML, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
|
||||
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
tenant, err := opClient.MinioV2().Tenants(params.Namespace).Get(params.HTTPRequest.Context(), params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
// remove managed fields
|
||||
tenant.ManagedFields = []metav1.ManagedFieldsEntry{}
|
||||
|
||||
//yb, err := yaml.Marshal(tenant)
|
||||
j8sJSONSerializer := k8sJson.NewSerializerWithOptions(
|
||||
k8sJson.DefaultMetaFactory, nil, nil,
|
||||
@@ -2514,7 +2515,7 @@ func getTenantYAML(session *models.Principal, params operator_api.GetTenantYAMLP
|
||||
|
||||
err = j8sJSONSerializer.Encode(tenant, buf)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
yb := buf.String()
|
||||
@@ -2523,6 +2524,8 @@ func getTenantYAML(session *models.Principal, params operator_api.GetTenantYAMLP
|
||||
}
|
||||
|
||||
func getUpdateTenantYAML(session *models.Principal, params operator_api.PutTenantYAMLParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// https://godoc.org/k8s.io/apimachinery/pkg/runtime#Scheme
|
||||
scheme := runtime.NewScheme()
|
||||
|
||||
@@ -2540,12 +2543,12 @@ func getUpdateTenantYAML(session *models.Principal, params operator_api.PutTenan
|
||||
// get Kubernetes Client
|
||||
opClient, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
tenant, err := opClient.MinioV2().Tenants(params.Namespace).Get(params.HTTPRequest.Context(), params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
upTenant := tenant.DeepCopy()
|
||||
// only update safe fields: spec, metadata.finalizers, metadata.labels and metadata.annotations
|
||||
@@ -2563,23 +2566,23 @@ func getUpdateTenantYAML(session *models.Principal, params operator_api.PutTenan
|
||||
}
|
||||
|
||||
func getTenantEventsResponse(session *models.Principal, params operator_api.GetTenantEventsParams) (models.EventListWrapper, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
tenant, err := client.MinioV2().Tenants(params.Namespace).Get(ctx, params.Tenant, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", tenant.UID)})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
retval := models.EventListWrapper{}
|
||||
for _, event := range events.Items {
|
||||
@@ -2603,7 +2606,7 @@ func getUpdateDomainsResponse(session *models.Principal, params operator_api.Upd
|
||||
|
||||
operatorCli, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
opClient := &operatorClient{
|
||||
@@ -2613,7 +2616,7 @@ func getUpdateDomainsResponse(session *models.Principal, params operator_api.Upd
|
||||
err = updateTenantDomains(ctx, opClient, params.Namespace, params.Tenant, params.Body.Domains)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return restapi.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
xerrors "github.com/minio/console/restapi"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
|
||||
"errors"
|
||||
@@ -108,25 +110,25 @@ func tenantUpdateCertificates(ctx context.Context, operatorClient OperatorClient
|
||||
|
||||
// getTenantUpdateCertificatesResponse wrapper of tenantUpdateCertificates
|
||||
func getTenantUpdateCertificatesResponse(session *models.Principal, params operator_api.TenantUpdateCertificateParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateCertificates(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorUnableToUpdateTenantCertificates)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUnableToUpdateTenantCertificates)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -239,42 +241,42 @@ func tenantUpdateEncryption(ctx context.Context, operatorClient OperatorClientI,
|
||||
|
||||
// getTenantDeleteEncryptionResponse is a wrapper for tenantDeleteEncryption
|
||||
func getTenantDeleteEncryptionResponse(session *models.Principal, params operator_api.TenantDeleteEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorDeletingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrDeletingEncryptionConfig)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantDeleteEncryption(ctx, &opClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorDeletingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrDeletingEncryptionConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTenantUpdateEncryptionResponse is a wrapper for tenantUpdateEncryption
|
||||
func getTenantUpdateEncryptionResponse(session *models.Principal, params operator_api.TenantUpdateEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
if err := tenantUpdateEncryption(ctx, &opClient, &k8sClient, params.Namespace, params); err != nil {
|
||||
return prepareError(err, errorUpdatingEncryptionConfig)
|
||||
return xerrors.ErrorWithContext(ctx, err, xerrors.ErrUpdatingEncryptionConfig)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -453,26 +455,26 @@ func tenantEncryptionInfo(ctx context.Context, operatorClient OperatorClientI, c
|
||||
|
||||
// getTenantEncryptionResponse is a wrapper for tenantEncryptionInfo
|
||||
func getTenantEncryptionInfoResponse(session *models.Principal, params operator_api.TenantEncryptionInfoParams) (*models.EncryptionConfigurationResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientSet, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
k8sClient := k8sClient{
|
||||
client: clientSet,
|
||||
}
|
||||
opClientClientSet, err := cluster.OperatorClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
opClient := operatorClient{
|
||||
client: opClientClientSet,
|
||||
}
|
||||
configuration, err := tenantEncryptionInfo(ctx, &opClient, &k8sClient, params.Namespace, params)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorEncryptionConfigNotFound)
|
||||
return nil, xerrors.ErrorWithContext(ctx, err, xerrors.ErrEncryptionConfigNotFound)
|
||||
}
|
||||
return configuration, nil
|
||||
}
|
||||
@@ -541,8 +543,8 @@ func createOrReplaceSecrets(ctx context.Context, clientSet K8sClientI, ns string
|
||||
// delete secret with same name if exists
|
||||
err := clientSet.deleteSecret(ctx, ns, secret.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", secret.Name, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", secret.Name, err)
|
||||
}
|
||||
k8sSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -579,8 +581,8 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
|
||||
// delete secret with same name if exists
|
||||
err := clientSet.deleteSecret(ctx, ns, keyPairSecretName, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", keyPairSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", keyPairSecretName, err)
|
||||
}
|
||||
imm := true
|
||||
tlsCrt, err := base64.StdEncoding.DecodeString(*keyPair.Crt)
|
||||
@@ -625,13 +627,13 @@ func createOrReplaceExternalCertSecrets(ctx context.Context, clientSet K8sClient
|
||||
func createOrReplaceKesConfigurationSecrets(ctx context.Context, clientSet K8sClientI, ns string, encryptionCfg *models.EncryptionConfiguration, kesConfigurationSecretName, kesClientCertSecretName, tenantName string) (*corev1.LocalObjectReference, *miniov2.LocalCertificateReference, error) {
|
||||
// delete KES configuration secret if exists
|
||||
if err := clientSet.deleteSecret(ctx, ns, kesConfigurationSecretName, metav1.DeleteOptions{}); err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", kesConfigurationSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", kesConfigurationSecretName, err)
|
||||
}
|
||||
// delete KES client cert secret if exists
|
||||
if err := clientSet.deleteSecret(ctx, ns, kesClientCertSecretName, metav1.DeleteOptions{}); err != nil {
|
||||
// log the error if any and continue
|
||||
LogError("deleting secret name %s failed: %v, continuing..", kesClientCertSecretName, err)
|
||||
// log the errors if any and continue
|
||||
xerrors.LogError("deleting secret name %s failed: %v, continuing..", kesClientCertSecretName, err)
|
||||
}
|
||||
// if autoCert is enabled then Operator will generate the client certificates, calculate the client cert identity
|
||||
// and pass it to KES via the ${MINIO_KES_IDENTITY} variable
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/console/operatorapi/operations/operator_api"
|
||||
|
||||
@@ -897,7 +897,7 @@ func Test_UpdateTenantAction(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
operatorClient OperatorClientI
|
||||
httpCl utils.HTTPClientI
|
||||
httpCl xhttp.ClientI
|
||||
nameSpace string
|
||||
tenantName string
|
||||
mockTenantPatch func(ctx context.Context, namespace string, tenantName string, pt types.PatchType, data []byte, options metav1.PatchOptions) (*miniov2.Tenant, error)
|
||||
|
||||
@@ -17,20 +17,24 @@
|
||||
package operatorapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/operatorapi/operations"
|
||||
"github.com/minio/console/operatorapi/operations/user_api"
|
||||
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
func registerVersionHandlers(api *operations.OperatorAPI) {
|
||||
api.UserAPICheckMinIOVersionHandler = user_api.CheckMinIOVersionHandlerFunc(func(params user_api.CheckMinIOVersionParams) middleware.Responder {
|
||||
versionResponse, err := getVersionResponse()
|
||||
versionResponse, err := getVersionResponse(params)
|
||||
if err != nil {
|
||||
return user_api.NewCheckMinIOVersionDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -39,13 +43,15 @@ func registerVersionHandlers(api *operations.OperatorAPI) {
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getVersionResponse() (*models.CheckOperatorVersionResponse, *models.Error) {
|
||||
ver, err := utils.GetLatestMinIOImage(&utils.HTTPClient{
|
||||
func getVersionResponse(params user_api.CheckMinIOVersionParams) (*models.CheckOperatorVersionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
ver, err := utils.GetLatestMinIOImage(&xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.CheckOperatorVersionResponse{
|
||||
LatestVersion: *ver,
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
errors "github.com/minio/console/restapi"
|
||||
|
||||
miniov1 "github.com/minio/operator/pkg/apis/minio.min.io/v1"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
@@ -33,7 +35,7 @@ import (
|
||||
|
||||
func registerVolumesHandlers(api *operations.OperatorAPI) {
|
||||
api.OperatorAPIListPVCsHandler = operator_api.ListPVCsHandlerFunc(func(params operator_api.ListPVCsParams, session *models.Principal) middleware.Responder {
|
||||
payload, err := getPVCsResponse(session)
|
||||
payload, err := getPVCsResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return operator_api.NewListPVCsDefault(int(err.Code)).WithPayload(err)
|
||||
@@ -72,13 +74,13 @@ func registerVolumesHandlers(api *operations.OperatorAPI) {
|
||||
|
||||
}
|
||||
|
||||
func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getPVCsResponse(session *models.Principal, params operator_api.ListPVCsParams) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Filter Tenant PVCs. They keep their v1 tenant annotation
|
||||
@@ -90,7 +92,7 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
listAllPvcs, err2 := clientset.CoreV1().PersistentVolumeClaims("").List(ctx, listOpts)
|
||||
|
||||
if err2 != nil {
|
||||
return nil, prepareError(err2)
|
||||
return nil, errors.ErrorWithContext(ctx, err2)
|
||||
}
|
||||
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
@@ -121,12 +123,12 @@ func getPVCsResponse(session *models.Principal) (*models.ListPVCsResponse, *mode
|
||||
}
|
||||
|
||||
func getPVCsForTenantResponse(session *models.Principal, params operator_api.ListPVCsForTenantParams) (*models.ListPVCsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// Filter Tenant PVCs. They keep their v1 tenant annotation
|
||||
@@ -138,7 +140,7 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
listAllPvcs, err2 := clientset.CoreV1().PersistentVolumeClaims(params.Namespace).List(ctx, listOpts)
|
||||
|
||||
if err2 != nil {
|
||||
return nil, prepareError(err2)
|
||||
return nil, errors.ErrorWithContext(ctx, err2)
|
||||
}
|
||||
|
||||
var ListPVCs []*models.PvcsListResponse
|
||||
@@ -169,37 +171,37 @@ func getPVCsForTenantResponse(session *models.Principal, params operator_api.Lis
|
||||
}
|
||||
|
||||
func getDeletePVCResponse(session *models.Principal, params operator_api.DeletePVCParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// get Kubernetes Client
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
listOpts := metav1.ListOptions{
|
||||
LabelSelector: fmt.Sprintf("v1.min.io/tenant=%s", params.Tenant),
|
||||
FieldSelector: fmt.Sprintf("metadata.name=%s", params.PVCName),
|
||||
}
|
||||
if err = clientset.CoreV1().PersistentVolumeClaims(params.Namespace).DeleteCollection(ctx, metav1.DeleteOptions{}, listOpts); err != nil {
|
||||
return prepareError(err)
|
||||
return errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPVCEventsResponse(session *models.Principal, params operator_api.GetPVCEventsParams) (models.EventListWrapper, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
clientset, err := cluster.K8sClient(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
PVC, err := clientset.CoreV1().PersistentVolumeClaims(params.Namespace).Get(ctx, params.PVCName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
events, err := clientset.CoreV1().Events(params.Namespace).List(ctx, metav1.ListOptions{FieldSelector: fmt.Sprintf("involvedObject.uid=%s", PVC.UID)})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, errors.ErrorWithContext(ctx, err)
|
||||
}
|
||||
retval := models.EventListWrapper{}
|
||||
for i := 0; i < len(events.Items); i++ {
|
||||
|
||||
@@ -89,11 +89,14 @@ func SessionTokenAuthenticate(token string) (*TokenClaims, error) {
|
||||
if token == "" {
|
||||
return nil, ErrNoAuthToken
|
||||
}
|
||||
// decrypt encrypted token
|
||||
claimTokens, err := decryptClaims(token)
|
||||
decryptedToken, err := DecryptToken(token)
|
||||
if err != nil {
|
||||
// we print decryption token error information for debugging purposes
|
||||
// we return a generic error that doesn't give any information to attackers
|
||||
// fail decrypting token
|
||||
return nil, errReadingToken
|
||||
}
|
||||
claimTokens, err := ParseClaimsFromToken(string(decryptedToken))
|
||||
if err != nil {
|
||||
// fail unmarshalling token into data structure
|
||||
return nil, errReadingToken
|
||||
}
|
||||
// claimsTokens contains the decrypted JWT for Console
|
||||
@@ -136,21 +139,26 @@ func encryptClaims(credentials *TokenClaims) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
// decryptClaims() receives base64 encoded ciphertext, decode it, decrypt it (AES-GCM) and produces a *TokenClaims object
|
||||
func decryptClaims(ciphertext string) (*TokenClaims, error) {
|
||||
// ParseClaimsFromToken receive token claims in string format, then unmarshal them to produce a *TokenClaims object
|
||||
func ParseClaimsFromToken(claims string) (*TokenClaims, error) {
|
||||
tokenClaims := &TokenClaims{}
|
||||
if err := json.Unmarshal([]byte(claims), tokenClaims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokenClaims, nil
|
||||
}
|
||||
|
||||
// DecryptToken receives base64 encoded ciphertext, decode it, decrypt it (AES-GCM) and produces []byte
|
||||
func DecryptToken(ciphertext string) (plaintext []byte, err error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext, err := decrypt(decoded, []byte{})
|
||||
plaintext, err = decrypt(decoded, []byte{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokenClaims := &TokenClaims{}
|
||||
if err = json.Unmarshal(plaintext, tokenClaims); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tokenClaims, nil
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -332,3 +332,12 @@ func GetAllCertificatesAndCAs() (*x509.CertPool, []*x509.Certificate, *xcerts.Ma
|
||||
}
|
||||
return rootCAs, publicCerts, certsManager, nil
|
||||
}
|
||||
|
||||
// EnsureCertAndKey checks if both client certificate and key paths are provided
|
||||
func EnsureCertAndKey(clientCert, clientKey string) error {
|
||||
if (clientCert != "" && clientKey == "") ||
|
||||
(clientCert == "" && clientKey != "") {
|
||||
return errors.New("cert and key must be specified as a pair")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
23
pkg/http/headers.go
Normal file
23
pkg/http/headers.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package http
|
||||
|
||||
// Standard S3 HTTP response constants
|
||||
const (
|
||||
ETag = "ETag"
|
||||
ContentType = "Content-Type"
|
||||
)
|
||||
@@ -14,40 +14,61 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTPClientI interface with all functions to be implemented
|
||||
// ClientI interface with all functions to be implemented
|
||||
// by mock when testing, it should include all HttpClient respective api calls
|
||||
// that are used within this project.
|
||||
type HTTPClientI interface {
|
||||
type ClientI interface {
|
||||
Get(url string) (resp *http.Response, err error)
|
||||
Post(url, contentType string, body io.Reader) (resp *http.Response, err error)
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// HTTPClient Interface implementation
|
||||
// Client is an HTTP Interface implementation
|
||||
//
|
||||
// Define the structure of a http client and define the functions that are actually used
|
||||
type HTTPClient struct {
|
||||
type Client struct {
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
// Get implements http.Client.Get()
|
||||
func (c *HTTPClient) Get(url string) (resp *http.Response, err error) {
|
||||
func (c *Client) Get(url string) (resp *http.Response, err error) {
|
||||
return c.Client.Get(url)
|
||||
}
|
||||
|
||||
// Post implements http.Client.Post()
|
||||
func (c *HTTPClient) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
func (c *Client) Post(url, contentType string, body io.Reader) (resp *http.Response, err error) {
|
||||
return c.Client.Post(url, contentType, body)
|
||||
}
|
||||
|
||||
// Do implement http.Client.Do()
|
||||
func (c *HTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
func (c *Client) Do(req *http.Request) (*http.Response, error) {
|
||||
return c.Client.Do(req)
|
||||
}
|
||||
|
||||
// DrainBody close non nil response with any response Body.
|
||||
// convenient wrapper to drain any remaining data on response body.
|
||||
//
|
||||
// Subsequently this allows golang http RoundTripper
|
||||
// to re-use the same connection for future requests.
|
||||
func DrainBody(respBody io.ReadCloser) {
|
||||
// Callers should close resp.Body when done reading from it.
|
||||
// If resp.Body is not closed, the Client's underlying RoundTripper
|
||||
// (typically Transport) may not be able to re-use a persistent TCP
|
||||
// connection to the server for a subsequent "keep-alive" request.
|
||||
if respBody != nil {
|
||||
// Drain any remaining Body and then close the connection.
|
||||
// Without this closing connection would disallow re-using
|
||||
// the same connection for future uses.
|
||||
// - http://stackoverflow.com/a/17961593/4465767
|
||||
defer respBody.Close()
|
||||
io.Copy(ioutil.Discard, respBody)
|
||||
}
|
||||
}
|
||||
228
pkg/logger/audit.go
Normal file
228
pkg/logger/audit.go
Normal file
@@ -0,0 +1,228 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/minio/console/pkg/logger/message/audit"
|
||||
)
|
||||
|
||||
// ResponseWriter - is a wrapper to trap the http response status code.
|
||||
type ResponseWriter struct {
|
||||
http.ResponseWriter
|
||||
StatusCode int
|
||||
// Log body of 4xx or 5xx responses
|
||||
LogErrBody bool
|
||||
// Log body of all responses
|
||||
LogAllBody bool
|
||||
|
||||
TimeToFirstByte time.Duration
|
||||
StartTime time.Time
|
||||
// number of bytes written
|
||||
bytesWritten int
|
||||
// Internal recording buffer
|
||||
headers bytes.Buffer
|
||||
body bytes.Buffer
|
||||
// Indicate if headers are written in the log
|
||||
headersLogged bool
|
||||
}
|
||||
|
||||
// NewResponseWriter - returns a wrapped response writer to trap
|
||||
// http status codes for auditing purposes.
|
||||
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
|
||||
return &ResponseWriter{
|
||||
ResponseWriter: w,
|
||||
StatusCode: http.StatusOK,
|
||||
StartTime: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
func (lrw *ResponseWriter) Write(p []byte) (int, error) {
|
||||
if !lrw.headersLogged {
|
||||
// We assume the response code to be '200 OK' when WriteHeader() is not called,
|
||||
// that way following Golang HTTP response behavior.
|
||||
lrw.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err := lrw.ResponseWriter.Write(p)
|
||||
lrw.bytesWritten += n
|
||||
if lrw.TimeToFirstByte == 0 {
|
||||
lrw.TimeToFirstByte = time.Now().UTC().Sub(lrw.StartTime)
|
||||
}
|
||||
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
|
||||
// Always logging error responses.
|
||||
lrw.body.Write(p)
|
||||
}
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// Write the headers into the given buffer
|
||||
func (lrw *ResponseWriter) writeHeaders(w io.Writer, statusCode int, headers http.Header) {
|
||||
n, _ := fmt.Fprintf(w, "%d %s\n", statusCode, http.StatusText(statusCode))
|
||||
lrw.bytesWritten += n
|
||||
for k, v := range headers {
|
||||
n, _ := fmt.Fprintf(w, "%s: %s\n", k, v[0])
|
||||
lrw.bytesWritten += n
|
||||
}
|
||||
}
|
||||
|
||||
// BodyPlaceHolder returns a dummy body placeholder
|
||||
var BodyPlaceHolder = []byte("<BODY>")
|
||||
|
||||
// Body - Return response body.
|
||||
func (lrw *ResponseWriter) Body() []byte {
|
||||
// If there was an error response or body logging is enabled
|
||||
// then we return the body contents
|
||||
if (lrw.LogErrBody && lrw.StatusCode >= http.StatusBadRequest) || lrw.LogAllBody {
|
||||
return lrw.body.Bytes()
|
||||
}
|
||||
// ... otherwise we return the <BODY> place holder
|
||||
return BodyPlaceHolder
|
||||
}
|
||||
|
||||
// WriteHeader - writes http status code
|
||||
func (lrw *ResponseWriter) WriteHeader(code int) {
|
||||
if !lrw.headersLogged {
|
||||
lrw.StatusCode = code
|
||||
lrw.writeHeaders(&lrw.headers, code, lrw.ResponseWriter.Header())
|
||||
lrw.headersLogged = true
|
||||
lrw.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
}
|
||||
|
||||
// Flush - Calls the underlying Flush.
|
||||
func (lrw *ResponseWriter) Flush() {
|
||||
lrw.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
// Size - reutrns the number of bytes written
|
||||
func (lrw *ResponseWriter) Size() int {
|
||||
return lrw.bytesWritten
|
||||
}
|
||||
|
||||
// SetAuditEntry sets Audit info in the context.
|
||||
func SetAuditEntry(ctx context.Context, audit *audit.Entry) context.Context {
|
||||
if ctx == nil {
|
||||
LogIf(context.Background(), fmt.Errorf("context is nil"))
|
||||
return nil
|
||||
}
|
||||
return context.WithValue(ctx, utils.ContextAuditKey, audit)
|
||||
}
|
||||
|
||||
// GetAuditEntry returns Audit entry if set.
|
||||
func GetAuditEntry(ctx context.Context) *audit.Entry {
|
||||
if ctx != nil {
|
||||
r, ok := ctx.Value(utils.ContextAuditKey).(*audit.Entry)
|
||||
if ok {
|
||||
return r
|
||||
}
|
||||
r = &audit.Entry{
|
||||
Version: audit.Version,
|
||||
//DeploymentID: globalDeploymentID,
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
SetAuditEntry(ctx, r)
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuditLog - logs audit logs to all audit targets.
|
||||
func AuditLog(ctx context.Context, w *ResponseWriter, r *http.Request, reqClaims map[string]interface{}, filterKeys ...string) {
|
||||
// Fast exit if there is not audit target configured
|
||||
if atomic.LoadInt32(&nAuditTargets) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var entry audit.Entry
|
||||
|
||||
if w != nil && r != nil {
|
||||
reqInfo := GetReqInfo(ctx)
|
||||
if reqInfo == nil {
|
||||
return
|
||||
}
|
||||
entry = audit.ToEntry(w, r, reqClaims, GetGlobalDeploymentID())
|
||||
// indicates all requests for this API call are inbound
|
||||
entry.Trigger = "incoming"
|
||||
|
||||
for _, filterKey := range filterKeys {
|
||||
delete(entry.ReqClaims, filterKey)
|
||||
delete(entry.ReqQuery, filterKey)
|
||||
delete(entry.ReqHeader, filterKey)
|
||||
delete(entry.RespHeader, filterKey)
|
||||
}
|
||||
|
||||
var (
|
||||
statusCode int
|
||||
timeToResponse time.Duration
|
||||
timeToFirstByte time.Duration
|
||||
outputBytes int64 = -1 // -1: unknown output bytes
|
||||
)
|
||||
|
||||
if w != nil {
|
||||
statusCode = w.StatusCode
|
||||
timeToResponse = time.Now().UTC().Sub(w.StartTime)
|
||||
timeToFirstByte = w.TimeToFirstByte
|
||||
outputBytes = int64(w.Size())
|
||||
}
|
||||
|
||||
entry.API.Path = r.URL.Path
|
||||
|
||||
entry.API.Status = http.StatusText(statusCode)
|
||||
entry.API.StatusCode = statusCode
|
||||
entry.API.Method = r.Method
|
||||
entry.API.InputBytes = r.ContentLength
|
||||
entry.API.OutputBytes = outputBytes
|
||||
entry.RequestID = reqInfo.RequestID
|
||||
|
||||
entry.API.TimeToResponse = strconv.FormatInt(timeToResponse.Nanoseconds(), 10) + "ns"
|
||||
entry.Tags = reqInfo.GetTagsMap()
|
||||
// ttfb will be recorded only for GET requests, Ignore such cases where ttfb will be empty.
|
||||
if timeToFirstByte != 0 {
|
||||
entry.API.TimeToFirstByte = strconv.FormatInt(timeToFirstByte.Nanoseconds(), 10) + "ns"
|
||||
}
|
||||
} else {
|
||||
auditEntry := GetAuditEntry(ctx)
|
||||
if auditEntry != nil {
|
||||
entry = *auditEntry
|
||||
}
|
||||
}
|
||||
|
||||
if anonFlag {
|
||||
entry.SessionID = hashString(entry.SessionID)
|
||||
entry.RemoteHost = hashString(entry.RemoteHost)
|
||||
}
|
||||
|
||||
// Send audit logs only to http targets.
|
||||
for _, t := range AuditTargets() {
|
||||
if err := t.Send(entry, string(All)); err != nil {
|
||||
LogAlwaysIf(context.Background(), fmt.Errorf("event(%v) was not sent to Audit target (%v): %v", entry, t, err), All)
|
||||
}
|
||||
}
|
||||
}
|
||||
60
pkg/logger/color/color.go
Normal file
60
pkg/logger/color/color.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// global colors.
|
||||
var (
|
||||
// Check if we stderr, stdout are dumb terminals, we do not apply
|
||||
// ansi coloring on dumb terminals.
|
||||
IsTerminal = func() bool {
|
||||
return !color.NoColor
|
||||
}
|
||||
|
||||
Bold = func() func(a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.Bold).SprintFunc()
|
||||
}
|
||||
return fmt.Sprint
|
||||
}()
|
||||
|
||||
FgRed = func() func(a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.FgRed).SprintFunc()
|
||||
}
|
||||
return fmt.Sprint
|
||||
}()
|
||||
|
||||
BgRed = func() func(format string, a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.BgRed).SprintfFunc()
|
||||
}
|
||||
return fmt.Sprintf
|
||||
}()
|
||||
|
||||
FgWhite = func() func(format string, a ...interface{}) string {
|
||||
if IsTerminal() {
|
||||
return color.New(color.FgWhite).SprintfFunc()
|
||||
}
|
||||
return fmt.Sprintf
|
||||
}()
|
||||
)
|
||||
213
pkg/logger/config.go
Normal file
213
pkg/logger/config.go
Normal file
@@ -0,0 +1,213 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/minio/console/pkg/logger/config"
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
"github.com/minio/pkg/env"
|
||||
)
|
||||
|
||||
// NewConfig - initialize new logger config.
|
||||
func NewConfig() Config {
|
||||
cfg := Config{
|
||||
HTTP: make(map[string]http.Config),
|
||||
AuditWebhook: make(map[string]http.Config),
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
func lookupLoggerWebhookConfig() (Config, error) {
|
||||
cfg := NewConfig()
|
||||
envs := env.List(EnvLoggerWebhookEndpoint)
|
||||
var loggerTargets []string
|
||||
for _, k := range envs {
|
||||
target := strings.TrimPrefix(k, EnvLoggerWebhookEndpoint+config.Default)
|
||||
if target == EnvLoggerWebhookEndpoint {
|
||||
target = config.Default
|
||||
}
|
||||
loggerTargets = append(loggerTargets, target)
|
||||
}
|
||||
|
||||
// Load HTTP logger from the environment if found
|
||||
for _, target := range loggerTargets {
|
||||
if v, ok := cfg.HTTP[target]; ok && v.Enabled {
|
||||
// This target is already enabled using the
|
||||
// legacy environment variables, ignore.
|
||||
continue
|
||||
}
|
||||
enableEnv := EnvLoggerWebhookEnable
|
||||
if target != config.Default {
|
||||
enableEnv = EnvLoggerWebhookEnable + config.Default + target
|
||||
}
|
||||
enable, err := config.ParseBool(env.Get(enableEnv, ""))
|
||||
if err != nil || !enable {
|
||||
continue
|
||||
}
|
||||
endpointEnv := EnvLoggerWebhookEndpoint
|
||||
if target != config.Default {
|
||||
endpointEnv = EnvLoggerWebhookEndpoint + config.Default + target
|
||||
}
|
||||
authTokenEnv := EnvLoggerWebhookAuthToken
|
||||
if target != config.Default {
|
||||
authTokenEnv = EnvLoggerWebhookAuthToken + config.Default + target
|
||||
}
|
||||
clientCertEnv := EnvLoggerWebhookClientCert
|
||||
if target != config.Default {
|
||||
clientCertEnv = EnvLoggerWebhookClientCert + config.Default + target
|
||||
}
|
||||
clientKeyEnv := EnvLoggerWebhookClientKey
|
||||
if target != config.Default {
|
||||
clientKeyEnv = EnvLoggerWebhookClientKey + config.Default + target
|
||||
}
|
||||
err = config.EnsureCertAndKey(env.Get(clientCertEnv, ""), env.Get(clientKeyEnv, ""))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
queueSizeEnv := EnvLoggerWebhookQueueSize
|
||||
if target != config.Default {
|
||||
queueSizeEnv = EnvLoggerWebhookQueueSize + config.Default + target
|
||||
}
|
||||
queueSize, err := strconv.Atoi(env.Get(queueSizeEnv, "100000"))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if queueSize <= 0 {
|
||||
return cfg, errors.New("invalid queue_size value")
|
||||
}
|
||||
cfg.HTTP[target] = http.Config{
|
||||
Enabled: true,
|
||||
Endpoint: env.Get(endpointEnv, ""),
|
||||
AuthToken: env.Get(authTokenEnv, ""),
|
||||
ClientCert: env.Get(clientCertEnv, ""),
|
||||
ClientKey: env.Get(clientKeyEnv, ""),
|
||||
QueueSize: queueSize,
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func lookupAuditWebhookConfig() (Config, error) {
|
||||
cfg := NewConfig()
|
||||
var loggerAuditTargets []string
|
||||
envs := env.List(EnvAuditWebhookEndpoint)
|
||||
for _, k := range envs {
|
||||
target := strings.TrimPrefix(k, EnvAuditWebhookEndpoint+config.Default)
|
||||
if target == EnvAuditWebhookEndpoint {
|
||||
target = config.Default
|
||||
}
|
||||
loggerAuditTargets = append(loggerAuditTargets, target)
|
||||
}
|
||||
|
||||
for _, target := range loggerAuditTargets {
|
||||
if v, ok := cfg.AuditWebhook[target]; ok && v.Enabled {
|
||||
// This target is already enabled using the
|
||||
// legacy environment variables, ignore.
|
||||
continue
|
||||
}
|
||||
enableEnv := EnvAuditWebhookEnable
|
||||
if target != config.Default {
|
||||
enableEnv = EnvAuditWebhookEnable + config.Default + target
|
||||
}
|
||||
enable, err := config.ParseBool(env.Get(enableEnv, ""))
|
||||
if err != nil || !enable {
|
||||
continue
|
||||
}
|
||||
endpointEnv := EnvAuditWebhookEndpoint
|
||||
if target != config.Default {
|
||||
endpointEnv = EnvAuditWebhookEndpoint + config.Default + target
|
||||
}
|
||||
authTokenEnv := EnvAuditWebhookAuthToken
|
||||
if target != config.Default {
|
||||
authTokenEnv = EnvAuditWebhookAuthToken + config.Default + target
|
||||
}
|
||||
clientCertEnv := EnvAuditWebhookClientCert
|
||||
if target != config.Default {
|
||||
clientCertEnv = EnvAuditWebhookClientCert + config.Default + target
|
||||
}
|
||||
clientKeyEnv := EnvAuditWebhookClientKey
|
||||
if target != config.Default {
|
||||
clientKeyEnv = EnvAuditWebhookClientKey + config.Default + target
|
||||
}
|
||||
err = config.EnsureCertAndKey(env.Get(clientCertEnv, ""), env.Get(clientKeyEnv, ""))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
queueSizeEnv := EnvAuditWebhookQueueSize
|
||||
if target != config.Default {
|
||||
queueSizeEnv = EnvAuditWebhookQueueSize + config.Default + target
|
||||
}
|
||||
queueSize, err := strconv.Atoi(env.Get(queueSizeEnv, "100000"))
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if queueSize <= 0 {
|
||||
return cfg, errors.New("invalid queue_size value")
|
||||
}
|
||||
cfg.AuditWebhook[target] = http.Config{
|
||||
Enabled: true,
|
||||
Endpoint: env.Get(endpointEnv, ""),
|
||||
AuthToken: env.Get(authTokenEnv, ""),
|
||||
ClientCert: env.Get(clientCertEnv, ""),
|
||||
ClientKey: env.Get(clientKeyEnv, ""),
|
||||
QueueSize: queueSize,
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// LookupConfigForSubSys - lookup logger config, override with ENVs if set, for the given sub-system
|
||||
func LookupConfigForSubSys(subSys string) (cfg Config, err error) {
|
||||
switch subSys {
|
||||
case config.LoggerWebhookSubSys:
|
||||
if cfg, err = lookupLoggerWebhookConfig(); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
case config.AuditWebhookSubSys:
|
||||
if cfg, err = lookupAuditWebhookConfig(); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// GetGlobalDeploymentID :
|
||||
func GetGlobalDeploymentID() string {
|
||||
if globalDeploymentID != "" {
|
||||
return globalDeploymentID
|
||||
}
|
||||
globalDeploymentID = env.Get(EnvGlobalDeploymentID, mustGetUUID())
|
||||
return globalDeploymentID
|
||||
}
|
||||
|
||||
// mustGetUUID - get a random UUID.
|
||||
func mustGetUUID() string {
|
||||
u, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
CriticalIf(GlobalContext, err)
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
80
pkg/logger/config/bool-flag.go
Normal file
80
pkg/logger/config/bool-flag.go
Normal file
@@ -0,0 +1,80 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BoolFlag - wrapper bool type.
|
||||
type BoolFlag bool
|
||||
|
||||
// String - returns string of BoolFlag.
|
||||
func (bf BoolFlag) String() string {
|
||||
if bf {
|
||||
return "on"
|
||||
}
|
||||
|
||||
return "off"
|
||||
}
|
||||
|
||||
// MarshalJSON - converts BoolFlag into JSON data.
|
||||
func (bf BoolFlag) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(bf.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON - parses given data into BoolFlag.
|
||||
func (bf *BoolFlag) UnmarshalJSON(data []byte) (err error) {
|
||||
var s string
|
||||
if err = json.Unmarshal(data, &s); err == nil {
|
||||
b := BoolFlag(true)
|
||||
if s == "" {
|
||||
// Empty string is treated as valid.
|
||||
*bf = b
|
||||
} else if b, err = ParseBoolFlag(s); err == nil {
|
||||
*bf = b
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ParseBool returns the boolean value represented by the string.
|
||||
func ParseBool(str string) (bool, error) {
|
||||
switch str {
|
||||
case "1", "t", "T", "true", "TRUE", "True", "on", "ON", "On":
|
||||
return true, nil
|
||||
case "0", "f", "F", "false", "FALSE", "False", "off", "OFF", "Off":
|
||||
return false, nil
|
||||
}
|
||||
if strings.EqualFold(str, "enabled") {
|
||||
return true, nil
|
||||
}
|
||||
if strings.EqualFold(str, "disabled") {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("ParseBool: parsing '%s': %w", str, strconv.ErrSyntax)
|
||||
}
|
||||
|
||||
// ParseBoolFlag - parses string into BoolFlag.
|
||||
func ParseBoolFlag(s string) (bf BoolFlag, err error) {
|
||||
b, err := ParseBool(s)
|
||||
return BoolFlag(b), err
|
||||
}
|
||||
126
pkg/logger/config/bool-flag_test.go
Normal file
126
pkg/logger/config/bool-flag_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package config
|
||||
|
||||
import "testing"
|
||||
|
||||
// Test BoolFlag.String()
|
||||
func TestBoolFlagString(t *testing.T) {
|
||||
var bf BoolFlag
|
||||
|
||||
testCases := []struct {
|
||||
flag BoolFlag
|
||||
expectedResult string
|
||||
}{
|
||||
{bf, "off"},
|
||||
{BoolFlag(true), "on"},
|
||||
{BoolFlag(false), "off"},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
str := testCase.flag.String()
|
||||
if testCase.expectedResult != str {
|
||||
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test BoolFlag.MarshalJSON()
|
||||
func TestBoolFlagMarshalJSON(t *testing.T) {
|
||||
var bf BoolFlag
|
||||
|
||||
testCases := []struct {
|
||||
flag BoolFlag
|
||||
expectedResult string
|
||||
}{
|
||||
{bf, `"off"`},
|
||||
{BoolFlag(true), `"on"`},
|
||||
{BoolFlag(false), `"off"`},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
data, _ := testCase.flag.MarshalJSON()
|
||||
if testCase.expectedResult != string(data) {
|
||||
t.Fatalf("expected: %v, got: %v", testCase.expectedResult, string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test BoolFlag.UnmarshalJSON()
|
||||
func TestBoolFlagUnmarshalJSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
data []byte
|
||||
expectedResult BoolFlag
|
||||
expectedErr bool
|
||||
}{
|
||||
{[]byte(`{}`), BoolFlag(false), true},
|
||||
{[]byte(`["on"]`), BoolFlag(false), true},
|
||||
{[]byte(`"junk"`), BoolFlag(false), true},
|
||||
{[]byte(`""`), BoolFlag(true), false},
|
||||
{[]byte(`"on"`), BoolFlag(true), false},
|
||||
{[]byte(`"off"`), BoolFlag(false), false},
|
||||
{[]byte(`"true"`), BoolFlag(true), false},
|
||||
{[]byte(`"false"`), BoolFlag(false), false},
|
||||
{[]byte(`"ON"`), BoolFlag(true), false},
|
||||
{[]byte(`"OFF"`), BoolFlag(false), false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
var flag BoolFlag
|
||||
err := (&flag).UnmarshalJSON(testCase.data)
|
||||
if !testCase.expectedErr && err != nil {
|
||||
t.Fatalf("error: expected = <nil>, got = %v", err)
|
||||
}
|
||||
if testCase.expectedErr && err == nil {
|
||||
t.Fatalf("error: expected error, got = <nil>")
|
||||
}
|
||||
if err == nil && testCase.expectedResult != flag {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, flag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test ParseBoolFlag()
|
||||
func TestParseBoolFlag(t *testing.T) {
|
||||
testCases := []struct {
|
||||
flagStr string
|
||||
expectedResult BoolFlag
|
||||
expectedErr bool
|
||||
}{
|
||||
{"", BoolFlag(false), true},
|
||||
{"junk", BoolFlag(false), true},
|
||||
{"true", BoolFlag(true), false},
|
||||
{"false", BoolFlag(false), false},
|
||||
{"ON", BoolFlag(true), false},
|
||||
{"OFF", BoolFlag(false), false},
|
||||
{"on", BoolFlag(true), false},
|
||||
{"off", BoolFlag(false), false},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
bf, err := ParseBoolFlag(testCase.flagStr)
|
||||
if !testCase.expectedErr && err != nil {
|
||||
t.Fatalf("error: expected = <nil>, got = %v", err)
|
||||
}
|
||||
if testCase.expectedErr && err == nil {
|
||||
t.Fatalf("error: expected error, got = <nil>")
|
||||
}
|
||||
if err == nil && testCase.expectedResult != bf {
|
||||
t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, bf)
|
||||
}
|
||||
}
|
||||
}
|
||||
30
pkg/logger/config/certs.go
Normal file
30
pkg/logger/config/certs.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// EnsureCertAndKey checks if both client certificate and key paths are provided
|
||||
func EnsureCertAndKey(clientCert, clientKey string) error {
|
||||
if (clientCert != "" && clientKey == "") ||
|
||||
(clientCert == "" && clientKey != "") {
|
||||
return errors.New("cert and key must be specified as a pair")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
34
pkg/logger/config/config.go
Normal file
34
pkg/logger/config/config.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
// Default keys
|
||||
const (
|
||||
Default = madmin.Default
|
||||
Enable = madmin.EnableKey
|
||||
License = "license" // Deprecated Dec 2021
|
||||
)
|
||||
|
||||
// Top level config constants.
|
||||
const (
|
||||
LoggerWebhookSubSys = "logger_webhook"
|
||||
AuditWebhookSubSys = "audit_webhook"
|
||||
)
|
||||
223
pkg/logger/console.go
Normal file
223
pkg/logger/console.go
Normal file
@@ -0,0 +1,223 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger/color"
|
||||
"github.com/minio/console/pkg/logger/message/log"
|
||||
c "github.com/minio/pkg/console"
|
||||
)
|
||||
|
||||
// ConsoleLoggerTgt is a stringified value to represent console logging
|
||||
const ConsoleLoggerTgt = "console+http"
|
||||
|
||||
// Logger interface describes the methods that need to be implemented to satisfy the interface requirements.
|
||||
type Logger interface {
|
||||
json(msg string, args ...interface{})
|
||||
quiet(msg string, args ...interface{})
|
||||
pretty(msg string, args ...interface{})
|
||||
}
|
||||
|
||||
func consoleLog(console Logger, msg string, args ...interface{}) {
|
||||
switch {
|
||||
case jsonFlag:
|
||||
// Strip escape control characters from json message
|
||||
msg = ansiRE.ReplaceAllLiteralString(msg, "")
|
||||
console.json(msg, args...)
|
||||
case quietFlag:
|
||||
console.quiet(msg+"\n", args...)
|
||||
default:
|
||||
console.pretty(msg+"\n", args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal prints only fatal errors message with no stack trace
|
||||
// it will be called for input validation failures
|
||||
func Fatal(err error, msg string, data ...interface{}) {
|
||||
fatal(err, msg, data...)
|
||||
}
|
||||
|
||||
func fatal(err error, msg string, data ...interface{}) {
|
||||
var errMsg string
|
||||
if msg != "" {
|
||||
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
|
||||
} else {
|
||||
errMsg = err.Error()
|
||||
}
|
||||
consoleLog(fatalMessage, errMsg)
|
||||
}
|
||||
|
||||
var fatalMessage fatalMsg
|
||||
|
||||
type fatalMsg struct{}
|
||||
|
||||
func (f fatalMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: FatalLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{Message: message, Source: []string{getSource(6)}},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (f fatalMsg) quiet(msg string, args ...interface{}) {
|
||||
f.pretty(msg, args...)
|
||||
}
|
||||
|
||||
var (
|
||||
logTag = "ERROR"
|
||||
logBanner = color.BgRed(color.FgWhite(color.Bold(logTag))) + " "
|
||||
emptyBanner = color.BgRed(strings.Repeat(" ", len(logTag))) + " "
|
||||
bannerWidth = len(logTag) + 1
|
||||
)
|
||||
|
||||
func (f fatalMsg) pretty(msg string, args ...interface{}) {
|
||||
// Build the passed errors message
|
||||
errMsg := fmt.Sprintf(msg, args...)
|
||||
|
||||
tagPrinted := false
|
||||
|
||||
// Print the errors message: the following code takes care
|
||||
// of splitting errors text and always pretty printing the
|
||||
// red banner along with the errors message. Since the errors
|
||||
// message itself contains some colored text, we needed
|
||||
// to use some ANSI control escapes to cursor color state
|
||||
// and freely move in the screen.
|
||||
for _, line := range strings.Split(errMsg, "\n") {
|
||||
if len(line) == 0 {
|
||||
// No more text to print, just quit.
|
||||
break
|
||||
}
|
||||
|
||||
for {
|
||||
// Save the attributes of the current cursor helps
|
||||
// us save the text color of the passed errors message
|
||||
ansiSaveAttributes()
|
||||
// Print banner with or without the log tag
|
||||
if !tagPrinted {
|
||||
c.Print(logBanner)
|
||||
tagPrinted = true
|
||||
} else {
|
||||
c.Print(emptyBanner)
|
||||
}
|
||||
// Restore the text color of the errors message
|
||||
ansiRestoreAttributes()
|
||||
ansiMoveRight(bannerWidth)
|
||||
// Continue errors message printing
|
||||
c.Println(line)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Exit because this is a fatal errors message
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type infoMsg struct{}
|
||||
|
||||
var info infoMsg
|
||||
|
||||
func (i infoMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: InformationLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
}
|
||||
|
||||
func (i infoMsg) quiet(msg string, args ...interface{}) {
|
||||
}
|
||||
|
||||
func (i infoMsg) pretty(msg string, args ...interface{}) {
|
||||
if msg == "" {
|
||||
c.Println(args...)
|
||||
}
|
||||
c.Printf(msg, args...)
|
||||
}
|
||||
|
||||
type errorMsg struct{}
|
||||
|
||||
var errorm errorMsg
|
||||
|
||||
func (i errorMsg) json(msg string, args ...interface{}) {
|
||||
var message string
|
||||
if msg != "" {
|
||||
message = fmt.Sprintf(msg, args...)
|
||||
} else {
|
||||
message = fmt.Sprint(args...)
|
||||
}
|
||||
logJSON, err := json.Marshal(&log.Entry{
|
||||
Level: ErrorLvl.String(),
|
||||
Message: message,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{Message: message, Source: []string{getSource(6)}},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(string(logJSON))
|
||||
}
|
||||
|
||||
func (i errorMsg) quiet(msg string, args ...interface{}) {
|
||||
i.pretty(msg, args...)
|
||||
}
|
||||
|
||||
func (i errorMsg) pretty(msg string, args ...interface{}) {
|
||||
if msg == "" {
|
||||
c.Println(args...)
|
||||
}
|
||||
c.Printf(msg, args...)
|
||||
c.Printf("\n")
|
||||
}
|
||||
|
||||
// Error :
|
||||
func Error(msg string, data ...interface{}) {
|
||||
consoleLog(errorm, msg, data...)
|
||||
}
|
||||
|
||||
// Info :
|
||||
func Info(msg string, data ...interface{}) {
|
||||
consoleLog(info, msg, data...)
|
||||
}
|
||||
56
pkg/logger/const.go
Normal file
56
pkg/logger/const.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
)
|
||||
|
||||
// Audit/Logger constants
|
||||
const (
|
||||
EnvLoggerJSONEnable = "CONSOLE_LOGGER_JSON_ENABLE"
|
||||
EnvLoggerAnonymousEnable = "CONSOLE_LOGGER_ANONYMOUS_ENABLE"
|
||||
EnvLoggerQuietEnable = "CONSOLE_LOGGER_QUIET_ENABLE"
|
||||
|
||||
EnvGlobalDeploymentID = "CONSOLE_GLOBAL_DEPLOYMENT_ID"
|
||||
EnvLoggerWebhookEnable = "CONSOLE_LOGGER_WEBHOOK_ENABLE"
|
||||
EnvLoggerWebhookEndpoint = "CONSOLE_LOGGER_WEBHOOK_ENDPOINT"
|
||||
EnvLoggerWebhookAuthToken = "CONSOLE_LOGGER_WEBHOOK_AUTH_TOKEN"
|
||||
EnvLoggerWebhookClientCert = "CONSOLE_LOGGER_WEBHOOK_CLIENT_CERT"
|
||||
EnvLoggerWebhookClientKey = "CONSOLE_LOGGER_WEBHOOK_CLIENT_KEY"
|
||||
EnvLoggerWebhookQueueSize = "CONSOLE_LOGGER_WEBHOOK_QUEUE_SIZE"
|
||||
|
||||
EnvAuditWebhookEnable = "CONSOLE_AUDIT_WEBHOOK_ENABLE"
|
||||
EnvAuditWebhookEndpoint = "CONSOLE_AUDIT_WEBHOOK_ENDPOINT"
|
||||
EnvAuditWebhookAuthToken = "CONSOLE_AUDIT_WEBHOOK_AUTH_TOKEN"
|
||||
EnvAuditWebhookClientCert = "CONSOLE_AUDIT_WEBHOOK_CLIENT_CERT"
|
||||
EnvAuditWebhookClientKey = "CONSOLE_AUDIT_WEBHOOK_CLIENT_KEY"
|
||||
EnvAuditWebhookQueueSize = "CONSOLE_AUDIT_WEBHOOK_QUEUE_SIZE"
|
||||
)
|
||||
|
||||
// Config console and http logger targets
|
||||
type Config struct {
|
||||
HTTP map[string]http.Config `json:"http"`
|
||||
AuditWebhook map[string]http.Config `json:"audit"`
|
||||
}
|
||||
|
||||
var (
|
||||
globalDeploymentID string
|
||||
GlobalContext context.Context
|
||||
)
|
||||
480
pkg/logger/logger.go
Normal file
480
pkg/logger/logger.go
Normal file
@@ -0,0 +1,480 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/minio/pkg/env"
|
||||
|
||||
"github.com/minio/console/pkg"
|
||||
"github.com/minio/pkg/certs"
|
||||
|
||||
"github.com/minio/console/pkg/logger/config"
|
||||
"github.com/minio/console/pkg/logger/message/log"
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/minio/minio-go/v7/pkg/set"
|
||||
)
|
||||
|
||||
// HighwayHash key for logging in anonymous mode
|
||||
var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0")
|
||||
|
||||
// Disable disables all logging, false by default. (used for "go test")
|
||||
var Disable = false
|
||||
|
||||
// Level type
|
||||
type Level int8
|
||||
|
||||
// Enumerated level types
|
||||
const (
|
||||
InformationLvl Level = iota + 1
|
||||
ErrorLvl
|
||||
FatalLvl
|
||||
)
|
||||
|
||||
var trimStrings []string
|
||||
|
||||
// TimeFormat - logging time format.
|
||||
const TimeFormat string = "15:04:05 MST 01/02/2006"
|
||||
|
||||
var matchingFuncNames = [...]string{
|
||||
"http.HandlerFunc.ServeHTTP",
|
||||
"cmd.serverMain",
|
||||
"cmd.StartGateway",
|
||||
// add more here ..
|
||||
}
|
||||
|
||||
func (level Level) String() string {
|
||||
var lvlStr string
|
||||
switch level {
|
||||
case InformationLvl:
|
||||
lvlStr = "INFO"
|
||||
case ErrorLvl:
|
||||
lvlStr = "ERROR"
|
||||
case FatalLvl:
|
||||
lvlStr = "FATAL"
|
||||
}
|
||||
return lvlStr
|
||||
}
|
||||
|
||||
// quietFlag: Hide startup messages if enabled
|
||||
// jsonFlag: Display in JSON format, if enabled
|
||||
var (
|
||||
quietFlag, jsonFlag, anonFlag bool
|
||||
// Custom function to format errors
|
||||
errorFmtFunc func(string, error, bool) string
|
||||
)
|
||||
|
||||
// EnableQuiet - turns quiet option on.
|
||||
func EnableQuiet() {
|
||||
quietFlag = true
|
||||
}
|
||||
|
||||
// EnableJSON - outputs logs in json format.
|
||||
func EnableJSON() {
|
||||
jsonFlag = true
|
||||
quietFlag = true
|
||||
}
|
||||
|
||||
// EnableAnonymous - turns anonymous flag
|
||||
// to avoid printing sensitive information.
|
||||
func EnableAnonymous() {
|
||||
anonFlag = true
|
||||
}
|
||||
|
||||
// IsAnonymous - returns true if anonFlag is true
|
||||
func IsAnonymous() bool {
|
||||
return anonFlag
|
||||
}
|
||||
|
||||
// IsJSON - returns true if jsonFlag is true
|
||||
func IsJSON() bool {
|
||||
return jsonFlag
|
||||
}
|
||||
|
||||
// IsQuiet - returns true if quietFlag is true
|
||||
func IsQuiet() bool {
|
||||
return quietFlag
|
||||
}
|
||||
|
||||
// RegisterError registers the specified rendering function. This latter
|
||||
// will be called for a pretty rendering of fatal errors.
|
||||
func RegisterError(f func(string, error, bool) string) {
|
||||
errorFmtFunc = f
|
||||
}
|
||||
|
||||
// Remove any duplicates and return unique entries.
|
||||
func uniqueEntries(paths []string) []string {
|
||||
m := make(set.StringSet)
|
||||
for _, p := range paths {
|
||||
if !m.Contains(p) {
|
||||
m.Add(p)
|
||||
}
|
||||
}
|
||||
return m.ToSlice()
|
||||
}
|
||||
|
||||
// Init sets the trimStrings to possible GOPATHs
|
||||
// and GOROOT directories. Also append github.com/minio/minio
|
||||
// This is done to clean up the filename, when stack trace is
|
||||
// displayed when an errors happens.
|
||||
func Init(goPath string, goRoot string) {
|
||||
var goPathList []string
|
||||
var goRootList []string
|
||||
var defaultgoPathList []string
|
||||
var defaultgoRootList []string
|
||||
pathSeperator := ":"
|
||||
// Add all possible GOPATH paths into trimStrings
|
||||
// Split GOPATH depending on the OS type
|
||||
if runtime.GOOS == "windows" {
|
||||
pathSeperator = ";"
|
||||
}
|
||||
|
||||
goPathList = strings.Split(goPath, pathSeperator)
|
||||
goRootList = strings.Split(goRoot, pathSeperator)
|
||||
defaultgoPathList = strings.Split(build.Default.GOPATH, pathSeperator)
|
||||
defaultgoRootList = strings.Split(build.Default.GOROOT, pathSeperator)
|
||||
|
||||
// Add trim string "{GOROOT}/src/" into trimStrings
|
||||
trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}
|
||||
|
||||
// Add all possible path from GOPATH=path1:path2...:pathN
|
||||
// as "{path#}/src/" into trimStrings
|
||||
for _, goPathString := range goPathList {
|
||||
trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, goRootString := range goRootList {
|
||||
trimStrings = append(trimStrings, filepath.Join(goRootString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, defaultgoPathString := range defaultgoPathList {
|
||||
trimStrings = append(trimStrings, filepath.Join(defaultgoPathString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
for _, defaultgoRootString := range defaultgoRootList {
|
||||
trimStrings = append(trimStrings, filepath.Join(defaultgoRootString, "src")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
// Remove duplicate entries.
|
||||
trimStrings = uniqueEntries(trimStrings)
|
||||
|
||||
// Add "github.com/minio/minio" as the last to cover
|
||||
// paths like "{GOROOT}/src/github.com/minio/minio"
|
||||
// and "{GOPATH}/src/github.com/minio/minio"
|
||||
trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
|
||||
}
|
||||
|
||||
func trimTrace(f string) string {
|
||||
for _, trimString := range trimStrings {
|
||||
f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
|
||||
}
|
||||
return filepath.FromSlash(f)
|
||||
}
|
||||
|
||||
func getSource(level int) string {
|
||||
pc, file, lineNumber, ok := runtime.Caller(level)
|
||||
if ok {
|
||||
// Clean up the common prefixes
|
||||
file = trimTrace(file)
|
||||
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
||||
return fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// getTrace method - creates and returns stack trace
|
||||
func getTrace(traceLevel int) []string {
|
||||
var trace []string
|
||||
pc, file, lineNumber, ok := runtime.Caller(traceLevel)
|
||||
|
||||
for ok && file != "" {
|
||||
// Clean up the common prefixes
|
||||
file = trimTrace(file)
|
||||
// Get the function name
|
||||
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
||||
// Skip duplicate traces that start with file name, "<autogenerated>"
|
||||
// and also skip traces with function name that starts with "runtime."
|
||||
if !strings.HasPrefix(file, "<autogenerated>") &&
|
||||
!strings.HasPrefix(funcName, "runtime.") {
|
||||
// Form and append a line of stack trace into a
|
||||
// collection, 'trace', to build full stack trace
|
||||
trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))
|
||||
|
||||
// Ignore trace logs beyond the following conditions
|
||||
for _, name := range matchingFuncNames {
|
||||
if funcName == name {
|
||||
return trace
|
||||
}
|
||||
}
|
||||
}
|
||||
traceLevel++
|
||||
// Read stack trace information from PC
|
||||
pc, file, lineNumber, ok = runtime.Caller(traceLevel)
|
||||
}
|
||||
return trace
|
||||
}
|
||||
|
||||
// Return the highway hash of the passed string
|
||||
func hashString(input string) string {
|
||||
hh, _ := highwayhash.New(magicHighwayHash256Key)
|
||||
hh.Write([]byte(input))
|
||||
return hex.EncodeToString(hh.Sum(nil))
|
||||
}
|
||||
|
||||
// Kind specifies the kind of errors log
|
||||
type Kind string
|
||||
|
||||
const (
|
||||
// Minio errors
|
||||
Minio Kind = "CONSOLE"
|
||||
// Application errors
|
||||
Application Kind = "APPLICATION"
|
||||
// All errors
|
||||
All Kind = "ALL"
|
||||
)
|
||||
|
||||
// LogAlwaysIf prints a detailed errors message during
|
||||
// the execution of the server.
|
||||
func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
logIf(ctx, err, errKind...)
|
||||
}
|
||||
|
||||
// LogIf prints a detailed errors message during
|
||||
// the execution of the server
|
||||
func LogIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
logIf(ctx, err, errKind...)
|
||||
}
|
||||
|
||||
// logIf prints a detailed errors message during
|
||||
// the execution of the server.
|
||||
func logIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if Disable {
|
||||
return
|
||||
}
|
||||
logKind := string(Minio)
|
||||
if len(errKind) > 0 {
|
||||
if ek, ok := errKind[0].(Kind); ok {
|
||||
logKind = string(ek)
|
||||
}
|
||||
}
|
||||
req := GetReqInfo(ctx)
|
||||
|
||||
if req == nil {
|
||||
req = &ReqInfo{API: "SYSTEM"}
|
||||
}
|
||||
|
||||
kv := req.GetTags()
|
||||
tags := make(map[string]interface{}, len(kv))
|
||||
for _, entry := range kv {
|
||||
tags[entry.Key] = entry.Val
|
||||
}
|
||||
|
||||
// Get full stack trace
|
||||
trace := getTrace(3)
|
||||
|
||||
// Get the cause for the Error
|
||||
message := fmt.Sprintf("%v (%T)", err, err)
|
||||
if req.DeploymentID == "" {
|
||||
req.DeploymentID = GetGlobalDeploymentID()
|
||||
}
|
||||
|
||||
entry := log.Entry{
|
||||
DeploymentID: req.DeploymentID,
|
||||
Level: ErrorLvl.String(),
|
||||
LogKind: logKind,
|
||||
RemoteHost: req.RemoteHost,
|
||||
Host: req.Host,
|
||||
RequestID: req.RequestID,
|
||||
SessionID: req.SessionID,
|
||||
UserAgent: req.UserAgent,
|
||||
Time: time.Now().UTC(),
|
||||
Trace: &log.Trace{
|
||||
Message: message,
|
||||
Source: trace,
|
||||
Variables: tags,
|
||||
},
|
||||
}
|
||||
|
||||
if anonFlag {
|
||||
entry.SessionID = hashString(entry.SessionID)
|
||||
entry.RemoteHost = hashString(entry.RemoteHost)
|
||||
entry.Trace.Message = reflect.TypeOf(err).String()
|
||||
entry.Trace.Variables = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// Iterate over all logger targets to send the log entry
|
||||
for _, t := range SystemTargets() {
|
||||
if err := t.Send(entry, entry.LogKind); err != nil {
|
||||
if consoleTgt != nil {
|
||||
entry.Trace.Message = fmt.Sprintf("event(%#v) was not sent to Logger target (%#v): %#v", entry, t, err)
|
||||
consoleTgt.Send(entry, entry.LogKind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrCritical is the value panic'd whenever CriticalIf is called.
|
||||
var ErrCritical struct{}
|
||||
|
||||
// CriticalIf logs the provided errors on the console. It fails the
|
||||
// current go-routine by causing a `panic(ErrCritical)`.
|
||||
func CriticalIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
if err != nil {
|
||||
LogIf(ctx, err, errKind...)
|
||||
panic(ErrCritical)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalIf is similar to Fatal() but it ignores passed nil errors
|
||||
func FatalIf(err error, msg string, data ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
fatal(err, msg, data...)
|
||||
}
|
||||
|
||||
func applyDynamicConfigForSubSys(ctx context.Context, transport *http.Transport, subSys string) error {
|
||||
switch subSys {
|
||||
case config.LoggerWebhookSubSys:
|
||||
loggerCfg, err := LookupConfigForSubSys(config.LoggerWebhookSubSys)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to load logger webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
userAgent := getUserAgent()
|
||||
for n, l := range loggerCfg.HTTP {
|
||||
if l.Enabled {
|
||||
l.LogOnce = LogOnceIf
|
||||
l.UserAgent = userAgent
|
||||
l.Transport = NewHTTPTransportWithClientCerts(transport, l.ClientCert, l.ClientKey)
|
||||
loggerCfg.HTTP[n] = l
|
||||
}
|
||||
}
|
||||
err = UpdateSystemTargets(loggerCfg)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to update logger webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
case config.AuditWebhookSubSys:
|
||||
loggerCfg, err := LookupConfigForSubSys(config.AuditWebhookSubSys)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("unable to load audit webhook config: %w", err))
|
||||
return err
|
||||
}
|
||||
userAgent := getUserAgent()
|
||||
for n, l := range loggerCfg.AuditWebhook {
|
||||
if l.Enabled {
|
||||
l.LogOnce = LogOnceIf
|
||||
l.UserAgent = userAgent
|
||||
l.Transport = NewHTTPTransportWithClientCerts(transport, l.ClientCert, l.ClientKey)
|
||||
loggerCfg.AuditWebhook[n] = l
|
||||
}
|
||||
}
|
||||
|
||||
err = UpdateAuditWebhookTargets(loggerCfg)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("Unable to update audit webhook targets: %w", err))
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitializeLogger :
|
||||
func InitializeLogger(ctx context.Context, transport *http.Transport) error {
|
||||
err := applyDynamicConfigForSubSys(ctx, transport, config.LoggerWebhookSubSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = applyDynamicConfigForSubSys(ctx, transport, config.AuditWebhookSubSys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerJSONEnable, "")); enable {
|
||||
EnableJSON()
|
||||
}
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerAnonymousEnable, "")); enable {
|
||||
EnableAnonymous()
|
||||
}
|
||||
if enable, _ := config.ParseBool(env.Get(EnvLoggerQuietEnable, "")); enable {
|
||||
EnableQuiet()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUserAgent() string {
|
||||
userAgentParts := []string{}
|
||||
// Helper function to concisely append a pair of strings to a
|
||||
// the user-agent slice.
|
||||
uaAppend := func(p, q string) {
|
||||
userAgentParts = append(userAgentParts, p, q)
|
||||
}
|
||||
uaAppend("Console (", runtime.GOOS)
|
||||
uaAppend("; ", runtime.GOARCH)
|
||||
uaAppend(") Console/", pkg.Version)
|
||||
uaAppend(" Console/", pkg.ReleaseTag)
|
||||
uaAppend(" Console/", pkg.CommitID)
|
||||
|
||||
return strings.Join(userAgentParts, "")
|
||||
}
|
||||
|
||||
// NewHTTPTransportWithClientCerts returns a new http configuration
|
||||
// used while communicating with the cloud backends.
|
||||
func NewHTTPTransportWithClientCerts(parentTransport *http.Transport, clientCert, clientKey string) *http.Transport {
|
||||
transport := parentTransport.Clone()
|
||||
if clientCert != "" && clientKey != "" {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
c, err := certs.NewManager(ctx, clientCert, clientKey, tls.LoadX509KeyPair)
|
||||
if err != nil {
|
||||
LogIf(ctx, fmt.Errorf("failed to load client key and cert, please check your endpoint configuration: %s",
|
||||
err.Error()))
|
||||
}
|
||||
if c != nil {
|
||||
c.UpdateReloadDuration(10 * time.Second)
|
||||
c.ReloadOnSignal(syscall.SIGHUP) // allow reloads upon SIGHUP
|
||||
transport.TLSClientConfig.GetClientCertificate = c.GetClientCertificate
|
||||
}
|
||||
}
|
||||
return transport
|
||||
}
|
||||
243
pkg/logger/logger_test.go
Normal file
243
pkg/logger/logger_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testServer(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func TestInitializeLogger(t *testing.T) {
|
||||
testServerWillStart := make(chan interface{})
|
||||
http.HandleFunc("/", testServer)
|
||||
go func() {
|
||||
close(testServerWillStart)
|
||||
err := http.ListenAndServe("127.0.0.1:1337", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}()
|
||||
<-testServerWillStart
|
||||
|
||||
loggerWebhookEnable := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEnable)
|
||||
loggerWebhookEndpoint := fmt.Sprintf("%s_TEST", EnvLoggerWebhookEndpoint)
|
||||
loggerWebhookAuthToken := fmt.Sprintf("%s_TEST", EnvLoggerWebhookAuthToken)
|
||||
loggerWebhookClientCert := fmt.Sprintf("%s_TEST", EnvLoggerWebhookClientCert)
|
||||
loggerWebhookClientKey := fmt.Sprintf("%s_TEST", EnvLoggerWebhookClientKey)
|
||||
loggerWebhookQueueSize := fmt.Sprintf("%s_TEST", EnvLoggerWebhookQueueSize)
|
||||
|
||||
auditWebhookEnable := fmt.Sprintf("%s_TEST", EnvAuditWebhookEnable)
|
||||
auditWebhookEndpoint := fmt.Sprintf("%s_TEST", EnvAuditWebhookEndpoint)
|
||||
auditWebhookAuthToken := fmt.Sprintf("%s_TEST", EnvAuditWebhookAuthToken)
|
||||
auditWebhookClientCert := fmt.Sprintf("%s_TEST", EnvAuditWebhookClientCert)
|
||||
auditWebhookClientKey := fmt.Sprintf("%s_TEST", EnvAuditWebhookClientKey)
|
||||
auditWebhookQueueSize := fmt.Sprintf("%s_TEST", EnvAuditWebhookQueueSize)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
transport *http.Transport
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantErr bool
|
||||
setEnvVars func()
|
||||
unsetEnvVars func()
|
||||
}{
|
||||
{
|
||||
name: "logger or auditlog is not enabled",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logger webhook initialized correctly",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(loggerWebhookEnable, "on")
|
||||
os.Setenv(loggerWebhookEndpoint, "http://127.0.0.1:1337/logger")
|
||||
os.Setenv(loggerWebhookAuthToken, "test")
|
||||
os.Setenv(loggerWebhookClientCert, "")
|
||||
os.Setenv(loggerWebhookClientKey, "")
|
||||
os.Setenv(loggerWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(loggerWebhookEnable)
|
||||
os.Unsetenv(loggerWebhookEndpoint)
|
||||
os.Unsetenv(loggerWebhookAuthToken)
|
||||
os.Unsetenv(loggerWebhookClientCert)
|
||||
os.Unsetenv(loggerWebhookClientKey)
|
||||
os.Unsetenv(loggerWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logger webhook failed to initialize",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: true,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(loggerWebhookEnable, "on")
|
||||
os.Setenv(loggerWebhookEndpoint, "https://aklsjdakljdjkalsd.com")
|
||||
os.Setenv(loggerWebhookAuthToken, "test")
|
||||
os.Setenv(loggerWebhookClientCert, "")
|
||||
os.Setenv(loggerWebhookClientKey, "")
|
||||
os.Setenv(loggerWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(loggerWebhookEnable)
|
||||
os.Unsetenv(loggerWebhookEndpoint)
|
||||
os.Unsetenv(loggerWebhookAuthToken)
|
||||
os.Unsetenv(loggerWebhookClientCert)
|
||||
os.Unsetenv(loggerWebhookClientKey)
|
||||
os.Unsetenv(loggerWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "auditlog webhook initialized correctly",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: false,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(auditWebhookEnable, "on")
|
||||
os.Setenv(auditWebhookEndpoint, "http://127.0.0.1:1337/audit")
|
||||
os.Setenv(auditWebhookAuthToken, "test")
|
||||
os.Setenv(auditWebhookClientCert, "")
|
||||
os.Setenv(auditWebhookClientKey, "")
|
||||
os.Setenv(auditWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(auditWebhookEnable)
|
||||
os.Unsetenv(auditWebhookEndpoint)
|
||||
os.Unsetenv(auditWebhookAuthToken)
|
||||
os.Unsetenv(auditWebhookClientCert)
|
||||
os.Unsetenv(auditWebhookClientKey)
|
||||
os.Unsetenv(auditWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "auditlog webhook failed to initialize",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
transport: http.DefaultTransport.(*http.Transport).Clone(),
|
||||
},
|
||||
wantErr: true,
|
||||
setEnvVars: func() {
|
||||
os.Setenv(auditWebhookEnable, "on")
|
||||
os.Setenv(auditWebhookEndpoint, "https://aklsjdakljdjkalsd.com")
|
||||
os.Setenv(auditWebhookAuthToken, "test")
|
||||
os.Setenv(auditWebhookClientCert, "")
|
||||
os.Setenv(auditWebhookClientKey, "")
|
||||
os.Setenv(auditWebhookQueueSize, "1000")
|
||||
},
|
||||
unsetEnvVars: func() {
|
||||
os.Unsetenv(auditWebhookEnable)
|
||||
os.Unsetenv(auditWebhookEndpoint)
|
||||
os.Unsetenv(auditWebhookAuthToken)
|
||||
os.Unsetenv(auditWebhookClientCert)
|
||||
os.Unsetenv(auditWebhookClientKey)
|
||||
os.Unsetenv(auditWebhookQueueSize)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.setEnvVars != nil {
|
||||
tt.setEnvVars()
|
||||
}
|
||||
if err := InitializeLogger(tt.args.ctx, tt.args.transport); (err != nil) != tt.wantErr {
|
||||
t.Errorf("InitializeLogger() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if tt.unsetEnvVars != nil {
|
||||
tt.unsetEnvVars()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable json",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableJSON()
|
||||
if !IsJSON() {
|
||||
t.Errorf("EnableJSON() = %v, want %v", IsJSON(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableQuiet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable quiet",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableQuiet()
|
||||
if !IsQuiet() {
|
||||
t.Errorf("EnableQuiet() = %v, want %v", IsQuiet(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableAnonymous(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "enable anonymous",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
EnableAnonymous()
|
||||
if !IsAnonymous() {
|
||||
t.Errorf("EnableAnonymous() = %v, want %v", IsAnonymous(), true)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
92
pkg/logger/logonce.go
Normal file
92
pkg/logger/logonce.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Holds a map of recently logged errors.
|
||||
type logOnceType struct {
|
||||
IDMap map[interface{}]error
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// One log message per errors.
|
||||
func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
l.Lock()
|
||||
shouldLog := false
|
||||
prevErr := l.IDMap[id]
|
||||
if prevErr == nil {
|
||||
l.IDMap[id] = err
|
||||
shouldLog = true
|
||||
} else if prevErr.Error() != err.Error() {
|
||||
l.IDMap[id] = err
|
||||
shouldLog = true
|
||||
}
|
||||
l.Unlock()
|
||||
|
||||
if shouldLog {
|
||||
LogIf(ctx, err, errKind...)
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup the map every 30 minutes so that the log message is printed again for the user to notice.
|
||||
func (l *logOnceType) cleanupRoutine() {
|
||||
for {
|
||||
l.Lock()
|
||||
l.IDMap = make(map[interface{}]error)
|
||||
l.Unlock()
|
||||
|
||||
time.Sleep(30 * time.Minute)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns logOnceType
|
||||
func newLogOnceType() *logOnceType {
|
||||
l := &logOnceType{IDMap: make(map[interface{}]error)}
|
||||
go l.cleanupRoutine()
|
||||
return l
|
||||
}
|
||||
|
||||
var logOnce = newLogOnceType()
|
||||
|
||||
// LogOnceIf - Logs notification errors - once per errors.
|
||||
// id is a unique identifier for related log messages, refer to cmd/notification.go
|
||||
// on how it is used.
|
||||
func LogOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
return
|
||||
}
|
||||
|
||||
if err.Error() == http.ErrServerClosed.Error() || err.Error() == "disk not found" {
|
||||
return
|
||||
}
|
||||
|
||||
logOnce.logOnceIf(ctx, err, id, errKind...)
|
||||
}
|
||||
130
pkg/logger/message/audit/entry.go
Normal file
130
pkg/logger/message/audit/entry.go
Normal file
@@ -0,0 +1,130 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
)
|
||||
|
||||
// Version - represents the current version of audit log structure.
|
||||
const Version = "1"
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string `json:"objectName"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
}
|
||||
|
||||
// Entry - audit entry logs.
|
||||
type Entry struct {
|
||||
Version string `json:"version"`
|
||||
DeploymentID string `json:"deploymentid,omitempty"`
|
||||
Time time.Time `json:"time"`
|
||||
Trigger string `json:"trigger"`
|
||||
API struct {
|
||||
Path string `json:"path,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Method string `json:"method"`
|
||||
StatusCode int `json:"statusCode,omitempty"`
|
||||
InputBytes int64 `json:"rx"`
|
||||
OutputBytes int64 `json:"tx"`
|
||||
TimeToFirstByte string `json:"timeToFirstByte,omitempty"`
|
||||
TimeToResponse string `json:"timeToResponse,omitempty"`
|
||||
} `json:"api"`
|
||||
RemoteHost string `json:"remotehost,omitempty"`
|
||||
RequestID string `json:"requestID,omitempty"`
|
||||
SessionID string `json:"sessionID,omitempty"`
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
ReqClaims map[string]interface{} `json:"requestClaims,omitempty"`
|
||||
ReqQuery map[string]string `json:"requestQuery,omitempty"`
|
||||
ReqHeader map[string]string `json:"requestHeader,omitempty"`
|
||||
RespHeader map[string]string `json:"responseHeader,omitempty"`
|
||||
Tags map[string]interface{} `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
// NewEntry - constructs an audit entry object with some fields filled
|
||||
func NewEntry(deploymentID string) Entry {
|
||||
return Entry{
|
||||
Version: Version,
|
||||
DeploymentID: deploymentID,
|
||||
Time: time.Now().UTC(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToEntry - constructs an audit entry from a http request
|
||||
func ToEntry(w http.ResponseWriter, r *http.Request, reqClaims map[string]interface{}, deploymentID string) Entry {
|
||||
entry := NewEntry(deploymentID)
|
||||
|
||||
entry.RemoteHost = r.RemoteAddr
|
||||
entry.UserAgent = r.UserAgent()
|
||||
entry.ReqClaims = reqClaims
|
||||
|
||||
q := r.URL.Query()
|
||||
reqQuery := make(map[string]string, len(q))
|
||||
for k, v := range q {
|
||||
reqQuery[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.ReqQuery = reqQuery
|
||||
|
||||
reqHeader := make(map[string]string, len(r.Header))
|
||||
for k, v := range r.Header {
|
||||
reqHeader[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.ReqHeader = reqHeader
|
||||
|
||||
wh := w.Header()
|
||||
|
||||
var requestID interface{}
|
||||
requestID = r.Context().Value(utils.ContextRequestID)
|
||||
if requestID == nil {
|
||||
requestID, _ = utils.NewUUID()
|
||||
}
|
||||
entry.RequestID = requestID.(string)
|
||||
|
||||
if val := r.Context().Value(utils.ContextRequestUserID); val != nil {
|
||||
sessionID := val.(string)
|
||||
if os.Getenv("CONSOLE_OPERATOR_MODE") != "" && os.Getenv("CONSOLE_OPERATOR_MODE") == "on" {
|
||||
claims := jwt.MapClaims{}
|
||||
_, _ = jwt.ParseWithClaims(sessionID, claims, nil)
|
||||
if sub, ok := claims["sub"]; ok {
|
||||
sessionID = sub.(string)
|
||||
}
|
||||
}
|
||||
entry.SessionID = sessionID
|
||||
}
|
||||
|
||||
respHeader := make(map[string]string, len(wh))
|
||||
for k, v := range wh {
|
||||
respHeader[k] = strings.Join(v, ",")
|
||||
}
|
||||
entry.RespHeader = respHeader
|
||||
|
||||
if etag := respHeader[xhttp.ETag]; etag != "" {
|
||||
respHeader[xhttp.ETag] = strings.Trim(etag, `"`)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
126
pkg/logger/message/audit/entry_test.go
Normal file
126
pkg/logger/message/audit/entry_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package audit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
func TestNewEntry(t *testing.T) {
|
||||
type args struct {
|
||||
deploymentID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Entry
|
||||
}{
|
||||
{
|
||||
name: "constructs an audit entry object with some fields filled",
|
||||
args: args{
|
||||
deploymentID: "1",
|
||||
},
|
||||
want: Entry{
|
||||
Version: Version,
|
||||
DeploymentID: "1",
|
||||
Time: time.Now().UTC(),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := NewEntry(tt.args.deploymentID); got.DeploymentID != tt.want.DeploymentID {
|
||||
t.Errorf("NewEntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToEntry(t *testing.T) {
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/tenants?test=xyz", nil)
|
||||
req.Header.Set("Authorization", "xyz")
|
||||
req.Header.Set("ETag", "\"ABCDE\"")
|
||||
|
||||
// applying context information
|
||||
ctx := context.WithValue(req.Context(), utils.ContextRequestUserID, "eyJhbGciOiJSUzI1NiIsImtpZCI6Ing5cS0wSkEwQzFMWDJlRlR3dHo2b0t0NVNnRzJad0llMGVNczMxbjU0b2sifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJtaW5pby1vcGVyYXRvciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJjb25zb2xlLXNhLXRva2VuLWJrZzZwIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImNvbnNvbGUtc2EiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJhZTE2ZGVkNS01MmM3LTRkZTQtOWUxYS1iNmI4NGU2OGMzM2UiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6bWluaW8tb3BlcmF0b3I6Y29uc29sZS1zYSJ9.AjhzekAPC59SQVBQL5sr-1dqr57-jH8a5LVazpnEr_cC0JqT4jXYjdfbrZSF9yaL4gHRv2l0kOhBlrjRK7y-IpMbxE71Fne_lSzaptSuqgI5I9dFvpVfZWP1yMAqav8mrlUoWkWDq9IAkyH4bvvZrVgQJGgd5t9U_7DQCVwbkQvy0wGS5zoMcZhYenn_Ub1BoxWcviADQ1aY1wQju8OP0IOwKTIMXMQqciOFdJ9T5-tQEGUrikTu_tW-1shUHzOxBcEzGVtBvBy2OmbNnRFYogbhmp-Dze6EAi035bY32bfL7XKBUNCW6_3VbN_h3pQNAuT2NJOSKuhJ3cGldCB2zg")
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
w.Header().Set("Authorization", "xyz")
|
||||
w.Header().Set("ETag", "\"ABCDE\"")
|
||||
|
||||
type args struct {
|
||||
w http.ResponseWriter
|
||||
r *http.Request
|
||||
reqClaims map[string]interface{}
|
||||
deploymentID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Entry
|
||||
preFunc func()
|
||||
postFunc func()
|
||||
}{
|
||||
{
|
||||
preFunc: func() {
|
||||
os.Setenv("CONSOLE_OPERATOR_MODE", "on")
|
||||
},
|
||||
postFunc: func() {
|
||||
os.Unsetenv("CONSOLE_OPERATOR_MODE")
|
||||
|
||||
},
|
||||
name: "constructs an audit entry from a http request",
|
||||
args: args{
|
||||
w: w,
|
||||
r: req,
|
||||
reqClaims: map[string]interface{}{},
|
||||
deploymentID: "1",
|
||||
},
|
||||
want: Entry{
|
||||
Version: "1",
|
||||
DeploymentID: "1",
|
||||
SessionID: "system:serviceaccount:minio-operator:console-sa",
|
||||
ReqQuery: map[string]string{"test": "xyz"},
|
||||
ReqHeader: map[string]string{"test": "xyz"},
|
||||
RespHeader: map[string]string{"test": "xyz", "ETag": "ABCDE"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.preFunc != nil {
|
||||
tt.preFunc()
|
||||
}
|
||||
if got := ToEntry(tt.args.w, tt.args.r, tt.args.reqClaims, tt.args.deploymentID); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ToEntry() = %v, want %v", got, tt.want)
|
||||
}
|
||||
if tt.postFunc != nil {
|
||||
tt.postFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
64
pkg/logger/message/log/entry.go
Normal file
64
pkg/logger/message/log/entry.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string `json:"objectName"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
}
|
||||
|
||||
// Args - defines the arguments for the API.
|
||||
type Args struct {
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
Object string `json:"object,omitempty"`
|
||||
VersionID string `json:"versionId,omitempty"`
|
||||
Objects []ObjectVersion `json:"objects,omitempty"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// Trace - defines the trace.
|
||||
type Trace struct {
|
||||
Message string `json:"message,omitempty"`
|
||||
Source []string `json:"source,omitempty"`
|
||||
Variables map[string]interface{} `json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
// API - defines the api type and its args.
|
||||
type API struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// Entry - defines fields and values of each log entry.
|
||||
type Entry struct {
|
||||
DeploymentID string `json:"deploymentid,omitempty"`
|
||||
Level string `json:"level"`
|
||||
LogKind string `json:"errKind"`
|
||||
Time time.Time `json:"time"`
|
||||
API *API `json:"api,omitempty"`
|
||||
RemoteHost string `json:"remotehost,omitempty"`
|
||||
Host string `json:"host,omitempty"`
|
||||
RequestID string `json:"requestID,omitempty"`
|
||||
SessionID string `json:"sessionID,omitempty"`
|
||||
UserAgent string `json:"userAgent,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Trace *Trace `json:"errors,omitempty"`
|
||||
}
|
||||
117
pkg/logger/reqinfo.go
Normal file
117
pkg/logger/reqinfo.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
)
|
||||
|
||||
// KeyVal - appended to ReqInfo.Tags
|
||||
type KeyVal struct {
|
||||
Key string
|
||||
Val interface{}
|
||||
}
|
||||
|
||||
// ObjectVersion object version key/versionId
|
||||
type ObjectVersion struct {
|
||||
ObjectName string
|
||||
VersionID string `json:"VersionId,omitempty"`
|
||||
}
|
||||
|
||||
// ReqInfo stores the request info.
|
||||
type ReqInfo struct {
|
||||
RemoteHost string // Client Host/IP
|
||||
Host string // Node Host/IP
|
||||
UserAgent string // User Agent
|
||||
DeploymentID string // x-minio-deployment-id
|
||||
RequestID string // x-amz-request-id
|
||||
SessionID string // custom session id
|
||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
||||
BucketName string `json:",omitempty"` // Bucket name
|
||||
ObjectName string `json:",omitempty"` // Object name
|
||||
VersionID string `json:",omitempty"` // corresponding versionID for the object
|
||||
Objects []ObjectVersion `json:",omitempty"` // Only set during MultiObject delete handler.
|
||||
AccessKey string // Access Key
|
||||
tags []KeyVal // Any additional info not accommodated by above fields
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
// GetTags - returns the user defined tags
|
||||
func (r *ReqInfo) GetTags() []KeyVal {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
return append([]KeyVal(nil), r.tags...)
|
||||
}
|
||||
|
||||
// GetTagsMap - returns the user defined tags in a map structure
|
||||
func (r *ReqInfo) GetTagsMap() map[string]interface{} {
|
||||
if r == nil {
|
||||
return nil
|
||||
}
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
m := make(map[string]interface{}, len(r.tags))
|
||||
for _, t := range r.tags {
|
||||
m[t.Key] = t.Val
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// SetReqInfo sets ReqInfo in the context.
|
||||
func SetReqInfo(ctx context.Context, req *ReqInfo) context.Context {
|
||||
if ctx == nil {
|
||||
LogIf(context.Background(), fmt.Errorf("context is nil"))
|
||||
return nil
|
||||
}
|
||||
return context.WithValue(ctx, utils.ContextLogKey, req)
|
||||
}
|
||||
|
||||
// GetReqInfo returns ReqInfo if set.
|
||||
func GetReqInfo(ctx context.Context) *ReqInfo {
|
||||
if ctx != nil {
|
||||
r, ok := ctx.Value(utils.ContextLogKey).(*ReqInfo)
|
||||
if ok {
|
||||
return r
|
||||
}
|
||||
r = &ReqInfo{}
|
||||
if val, o := ctx.Value(utils.ContextRequestID).(string); o {
|
||||
r.RequestID = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestUserID).(string); o {
|
||||
r.SessionID = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestUserAgent).(string); o {
|
||||
r.UserAgent = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestHost).(string); o {
|
||||
r.Host = val
|
||||
}
|
||||
if val, o := ctx.Value(utils.ContextRequestRemoteAddr).(string); o {
|
||||
r.RemoteHost = val
|
||||
}
|
||||
SetReqInfo(ctx, r)
|
||||
return r
|
||||
}
|
||||
return nil
|
||||
}
|
||||
226
pkg/logger/target/http/http.go
Normal file
226
pkg/logger/target/http/http.go
Normal file
@@ -0,0 +1,226 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
"github.com/minio/console/pkg/logger/target/types"
|
||||
)
|
||||
|
||||
// Timeout for the webhook http call
|
||||
const webhookCallTimeout = 5 * time.Second
|
||||
|
||||
// Config http logger target
|
||||
type Config struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Name string `json:"name"`
|
||||
UserAgent string `json:"userAgent"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
AuthToken string `json:"authToken"`
|
||||
ClientCert string `json:"clientCert"`
|
||||
ClientKey string `json:"clientKey"`
|
||||
QueueSize int `json:"queueSize"`
|
||||
Transport http.RoundTripper `json:"-"`
|
||||
|
||||
// Custom logger
|
||||
LogOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{}) `json:"-"`
|
||||
}
|
||||
|
||||
// Target implements logger.Target and sends the json
|
||||
// format of a log entry to the configured http endpoint.
|
||||
// An internal buffer of logs is maintained but when the
|
||||
// buffer is full, new logs are just ignored and an errors
|
||||
// is returned to the caller.
|
||||
type Target struct {
|
||||
status int32
|
||||
wg sync.WaitGroup
|
||||
|
||||
// Channel of log entries
|
||||
logCh chan interface{}
|
||||
|
||||
config Config
|
||||
}
|
||||
|
||||
// Endpoint returns the backend endpoint
|
||||
func (h *Target) Endpoint() string {
|
||||
return h.config.Endpoint
|
||||
}
|
||||
|
||||
func (h *Target) String() string {
|
||||
return h.config.Name
|
||||
}
|
||||
|
||||
// Init validate and initialize the http target
|
||||
func (h *Target) Init() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*webhookCallTimeout)
|
||||
defer cancel()
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, h.config.Endpoint, strings.NewReader(`{}`))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req.Header.Set(xhttp.ContentType, "application/json")
|
||||
|
||||
// Set user-agent to indicate MinIO release
|
||||
// version to the configured log endpoint
|
||||
req.Header.Set("User-Agent", h.config.UserAgent)
|
||||
|
||||
if h.config.AuthToken != "" {
|
||||
req.Header.Set("Authorization", h.config.AuthToken)
|
||||
}
|
||||
|
||||
client := http.Client{Transport: h.config.Transport}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Drain any response.
|
||||
xhttp.DrainBody(resp.Body)
|
||||
|
||||
if !acceptedResponseStatusCode(resp.StatusCode) {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusForbidden:
|
||||
return fmt.Errorf("%s returned '%s', please check if your auth token is correctly set",
|
||||
h.config.Endpoint, resp.Status)
|
||||
}
|
||||
return fmt.Errorf("%s returned '%s', please check your endpoint configuration",
|
||||
h.config.Endpoint, resp.Status)
|
||||
}
|
||||
|
||||
h.status = 1
|
||||
go h.startHTTPLogger()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Accepted HTTP Status Codes
|
||||
var acceptedStatusCodeMap = map[int]bool{http.StatusOK: true, http.StatusCreated: true, http.StatusAccepted: true, http.StatusNoContent: true}
|
||||
|
||||
func acceptedResponseStatusCode(code int) bool {
|
||||
return acceptedStatusCodeMap[code]
|
||||
}
|
||||
|
||||
func (h *Target) logEntry(entry interface{}) {
|
||||
logJSON, err := json.Marshal(&entry)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), webhookCallTimeout)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost,
|
||||
h.config.Endpoint, bytes.NewReader(logJSON))
|
||||
if err != nil {
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%w', please check your endpoint configuration", h.config.Endpoint, err), h.config.Endpoint)
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
req.Header.Set(xhttp.ContentType, "application/json")
|
||||
|
||||
// Set user-agent to indicate MinIO release
|
||||
// version to the configured log endpoint
|
||||
req.Header.Set("User-Agent", h.config.UserAgent)
|
||||
|
||||
if h.config.AuthToken != "" {
|
||||
req.Header.Set("Authorization", h.config.AuthToken)
|
||||
}
|
||||
|
||||
client := http.Client{Transport: h.config.Transport}
|
||||
resp, err := client.Do(req)
|
||||
cancel()
|
||||
if err != nil {
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%w', please check your endpoint configuration", h.config.Endpoint, err), h.config.Endpoint)
|
||||
return
|
||||
}
|
||||
|
||||
// Drain any response.
|
||||
xhttp.DrainBody(resp.Body)
|
||||
|
||||
if !acceptedResponseStatusCode(resp.StatusCode) {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusForbidden:
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%s', please check if your auth token is correctly set", h.config.Endpoint, resp.Status), h.config.Endpoint)
|
||||
default:
|
||||
h.config.LogOnce(ctx, fmt.Errorf("%s returned '%s', please check your endpoint configuration", h.config.Endpoint, resp.Status), h.config.Endpoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Target) startHTTPLogger() {
|
||||
// Create a routine which sends json logs received
|
||||
// from an internal channel.
|
||||
go func() {
|
||||
h.wg.Add(1)
|
||||
defer h.wg.Done()
|
||||
for entry := range h.logCh {
|
||||
h.logEntry(entry)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// New initializes a new logger target which
|
||||
// sends log over http to the specified endpoint
|
||||
func New(config Config) *Target {
|
||||
h := &Target{
|
||||
logCh: make(chan interface{}, config.QueueSize),
|
||||
config: config,
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Send log message 'e' to http target.
|
||||
func (h *Target) Send(entry interface{}, errKind string) error {
|
||||
if atomic.LoadInt32(&h.status) == 0 {
|
||||
// Channel was closed or used before init.
|
||||
return nil
|
||||
}
|
||||
|
||||
select {
|
||||
case h.logCh <- entry:
|
||||
default:
|
||||
// log channel is full, do not wait and return
|
||||
// an errors immediately to the caller
|
||||
return errors.New("log buffer full")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cancel - cancels the target
|
||||
func (h *Target) Cancel() {
|
||||
if atomic.CompareAndSwapInt32(&h.status, 1, 0) {
|
||||
close(h.logCh)
|
||||
}
|
||||
h.wg.Wait()
|
||||
}
|
||||
|
||||
// Type - returns type of the target
|
||||
func (h *Target) Type() types.TargetType {
|
||||
return types.TargetHTTP
|
||||
}
|
||||
27
pkg/logger/target/types/types.go
Normal file
27
pkg/logger/target/types/types.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
// TargetType indicates type of the target e.g. console, http, kafka
|
||||
type TargetType uint8
|
||||
|
||||
// Constants for target types
|
||||
const (
|
||||
_ TargetType = iota
|
||||
TargetConsole
|
||||
TargetHTTP
|
||||
)
|
||||
151
pkg/logger/targets.go
Normal file
151
pkg/logger/targets.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/minio/console/pkg/logger/target/http"
|
||||
"github.com/minio/console/pkg/logger/target/types"
|
||||
)
|
||||
|
||||
// Target is the entity that we will receive
|
||||
// a single log entry and Send it to the log target
|
||||
// e.g. Send the log to a http server
|
||||
type Target interface {
|
||||
String() string
|
||||
Endpoint() string
|
||||
Init() error
|
||||
Cancel()
|
||||
Send(entry interface{}, errKind string) error
|
||||
Type() types.TargetType
|
||||
}
|
||||
|
||||
var (
|
||||
// swapMu must be held while reading slice info or swapping targets or auditTargets.
|
||||
swapMu sync.Mutex
|
||||
|
||||
// systemTargets is the set of enabled loggers.
|
||||
// Must be immutable at all times.
|
||||
// Can be swapped to another while holding swapMu
|
||||
systemTargets = []Target{}
|
||||
|
||||
// This is always set represent /dev/console target
|
||||
consoleTgt Target
|
||||
|
||||
nTargets int32 // atomic count of len(targets)
|
||||
)
|
||||
|
||||
// SystemTargets returns active targets.
|
||||
// Returned slice may not be modified in any way.
|
||||
func SystemTargets() []Target {
|
||||
if atomic.LoadInt32(&nTargets) == 0 {
|
||||
// Lock free if none...
|
||||
return nil
|
||||
}
|
||||
swapMu.Lock()
|
||||
res := systemTargets
|
||||
swapMu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// AuditTargets returns active audit targets.
|
||||
// Returned slice may not be modified in any way.
|
||||
func AuditTargets() []Target {
|
||||
if atomic.LoadInt32(&nAuditTargets) == 0 {
|
||||
// Lock free if none...
|
||||
return nil
|
||||
}
|
||||
swapMu.Lock()
|
||||
res := auditTargets
|
||||
swapMu.Unlock()
|
||||
return res
|
||||
}
|
||||
|
||||
// auditTargets is the list of enabled audit loggers
|
||||
// Must be immutable at all times.
|
||||
// Can be swapped to another while holding swapMu
|
||||
var (
|
||||
auditTargets = []Target{}
|
||||
nAuditTargets int32 // atomic count of len(auditTargets)
|
||||
)
|
||||
|
||||
func cancelAllSystemTargets() {
|
||||
for _, tgt := range systemTargets {
|
||||
tgt.Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func initSystemTargets(cfgMap map[string]http.Config) (tgts []Target, err error) {
|
||||
for _, l := range cfgMap {
|
||||
if l.Enabled {
|
||||
t := http.New(l)
|
||||
if err = t.Init(); err != nil {
|
||||
return tgts, err
|
||||
}
|
||||
tgts = append(tgts, t)
|
||||
}
|
||||
}
|
||||
return tgts, err
|
||||
}
|
||||
|
||||
// UpdateSystemTargets swaps targets with newly loaded ones from the cfg
|
||||
func UpdateSystemTargets(cfg Config) error {
|
||||
updated, err := initSystemTargets(cfg.HTTP)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
swapMu.Lock()
|
||||
for _, tgt := range systemTargets {
|
||||
// Preserve console target when dynamically updating
|
||||
// other HTTP targets, console target is always present.
|
||||
if tgt.Type() == types.TargetConsole {
|
||||
updated = append(updated, tgt)
|
||||
break
|
||||
}
|
||||
}
|
||||
atomic.StoreInt32(&nTargets, int32(len(updated)))
|
||||
cancelAllSystemTargets() // cancel running targets
|
||||
systemTargets = updated
|
||||
swapMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func cancelAuditTargetType(t types.TargetType) {
|
||||
for _, tgt := range auditTargets {
|
||||
if tgt.Type() == t {
|
||||
tgt.Cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateAuditWebhookTargets swaps audit webhook targets with newly loaded ones from the cfg
|
||||
func UpdateAuditWebhookTargets(cfg Config) error {
|
||||
updated, err := initSystemTargets(cfg.AuditWebhook)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
swapMu.Lock()
|
||||
atomic.StoreInt32(&nAuditTargets, int32(len(updated)))
|
||||
cancelAuditTargetType(types.TargetHTTP) // cancel running targets
|
||||
auditTargets = updated
|
||||
swapMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
60
pkg/logger/utils.go
Normal file
60
pkg/logger/utils.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"runtime"
|
||||
|
||||
"github.com/minio/console/pkg/logger/color"
|
||||
)
|
||||
|
||||
var ansiRE = regexp.MustCompile("(\x1b[^m]*m)")
|
||||
|
||||
// Print ANSI Control escape
|
||||
func ansiEscape(format string, args ...interface{}) {
|
||||
Esc := "\x1b"
|
||||
fmt.Printf("%s%s", Esc, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func ansiMoveRight(n int) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("[%dC", n)
|
||||
}
|
||||
}
|
||||
|
||||
func ansiSaveAttributes() {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("7")
|
||||
}
|
||||
}
|
||||
|
||||
func ansiRestoreAttributes() {
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
if color.IsTerminal() {
|
||||
ansiEscape("8")
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
"github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/pkg/licverifier"
|
||||
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func LoginWithMFA(client utils.HTTPClientI, username, mfaToken, otp string) (*LoginResp, error) {
|
||||
func LoginWithMFA(client http.ClientI, username, mfaToken, otp string) (*LoginResp, error) {
|
||||
mfaLoginReq := MfaReq{Username: username, OTP: otp, Token: mfaToken}
|
||||
resp, err := subnetPostReq(client, subnetMFAURL(), mfaLoginReq, nil)
|
||||
if err != nil {
|
||||
@@ -47,7 +47,7 @@ func LoginWithMFA(client utils.HTTPClientI, username, mfaToken, otp string) (*Lo
|
||||
return nil, errors.New("access token not found in response")
|
||||
}
|
||||
|
||||
func Login(client utils.HTTPClientI, username, password string) (*LoginResp, error) {
|
||||
func Login(client http.ClientI, username, password string) (*LoginResp, error) {
|
||||
loginReq := map[string]string{
|
||||
"username": username,
|
||||
"password": password,
|
||||
@@ -71,7 +71,7 @@ func Login(client utils.HTTPClientI, username, password string) (*LoginResp, err
|
||||
return nil, errors.New("access token not found in response")
|
||||
}
|
||||
|
||||
func GetOrganizations(client utils.HTTPClientI, token string) ([]*models.SubnetOrganization, error) {
|
||||
func GetOrganizations(client http.ClientI, token string) ([]*models.SubnetOrganization, error) {
|
||||
headers := subnetAuthHeaders(token)
|
||||
respStr, err := subnetGetReq(client, subnetOrgsURL(), headers)
|
||||
if err != nil {
|
||||
@@ -90,7 +90,7 @@ type LicenseTokenConfig struct {
|
||||
Proxy string
|
||||
}
|
||||
|
||||
func Register(client utils.HTTPClientI, admInfo madmin.InfoMessage, apiKey, token, accountID string) (*LicenseTokenConfig, error) {
|
||||
func Register(client http.ClientI, admInfo madmin.InfoMessage, apiKey, token, accountID string) (*LicenseTokenConfig, error) {
|
||||
var headers map[string]string
|
||||
regInfo := GetClusterRegInfo(admInfo)
|
||||
regURL := subnetRegisterURL()
|
||||
@@ -128,7 +128,7 @@ func Register(client utils.HTTPClientI, admInfo madmin.InfoMessage, apiKey, toke
|
||||
const publicKey = "/downloads/license-pubkey.pem"
|
||||
|
||||
// downloadSubnetPublicKey will download the current subnet public key.
|
||||
func downloadSubnetPublicKey(client utils.HTTPClientI) (string, error) {
|
||||
func downloadSubnetPublicKey(client http.ClientI) (string, error) {
|
||||
// Get the public key directly from Subnet
|
||||
url := fmt.Sprintf("%s%s", subnetBaseURL(), publicKey)
|
||||
resp, err := client.Get(url)
|
||||
@@ -145,7 +145,7 @@ func downloadSubnetPublicKey(client utils.HTTPClientI) (string, error) {
|
||||
}
|
||||
|
||||
// ParseLicense parses the license with the bundle public key and return it's information
|
||||
func ParseLicense(client utils.HTTPClientI, license string) (*licverifier.LicenseInfo, error) {
|
||||
func ParseLicense(client http.ClientI, license string) (*licverifier.LicenseInfo, error) {
|
||||
var publicKeys []string
|
||||
|
||||
subnetPubKey, err := downloadSubnetPublicKey(client)
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/minio/madmin-go"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
@@ -69,11 +69,11 @@ func subnetAuthHeaders(authToken string) map[string]string {
|
||||
return map[string]string{"Authorization": "Bearer " + authToken}
|
||||
}
|
||||
|
||||
func httpDo(client utils.HTTPClientI, req *http.Request) (*http.Response, error) {
|
||||
func httpDo(client xhttp.ClientI, req *http.Request) (*http.Response, error) {
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func subnetReqDo(client utils.HTTPClientI, r *http.Request, headers map[string]string) (string, error) {
|
||||
func subnetReqDo(client xhttp.ClientI, r *http.Request, headers map[string]string) (string, error) {
|
||||
for k, v := range headers {
|
||||
r.Header.Add(k, v)
|
||||
}
|
||||
@@ -98,10 +98,10 @@ func subnetReqDo(client utils.HTTPClientI, r *http.Request, headers map[string]s
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
return respStr, nil
|
||||
}
|
||||
return respStr, fmt.Errorf("Request failed with code %d and error: %s", resp.StatusCode, respStr)
|
||||
return respStr, fmt.Errorf("Request failed with code %d and errors: %s", resp.StatusCode, respStr)
|
||||
}
|
||||
|
||||
func subnetGetReq(client utils.HTTPClientI, reqURL string, headers map[string]string) (string, error) {
|
||||
func subnetGetReq(client xhttp.ClientI, reqURL string, headers map[string]string) (string, error) {
|
||||
r, e := http.NewRequest(http.MethodGet, reqURL, nil)
|
||||
if e != nil {
|
||||
return "", e
|
||||
@@ -109,7 +109,7 @@ func subnetGetReq(client utils.HTTPClientI, reqURL string, headers map[string]st
|
||||
return subnetReqDo(client, r, headers)
|
||||
}
|
||||
|
||||
func subnetPostReq(client utils.HTTPClientI, reqURL string, payload interface{}, headers map[string]string) (string, error) {
|
||||
func subnetPostReq(client xhttp.ClientI, reqURL string, payload interface{}, headers map[string]string) (string, error) {
|
||||
body, e := json.Marshal(payload)
|
||||
if e != nil {
|
||||
return "", e
|
||||
|
||||
39
pkg/utils/utils.go
Normal file
39
pkg/utils/utils.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
import "github.com/google/uuid"
|
||||
|
||||
// NewUUID - get a random UUID.
|
||||
func NewUUID() (string, error) {
|
||||
u, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// Key used for Get/SetReqInfo
|
||||
type key string
|
||||
|
||||
const ContextLogKey = key("console-log")
|
||||
const ContextRequestID = key("request-id")
|
||||
const ContextRequestUserID = key("request-user-id")
|
||||
const ContextRequestUserAgent = key("request-user-agent")
|
||||
const ContextRequestHost = key("request-host")
|
||||
const ContextRequestRemoteAddr = key("request-remote-addr")
|
||||
const ContextAuditKey = key("request-audit-entry")
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
|
||||
"github.com/minio/console/pkg/http"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,7 +30,7 @@ var (
|
||||
)
|
||||
|
||||
// getLatestMinIOImage returns the latest docker image for MinIO if found on the internet
|
||||
func GetLatestMinIOImage(client HTTPClientI) (*string, error) {
|
||||
func GetLatestMinIOImage(client http.ClientI) (*string, error) {
|
||||
resp, err := client.Get("https://dl.min.io/server/minio/release/linux-amd64/")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
func registerAdminArnsHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of arns
|
||||
api.SystemArnListHandler = systemApi.ArnListHandlerFunc(func(params systemApi.ArnListParams, session *models.Principal) middleware.Responder {
|
||||
arnsResp, err := getArnsResponse(session)
|
||||
arnsResp, err := getArnsResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewArnListDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -51,21 +51,21 @@ func getArns(ctx context.Context, client MinioAdmin) (*models.ArnsResponse, erro
|
||||
}
|
||||
|
||||
// getArnsResponse returns a list of active arns in the instance
|
||||
func getArnsResponse(session *models.Principal) (*models.ArnsResponse, *models.Error) {
|
||||
func getArnsResponse(session *models.Principal, params systemApi.ArnListParams) (*models.ArnsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
arnsList, err := getArns(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return arnsList, nil
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
// List Configurations
|
||||
api.ConfigurationListConfigHandler = cfgApi.ListConfigHandlerFunc(func(params cfgApi.ListConfigParams, session *models.Principal) middleware.Responder {
|
||||
configListResp, err := getListConfigResponse(session)
|
||||
configListResp, err := getListConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewListConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -49,7 +49,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Set Configuration
|
||||
api.ConfigurationSetConfigHandler = cfgApi.SetConfigHandlerFunc(func(params cfgApi.SetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := setConfigResponse(session, params.Name, params.Body)
|
||||
resp, err := setConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewSetConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func registerConfigHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Reset Configuration
|
||||
api.ConfigurationResetConfigHandler = cfgApi.ResetConfigHandlerFunc(func(params cfgApi.ResetConfigParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := resetConfigResponse(session, params.Name)
|
||||
resp, err := resetConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return cfgApi.NewResetConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -86,10 +86,12 @@ func listConfig(client MinioAdmin) ([]*models.ConfigDescription, error) {
|
||||
}
|
||||
|
||||
// getListConfigResponse performs listConfig() and serializes it to the handler's output
|
||||
func getListConfigResponse(session *models.Principal) (*models.ListConfigResponse, *models.Error) {
|
||||
func getListConfigResponse(session *models.Principal, params cfgApi.ListConfigParams) (*models.ListConfigResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -97,7 +99,7 @@ func getListConfigResponse(session *models.Principal) (*models.ListConfigRespons
|
||||
|
||||
configDescs, err := listConfig(adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
listGroupsResponse := &models.ListConfigResponse{
|
||||
Configurations: configDescs,
|
||||
@@ -134,11 +136,11 @@ func getConfig(ctx context.Context, client MinioAdmin, name string) ([]*models.C
|
||||
|
||||
// getConfigResponse performs getConfig() and serializes it to the handler's output
|
||||
func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams) (*models.Configuration, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -146,7 +148,7 @@ func getConfigResponse(session *models.Principal, params cfgApi.ConfigInfoParams
|
||||
|
||||
configkv, err := getConfig(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
configurationObj := &models.Configuration{
|
||||
Name: params.Name,
|
||||
@@ -191,22 +193,22 @@ func buildConfig(configName *string, kvs []*models.ConfigurationKV) *string {
|
||||
}
|
||||
|
||||
// setConfigResponse implements setConfig() to be used by handler
|
||||
func setConfigResponse(session *models.Principal, name string, configRequest *models.SetConfigRequest) (*models.SetConfigResponse, *models.Error) {
|
||||
func setConfigResponse(session *models.Principal, params cfgApi.SetConfigParams) (*models.SetConfigResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
configName := name
|
||||
configName := params.Name
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
needsRestart, err := setConfigWithARNAccountID(ctx, adminClient, &configName, configRequest.KeyValues, configRequest.ArnResourceID)
|
||||
needsRestart, err := setConfigWithARNAccountID(ctx, adminClient, &configName, params.Body.KeyValues, params.Body.ArnResourceID)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SetConfigResponse{Restart: needsRestart}, nil
|
||||
}
|
||||
@@ -217,22 +219,22 @@ func resetConfig(ctx context.Context, client MinioAdmin, configName *string) (er
|
||||
}
|
||||
|
||||
// resetConfigResponse implements resetConfig() to be used by handler
|
||||
func resetConfigResponse(session *models.Principal, configName string) (*models.SetConfigResponse, *models.Error) {
|
||||
func resetConfigResponse(session *models.Principal, params cfgApi.ResetConfigParams) (*models.SetConfigResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
err = resetConfig(ctx, adminClient, &configName)
|
||||
err = resetConfig(ctx, adminClient, ¶ms.Name)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return &models.SetConfigResponse{Restart: true}, nil
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
// List Groups
|
||||
api.GroupListGroupsHandler = groupApi.ListGroupsHandlerFunc(func(params groupApi.ListGroupsParams, session *models.Principal) middleware.Responder {
|
||||
listGroupsResponse, err := getListGroupsResponse(session)
|
||||
listGroupsResponse, err := getListGroupsResponse(session, params)
|
||||
if err != nil {
|
||||
return groupApi.NewListGroupsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Add Group
|
||||
api.GroupAddGroupHandler = groupApi.AddGroupHandlerFunc(func(params groupApi.AddGroupParams, session *models.Principal) middleware.Responder {
|
||||
if err := getAddGroupResponse(session, params.Body); err != nil {
|
||||
if err := getAddGroupResponse(session, params); err != nil {
|
||||
return groupApi.NewAddGroupDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return groupApi.NewAddGroupCreated()
|
||||
@@ -71,12 +71,12 @@ func registerGroupsHandlers(api *operations.ConsoleAPI) {
|
||||
}
|
||||
|
||||
// getListGroupsResponse performs listGroups() and serializes it to the handler's output
|
||||
func getListGroupsResponse(session *models.Principal) (*models.ListGroupsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListGroupsResponse(session *models.Principal, params groupApi.ListGroupsParams) (*models.ListGroupsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -84,7 +84,7 @@ func getListGroupsResponse(session *models.Principal) (*models.ListGroupsRespons
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -107,11 +107,11 @@ func groupInfo(ctx context.Context, client MinioAdmin, group string) (*madmin.Gr
|
||||
|
||||
// getGroupInfoResponse performs groupInfo() and serializes it to the handler's output
|
||||
func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoParams) (*models.Group, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -119,7 +119,7 @@ func getGroupInfoResponse(session *models.Principal, params groupApi.GroupInfoPa
|
||||
|
||||
groupDesc, err := groupInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
groupResponse := &models.Group{
|
||||
@@ -146,16 +146,17 @@ func addGroup(ctx context.Context, client MinioAdmin, group string, members []st
|
||||
}
|
||||
|
||||
// getAddGroupResponse performs addGroup() and serializes it to the handler's output
|
||||
func getAddGroupResponse(session *models.Principal, params *models.AddGroupRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getAddGroupResponse(session *models.Principal, params groupApi.AddGroupParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// AddGroup request needed to proceed
|
||||
if params == nil {
|
||||
return prepareError(errGroupBodyNotInRequest)
|
||||
if params.Body == nil {
|
||||
return ErrorWithContext(ctx, ErrGroupBodyNotInRequest)
|
||||
}
|
||||
groupRequest := params.Body
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -164,13 +165,13 @@ func getAddGroupResponse(session *models.Principal, params *models.AddGroupReque
|
||||
groupList, _ := adminClient.listGroups(ctx)
|
||||
|
||||
for _, b := range groupList {
|
||||
if b == *params.Group {
|
||||
return prepareError(errGroupAlreadyExists)
|
||||
if b == *groupRequest.Group {
|
||||
return ErrorWithContext(ctx, ErrGroupAlreadyExists)
|
||||
}
|
||||
}
|
||||
|
||||
if err := addGroup(ctx, adminClient, *params.Group, params.Members); err != nil {
|
||||
return prepareError(err)
|
||||
if err := addGroup(ctx, adminClient, *groupRequest.Group, groupRequest.Members); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -191,21 +192,21 @@ func removeGroup(ctx context.Context, client MinioAdmin, group string) error {
|
||||
|
||||
// getRemoveGroupResponse performs removeGroup() and serializes it to the handler's output
|
||||
func getRemoveGroupResponse(session *models.Principal, params groupApi.RemoveGroupParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return prepareError(errGroupNameNotInRequest)
|
||||
return ErrorWithContext(ctx, ErrGroupNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// createad a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := removeGroup(ctx, adminClient, params.Name); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -265,13 +266,13 @@ func setGroupStatus(ctx context.Context, client MinioAdmin, group, status string
|
||||
// also sets the group's status if status in the request is different than the current one.
|
||||
// Then serializes the output to be used by the handler.
|
||||
func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGroupParams) (*models.Group, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return nil, prepareError(errGroupNameNotInRequest)
|
||||
return nil, ErrorWithContext(ctx, ErrGroupNameNotInRequest)
|
||||
}
|
||||
if params.Body == nil {
|
||||
return nil, prepareError(errGroupBodyNotInRequest)
|
||||
return nil, ErrorWithContext(ctx, ErrGroupBodyNotInRequest)
|
||||
|
||||
}
|
||||
expectedGroupUpdate := params.Body
|
||||
@@ -279,7 +280,7 @@ func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGro
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -287,7 +288,7 @@ func getUpdateGroupResponse(session *models.Principal, params groupApi.UpdateGro
|
||||
|
||||
groupUpdated, err := groupUpdate(ctx, adminClient, groupName, expectedGroupUpdate)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
groupResponse := &models.Group{
|
||||
Name: groupUpdated.Name,
|
||||
|
||||
@@ -56,7 +56,7 @@ var (
|
||||
|
||||
type healItemStatus struct {
|
||||
Status string `json:"status"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Error string `json:"errors,omitempty"`
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Before struct {
|
||||
@@ -143,7 +143,7 @@ func startHeal(ctx context.Context, conn WSConn, client MinioAdmin, hOpts *healO
|
||||
}
|
||||
|
||||
if res.Summary == "stopped" {
|
||||
return fmt.Errorf("heal had an error - %s", res.FailureDetail)
|
||||
return fmt.Errorf("heal had an errors - %s", res.FailureDetail)
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
@@ -826,6 +826,8 @@ type LabelResults struct {
|
||||
|
||||
// getAdminInfoResponse returns the response containing total buckets, objects and usage.
|
||||
func getAdminInfoResponse(session *models.Principal, params systemApi.AdminInfoParams) (*models.AdminInfoResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
prometheusURL := ""
|
||||
|
||||
if !*params.DefaultOnly {
|
||||
@@ -834,35 +836,30 @@ func getAdminInfoResponse(session *models.Principal, params systemApi.AdminInfoP
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
sessionResp, err2 := getUsageWidgetsForDeployment(prometheusURL, mAdmin)
|
||||
sessionResp, err2 := getUsageWidgetsForDeployment(ctx, prometheusURL, mAdmin)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
return nil, ErrorWithContext(ctx, err2)
|
||||
}
|
||||
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
func getUsageWidgetsForDeployment(prometheusURL string, mAdmin *madmin.AdminClient) (*models.AdminInfoResponse, *models.Error) {
|
||||
func getUsageWidgetsForDeployment(ctx context.Context, prometheusURL string, mAdmin *madmin.AdminClient) (*models.AdminInfoResponse, error) {
|
||||
prometheusNotReady := false
|
||||
|
||||
if prometheusURL != "" && !testPrometheusURL(prometheusURL) {
|
||||
if prometheusURL != "" && !testPrometheusURL(ctx, prometheusURL) {
|
||||
prometheusNotReady = true
|
||||
}
|
||||
if prometheusURL == "" || prometheusNotReady {
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
defer cancel()
|
||||
// serialize output
|
||||
usage, err := GetAdminInfo(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, err
|
||||
}
|
||||
sessionResp := &models.AdminInfoResponse{
|
||||
Buckets: usage.Buckets,
|
||||
@@ -901,80 +898,69 @@ func getUsageWidgetsForDeployment(prometheusURL string, mAdmin *madmin.AdminClie
|
||||
return sessionResp, nil
|
||||
}
|
||||
|
||||
func unmarshalPrometheus(endpoint string, data interface{}) bool {
|
||||
func unmarshalPrometheus(ctx context.Context, endpoint string, data interface{}) bool {
|
||||
httpClnt := GetConsoleHTTPClient()
|
||||
resp, err := httpClnt.Get(endpoint)
|
||||
if err != nil {
|
||||
LogError("Unable to fetch labels from prometheus (%s)", resp.Status)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to fetch labels from prometheus (%s)", resp.Status))
|
||||
return true
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
LogError("Unexpected error from prometheus (%s)", resp.Status)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unexpected errors from prometheus (%s)", resp.Status))
|
||||
return true
|
||||
}
|
||||
|
||||
if err = json.NewDecoder(resp.Body).Decode(data); err != nil {
|
||||
LogError("Unexpected error reading response from prometheus, %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unexpected errors from prometheus (%s)", resp.Status))
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func testPrometheusURL(url string) bool {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
func testPrometheusURL(ctx context.Context, url string) bool {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url+"/-/healthy", nil)
|
||||
|
||||
if err != nil {
|
||||
LogError("Error Building Request: (%v)", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error Building Request: (%v)", err))
|
||||
return false
|
||||
}
|
||||
|
||||
response, err := GetConsoleHTTPClient().Do(req)
|
||||
|
||||
if err != nil {
|
||||
LogError("Default Prometheus URL not reachable, trying root testing: (%v)", err)
|
||||
|
||||
ErrorWithContext(ctx, fmt.Errorf("default Prometheus URL not reachable, trying root testing: (%v)", err))
|
||||
newTestURL := req.URL.Scheme + "://" + req.URL.Host + "/-/healthy"
|
||||
|
||||
req2, err := http.NewRequestWithContext(ctx, http.MethodGet, newTestURL, nil)
|
||||
|
||||
if err != nil {
|
||||
LogError("Error Building Root Request: (%v)", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error Building Root Request: (%v)", err))
|
||||
return false
|
||||
}
|
||||
|
||||
rootResponse, err := GetConsoleHTTPClient().Do(req2)
|
||||
|
||||
if err != nil {
|
||||
// URL & Root tests didn't work. Prometheus not reachable
|
||||
LogError("Root Prometheus URL not reachable: (%v)", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("root Prometheus URL not reachable: (%v)", err))
|
||||
return false
|
||||
}
|
||||
|
||||
return rootResponse.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
return response.StatusCode == http.StatusOK
|
||||
}
|
||||
|
||||
func getAdminInfoWidgetResponse(params systemApi.DashboardWidgetDetailsParams) (*models.WidgetDetails, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
prometheusURL := getPrometheusURL()
|
||||
prometheusJobID := getPrometheusJobID()
|
||||
|
||||
// We test if prometheus URL is reachable. this is meant to avoid unuseful calls and application hang.
|
||||
if !testPrometheusURL(prometheusURL) {
|
||||
error := errors.New("Prometheus URL is unreachable")
|
||||
return nil, prepareError(error)
|
||||
if !testPrometheusURL(ctx, prometheusURL) {
|
||||
return nil, ErrorWithContext(ctx, errors.New("Prometheus URL is unreachable"))
|
||||
}
|
||||
|
||||
return getWidgetDetails(prometheusURL, prometheusJobID, params.WidgetID, params.Step, params.Start, params.End)
|
||||
return getWidgetDetails(ctx, prometheusURL, prometheusJobID, params.WidgetID, params.Step, params.Start, params.End)
|
||||
}
|
||||
|
||||
func getWidgetDetails(prometheusURL string, prometheusJobID string, widgetID int32, step *int32, start *int64, end *int64) (*models.WidgetDetails, *models.Error) {
|
||||
func getWidgetDetails(ctx context.Context, prometheusURL string, prometheusJobID string, widgetID int32, step *int32, start *int64, end *int64) (*models.WidgetDetails, *models.Error) {
|
||||
labelResultsCh := make(chan LabelResults)
|
||||
|
||||
for _, lbl := range labels {
|
||||
@@ -982,7 +968,7 @@ func getWidgetDetails(prometheusURL string, prometheusJobID string, widgetID int
|
||||
endpoint := fmt.Sprintf("%s/api/v1/label/%s/values", prometheusURL, lbl.Name)
|
||||
|
||||
var response LabelResponse
|
||||
if unmarshalPrometheus(endpoint, &response) {
|
||||
if unmarshalPrometheus(ctx, endpoint, &response) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1054,7 +1040,7 @@ LabelsWaitLoop:
|
||||
endpoint := fmt.Sprintf("%s/api/v1/%s?query=%s%s", prometheusURL, apiType, url.QueryEscape(queryExpr), extraParamters)
|
||||
|
||||
var response PromResp
|
||||
if unmarshalPrometheus(endpoint, &response) {
|
||||
if unmarshalPrometheus(ctx, endpoint, &response) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -49,11 +49,11 @@ func registerInspectHandler(api *operations.ConsoleAPI) {
|
||||
}
|
||||
|
||||
func getInspectResult(session *models.Principal, params *inspectApi.InspectParams) (*[32]byte, io.ReadCloser, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, nil, prepareError(err)
|
||||
return nil, nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var cfg madmin.InspectOptions
|
||||
@@ -67,7 +67,7 @@ func getInspectResult(session *models.Principal, params *inspectApi.InspectParam
|
||||
k, r, err := adminClient.inspect(ctx, cfg)
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, prepareError(err)
|
||||
return nil, nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &k, r, nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
func registerNodesHandler(api *operations.ConsoleAPI) {
|
||||
|
||||
api.SystemListNodesHandler = systemApi.ListNodesHandlerFunc(func(params systemApi.ListNodesParams, session *models.Principal) middleware.Responder {
|
||||
listNodesResponse, err := getListNodesResponse(session)
|
||||
listNodesResponse, err := getListNodesResponse(session, params)
|
||||
if err != nil {
|
||||
return systemApi.NewListNodesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -37,12 +37,12 @@ func registerNodesHandler(api *operations.ConsoleAPI) {
|
||||
}
|
||||
|
||||
// getListNodesResponse returns a list of available node endpoints .
|
||||
func getListNodesResponse(session *models.Principal) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListNodesResponse(session *models.Principal, params systemApi.ListNodesParams) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
var nodeList []string
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of notification endpoints
|
||||
api.ConfigurationNotificationEndpointListHandler = configurationApi.NotificationEndpointListHandlerFunc(func(params configurationApi.NotificationEndpointListParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getNotificationEndpointsResponse(session)
|
||||
notifEndpoints, err := getNotificationEndpointsResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewNotificationEndpointListDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -37,7 +37,7 @@ func registerAdminNotificationEndpointsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// add a new notification endpoints
|
||||
api.ConfigurationAddNotificationEndpointHandler = configurationApi.AddNotificationEndpointHandlerFunc(func(params configurationApi.AddNotificationEndpointParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getAddNotificationEndpointResponse(session, ¶ms)
|
||||
notifEndpoints, err := getAddNotificationEndpointResponse(session, params)
|
||||
if err != nil {
|
||||
return configurationApi.NewAddNotificationEndpointDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -75,20 +75,20 @@ func getNotificationEndpoints(ctx context.Context, client MinioAdmin) (*models.N
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getNotificationEndpointsResponse(session *models.Principal) (*models.NotifEndpointResponse, *models.Error) {
|
||||
func getNotificationEndpointsResponse(session *models.Principal, params configurationApi.NotificationEndpointListParams) (*models.NotifEndpointResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
notfEndpointResp, err := getNotificationEndpoints(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
@@ -145,20 +145,20 @@ func addNotificationEndpoint(ctx context.Context, client MinioAdmin, params *con
|
||||
}
|
||||
|
||||
// getNotificationEndpointsResponse returns a list of notification endpoints in the instance
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params *configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, *models.Error) {
|
||||
func getAddNotificationEndpointResponse(session *models.Principal, params configurationApi.AddNotificationEndpointParams) (*models.SetNotificationEndpointResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
notfEndpointResp, err := addNotificationEndpoint(ctx, adminClient, params)
|
||||
notfEndpointResp, err := addNotificationEndpoint(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return notfEndpointResp, nil
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
policyApi "github.com/minio/console/restapi/operations/policy"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
iampolicy "github.com/minio/pkg/iam/policy"
|
||||
@@ -37,7 +36,7 @@ import (
|
||||
func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
// List Policies
|
||||
api.PolicyListPoliciesHandler = policyApi.ListPoliciesHandlerFunc(func(params policyApi.ListPoliciesParams, session *models.Principal) middleware.Responder {
|
||||
listPoliciesResponse, err := getListPoliciesResponse(session)
|
||||
listPoliciesResponse, err := getListPoliciesResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListPoliciesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -53,7 +52,7 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Add Policy
|
||||
api.PolicyAddPolicyHandler = policyApi.AddPolicyHandlerFunc(func(params policyApi.AddPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getAddPolicyResponse(session, params.Body)
|
||||
policyResponse, err := getAddPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewAddPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -68,55 +67,55 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Set Policy
|
||||
api.PolicySetPolicyHandler = policyApi.SetPolicyHandlerFunc(func(params policyApi.SetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetPolicyResponse(session, params.Body); err != nil {
|
||||
if err := getSetPolicyResponse(session, params); err != nil {
|
||||
return policyApi.NewSetPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return policyApi.NewSetPolicyNoContent()
|
||||
})
|
||||
// Set Policy Multiple User/Groups
|
||||
api.PolicySetPolicyMultipleHandler = policyApi.SetPolicyMultipleHandlerFunc(func(params policyApi.SetPolicyMultipleParams, session *models.Principal) middleware.Responder {
|
||||
if err := getSetPolicyMultipleResponse(session, params.Body); err != nil {
|
||||
if err := getSetPolicyMultipleResponse(session, params); err != nil {
|
||||
return policyApi.NewSetPolicyMultipleDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return policyApi.NewSetPolicyMultipleNoContent()
|
||||
})
|
||||
api.BucketListPoliciesWithBucketHandler = bucketApi.ListPoliciesWithBucketHandlerFunc(func(params bucketApi.ListPoliciesWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getListPoliciesWithBucketResponse(session, params.Bucket)
|
||||
policyResponse, err := getListPoliciesWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListPoliciesWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewListPoliciesWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketListAccessRulesWithBucketHandler = bucketApi.ListAccessRulesWithBucketHandlerFunc(func(params bucketApi.ListAccessRulesWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getListAccessRulesWithBucketResponse(session, params.Bucket)
|
||||
policyResponse, err := getListAccessRulesWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListAccessRulesWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewListAccessRulesWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketSetAccessRuleWithBucketHandler = bucketApi.SetAccessRuleWithBucketHandlerFunc(func(params bucketApi.SetAccessRuleWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getSetAccessRuleWithBucketResponse(session, params.Bucket, params.Prefixaccess)
|
||||
policyResponse, err := getSetAccessRuleWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewSetAccessRuleWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewSetAccessRuleWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.BucketDeleteAccessRuleWithBucketHandler = bucketApi.DeleteAccessRuleWithBucketHandlerFunc(func(params bucketApi.DeleteAccessRuleWithBucketParams, session *models.Principal) middleware.Responder {
|
||||
policyResponse, err := getDeleteAccessRuleWithBucketResponse(session, params.Bucket, params.Prefix)
|
||||
policyResponse, err := getDeleteAccessRuleWithBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteAccessRuleWithBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewDeleteAccessRuleWithBucketOK().WithPayload(policyResponse)
|
||||
})
|
||||
api.PolicyListUsersForPolicyHandler = policyApi.ListUsersForPolicyHandlerFunc(func(params policyApi.ListUsersForPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyUsersResponse, err := getListUsersForPolicyResponse(session, params.Policy)
|
||||
policyUsersResponse, err := getListUsersForPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListUsersForPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return policyApi.NewListUsersForPolicyOK().WithPayload(policyUsersResponse)
|
||||
})
|
||||
api.PolicyListGroupsForPolicyHandler = policyApi.ListGroupsForPolicyHandlerFunc(func(params policyApi.ListGroupsForPolicyParams, session *models.Principal) middleware.Responder {
|
||||
policyGroupsResponse, err := getListGroupsForPolicyResponse(session, params.Policy)
|
||||
policyGroupsResponse, err := getListGroupsForPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return policyApi.NewListGroupsForPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -124,12 +123,13 @@ func registersPoliciesHandler(api *operations.ConsoleAPI) {
|
||||
})
|
||||
}
|
||||
|
||||
func getListAccessRulesWithBucketResponse(session *models.Principal, bucket string) (*models.ListAccessRulesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListAccessRulesWithBucketResponse(session *models.Principal, params bucketApi.ListAccessRulesWithBucketParams) (*models.ListAccessRulesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucket := params.Bucket
|
||||
client, err := newS3BucketClient(session, bucket, "")
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
accessRules, _ := client.GetAccessRules(ctx)
|
||||
var accessRuleList []*models.AccessRule
|
||||
@@ -139,48 +139,51 @@ func getListAccessRulesWithBucketResponse(session *models.Principal, bucket stri
|
||||
return &models.ListAccessRulesResponse{AccessRules: accessRuleList}, nil
|
||||
}
|
||||
|
||||
func getSetAccessRuleWithBucketResponse(session *models.Principal, bucket string, prefixAccess *models.PrefixAccessPair) (bool, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getSetAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.SetAccessRuleWithBucketParams) (bool, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
client, err := newS3BucketClient(session, bucket, prefixAccess.Prefix)
|
||||
prefixAccess := params.Prefixaccess
|
||||
client, err := newS3BucketClient(session, params.Bucket, prefixAccess.Prefix)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
errorVal := client.SetAccess(ctx, prefixAccess.Access, false)
|
||||
if errorVal != nil {
|
||||
return false, prepareError(errorVal.Cause)
|
||||
return false, ErrorWithContext(ctx, errorVal.Cause)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getDeleteAccessRuleWithBucketResponse(session *models.Principal, bucket string, prefix *models.PrefixWrapper) (bool, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getDeleteAccessRuleWithBucketResponse(session *models.Principal, params bucketApi.DeleteAccessRuleWithBucketParams) (bool, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucket := params.Bucket
|
||||
prefix := params.Prefix
|
||||
client, err := newS3BucketClient(session, bucket, prefix.Prefix)
|
||||
if err != nil {
|
||||
return false, prepareError(err)
|
||||
return false, ErrorWithContext(ctx, err)
|
||||
}
|
||||
errorVal := client.SetAccess(ctx, "none", false)
|
||||
if errorVal != nil {
|
||||
return false, prepareError(errorVal.Cause)
|
||||
return false, ErrorWithContext(ctx, errorVal.Cause)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func getListPoliciesWithBucketResponse(session *models.Principal, bucket string) (*models.ListPoliciesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListPoliciesWithBucketResponse(session *models.Principal, params bucketApi.ListPoliciesWithBucketParams) (*models.ListPoliciesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
policies, err := listPoliciesWithBucket(ctx, bucket, adminClient)
|
||||
policies, err := listPoliciesWithBucket(ctx, params.Bucket, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listPoliciesResponse := &models.ListPoliciesResponse{
|
||||
@@ -205,18 +208,18 @@ func listPoliciesWithBucket(ctx context.Context, bucket string, client MinioAdmi
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policyMatchesBucket(policy, bucket) {
|
||||
if policyMatchesBucket(ctx, policy, bucket) {
|
||||
policies = append(policies, policy)
|
||||
}
|
||||
}
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func policyMatchesBucket(policy *models.Policy, bucket string) bool {
|
||||
func policyMatchesBucket(ctx context.Context, policy *models.Policy, bucket string) bool {
|
||||
policyData := &iampolicy.Policy{}
|
||||
err := json.Unmarshal([]byte(policy.Policy), policyData)
|
||||
if err != nil {
|
||||
LogError("error parsing policy: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error parsing policy: %v", err))
|
||||
return false
|
||||
}
|
||||
policyStatements := policyData.Statements
|
||||
@@ -253,12 +256,12 @@ func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, err
|
||||
}
|
||||
|
||||
// getListPoliciesResponse performs listPolicies() and serializes it to the handler's output
|
||||
func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListPoliciesResponse(session *models.Principal, params policyApi.ListPoliciesParams) (*models.ListPoliciesResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -266,7 +269,7 @@ func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesRes
|
||||
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listPoliciesResponse := &models.ListPoliciesResponse{
|
||||
@@ -277,19 +280,20 @@ func getListPoliciesResponse(session *models.Principal) (*models.ListPoliciesRes
|
||||
}
|
||||
|
||||
// getListUsersForPoliciesResponse performs lists users affected by a given policy.
|
||||
func getListUsersForPolicyResponse(session *models.Principal, policy string) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListUsersForPolicyResponse(session *models.Principal, params policyApi.ListUsersForPolicyParams) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
policy := params.Policy
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
@@ -298,15 +302,11 @@ func getListUsersForPolicyResponse(session *models.Principal, policy string) ([]
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, &models.Error{
|
||||
Code: int32(404),
|
||||
Message: swag.String("Policy does not exist"),
|
||||
DetailedMessage: swag.String(fmt.Sprintf("The policy %s does not extist", policy)),
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
}
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var filteredUsers []string
|
||||
@@ -322,19 +322,20 @@ func getListUsersForPolicyResponse(session *models.Principal, policy string) ([]
|
||||
return filteredUsers, nil
|
||||
}
|
||||
|
||||
func getListGroupsForPolicyResponse(session *models.Principal, policy string) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListGroupsForPolicyResponse(session *models.Principal, params policyApi.ListGroupsForPolicyParams) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
policy := params.Policy
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policies, err := listPolicies(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
found := false
|
||||
for i := range policies {
|
||||
@@ -343,23 +344,19 @@ func getListGroupsForPolicyResponse(session *models.Principal, policy string) ([
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return nil, &models.Error{
|
||||
Code: int32(404),
|
||||
Message: swag.String("Policy does not exist"),
|
||||
DetailedMessage: swag.String(fmt.Sprintf("The policy %s does not extist", policy)),
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyNotFound, fmt.Errorf("the policy %s does not exist", policy))
|
||||
}
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var filteredGroups []string
|
||||
for _, group := range groups {
|
||||
info, err := groupInfo(ctx, adminClient, group)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
if info.Policy == policy {
|
||||
filteredGroups = append(filteredGroups, group)
|
||||
@@ -380,21 +377,21 @@ func removePolicy(ctx context.Context, client MinioAdmin, name string) error {
|
||||
|
||||
// getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output
|
||||
func getRemovePolicyResponse(session *models.Principal, params policyApi.RemovePolicyParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return prepareError(errPolicyNameNotInRequest)
|
||||
return ErrorWithContext(ctx, ErrPolicyNameNotInRequest)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := removePolicy(ctx, adminClient, params.Name); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -419,23 +416,22 @@ func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*mo
|
||||
}
|
||||
|
||||
// getAddPolicyResponse performs addPolicy() and serializes it to the handler's output
|
||||
func getAddPolicyResponse(session *models.Principal, params *models.AddPolicyRequest) (*models.Policy, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getAddPolicyResponse(session *models.Principal, params policyApi.AddPolicyParams) (*models.Policy, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params == nil {
|
||||
return nil, prepareError(errPolicyBodyNotInRequest)
|
||||
if params.Body == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyBodyNotInRequest)
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policy, err := addPolicy(ctx, adminClient, *params.Name, *params.Policy)
|
||||
policy, err := addPolicy(ctx, adminClient, *params.Body.Name, *params.Body.Policy)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
@@ -458,18 +454,18 @@ func policyInfo(ctx context.Context, client MinioAdmin, name string) (*models.Po
|
||||
|
||||
// getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output
|
||||
func getPolicyInfoResponse(session *models.Principal, params policyApi.PolicyInfoParams) (*models.Policy, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
policy, err := policyInfo(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return policy, nil
|
||||
}
|
||||
@@ -484,40 +480,37 @@ func setPolicy(ctx context.Context, client MinioAdmin, name, entityName string,
|
||||
}
|
||||
|
||||
// getSetPolicyResponse() performs setPolicy() and serializes it to the handler's output
|
||||
func getSetPolicyResponse(session *models.Principal, params *models.SetPolicyNameRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getSetPolicyResponse(session *models.Principal, params policyApi.SetPolicyParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// if len(params.Name) == 0 {
|
||||
// return prepareError(errPolicyNameNotInRequest)
|
||||
// }
|
||||
// Removing this section
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := setPolicy(ctx, adminClient, strings.Join(params.Name, ","), *params.EntityName, *params.EntityType); err != nil {
|
||||
return prepareError(err)
|
||||
if err := setPolicy(ctx, adminClient, strings.Join(params.Body.Name, ","), *params.Body.EntityName, *params.Body.EntityType); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSetPolicyMultipleResponse(session *models.Principal, params *models.SetPolicyMultipleNameRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getSetPolicyMultipleResponse(session *models.Principal, params policyApi.SetPolicyMultipleParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := setPolicyMultipleEntities(ctx, adminClient, strings.Join(params.Name, ","), params.Users, params.Groups); err != nil {
|
||||
return prepareError(err)
|
||||
if err := setPolicyMultipleEntities(ctx, adminClient, strings.Join(params.Body.Name, ","), params.Body.Users, params.Body.Groups); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -300,6 +300,7 @@ func Test_SetPolicyMultiple(t *testing.T) {
|
||||
|
||||
func Test_policyMatchesBucket(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *models.Policy
|
||||
bucket string
|
||||
}
|
||||
@@ -310,7 +311,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "Test1",
|
||||
args: args{policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
@@ -334,7 +335,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Test2",
|
||||
args: args{policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
@@ -352,7 +353,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Test3",
|
||||
args: args{policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
@@ -388,7 +389,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Test4",
|
||||
args: args{policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
args: args{ctx: context.Background(), policy: &models.Policy{Name: "consoleAdmin", Policy: `{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
@@ -407,7 +408,7 @@ func Test_policyMatchesBucket(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := policyMatchesBucket(tt.args.policy, tt.args.bucket); got != tt.want {
|
||||
if got := policyMatchesBucket(tt.args.ctx, tt.args.policy, tt.args.bucket); got != tt.want {
|
||||
t.Errorf("policyMatchesBucket() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
// Start Profiling
|
||||
api.ProfileProfilingStartHandler = profileApi.ProfilingStartHandlerFunc(func(params profileApi.ProfilingStartParams, session *models.Principal) middleware.Responder {
|
||||
profilingStartResponse, err := getProfilingStartResponse(session, params.Body)
|
||||
profilingStartResponse, err := getProfilingStartResponse(session, params)
|
||||
if err != nil {
|
||||
return profileApi.NewProfilingStartDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Stop and download profiling data
|
||||
api.ProfileProfilingStopHandler = profileApi.ProfilingStopHandlerFunc(func(params profileApi.ProfilingStopParams, session *models.Principal) middleware.Responder {
|
||||
profilingStopResponse, err := getProfilingStopResponse(session)
|
||||
profilingStopResponse, err := getProfilingStopResponse(session, params)
|
||||
if err != nil {
|
||||
return profileApi.NewProfilingStopDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
// {
|
||||
// "Success": true,
|
||||
// "nodeName": "127.0.0.1:9000"
|
||||
// "error": ""
|
||||
// "errors": ""
|
||||
// }
|
||||
func startProfiling(ctx context.Context, client MinioAdmin, profilerType string) ([]*models.StartProfilingItem, error) {
|
||||
profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(profilerType))
|
||||
@@ -82,22 +82,22 @@ func startProfiling(ctx context.Context, client MinioAdmin, profilerType string)
|
||||
}
|
||||
|
||||
// getProfilingStartResponse performs startProfiling() and serializes it to the handler's output
|
||||
func getProfilingStartResponse(session *models.Principal, params *models.ProfilingStartRequest) (*models.StartProfilingList, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getProfilingStartResponse(session *models.Principal, params profileApi.ProfilingStartParams) (*models.StartProfilingList, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params == nil {
|
||||
return nil, prepareError(errPolicyBodyNotInRequest)
|
||||
if params.Body == nil {
|
||||
return nil, ErrorWithContext(ctx, ErrPolicyBodyNotInRequest)
|
||||
}
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
profilingItems, err := startProfiling(ctx, adminClient, *params.Type)
|
||||
profilingItems, err := startProfiling(ctx, adminClient, *params.Body.Type)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
profilingList := &models.StartProfilingList{
|
||||
StartResults: profilingItems,
|
||||
@@ -117,18 +117,18 @@ func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error
|
||||
}
|
||||
|
||||
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
|
||||
func getProfilingStopResponse(session *models.Principal) (io.ReadCloser, *models.Error) {
|
||||
ctx := context.Background()
|
||||
func getProfilingStopResponse(session *models.Principal, params profileApi.ProfilingStopParams) (io.ReadCloser, *models.Error) {
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
profilingData, err := stopProfiling(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return profilingData, nil
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@ type RemoteBucketResult struct {
|
||||
func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
// return list of remote buckets
|
||||
api.BucketListRemoteBucketsHandler = bucketApi.ListRemoteBucketsHandlerFunc(func(params bucketApi.ListRemoteBucketsParams, session *models.Principal) middleware.Responder {
|
||||
listResp, err := getListRemoteBucketsResponse(session)
|
||||
listResp, err := getListRemoteBucketsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListRemoteBucketsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewListRemoteBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewListRemoteBucketsOK().WithPayload(listResp)
|
||||
})
|
||||
@@ -54,7 +54,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketRemoteBucketDetailsHandler = bucketApi.RemoteBucketDetailsHandlerFunc(func(params bucketApi.RemoteBucketDetailsParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getRemoteBucketDetailsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewRemoteBucketDetailsDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewRemoteBucketDetailsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewRemoteBucketDetailsOK().WithPayload(response)
|
||||
})
|
||||
@@ -63,7 +63,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketDeleteRemoteBucketHandler = bucketApi.DeleteRemoteBucketHandlerFunc(func(params bucketApi.DeleteRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getDeleteRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteRemoteBucketDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewDeleteRemoteBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewDeleteRemoteBucketNoContent()
|
||||
})
|
||||
@@ -72,7 +72,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketAddRemoteBucketHandler = bucketApi.AddRemoteBucketHandlerFunc(func(params bucketApi.AddRemoteBucketParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddRemoteBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewAddRemoteBucketDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewAddRemoteBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewAddRemoteBucketCreated()
|
||||
})
|
||||
@@ -82,7 +82,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
response, err := setMultiBucketReplicationResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return bucketApi.NewSetMultiBucketReplicationDefault(500).WithPayload(err)
|
||||
return bucketApi.NewSetMultiBucketReplicationDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return bucketApi.NewSetMultiBucketReplicationOK().WithPayload(response)
|
||||
@@ -93,7 +93,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
response, err := listExternalBucketsResponse(params)
|
||||
|
||||
if err != nil {
|
||||
return bucketApi.NewListExternalBucketsDefault(500).WithPayload(err)
|
||||
return bucketApi.NewListExternalBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return bucketApi.NewListExternalBucketsOK().WithPayload(response)
|
||||
@@ -104,7 +104,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
err := deleteReplicationRuleResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteBucketReplicationRuleDefault(500).WithPayload(err)
|
||||
return bucketApi.NewDeleteBucketReplicationRuleDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteBucketReplicationRuleNoContent()
|
||||
@@ -115,7 +115,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
err := deleteBucketReplicationRulesResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteAllReplicationRulesDefault(500).WithPayload(err)
|
||||
return bucketApi.NewDeleteAllReplicationRulesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteAllReplicationRulesNoContent()
|
||||
@@ -126,7 +126,7 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
err := deleteSelectedReplicationRulesResponse(session, params)
|
||||
|
||||
if err != nil {
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesDefault(500).WithPayload(err)
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return bucketApi.NewDeleteSelectedReplicationRulesNoContent()
|
||||
@@ -136,25 +136,23 @@ func registerAdminBucketRemoteHandlers(api *operations.ConsoleAPI) {
|
||||
api.BucketUpdateMultiBucketReplicationHandler = bucketApi.UpdateMultiBucketReplicationHandlerFunc(func(params bucketApi.UpdateMultiBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
err := updateBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewUpdateMultiBucketReplicationDefault(500).WithPayload(err)
|
||||
return bucketApi.NewUpdateMultiBucketReplicationDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewUpdateMultiBucketReplicationCreated()
|
||||
})
|
||||
}
|
||||
|
||||
func getListRemoteBucketsResponse(session *models.Principal) (*models.ListRemoteBucketsResponse, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListRemoteBucketsResponse(session *models.Principal, params bucketApi.ListRemoteBucketsParams) (*models.ListRemoteBucketsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Madmin Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
buckets, err := listRemoteBuckets(ctx, adminClient)
|
||||
if err != nil {
|
||||
LogError("error listing remote buckets: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error listing remote buckets: %v", err))
|
||||
}
|
||||
return &models.ListRemoteBucketsResponse{
|
||||
Buckets: buckets,
|
||||
@@ -162,55 +160,49 @@ func getListRemoteBucketsResponse(session *models.Principal) (*models.ListRemote
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getRemoteBucketDetailsResponse(session *models.Principal, params bucketApi.RemoteBucketDetailsParams) (*models.RemoteBucket, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Madmin Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
bucket, err := getRemoteBucket(ctx, adminClient, params.Name)
|
||||
if err != nil {
|
||||
LogError("error getting remote bucket details: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error getting remote bucket details: %v", err))
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getDeleteRemoteBucketResponse(session *models.Principal, params bucketApi.DeleteRemoteBucketParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Madmin Client: %v", err)
|
||||
return err
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
err = deleteRemoteBucket(ctx, adminClient, params.SourceBucketName, params.Arn)
|
||||
if err != nil {
|
||||
LogError("error deleting remote bucket: %v", err)
|
||||
return err
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error deleting remote bucket: %v", err))
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getAddRemoteBucketResponse(session *models.Principal, params bucketApi.AddRemoteBucketParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Madmin Client: %v", err)
|
||||
return err
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
_, err = addRemoteBucket(ctx, adminClient, *params.Body)
|
||||
if err != nil {
|
||||
LogError("error adding remote bucket: %v", err)
|
||||
return err
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error adding remote bucket: %v", err))
|
||||
}
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
func listRemoteBuckets(ctx context.Context, client MinioAdmin) ([]*models.RemoteBucket, error) {
|
||||
@@ -309,7 +301,7 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi
|
||||
// we will tolerate this call failing
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
LogError("error fetching replication configuration for bucket %s: %v", bucketName, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err))
|
||||
}
|
||||
|
||||
// add rule
|
||||
@@ -328,7 +320,7 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix)
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error creating S3Client: %v", err))
|
||||
return err
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
@@ -365,7 +357,7 @@ func addBucketReplicationItem(ctx context.Context, session *models.Principal, mi
|
||||
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
LogError("error creating replication for bucket:", err2.Cause)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error creating replication for bucket: %v", err2.Cause))
|
||||
return err2.Cause
|
||||
}
|
||||
return nil
|
||||
@@ -375,15 +367,14 @@ func editBucketReplicationItem(ctx context.Context, session *models.Principal, m
|
||||
// we will tolerate this call failing
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
LogError("error fetching replication configuration for bucket %s: %v", bucketName, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error fetching replication configuration for bucket %s: %v", bucketName, err))
|
||||
}
|
||||
|
||||
maxPrio := int(priority)
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, prefix)
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -432,8 +423,7 @@ func editBucketReplicationItem(ctx context.Context, session *models.Principal, m
|
||||
|
||||
err2 := mcClient.setReplication(ctx, &cfg, opts)
|
||||
if err2 != nil {
|
||||
LogError("error modifying replication for bucket:", err2.Cause)
|
||||
return err2.Cause
|
||||
return fmt.Errorf("error modifying replication for bucket: %v", err2.Cause)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -516,20 +506,18 @@ func setMultiBucketReplication(ctx context.Context, session *models.Principal, c
|
||||
}
|
||||
|
||||
func setMultiBucketReplicationResponse(session *models.Principal, params bucketApi.SetMultiBucketReplicationParams) (*models.MultiBucketResponseState, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Madmin Client:", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating Madmin Client: %v", err))
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client:", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err))
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -538,8 +526,7 @@ func setMultiBucketReplicationResponse(session *models.Principal, params bucketA
|
||||
replicationResults := setMultiBucketReplication(ctx, session, adminClient, mnClient, params)
|
||||
|
||||
if replicationResults == nil {
|
||||
err = errors.New("error setting buckets replication")
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, errors.New("error setting buckets replication"))
|
||||
}
|
||||
|
||||
resParsed := []*models.MultiBucketResponseItem{}
|
||||
@@ -562,18 +549,18 @@ func setMultiBucketReplicationResponse(session *models.Principal, params bucketA
|
||||
}
|
||||
|
||||
func listExternalBucketsResponse(params bucketApi.ListExternalBucketsParams) (*models.ListBucketsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
remoteAdmin, err := newAdminFromCreds(*params.Body.AccessKey, *params.Body.SecretKey, *params.Body.TargetURL, *params.Body.UseTLS)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
remoteClient := AdminClient{Client: remoteAdmin}
|
||||
buckets, err := getAccountBuckets(ctx, remoteClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -610,8 +597,7 @@ func getARNsFromIDs(conf *replication.Config, rules []string) []string {
|
||||
func deleteReplicationRule(ctx context.Context, session *models.Principal, bucketName, ruleID string) error {
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -619,19 +605,17 @@ func deleteReplicationRule(ctx context.Context, session *models.Principal, bucke
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
LogError("error versioning bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Admin Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
@@ -659,8 +643,7 @@ func deleteReplicationRule(ctx context.Context, session *models.Principal, bucke
|
||||
func deleteAllReplicationRules(ctx context.Context, session *models.Principal, bucketName string) error {
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -668,8 +651,7 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -677,13 +659,12 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
LogError("error versioning bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Admin Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
@@ -707,8 +688,7 @@ func deleteAllReplicationRules(ctx context.Context, session *models.Principal, b
|
||||
func deleteSelectedReplicationRules(ctx context.Context, session *models.Principal, bucketName string, rules []string) error {
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating MinIO Client: %v", err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -716,13 +696,12 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
|
||||
cfg, err := minClient.getBucketReplication(ctx, bucketName)
|
||||
if err != nil {
|
||||
LogError("error versioning bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating S3Client: %v", err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -730,8 +709,7 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating Admin Client: %v", err)
|
||||
return err
|
||||
return fmt.Errorf("error creating Admin Client: %v", err)
|
||||
}
|
||||
admClient := AdminClient{Client: mAdmin}
|
||||
|
||||
@@ -756,49 +734,48 @@ func deleteSelectedReplicationRules(ctx context.Context, session *models.Princip
|
||||
}
|
||||
|
||||
func deleteReplicationRuleResponse(session *models.Principal, params bucketApi.DeleteBucketReplicationRuleParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
err := deleteReplicationRule(ctx, session, params.BucketName, params.RuleID)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteBucketReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteAllReplicationRulesParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
err := deleteAllReplicationRules(ctx, session, params.BucketName)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteSelectedReplicationRulesResponse(session *models.Principal, params bucketApi.DeleteSelectedReplicationRulesParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
err := deleteSelectedReplicationRules(ctx, session, params.BucketName, params.Rules.Rules)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func updateBucketReplicationResponse(session *models.Principal, params bucketApi.UpdateMultiBucketReplicationParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client:", err)
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -822,7 +799,7 @@ func updateBucketReplicationResponse(session *models.Principal, params bucketApi
|
||||
params.Body.StorageClass)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -31,26 +31,24 @@ func registerSiteReplicationStatusHandler(api *operations.ConsoleAPI) {
|
||||
api.SiteReplicationGetSiteReplicationStatusHandler = siteRepApi.GetSiteReplicationStatusHandlerFunc(func(params siteRepApi.GetSiteReplicationStatusParams, session *models.Principal) middleware.Responder {
|
||||
rInfo, err := getSRStatusResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewGetSiteReplicationStatusDefault(500).WithPayload(prepareError(err))
|
||||
return siteRepApi.NewGetSiteReplicationStatusDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return siteRepApi.NewGetSiteReplicationStatusOK().WithPayload(rInfo)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func getSRStatusResponse(session *models.Principal, params siteRepApi.GetSiteReplicationStatusParams) (info *models.SiteReplicationStatusResponse, err error) {
|
||||
|
||||
func getSRStatusResponse(session *models.Principal, params siteRepApi.GetSiteReplicationStatusParams) (*models.SiteReplicationStatusResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx := context.Background()
|
||||
|
||||
res, err := getSRStats(ctx, adminClient, params)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
func registerServiceHandlers(api *operations.ConsoleAPI) {
|
||||
// Restart Service
|
||||
api.ServiceRestartServiceHandler = svcApi.RestartServiceHandlerFunc(func(params svcApi.RestartServiceParams, session *models.Principal) middleware.Responder {
|
||||
if err := getRestartServiceResponse(session); err != nil {
|
||||
if err := getRestartServiceResponse(session, params); err != nil {
|
||||
return svcApi.NewRestartServiceDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return svcApi.NewRestartServiceNoContent()
|
||||
@@ -59,19 +59,19 @@ func serviceRestart(ctx context.Context, client MinioAdmin) error {
|
||||
}
|
||||
|
||||
// getRestartServiceResponse performs serviceRestart()
|
||||
func getRestartServiceResponse(session *models.Principal) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getRestartServiceResponse(session *models.Principal, params svcApi.RestartServiceParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -37,10 +37,9 @@ func (ac adminClientMock) serviceRestart(ctx context.Context) error {
|
||||
func TestServiceRestart(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
adminClient := adminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
function := "serviceRestart()"
|
||||
// Test-1 : serviceRestart() restart services no error
|
||||
// Test-1 : serviceRestart() restart services no errors
|
||||
// mock function response from listGroups()
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
return nil
|
||||
@@ -49,11 +48,11 @@ func TestServiceRestart(t *testing.T) {
|
||||
return madmin.InfoMessage{}, nil
|
||||
}
|
||||
if err := serviceRestart(ctx, adminClient); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 : serviceRestart() returns error on client.serviceRestart call
|
||||
// and see that the error is handled correctly and returned
|
||||
// Test-2 : serviceRestart() returns errors on client.serviceRestart call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
@@ -64,8 +63,8 @@ func TestServiceRestart(t *testing.T) {
|
||||
assert.Equal("error", err.Error())
|
||||
}
|
||||
|
||||
// Test-3 : serviceRestart() returns error on client.serverInfo() call
|
||||
// and see that the error is handled correctly and returned
|
||||
// Test-3 : serviceRestart() returns errors on client.serverInfo() call
|
||||
// and see that the errors is handled correctly and returned
|
||||
minioServiceRestartMock = func(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -29,105 +29,98 @@ import (
|
||||
func registerSiteReplicationHandler(api *operations.ConsoleAPI) {
|
||||
|
||||
api.SiteReplicationGetSiteReplicationInfoHandler = siteRepApi.GetSiteReplicationInfoHandlerFunc(func(params siteRepApi.GetSiteReplicationInfoParams, session *models.Principal) middleware.Responder {
|
||||
rInfo, err := getSRInfoResponse(session)
|
||||
rInfo, err := getSRInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewGetSiteReplicationInfoDefault(500).WithPayload(prepareError(err))
|
||||
return siteRepApi.NewGetSiteReplicationInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return siteRepApi.NewGetSiteReplicationInfoOK().WithPayload(rInfo)
|
||||
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationInfoAddHandler = siteRepApi.SiteReplicationInfoAddHandlerFunc(func(params siteRepApi.SiteReplicationInfoAddParams, session *models.Principal) middleware.Responder {
|
||||
eInfo, err := getSRAddResponse(session, ¶ms)
|
||||
eInfo, err := getSRAddResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationInfoAddDefault(500).WithPayload(err)
|
||||
return siteRepApi.NewSiteReplicationInfoAddDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return siteRepApi.NewSiteReplicationInfoAddOK().WithPayload(eInfo)
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationRemoveHandler = siteRepApi.SiteReplicationRemoveHandlerFunc(func(params siteRepApi.SiteReplicationRemoveParams, session *models.Principal) middleware.Responder {
|
||||
remRes, err := getSRRemoveResponse(session, ¶ms)
|
||||
remRes, err := getSRRemoveResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(500).WithPayload(err)
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return siteRepApi.NewSiteReplicationRemoveNoContent().WithPayload(remRes)
|
||||
})
|
||||
|
||||
api.SiteReplicationSiteReplicationEditHandler = siteRepApi.SiteReplicationEditHandlerFunc(func(params siteRepApi.SiteReplicationEditParams, session *models.Principal) middleware.Responder {
|
||||
|
||||
eInfo, err := getSREditResponse(session, ¶ms)
|
||||
eInfo, err := getSREditResponse(session, params)
|
||||
if err != nil {
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(500).WithPayload(err)
|
||||
return siteRepApi.NewSiteReplicationRemoveDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
|
||||
return siteRepApi.NewSiteReplicationEditOK().WithPayload(eInfo)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func getSRInfoResponse(session *models.Principal) (info *models.SiteReplicationInfoResponse, err error) {
|
||||
func getSRInfoResponse(session *models.Principal, params siteRepApi.GetSiteReplicationInfoParams) (*models.SiteReplicationInfoResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
res, err := getSRConfig(ctx, adminClient)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
|
||||
}
|
||||
func getSRAddResponse(session *models.Principal, params *siteRepApi.SiteReplicationInfoAddParams) (*models.SiteReplicationAddResponse, *models.Error) {
|
||||
|
||||
func getSRAddResponse(session *models.Principal, params siteRepApi.SiteReplicationInfoAddParams) (*models.SiteReplicationAddResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
res, err := addSiteReplication(ctx, adminClient, params)
|
||||
res, err := addSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return res, nil
|
||||
|
||||
}
|
||||
func getSREditResponse(session *models.Principal, params *siteRepApi.SiteReplicationEditParams) (*models.PeerSiteEditResponse, *models.Error) {
|
||||
func getSREditResponse(session *models.Principal, params siteRepApi.SiteReplicationEditParams) (*models.PeerSiteEditResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
eRes, err := editSiteReplication(ctx, adminClient, params)
|
||||
|
||||
eRes, err := editSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return eRes, nil
|
||||
|
||||
}
|
||||
func getSRRemoveResponse(session *models.Principal, params *siteRepApi.SiteReplicationRemoveParams) (*models.PeerSiteRemoveResponse, *models.Error) {
|
||||
func getSRRemoveResponse(session *models.Principal, params siteRepApi.SiteReplicationRemoveParams) (*models.PeerSiteRemoveResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
rRes, err := removeSiteReplication(ctx, adminClient, params)
|
||||
rRes, err := removeSiteReplication(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return rRes, nil
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
@@ -61,7 +61,7 @@ func registerSubnetHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Get subnet info
|
||||
api.SubnetSubnetInfoHandler = subnetApi.SubnetInfoHandlerFunc(func(params subnetApi.SubnetInfoParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetInfoResponse(session)
|
||||
resp, err := GetSubnetInfoResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetInfoDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func registerSubnetHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Get subnet registration token
|
||||
api.SubnetSubnetRegTokenHandler = subnetApi.SubnetRegTokenHandlerFunc(func(params subnetApi.SubnetRegTokenParams, session *models.Principal) middleware.Responder {
|
||||
resp, err := GetSubnetRegTokenResponse(session)
|
||||
resp, err := GetSubnetRegTokenResponse(session, params)
|
||||
if err != nil {
|
||||
return subnetApi.NewSubnetRegTokenDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func SubnetRegisterWithAPIKey(ctx context.Context, minioClient MinioAdmin, apiKe
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func SubnetLogin(client utils.HTTPClientI, username, password string) (string, string, error) {
|
||||
func SubnetLogin(client xhttp.ClientI, username, password string) (string, string, error) {
|
||||
tokens, err := subnet.Login(client, username, password)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
@@ -117,22 +117,22 @@ func SubnetLogin(client utils.HTTPClientI, username, password string) (string, s
|
||||
}
|
||||
|
||||
func GetSubnetLoginResponse(session *models.Principal, params subnetApi.SubnetLoginParams) (*models.SubnetLoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
minioClient := AdminClient{Client: mAdmin}
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
apiKey := params.Body.APIKey
|
||||
if apiKey != "" {
|
||||
registered, err := SubnetRegisterWithAPIKey(ctx, minioClient, apiKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
Registered: registered,
|
||||
@@ -144,7 +144,7 @@ func GetSubnetLoginResponse(session *models.Principal, params subnetApi.SubnetLo
|
||||
if username != "" && password != "" {
|
||||
token, mfa, err := SubnetLogin(subnetHTTPClient, username, password)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetLoginResponse{
|
||||
MfaToken: mfa,
|
||||
@@ -152,7 +152,7 @@ func GetSubnetLoginResponse(session *models.Principal, params subnetApi.SubnetLo
|
||||
Organizations: []*models.SubnetOrganization{},
|
||||
}, nil
|
||||
}
|
||||
return nil, prepareError(ErrorGeneric)
|
||||
return nil, ErrorWithContext(ctx, ErrDefault)
|
||||
}
|
||||
|
||||
type SubnetRegistration struct {
|
||||
@@ -161,7 +161,7 @@ type SubnetRegistration struct {
|
||||
Organizations []models.SubnetOrganization
|
||||
}
|
||||
|
||||
func SubnetLoginWithMFA(client utils.HTTPClientI, username, mfaToken, otp string) (*models.SubnetLoginResponse, error) {
|
||||
func SubnetLoginWithMFA(client xhttp.ClientI, username, mfaToken, otp string) (*models.SubnetLoginResponse, error) {
|
||||
tokens, err := subnet.LoginWithMFA(client, username, mfaToken, otp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -180,7 +180,7 @@ func SubnetLoginWithMFA(client utils.HTTPClientI, username, mfaToken, otp string
|
||||
}
|
||||
|
||||
// GetSubnetHTTPClient will return a client with proxy if configured, otherwise will return the default console http client
|
||||
func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*utils.HTTPClient, error) {
|
||||
func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*xhttp.Client, error) {
|
||||
var subnetHTTPClient *http.Client
|
||||
var proxy string
|
||||
envProxy := getSubnetProxy()
|
||||
@@ -194,7 +194,7 @@ func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*utils.HT
|
||||
proxy = envProxy
|
||||
}
|
||||
if proxy != "" {
|
||||
transport := prepareSTSClientTransport(false)
|
||||
transport := PrepareSTSClientTransport(false)
|
||||
subnetHTTPClient = &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
@@ -206,27 +206,27 @@ func GetSubnetHTTPClient(ctx context.Context, minioClient MinioAdmin) (*utils.HT
|
||||
} else {
|
||||
subnetHTTPClient = GetConsoleHTTPClient()
|
||||
}
|
||||
clientI := &utils.HTTPClient{
|
||||
clientI := &xhttp.Client{
|
||||
Client: subnetHTTPClient,
|
||||
}
|
||||
return clientI, nil
|
||||
}
|
||||
|
||||
func GetSubnetLoginWithMFAResponse(session *models.Principal, params subnetApi.SubnetLoginMFAParams) (*models.SubnetLoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
minioClient := AdminClient{Client: mAdmin}
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, minioClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
resp, err := SubnetLoginWithMFA(subnetHTTPClient, *params.Body.Username, *params.Body.MfaToken, *params.Body.Otp)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func GetSubnetKeyFromMinIOConfig(ctx context.Context, minioClient MinioAdmin) (*
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func GetSubnetRegister(ctx context.Context, minioClient MinioAdmin, httpClient utils.HTTPClientI, params subnetApi.SubnetRegisterParams) error {
|
||||
func GetSubnetRegister(ctx context.Context, minioClient MinioAdmin, httpClient xhttp.ClientI, params subnetApi.SubnetRegisterParams) error {
|
||||
serverInfo, err := minioClient.serverInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -280,45 +280,45 @@ func GetSubnetRegister(ctx context.Context, minioClient MinioAdmin, httpClient u
|
||||
}
|
||||
|
||||
func GetSubnetRegisterResponse(session *models.Principal, params subnetApi.SubnetRegisterParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
subnetHTTPClient, err := GetSubnetHTTPClient(ctx, adminClient)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
err = GetSubnetRegister(ctx, adminClient, subnetHTTPClient, params)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetSubnetInfoResponse(session *models.Principal) (*models.License, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func GetSubnetInfoResponse(session *models.Principal, params subnetApi.SubnetInfoParams) (*models.License, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
subnetTokens, err := GetSubnetKeyFromMinIOConfig(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
if subnetTokens.APIKey == "" {
|
||||
return nil, prepareError(errLicenseNotFound)
|
||||
return nil, ErrorWithContext(ctx, ErrLicenseNotFound)
|
||||
}
|
||||
client := &utils.HTTPClient{
|
||||
client := &xhttp.Client{
|
||||
Client: GetConsoleHTTPClient(),
|
||||
}
|
||||
licenseInfo, err := subnet.ParseLicense(client, subnetTokens.License)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
license := &models.License{
|
||||
Email: licenseInfo.Email,
|
||||
@@ -345,17 +345,17 @@ func GetSubnetRegToken(ctx context.Context, minioClient MinioAdmin) (string, err
|
||||
return regToken, nil
|
||||
}
|
||||
|
||||
func GetSubnetRegTokenResponse(session *models.Principal) (*models.SubnetRegTokenResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func GetSubnetRegTokenResponse(session *models.Principal, params subnetApi.SubnetRegTokenParams) (*models.SubnetRegTokenResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
token, err := GetSubnetRegToken(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.SubnetRegTokenResponse{
|
||||
RegToken: token,
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
func registerAdminTiersHandlers(api *operations.ConsoleAPI) {
|
||||
// return a list of notification endpoints
|
||||
api.TieringTiersListHandler = tieringApi.TiersListHandlerFunc(func(params tieringApi.TiersListParams, session *models.Principal) middleware.Responder {
|
||||
tierList, err := getTiersResponse(session)
|
||||
tierList, err := getTiersResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewTiersListDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// add a new tiers
|
||||
api.TieringAddTierHandler = tieringApi.AddTierHandlerFunc(func(params tieringApi.AddTierParams, session *models.Principal) middleware.Responder {
|
||||
err := getAddTierResponse(session, ¶ms)
|
||||
err := getAddTierResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewAddTierDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get a tier
|
||||
api.TieringGetTierHandler = tieringApi.GetTierHandlerFunc(func(params tieringApi.GetTierParams, session *models.Principal) middleware.Responder {
|
||||
notifEndpoints, err := getGetTierResponse(session, ¶ms)
|
||||
notifEndpoints, err := getGetTierResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewGetTierDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -56,7 +56,7 @@ func registerAdminTiersHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// edit credentials for a tier
|
||||
api.TieringEditTierCredentialsHandler = tieringApi.EditTierCredentialsHandlerFunc(func(params tieringApi.EditTierCredentialsParams, session *models.Principal) middleware.Responder {
|
||||
err := getEditTierCredentialsResponse(session, ¶ms)
|
||||
err := getEditTierCredentialsResponse(session, params)
|
||||
if err != nil {
|
||||
return tieringApi.NewEditTierCredentialsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -141,21 +141,20 @@ func getTiers(ctx context.Context, client MinioAdmin) (*models.TierListResponse,
|
||||
}
|
||||
|
||||
// getTiersResponse returns a response with a list of tiers
|
||||
func getTiersResponse(session *models.Principal) (*models.TierListResponse, *models.Error) {
|
||||
func getTiersResponse(session *models.Principal, params tieringApi.TiersListParams) (*models.TierListResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
tiersResp, err := getTiers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return tiersResp, nil
|
||||
}
|
||||
@@ -231,20 +230,21 @@ func addTier(ctx context.Context, client MinioAdmin, params *tieringApi.AddTierP
|
||||
}
|
||||
|
||||
// getAddTierResponse returns the response of admin tier
|
||||
func getAddTierResponse(session *models.Principal, params *tieringApi.AddTierParams) *models.Error {
|
||||
func getAddTierResponse(session *models.Principal, params tieringApi.AddTierParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// serialize output
|
||||
errTier := addTier(ctx, adminClient, params)
|
||||
errTier := addTier(ctx, adminClient, ¶ms)
|
||||
if errTier != nil {
|
||||
return prepareError(errTier)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -309,25 +309,24 @@ func getTier(ctx context.Context, client MinioAdmin, params *tieringApi.GetTierP
|
||||
}
|
||||
|
||||
// build response
|
||||
return nil, ErrorGenericNotFound
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// getGetTierResponse returns a tier
|
||||
func getGetTierResponse(session *models.Principal, params *tieringApi.GetTierParams) (*models.Tier, *models.Error) {
|
||||
func getGetTierResponse(session *models.Principal, params tieringApi.GetTierParams) (*models.Tier, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
addTierResp, err := getTier(ctx, adminClient, params)
|
||||
addTierResp, err := getTier(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return addTierResp, nil
|
||||
}
|
||||
@@ -349,21 +348,20 @@ func editTierCredentials(ctx context.Context, client MinioAdmin, params *tiering
|
||||
}
|
||||
|
||||
// getEditTierCredentialsResponse returns the result of editing credentials for a tier
|
||||
func getEditTierCredentialsResponse(session *models.Principal, params *tieringApi.EditTierCredentialsParams) *models.Error {
|
||||
func getEditTierCredentialsResponse(session *models.Principal, params tieringApi.EditTierCredentialsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// serialize output
|
||||
err = editTierCredentials(ctx, adminClient, params)
|
||||
err = editTierCredentials(ctx, adminClient, ¶ms)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ const (
|
||||
func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
// List Users
|
||||
api.UserListUsersHandler = userApi.ListUsersHandlerFunc(func(params userApi.ListUsersParams, session *models.Principal) middleware.Responder {
|
||||
listUsersResponse, err := getListUsersResponse(session)
|
||||
listUsersResponse, err := getListUsersResponse(session, params)
|
||||
if err != nil {
|
||||
return userApi.NewListUsersDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func registerUsersHandlers(api *operations.ConsoleAPI) {
|
||||
return userApi.NewBulkUpdateUsersGroupsOK()
|
||||
})
|
||||
api.BucketListUsersWithAccessToBucketHandler = bucketApi.ListUsersWithAccessToBucketHandlerFunc(func(params bucketApi.ListUsersWithAccessToBucketParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getListUsersWithAccessToBucketResponse(session, params.Bucket)
|
||||
response, err := getListUsersWithAccessToBucketResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListUsersWithAccessToBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -145,20 +145,19 @@ func listUsers(ctx context.Context, client MinioAdmin) ([]*models.User, error) {
|
||||
}
|
||||
|
||||
// getListUsersResponse performs listUsers() and serializes it to the handler's output
|
||||
func getListUsersResponse(session *models.Principal) (*models.ListUsersResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListUsersResponse(session *models.Principal, params userApi.ListUsersParams) (*models.ListUsersResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
users, err := listUsers(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listUsersResponse := &models.ListUsersResponse{
|
||||
@@ -169,7 +168,7 @@ func getListUsersResponse(session *models.Principal) (*models.ListUsersResponse,
|
||||
|
||||
// addUser invokes adding a users on `MinioAdmin` and builds the response `models.User`
|
||||
func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *string, groups []string, policies []string) (*models.User, error) {
|
||||
// Calls into MinIO to add a new user if there's an error return it
|
||||
// Calls into MinIO to add a new user if there's an errors return it
|
||||
if err := client.addUser(ctx, *accessKey, *secretKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -208,11 +207,11 @@ func addUser(ctx context.Context, client MinioAdmin, accessKey, secretKey *strin
|
||||
}
|
||||
|
||||
func getUserAddResponse(session *models.Principal, params userApi.AddUserParams) (*models.User, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -223,7 +222,7 @@ func getUserAddResponse(session *models.Principal, params userApi.AddUserParams)
|
||||
userExists = err == nil
|
||||
|
||||
if userExists {
|
||||
return nil, prepareError(errNonUniqueAccessKey)
|
||||
return nil, ErrorWithContext(ctx, ErrNonUniqueAccessKey)
|
||||
}
|
||||
user, err := addUser(
|
||||
ctx,
|
||||
@@ -234,7 +233,7 @@ func getUserAddResponse(session *models.Principal, params userApi.AddUserParams)
|
||||
params.Body.Policies,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
@@ -245,26 +244,21 @@ func removeUser(ctx context.Context, client MinioAdmin, accessKey string) error
|
||||
}
|
||||
|
||||
func getRemoveUserResponse(session *models.Principal, params userApi.RemoveUserParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
if session.AccountAccessKey == params.Name {
|
||||
return prepareError(errAvoidSelfAccountDelete)
|
||||
return ErrorWithContext(ctx, ErrAvoidSelfAccountDelete)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := removeUser(ctx, adminClient, params.Name); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -279,12 +273,12 @@ func getUserInfo(ctx context.Context, client MinioAdmin, accessKey string) (*mad
|
||||
}
|
||||
|
||||
func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoParams) (*models.User, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -299,7 +293,7 @@ func getUserInfoResponse(session *models.Principal, params userApi.GetUserInfoPa
|
||||
errorMessage := "User doesn't exist"
|
||||
return nil, &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err.Error())}
|
||||
}
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var policies []string
|
||||
@@ -425,12 +419,12 @@ func updateUserGroups(ctx context.Context, client MinioAdmin, user string, group
|
||||
}
|
||||
|
||||
func getUpdateUserGroupsResponse(session *models.Principal, params userApi.UpdateUserGroupsParams) (*models.User, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -440,7 +434,7 @@ func getUpdateUserGroupsResponse(session *models.Principal, params userApi.Updat
|
||||
user, err := updateUserGroups(ctx, adminClient, params.Name, params.Body.Groups)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return user, nil
|
||||
@@ -462,12 +456,12 @@ func setUserStatus(ctx context.Context, client MinioAdmin, user string, status s
|
||||
}
|
||||
|
||||
func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserInfoParams) (*models.User, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -479,13 +473,13 @@ func getUpdateUserResponse(session *models.Principal, params userApi.UpdateUserI
|
||||
groups := params.Body.Groups
|
||||
|
||||
if err := setUserStatus(ctx, adminClient, name, status); err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
userElem, errUG := updateUserGroups(ctx, adminClient, name, groups)
|
||||
|
||||
if errUG != nil {
|
||||
return nil, prepareError(errUG)
|
||||
return nil, ErrorWithContext(ctx, errUG)
|
||||
}
|
||||
return userElem, nil
|
||||
}
|
||||
@@ -517,14 +511,14 @@ func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate
|
||||
|
||||
errorsList := []string{} // We get the errors list because we want to have all errors at once.
|
||||
for _, err := range groupsUpdateList {
|
||||
errorFromUpdate := <-err // We store the error to avoid Data Race
|
||||
errorFromUpdate := <-err // We store the errors to avoid Data Race
|
||||
if errorFromUpdate != nil {
|
||||
// If there is an error, we store the errors strings so we can join them after we receive all errors
|
||||
// If there is an errors, we store the errors strings so we can join them after we receive all errors
|
||||
errorsList = append(errorsList, errorFromUpdate.Error()) // We wait until all the channels have been closed.
|
||||
}
|
||||
}
|
||||
|
||||
// If there are errors, we throw the final error with the errors inside
|
||||
// If there are errors, we throw the final errors with the errors inside
|
||||
if len(errorsList) > 0 {
|
||||
errGen := fmt.Errorf("error in users-groups assignation: %q", strings.Join(errorsList[:], ","))
|
||||
return errGen
|
||||
@@ -534,12 +528,12 @@ func addUsersListToGroups(ctx context.Context, client MinioAdmin, usersToUpdate
|
||||
}
|
||||
|
||||
func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.BulkUpdateUsersGroupsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -550,24 +544,27 @@ func getAddUsersListToGroupsResponse(session *models.Principal, params userApi.B
|
||||
groupsList := params.Body.Groups
|
||||
|
||||
if err := addUsersListToGroups(ctx, adminClient, usersList, groupsList); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getListUsersWithAccessToBucketResponse(session *models.Principal, bucket string) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListUsersWithAccessToBucketResponse(session *models.Principal, params bucketApi.ListUsersWithAccessToBucketParams) ([]string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
return listUsersWithAccessToBucket(ctx, adminClient, bucket)
|
||||
list, err := listUsersWithAccessToBucket(ctx, adminClient, params.Bucket)
|
||||
if err != nil {
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func policyAllowsAndMatchesBucket(policy *iampolicy.Policy, bucket string) int {
|
||||
@@ -587,10 +584,10 @@ func policyAllowsAndMatchesBucket(policy *iampolicy.Policy, bucket string) int {
|
||||
return Unknown
|
||||
}
|
||||
|
||||
func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bucket string) ([]string, *models.Error) {
|
||||
func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bucket string) ([]string, error) {
|
||||
users, err := adminClient.listUsers(ctx)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, err
|
||||
}
|
||||
var retval []string
|
||||
akHasAccess := make(map[string]struct{})
|
||||
@@ -603,7 +600,7 @@ func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bu
|
||||
}
|
||||
policy, err := adminClient.getPolicy(ctx, policyName)
|
||||
if err != nil {
|
||||
LogError("unable to fetch policy %s: %v", policyName, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch policy %s: %v", policyName, err))
|
||||
continue
|
||||
}
|
||||
if _, ok := akIsDenied[k]; !ok {
|
||||
@@ -622,19 +619,19 @@ func listUsersWithAccessToBucket(ctx context.Context, adminClient MinioAdmin, bu
|
||||
|
||||
groups, err := adminClient.listGroups(ctx)
|
||||
if err != nil {
|
||||
LogError("unable to list groups: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to list groups: %v", err))
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
for _, groupName := range groups {
|
||||
info, err := groupInfo(ctx, adminClient, groupName)
|
||||
if err != nil {
|
||||
LogError("unable to fetch group info %s: %v", groupName, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch group info %s: %v", groupName, err))
|
||||
continue
|
||||
}
|
||||
policy, err := adminClient.getPolicy(ctx, info.Policy)
|
||||
if err != nil {
|
||||
LogError("unable to fetch group policy %s: %v", info.Policy, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("unable to fetch group policy %s: %v", info.Policy, err))
|
||||
continue
|
||||
}
|
||||
for _, member := range info.Members {
|
||||
@@ -665,11 +662,11 @@ func changeUserPassword(ctx context.Context, client MinioAdmin, selectedUser str
|
||||
|
||||
// getChangeUserPasswordResponse will change the password of selctedUser to newSecretKey
|
||||
func getChangeUserPasswordResponse(session *models.Principal, params accountApi.ChangeUserPasswordParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -681,7 +678,7 @@ func getChangeUserPasswordResponse(session *models.Principal, params accountApi.
|
||||
|
||||
// changes password of user to newSecretKey
|
||||
if err := changeUserPassword(ctx, adminClient, user, newSecretKey); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -525,7 +525,7 @@ func TestListUsersWithAccessToBucket(t *testing.T) {
|
||||
}
|
||||
return mockResponse, nil
|
||||
}
|
||||
return nil, ErrorGeneric
|
||||
return nil, ErrDefault
|
||||
}
|
||||
type args struct {
|
||||
bucket string
|
||||
|
||||
@@ -454,7 +454,7 @@ func newS3Config(endpoint, accessKey, secretKey, sessionToken string, insecure b
|
||||
s3Config.SecretKey = secretKey
|
||||
s3Config.SessionToken = sessionToken
|
||||
s3Config.Signature = "S3v4"
|
||||
s3Config.Transport = prepareSTSClientTransport(insecure)
|
||||
s3Config.Transport = PrepareSTSClientTransport(insecure)
|
||||
|
||||
return s3Config
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func Test_computeObjectURLWithoutEncode(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := computeObjectURLWithoutEncode(tt.args.bucketName, tt.args.prefix)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("computeObjectURLWithoutEncode() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("computeObjectURLWithoutEncode() errors = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if got != tt.want {
|
||||
|
||||
@@ -20,6 +20,7 @@ package restapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -34,6 +35,9 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/logger"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/klauspost/compress/gzhttp"
|
||||
|
||||
portal_ui "github.com/minio/console/portal-ui"
|
||||
@@ -75,7 +79,7 @@ func configureAPI(api *operations.ConsoleAPI) http.Handler {
|
||||
api.KeyAuth = func(token string, scopes []string) (*models.Principal, error) {
|
||||
// we are validating the session token by decrypting the claims inside, if the operation succeed that means the jwt
|
||||
// was generated and signed by us in the first place
|
||||
claims, err := auth.SessionTokenAuthenticate(token)
|
||||
claims, err := auth.ParseClaimsFromToken(token)
|
||||
if err != nil {
|
||||
api.Logger("Unable to validate the session token %s: %v", token, err)
|
||||
return nil, errors.New(401, "incorrect api key auth")
|
||||
@@ -167,13 +171,43 @@ func setupMiddlewares(handler http.Handler) http.Handler {
|
||||
return handler
|
||||
}
|
||||
|
||||
func ContextMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
requestID, err := utils.NewUUID()
|
||||
if err != nil && err != auth.ErrNoAuthToken {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), utils.ContextRequestID, requestID)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestUserAgent, r.UserAgent())
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestHost, r.Host)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestRemoteAddr, r.RemoteAddr)
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func AuditLogMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
rw := logger.NewResponseWriter(w)
|
||||
next.ServeHTTP(rw, r)
|
||||
if strings.HasPrefix(r.URL.Path, "/ws") || strings.HasPrefix(r.URL.Path, "/api") {
|
||||
logger.AuditLog(r.Context(), rw, r, map[string]interface{}{}, "Authorization", "Cookie", "Set-Cookie")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The middleware configuration happens before anything, this middleware also applies to serving the swagger.json document.
|
||||
// So this is a good place to plug in a panic handling middleware, logging and metrics
|
||||
// So this is a good place to plug in a panic handling middleware, logger and metrics
|
||||
func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
// handle cookie or authorization header for session
|
||||
next := AuthenticationMiddleware(handler)
|
||||
gnext := gzhttp.GzipHandler(handler)
|
||||
// if audit-log is enabled console will log all incoming request
|
||||
next := AuditLogMiddleware(gnext)
|
||||
// serve static files
|
||||
next = FileServerMiddleware(next)
|
||||
// add information to request context
|
||||
next = ContextMiddleware(next)
|
||||
// handle cookie or authorization header for session
|
||||
next = AuthenticationMiddleware(next)
|
||||
|
||||
sslHostFn := secure.SSLHostFunc(func(host string) string {
|
||||
h, _, err := net.SplitHostPort(host)
|
||||
@@ -210,8 +244,7 @@ func setupGlobalMiddleware(handler http.Handler) http.Handler {
|
||||
}
|
||||
secureMiddleware := secure.New(secureOptions)
|
||||
next = secureMiddleware.Handler(next)
|
||||
gnext := gzhttp.GzipHandler(next)
|
||||
return RejectS3Middleware(gnext)
|
||||
return RejectS3Middleware(next)
|
||||
}
|
||||
|
||||
// RejectS3Middleware will reject requests that have AWS S3 specific headers.
|
||||
@@ -242,14 +275,21 @@ func AuthenticationMiddleware(next http.Handler) http.Handler {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
sessionToken, _ := auth.DecryptToken(token)
|
||||
// All handlers handle appropriately to return errors
|
||||
// based on their swagger rules, we do not need to
|
||||
// additionally return error here, let the next ServeHTTPs
|
||||
// handle it appropriately.
|
||||
if token != "" {
|
||||
r.Header.Add("Authorization", "Bearer "+token)
|
||||
if len(sessionToken) > 0 {
|
||||
r.Header.Add("Authorization", fmt.Sprintf("Bearer %s", string(sessionToken)))
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
ctx := r.Context()
|
||||
claims, _ := auth.ParseClaimsFromToken(string(sessionToken))
|
||||
if claims != nil {
|
||||
// save user session id context
|
||||
ctx = context.WithValue(r.Context(), utils.ContextRequestUserID, claims.STSSessionToken)
|
||||
}
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -362,7 +402,7 @@ func (lw nullWriter) Write(b []byte) (int, error) {
|
||||
// This function can be called multiple times, depending on the number of serving schemes.
|
||||
// scheme value will be set accordingly: "http", "https" or "unix"
|
||||
func configureServer(s *http.Server, _, _ string) {
|
||||
// Turn-off random logging by Go net/http
|
||||
// Turn-off random logger by Go net/http
|
||||
s.ErrorLog = log.New(&nullWriter{}, "", 0)
|
||||
}
|
||||
|
||||
|
||||
232
restapi/error.go
232
restapi/error.go
@@ -1,232 +0,0 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrorGeneric is a generic error message
|
||||
ErrorGeneric = errors.New("an error occurred, please try again")
|
||||
errInvalidCredentials = errors.New("invalid Login")
|
||||
errForbidden = errors.New("403 Forbidden")
|
||||
errFileTooLarge = errors.New("413 File too Large")
|
||||
errorGenericInvalidSession = errors.New("invalid session")
|
||||
// ErrorGenericNotFound Generic error for not found
|
||||
ErrorGenericNotFound = errors.New("not found")
|
||||
// Explicit error messages
|
||||
errorInvalidErasureCodingValue = errors.New("invalid Erasure Coding Value")
|
||||
errBucketBodyNotInRequest = errors.New("error bucket body not in request")
|
||||
errBucketNameNotInRequest = errors.New("error bucket name not in request")
|
||||
errGroupBodyNotInRequest = errors.New("error group body not in request")
|
||||
errGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
errGroupAlreadyExists = errors.New("error group name already in use")
|
||||
errPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
errPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
errInvalidEncryptionAlgorithm = errors.New("error invalid encryption algorithm")
|
||||
errSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
errBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
errChangePassword = errors.New("error please check your current password")
|
||||
errInvalidLicense = errors.New("invalid license key")
|
||||
errLicenseNotFound = errors.New("license not found")
|
||||
errAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
|
||||
errAccessDenied = errors.New("access denied")
|
||||
errOauth2Provider = errors.New("unable to contact configured identity provider")
|
||||
errNonUniqueAccessKey = errors.New("access key already in use")
|
||||
)
|
||||
|
||||
// Tiering errors
|
||||
var (
|
||||
errRemoteTierExists = errors.New("Specified remote tier already exists")
|
||||
errRemoteTierNotFound = errors.New("Specified remote tier was not found")
|
||||
errRemoteTierUppercase = errors.New("Tier name must be in uppercase")
|
||||
errRemoteTierBucketNotFound = errors.New("Remote tier bucket not found")
|
||||
errRemoteInvalidCredentials = errors.New("Invalid remote tier credentials")
|
||||
)
|
||||
|
||||
// prepareError receives an error object and parse it against k8sErrors, returns the right error code paired with a generic error message
|
||||
func prepareError(err ...error) *models.Error {
|
||||
errorCode := int32(500)
|
||||
errorMessage := ErrorGeneric.Error()
|
||||
if len(err) > 0 {
|
||||
frame := getFrame(2)
|
||||
fileParts := strings.Split(frame.File, "/")
|
||||
LogError("original error -> (%s:%d: %v)", fileParts[len(fileParts)-1], frame.Line, err[0])
|
||||
if err[0].Error() == errForbidden.Error() {
|
||||
errorCode = 403
|
||||
}
|
||||
if err[0] == ErrorGenericNotFound {
|
||||
errorCode = 404
|
||||
errorMessage = ErrorGenericNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidCredentials) {
|
||||
errorCode = 401
|
||||
errorMessage = errInvalidCredentials.Error()
|
||||
}
|
||||
// console invalid erasure coding value
|
||||
if errors.Is(err[0], errorInvalidErasureCodingValue) {
|
||||
errorCode = 400
|
||||
errorMessage = errorInvalidErasureCodingValue.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errBucketNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errBucketNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errGroupAlreadyExists) {
|
||||
errorCode = 400
|
||||
errorMessage = errGroupAlreadyExists.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err[0], errPolicyBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = errPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if errors.Is(err[0], errorGenericInvalidSession) {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// Bucket life cycle not configured
|
||||
if errors.Is(err[0], errBucketLifeCycleNotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errBucketLifeCycleNotConfigured.Error()
|
||||
}
|
||||
// Encryption not configured
|
||||
if errors.Is(err[0], errSSENotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = errSSENotConfigured.Error()
|
||||
}
|
||||
// account change password
|
||||
if madmin.ToErrorResponse(err[0]).Code == "SignatureDoesNotMatch" {
|
||||
errorCode = 403
|
||||
errorMessage = errChangePassword.Error()
|
||||
}
|
||||
if errors.Is(err[0], errLicenseNotFound) {
|
||||
errorCode = 404
|
||||
errorMessage = errLicenseNotFound.Error()
|
||||
}
|
||||
if errors.Is(err[0], errInvalidLicense) {
|
||||
errorCode = 404
|
||||
errorMessage = errInvalidLicense.Error()
|
||||
}
|
||||
if errors.Is(err[0], errAvoidSelfAccountDelete) {
|
||||
errorCode = 403
|
||||
errorMessage = errAvoidSelfAccountDelete.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "AccessDenied" {
|
||||
errorCode = 403
|
||||
errorMessage = errAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err[0]).Code == "InvalidAccessKeyId" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// console invalid session error
|
||||
if madmin.ToErrorResponse(err[0]).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode = 401
|
||||
errorMessage = errorGenericInvalidSession.Error()
|
||||
}
|
||||
// if we received a second error take that as friendly message but don't override the code
|
||||
if len(err) > 1 && err[1] != nil {
|
||||
LogError("friendly error: %v", err[1].Error())
|
||||
errorMessage = err[1].Error()
|
||||
}
|
||||
// if we receive third error we just print that as debugging
|
||||
if len(err) > 2 && err[2] != nil {
|
||||
LogError("debugging error: %v", err[2].Error())
|
||||
}
|
||||
// tiering errors
|
||||
if err[0].Error() == errRemoteTierExists.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if err[0].Error() == errRemoteTierNotFound.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
|
||||
if err[0].Error() == errRemoteTierUppercase.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if err[0].Error() == errRemoteTierBucketNotFound.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if err[0].Error() == errRemoteInvalidCredentials.Error() {
|
||||
errorCode = 403
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
if err[0].Error() == errFileTooLarge.Error() {
|
||||
errorCode = 413
|
||||
errorMessage = err[0].Error()
|
||||
}
|
||||
// bucket already exists
|
||||
if minio.ToErrorResponse(err[0]).Code == "BucketAlreadyOwnedByYou" {
|
||||
errorCode = 400
|
||||
errorMessage = "Bucket already exists"
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err[0].Error())}
|
||||
}
|
||||
|
||||
func getFrame(skipFrames int) runtime.Frame {
|
||||
// We need the frame at index skipFrames+2, since we never want runtime.Callers and getFrame
|
||||
targetFrameIndex := skipFrames + 2
|
||||
|
||||
// Set size to targetFrameIndex+2 to ensure we have room for one more caller than we need
|
||||
programCounters := make([]uintptr, targetFrameIndex+2)
|
||||
n := runtime.Callers(0, programCounters)
|
||||
|
||||
frame := runtime.Frame{Function: "unknown"}
|
||||
if n > 0 {
|
||||
frames := runtime.CallersFrames(programCounters[:n])
|
||||
for more, frameIndex := true, 0; more && frameIndex <= targetFrameIndex; frameIndex++ {
|
||||
var frameCandidate runtime.Frame
|
||||
frameCandidate, more = frames.Next()
|
||||
if frameIndex == targetFrameIndex {
|
||||
frame = frameCandidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return frame
|
||||
}
|
||||
231
restapi/errors.go
Normal file
231
restapi/errors.go
Normal file
@@ -0,0 +1,231 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/minio/minio-go/v7"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDefault = errors.New("an errors occurred, please try again")
|
||||
ErrInvalidLogin = errors.New("invalid Login")
|
||||
ErrForbidden = errors.New("403 Forbidden")
|
||||
ErrFileTooLarge = errors.New("413 File too Large")
|
||||
ErrInvalidSession = errors.New("invalid session")
|
||||
ErrNotFound = errors.New("not found")
|
||||
ErrGroupAlreadyExists = errors.New("error group name already in use")
|
||||
ErrInvalidErasureCodingValue = errors.New("invalid Erasure Coding Value")
|
||||
ErrBucketBodyNotInRequest = errors.New("error bucket body not in request")
|
||||
ErrBucketNameNotInRequest = errors.New("error bucket name not in request")
|
||||
ErrGroupBodyNotInRequest = errors.New("error group body not in request")
|
||||
ErrGroupNameNotInRequest = errors.New("error group name not in request")
|
||||
ErrPolicyNameNotInRequest = errors.New("error policy name not in request")
|
||||
ErrPolicyBodyNotInRequest = errors.New("error policy body not in request")
|
||||
ErrInvalidEncryptionAlgorithm = errors.New("error invalid encryption algorithm")
|
||||
ErrSSENotConfigured = errors.New("error server side encryption configuration not found")
|
||||
ErrBucketLifeCycleNotConfigured = errors.New("error bucket life cycle configuration not found")
|
||||
ErrChangePassword = errors.New("error please check your current password")
|
||||
ErrInvalidLicense = errors.New("invalid license key")
|
||||
ErrLicenseNotFound = errors.New("license not found")
|
||||
ErrAvoidSelfAccountDelete = errors.New("logged in user cannot be deleted by itself")
|
||||
ErrAccessDenied = errors.New("access denied")
|
||||
ErrOauth2Provider = errors.New("unable to contact configured identity provider")
|
||||
ErrNonUniqueAccessKey = errors.New("access key already in use")
|
||||
ErrRemoteTierExists = errors.New("specified remote tier already exists")
|
||||
ErrRemoteTierNotFound = errors.New("specified remote tier was not found")
|
||||
ErrRemoteTierUppercase = errors.New("tier name must be in uppercase")
|
||||
ErrRemoteTierBucketNotFound = errors.New("remote tier bucket not found")
|
||||
ErrRemoteInvalidCredentials = errors.New("invalid remote tier credentials")
|
||||
ErrUnableToGetTenantUsage = errors.New("unable to get tenant usage")
|
||||
ErrTooManyNodes = errors.New("cannot request more nodes than what is available in the cluster")
|
||||
ErrTooFewNodes = errors.New("there are not enough nodes in the cluster to support this tenant")
|
||||
ErrTooFewAvailableNodes = errors.New("there is not enough available nodes to satisfy this requirement")
|
||||
ErrFewerThanFourNodes = errors.New("at least 4 nodes are required for a tenant")
|
||||
ErrUnableToGetTenantLogs = errors.New("unable to get tenant logs")
|
||||
ErrUnableToUpdateTenantCertificates = errors.New("unable to update tenant certificates")
|
||||
ErrUpdatingEncryptionConfig = errors.New("unable to update encryption configuration")
|
||||
ErrDeletingEncryptionConfig = errors.New("error disabling tenant encryption")
|
||||
ErrEncryptionConfigNotFound = errors.New("encryption configuration not found")
|
||||
ErrPolicyNotFound = errors.New("policy does not exist")
|
||||
)
|
||||
|
||||
// ErrorWithContext :
|
||||
func ErrorWithContext(ctx context.Context, err ...interface{}) *models.Error {
|
||||
errorCode := int32(500)
|
||||
errorMessage := ErrDefault.Error()
|
||||
var err1 error
|
||||
var exists bool
|
||||
if len(err) > 0 {
|
||||
if err1, exists = err[0].(error); exists {
|
||||
if err1.Error() == ErrForbidden.Error() {
|
||||
errorCode = 403
|
||||
}
|
||||
if err1 == ErrNotFound {
|
||||
errorCode = 404
|
||||
errorMessage = ErrNotFound.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrInvalidLogin) {
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidLogin.Error()
|
||||
}
|
||||
// console invalid erasure coding value
|
||||
if errors.Is(err1, ErrInvalidErasureCodingValue) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrInvalidErasureCodingValue.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrBucketBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrBucketBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrBucketNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrBucketNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrGroupBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrGroupBodyNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrGroupNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrGroupNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrPolicyNameNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyNameNotInRequest.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrPolicyBodyNotInRequest) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrPolicyBodyNotInRequest.Error()
|
||||
}
|
||||
// console invalid session errors
|
||||
if errors.Is(err1, ErrInvalidSession) {
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidSession.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrGroupAlreadyExists) {
|
||||
errorCode = 400
|
||||
errorMessage = ErrGroupAlreadyExists.Error()
|
||||
}
|
||||
// Bucket life cycle not configured
|
||||
if errors.Is(err1, ErrBucketLifeCycleNotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrBucketLifeCycleNotConfigured.Error()
|
||||
}
|
||||
// Encryption not configured
|
||||
if errors.Is(err1, ErrSSENotConfigured) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrSSENotConfigured.Error()
|
||||
}
|
||||
// account change password
|
||||
if errors.Is(err1, ErrChangePassword) {
|
||||
errorCode = 403
|
||||
errorMessage = ErrChangePassword.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err1).Code == "SignatureDoesNotMatch" {
|
||||
errorCode = 403
|
||||
errorMessage = ErrChangePassword.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrLicenseNotFound) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrLicenseNotFound.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrInvalidLicense) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrInvalidLicense.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrAvoidSelfAccountDelete) {
|
||||
errorCode = 403
|
||||
errorMessage = ErrAvoidSelfAccountDelete.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrAccessDenied) {
|
||||
errorCode = 403
|
||||
errorMessage = ErrAccessDenied.Error()
|
||||
}
|
||||
if errors.Is(err1, ErrPolicyNotFound) {
|
||||
errorCode = 404
|
||||
errorMessage = ErrPolicyNotFound.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err1).Code == "AccessDenied" {
|
||||
errorCode = 403
|
||||
errorMessage = ErrAccessDenied.Error()
|
||||
}
|
||||
if madmin.ToErrorResponse(err1).Code == "InvalidAccessKeyId" {
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidSession.Error()
|
||||
}
|
||||
// console invalid session errors
|
||||
if madmin.ToErrorResponse(err1).Code == "XMinioAdminNoSuchUser" {
|
||||
errorCode = 401
|
||||
errorMessage = ErrInvalidSession.Error()
|
||||
}
|
||||
// tiering errors
|
||||
if err1.Error() == ErrRemoteTierExists.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
if err1.Error() == ErrRemoteTierNotFound.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
|
||||
if err1.Error() == ErrRemoteTierUppercase.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
if err1.Error() == ErrRemoteTierBucketNotFound.Error() {
|
||||
errorCode = 400
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
if err1.Error() == ErrRemoteInvalidCredentials.Error() {
|
||||
errorCode = 403
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
if err1.Error() == ErrFileTooLarge.Error() {
|
||||
errorCode = 413
|
||||
errorMessage = err1.Error()
|
||||
}
|
||||
// bucket already exists
|
||||
if minio.ToErrorResponse(err1).Code == "BucketAlreadyOwnedByYou" {
|
||||
errorCode = 400
|
||||
errorMessage = "Bucket already exists"
|
||||
}
|
||||
LogError(err1.Error(), err...)
|
||||
LogIf(ctx, err1, err...)
|
||||
}
|
||||
|
||||
if len(err) > 1 && err[1] != nil {
|
||||
if err2, ok := err[1].(error); ok {
|
||||
errorMessage = err2.Error()
|
||||
}
|
||||
}
|
||||
}
|
||||
return &models.Error{Code: errorCode, Message: swag.String(errorMessage), DetailedMessage: swag.String(err1.Error())}
|
||||
|
||||
}
|
||||
|
||||
// Error receives an errors object and parse it against k8sErrors, returns the right errors code paired with a generic errors message
|
||||
func Error(err ...interface{}) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
return ErrorWithContext(ctx, err...)
|
||||
}
|
||||
138
restapi/errors_test.go
Normal file
138
restapi/errors_test.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2021 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
type args struct {
|
||||
err []interface{}
|
||||
}
|
||||
|
||||
type testError struct {
|
||||
name string
|
||||
args args
|
||||
want *models.Error
|
||||
}
|
||||
|
||||
var tests []testError
|
||||
|
||||
type expectedError struct {
|
||||
err error
|
||||
code int
|
||||
}
|
||||
|
||||
appErrors := map[string]expectedError{
|
||||
"ErrDefault": {code: 500, err: ErrDefault},
|
||||
"ErrInvalidLogin": {code: 401, err: ErrInvalidLogin},
|
||||
"ErrForbidden": {code: 403, err: ErrForbidden},
|
||||
"ErrFileTooLarge": {code: 413, err: ErrFileTooLarge},
|
||||
"ErrInvalidSession": {code: 401, err: ErrInvalidSession},
|
||||
"ErrNotFound": {code: 404, err: ErrNotFound},
|
||||
"ErrGroupAlreadyExists": {code: 400, err: ErrGroupAlreadyExists},
|
||||
"ErrInvalidErasureCodingValue": {code: 400, err: ErrInvalidErasureCodingValue},
|
||||
"ErrBucketBodyNotInRequest": {code: 400, err: ErrBucketBodyNotInRequest},
|
||||
"ErrBucketNameNotInRequest": {code: 400, err: ErrBucketNameNotInRequest},
|
||||
"ErrGroupBodyNotInRequest": {code: 400, err: ErrGroupBodyNotInRequest},
|
||||
"ErrGroupNameNotInRequest": {code: 400, err: ErrGroupNameNotInRequest},
|
||||
"ErrPolicyNameNotInRequest": {code: 400, err: ErrPolicyNameNotInRequest},
|
||||
"ErrPolicyBodyNotInRequest": {code: 400, err: ErrPolicyBodyNotInRequest},
|
||||
"ErrInvalidEncryptionAlgorithm": {code: 500, err: ErrInvalidEncryptionAlgorithm},
|
||||
"ErrSSENotConfigured": {code: 404, err: ErrSSENotConfigured},
|
||||
"ErrBucketLifeCycleNotConfigured": {code: 404, err: ErrBucketLifeCycleNotConfigured},
|
||||
"ErrChangePassword": {code: 403, err: ErrChangePassword},
|
||||
"ErrInvalidLicense": {code: 404, err: ErrInvalidLicense},
|
||||
"ErrLicenseNotFound": {code: 404, err: ErrLicenseNotFound},
|
||||
"ErrAvoidSelfAccountDelete": {code: 403, err: ErrAvoidSelfAccountDelete},
|
||||
"ErrAccessDenied": {code: 403, err: ErrAccessDenied},
|
||||
"ErrNonUniqueAccessKey": {code: 500, err: ErrNonUniqueAccessKey},
|
||||
"ErrRemoteTierExists": {code: 400, err: ErrRemoteTierExists},
|
||||
"ErrRemoteTierNotFound": {code: 400, err: ErrRemoteTierNotFound},
|
||||
"ErrRemoteTierUppercase": {code: 400, err: ErrRemoteTierUppercase},
|
||||
"ErrRemoteTierBucketNotFound": {code: 400, err: ErrRemoteTierBucketNotFound},
|
||||
"ErrRemoteInvalidCredentials": {code: 403, err: ErrRemoteInvalidCredentials},
|
||||
"ErrUnableToGetTenantUsage": {code: 500, err: ErrUnableToGetTenantUsage},
|
||||
"ErrTooManyNodes": {code: 500, err: ErrTooManyNodes},
|
||||
"ErrTooFewNodes": {code: 500, err: ErrTooFewNodes},
|
||||
"ErrTooFewAvailableNodes": {code: 500, err: ErrTooFewAvailableNodes},
|
||||
"ErrFewerThanFourNodes": {code: 500, err: ErrFewerThanFourNodes},
|
||||
"ErrUnableToGetTenantLogs": {code: 500, err: ErrUnableToGetTenantLogs},
|
||||
"ErrUnableToUpdateTenantCertificates": {code: 500, err: ErrUnableToUpdateTenantCertificates},
|
||||
"ErrUpdatingEncryptionConfig": {code: 500, err: ErrUpdatingEncryptionConfig},
|
||||
"ErrDeletingEncryptionConfig": {code: 500, err: ErrDeletingEncryptionConfig},
|
||||
"ErrEncryptionConfigNotFound": {code: 500, err: ErrEncryptionConfigNotFound},
|
||||
}
|
||||
|
||||
for k, e := range appErrors {
|
||||
tests = append(tests, testError{
|
||||
name: fmt.Sprintf("%s error", k),
|
||||
args: args{
|
||||
err: []interface{}{e.err},
|
||||
},
|
||||
want: &models.Error{Code: int32(e.code), Message: swag.String(e.err.Error()), DetailedMessage: swag.String(e.err.Error())},
|
||||
})
|
||||
}
|
||||
tests = append(tests,
|
||||
testError{
|
||||
name: "passing multiple errors",
|
||||
args: args{
|
||||
err: []interface{}{ErrDefault, ErrInvalidLogin},
|
||||
},
|
||||
want: &models.Error{Code: int32(500), Message: swag.String(ErrDefault.Error()), DetailedMessage: swag.String(ErrDefault.Error())},
|
||||
})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := Error(tt.args.err...)
|
||||
assert.Equalf(t, tt.want.Code, got.Code, "Error(%v) Got (%v)", tt.want.Code, got.Code)
|
||||
assert.Equalf(t, *tt.want.DetailedMessage, *got.DetailedMessage, "Error(%s) Got (%s)", *tt.want.DetailedMessage, *got.DetailedMessage)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWithContext(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
err []interface{}
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *models.Error
|
||||
}{
|
||||
{
|
||||
name: "default error",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
err: []interface{}{ErrDefault},
|
||||
},
|
||||
want: &models.Error{Code: 500, Message: swag.String(ErrDefault.Error()), DetailedMessage: swag.String(ErrDefault.Error())},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, ErrorWithContext(tt.args.ctx, tt.args.err...), "ErrorWithContext(%v, %v)", tt.args.ctx, tt.args.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
@@ -36,10 +37,14 @@ func logError(msg string, data ...interface{}) {
|
||||
errorLog.Printf(msg+"\n", data...)
|
||||
}
|
||||
|
||||
func logIf(ctx context.Context, err error, errKind ...interface{}) {
|
||||
}
|
||||
|
||||
// globally changeable logger styles
|
||||
var (
|
||||
LogInfo = logInfo
|
||||
LogError = logError
|
||||
LogIf = logIf
|
||||
)
|
||||
|
||||
// Context captures all command line flags values
|
||||
|
||||
@@ -23,7 +23,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func prepareSTSClientTransport(insecure bool) *http.Transport {
|
||||
// PrepareSTSClientTransport :
|
||||
func PrepareSTSClientTransport(insecure bool) *http.Transport {
|
||||
// This takes github.com/minio/madmin-go/transport.go as an example
|
||||
//
|
||||
// DefaultTransport - this default transport is similar to
|
||||
@@ -56,7 +57,7 @@ func prepareSTSClientTransport(insecure bool) *http.Transport {
|
||||
// PrepareConsoleHTTPClient returns an http.Client with custom configurations need it by *credentials.STSAssumeRole
|
||||
// custom configurations include the use of CA certificates
|
||||
func PrepareConsoleHTTPClient(insecure bool) *http.Client {
|
||||
transport := prepareSTSClientTransport(insecure)
|
||||
transport := PrepareSTSClientTransport(insecure)
|
||||
// Return http client with default configuration
|
||||
c := &http.Client{
|
||||
Transport: transport,
|
||||
|
||||
@@ -55,7 +55,7 @@ func changePassword(ctx context.Context, client MinioAdmin, session *models.Prin
|
||||
// getChangePasswordResponse will validate user knows what is the current password (avoid account hijacking), update user account password
|
||||
// and authenticate the user generating a new session token/cookie
|
||||
func getChangePasswordResponse(session *models.Principal, params accountApi.AccountChangePasswordParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// changePassword operations requires an AdminClient initialized with parent account credentials not
|
||||
// STS credentials
|
||||
@@ -64,7 +64,7 @@ func getChangePasswordResponse(session *models.Principal, params accountApi.Acco
|
||||
STSSecretAccessKey: *params.Body.CurrentSecretKey,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// parentAccountClient will contain access and secret key credentials for the user
|
||||
userClient := AdminClient{Client: parentAccountClient}
|
||||
@@ -73,18 +73,18 @@ func getChangePasswordResponse(session *models.Principal, params accountApi.Acco
|
||||
|
||||
// currentSecretKey will compare currentSecretKey against the stored secret key inside the encrypted session
|
||||
if err := changePassword(ctx, userClient, session, newSecretKey); err != nil {
|
||||
return nil, prepareError(errChangePassword, nil, err)
|
||||
return nil, ErrorWithContext(ctx, ErrChangePassword, nil, err)
|
||||
}
|
||||
// user credentials are updated at this point, we need to generate a new admin client and authenticate using
|
||||
// the new credentials
|
||||
credentials, err := getConsoleCredentials(accessKey, newSecretKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, ErrorWithContext(ctx, ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// authenticate user and generate new session token
|
||||
sessionID, err := login(credentials, &auth.SessionFeatures{HideMenu: session.Hm})
|
||||
if err != nil {
|
||||
return nil, prepareError(errInvalidCredentials, nil, err)
|
||||
return nil, ErrorWithContext(ctx, ErrInvalidLogin, nil, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
|
||||
@@ -19,6 +19,7 @@ package restapi
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
@@ -36,6 +37,7 @@ func Test_getChangePasswordResponse(t *testing.T) {
|
||||
CurrentSecretKey := "string"
|
||||
NewSecretKey := "string"
|
||||
changePasswordParameters := accountApi.AccountChangePasswordParams{
|
||||
HTTPRequest: &http.Request{},
|
||||
Body: &models.AccountChangePasswordRequest{
|
||||
CurrentSecretKey: &CurrentSecretKey,
|
||||
NewSecretKey: &NewSecretKey,
|
||||
|
||||
@@ -21,8 +21,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/restapi/operations"
|
||||
bucektApi "github.com/minio/console/restapi/operations/bucket"
|
||||
@@ -53,19 +51,17 @@ func registerBucketQuotaHandlers(api *operations.ConsoleAPI) {
|
||||
}
|
||||
|
||||
func setBucketQuotaResponse(session *models.Principal, params bucektApi.SetBucketQuotaParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
if err := setBucketQuota(params.HTTPRequest.Context(), &adminClient, ¶ms.Name, params.Body); err != nil {
|
||||
return &models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String(err.Error()),
|
||||
}
|
||||
if err := setBucketQuota(ctx, &adminClient, ¶ms.Name, params.Body); err != nil {
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -97,32 +93,28 @@ func setBucketQuota(ctx context.Context, ac *AdminClient, bucket *string, bucket
|
||||
}
|
||||
|
||||
func getBucketQuotaResponse(session *models.Principal, params bucektApi.GetBucketQuotaParams) (*models.BucketQuota, *models.Error) {
|
||||
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
quota, err := getBucketQuota(params.HTTPRequest.Context(), &adminClient, ¶ms.Name)
|
||||
quota, err := getBucketQuota(ctx, &adminClient, ¶ms.Name)
|
||||
if err != nil {
|
||||
return nil, &models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String(err.Error()),
|
||||
}
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return quota, nil
|
||||
}
|
||||
|
||||
func getBucketQuota(ctx context.Context, ac *AdminClient, bucket *string) (*models.BucketQuota, error) {
|
||||
|
||||
quota, err := ac.getBucketQuota(ctx, *bucket)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.BucketQuota{
|
||||
Quota: int64(quota.Quota),
|
||||
Type: string(quota.Type),
|
||||
|
||||
@@ -47,7 +47,7 @@ import (
|
||||
func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
// list buckets
|
||||
api.BucketListBucketsHandler = bucketApi.ListBucketsHandlerFunc(func(params bucketApi.ListBucketsParams, session *models.Principal) middleware.Responder {
|
||||
listBucketsResponse, err := getListBucketsResponse(session)
|
||||
listBucketsResponse, err := getListBucketsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewListBucketsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// make bucket
|
||||
api.BucketMakeBucketHandler = bucketApi.MakeBucketHandlerFunc(func(params bucketApi.MakeBucketParams, session *models.Principal) middleware.Responder {
|
||||
if err := getMakeBucketResponse(session, params.Body); err != nil {
|
||||
if err := getMakeBucketResponse(session, params); err != nil {
|
||||
return bucketApi.NewMakeBucketDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewMakeBucketCreated()
|
||||
@@ -79,7 +79,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// set bucket policy
|
||||
api.BucketBucketSetPolicyHandler = bucketApi.BucketSetPolicyHandlerFunc(func(params bucketApi.BucketSetPolicyParams, session *models.Principal) middleware.Responder {
|
||||
bucketSetPolicyResp, err := getBucketSetPolicyResponse(session, params.Name, params.Body)
|
||||
bucketSetPolicyResp, err := getBucketSetPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewBucketSetPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// set bucket tags
|
||||
api.BucketPutBucketTagsHandler = bucketApi.PutBucketTagsHandlerFunc(func(params bucketApi.PutBucketTagsParams, session *models.Principal) middleware.Responder {
|
||||
err := getPutBucketTagsResponse(session, params.BucketName, params.Body)
|
||||
err := getPutBucketTagsResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewPutBucketTagsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -95,15 +95,15 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get bucket versioning
|
||||
api.BucketGetBucketVersioningHandler = bucketApi.GetBucketVersioningHandlerFunc(func(params bucketApi.GetBucketVersioningParams, session *models.Principal) middleware.Responder {
|
||||
getBucketVersioning, err := getBucketVersionedResponse(session, params.BucketName)
|
||||
getBucketVersioning, err := getBucketVersionedResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewGetBucketVersioningDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewGetBucketVersioningDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewGetBucketVersioningOK().WithPayload(getBucketVersioning)
|
||||
})
|
||||
// update bucket versioning
|
||||
api.BucketSetBucketVersioningHandler = bucketApi.SetBucketVersioningHandlerFunc(func(params bucketApi.SetBucketVersioningParams, session *models.Principal) middleware.Responder {
|
||||
err := setBucketVersioningResponse(session, params.BucketName, ¶ms)
|
||||
err := setBucketVersioningResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewSetBucketVersioningDefault(500).WithPayload(err)
|
||||
}
|
||||
@@ -111,17 +111,17 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get bucket replication
|
||||
api.BucketGetBucketReplicationHandler = bucketApi.GetBucketReplicationHandlerFunc(func(params bucketApi.GetBucketReplicationParams, session *models.Principal) middleware.Responder {
|
||||
getBucketReplication, err := getBucketReplicationResponse(session, params.BucketName)
|
||||
getBucketReplication, err := getBucketReplicationResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewGetBucketReplicationDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewGetBucketReplicationDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewGetBucketReplicationOK().WithPayload(getBucketReplication)
|
||||
})
|
||||
// get single bucket replication rule
|
||||
api.BucketGetBucketReplicationRuleHandler = bucketApi.GetBucketReplicationRuleHandlerFunc(func(params bucketApi.GetBucketReplicationRuleParams, session *models.Principal) middleware.Responder {
|
||||
getBucketReplicationRule, err := getBucketReplicationRuleResponse(session, params.BucketName, params.RuleID)
|
||||
getBucketReplicationRule, err := getBucketReplicationRuleResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewGetBucketReplicationRuleDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewGetBucketReplicationRuleDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewGetBucketReplicationRuleOK().WithPayload(getBucketReplicationRule)
|
||||
})
|
||||
@@ -157,7 +157,7 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get bucket retention config
|
||||
api.BucketGetBucketRetentionConfigHandler = bucketApi.GetBucketRetentionConfigHandlerFunc(func(params bucketApi.GetBucketRetentionConfigParams, session *models.Principal) middleware.Responder {
|
||||
response, err := getBucketRetentionConfigResponse(session, params.BucketName)
|
||||
response, err := getBucketRetentionConfigResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewGetBucketRetentionConfigDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -165,9 +165,9 @@ func registerBucketsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// get bucket object locking status
|
||||
api.BucketGetBucketObjectLockingStatusHandler = bucketApi.GetBucketObjectLockingStatusHandlerFunc(func(params bucketApi.GetBucketObjectLockingStatusParams, session *models.Principal) middleware.Responder {
|
||||
getBucketObjectLockingStatus, err := getBucketObjectLockingResponse(session, params.BucketName)
|
||||
getBucketObjectLockingStatus, err := getBucketObjectLockingResponse(session, params)
|
||||
if err != nil {
|
||||
return bucketApi.NewGetBucketObjectLockingStatusDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
|
||||
return bucketApi.NewGetBucketObjectLockingStatusDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewGetBucketObjectLockingStatusOK().WithPayload(getBucketObjectLockingStatus)
|
||||
})
|
||||
@@ -192,17 +192,20 @@ const (
|
||||
func doSetVersioning(client MCClient, state VersionState) error {
|
||||
err := client.setVersioning(context.Background(), string(state))
|
||||
if err != nil {
|
||||
LogError("error setting versioning for bucket: %s", err.Cause)
|
||||
return err.Cause
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBucketVersioningResponse(session *models.Principal, bucketName string, params *bucketApi.SetBucketVersioningParams) *models.Error {
|
||||
func setBucketVersioningResponse(session *models.Principal, params bucketApi.SetBucketVersioningParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
bucketName := params.BucketName
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -215,28 +218,27 @@ func setBucketVersioningResponse(session *models.Principal, bucketName string, p
|
||||
}
|
||||
|
||||
if err := doSetVersioning(amcClient, versioningState); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getBucketReplicationResponse(session *models.Principal, bucketName string) (*models.BucketReplicationResponse, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketReplicationResponse(session *models.Principal, params bucketApi.GetBucketReplicationParams) (*models.BucketReplicationResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
// we will tolerate this call failing
|
||||
res, err := minioClient.getBucketReplication(ctx, bucketName)
|
||||
res, err := minioClient.getBucketReplication(ctx, params.BucketName)
|
||||
if err != nil {
|
||||
LogError("error versioning bucket: %v", err)
|
||||
ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var rules []*models.BucketReplicationRule
|
||||
@@ -271,31 +273,30 @@ func getBucketReplicationResponse(session *models.Principal, bucketName string)
|
||||
return bucketRResponse, nil
|
||||
}
|
||||
|
||||
func getBucketReplicationRuleResponse(session *models.Principal, bucketName, ruleID string) (*models.BucketReplicationRule, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketReplicationRuleResponse(session *models.Principal, params bucketApi.GetBucketReplicationRuleParams) (*models.BucketReplicationRule, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
replicationRules, err := minioClient.getBucketReplication(ctx, bucketName)
|
||||
replicationRules, err := minioClient.getBucketReplication(ctx, params.BucketName)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
var foundRule replication.Rule
|
||||
found := false
|
||||
|
||||
for i := range replicationRules.Rules {
|
||||
if replicationRules.Rules[i].ID == ruleID {
|
||||
if replicationRules.Rules[i].ID == params.RuleID {
|
||||
foundRule = replicationRules.Rules[i]
|
||||
found = true
|
||||
break
|
||||
@@ -303,7 +304,7 @@ func getBucketReplicationRuleResponse(session *models.Principal, bucketName, rul
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, errors.New("no rule is set with this ID")
|
||||
return nil, ErrorWithContext(ctx, errors.New("no rule is set with this ID"))
|
||||
}
|
||||
|
||||
repDelMarkerStatus := false
|
||||
@@ -340,14 +341,13 @@ func getBucketReplicationRuleResponse(session *models.Principal, bucketName, rul
|
||||
return returnRule, nil
|
||||
}
|
||||
|
||||
func getBucketVersionedResponse(session *models.Principal, bucketName string) (*models.BucketVersioningResponse, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketVersionedResponse(session *models.Principal, params bucketApi.GetBucketVersioningParams) (*models.BucketVersioningResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -355,9 +355,9 @@ func getBucketVersionedResponse(session *models.Principal, bucketName string) (*
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
// we will tolerate this call failing
|
||||
res, err := minioClient.getBucketVersioning(ctx, bucketName)
|
||||
res, err := minioClient.getBucketVersioning(ctx, params.BucketName)
|
||||
if err != nil {
|
||||
LogError("error versioning bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -412,20 +412,20 @@ func getAccountBuckets(ctx context.Context, client MinioAdmin) ([]*models.Bucket
|
||||
}
|
||||
|
||||
// getListBucketsResponse performs listBuckets() and serializes it to the handler's output
|
||||
func getListBucketsResponse(session *models.Principal) (*models.ListBucketsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getListBucketsResponse(session *models.Principal, params bucketApi.ListBucketsParams) (*models.ListBucketsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
buckets, err := getAccountBuckets(ctx, adminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -443,16 +443,17 @@ func makeBucket(ctx context.Context, client MinioClient, bucketName string, obje
|
||||
}
|
||||
|
||||
// getMakeBucketResponse performs makeBucket() to create a bucket with its access policy
|
||||
func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getMakeBucketResponse(session *models.Principal, params bucketApi.MakeBucketParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
// bucket request needed to proceed
|
||||
br := params.Body
|
||||
if br == nil {
|
||||
return prepareError(errBucketBodyNotInRequest)
|
||||
return ErrorWithContext(ctx, ErrBucketBodyNotInRequest)
|
||||
}
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -464,15 +465,15 @@ func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketReque
|
||||
}
|
||||
|
||||
if err := makeBucket(ctx, minioClient, *br.Name, br.Locking); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// make sure to delete bucket if an error occurs after bucket was created
|
||||
// make sure to delete bucket if an errors occurs after bucket was created
|
||||
defer func() {
|
||||
if err != nil {
|
||||
LogError("error creating bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error creating bucket: %v", err))
|
||||
if err := removeBucket(minioClient, *br.Name); err != nil {
|
||||
LogError("error removing bucket: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error removing bucket: %v", err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -481,14 +482,14 @@ func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketReque
|
||||
if br.Versioning || br.Retention != nil {
|
||||
s3Client, err := newS3BucketClient(session, *br.Name, "")
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
amcClient := mcClient{client: s3Client}
|
||||
|
||||
if err = doSetVersioning(amcClient, VersionEnable); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, fmt.Errorf("error setting versioning for bucket: %s", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,14 +497,14 @@ func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketReque
|
||||
if br.Quota != nil && br.Quota.Enabled != nil && *br.Quota.Enabled {
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
// we will tolerate this call failing
|
||||
if err := setBucketQuota(ctx, &adminClient, br.Name, br.Quota); err != nil {
|
||||
LogError("error versioning bucket:", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error versioning bucket: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,7 +512,7 @@ func getMakeBucketResponse(session *models.Principal, br *models.MakeBucketReque
|
||||
if br.Retention != nil {
|
||||
err = setBucketRetentionConfig(ctx, minioClient, *br.Name, *br.Retention.Mode, *br.Retention.Unit, br.Retention.Validity)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -550,14 +551,14 @@ func setBucketAccessPolicy(ctx context.Context, client MinioClient, bucketName s
|
||||
|
||||
// getBucketSetPolicyResponse calls setBucketAccessPolicy() to set a access policy to a bucket
|
||||
// and returns the serialized output.
|
||||
func getBucketSetPolicyResponse(session *models.Principal, bucketName string, req *models.SetBucketPolicyRequest) (*models.Bucket, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketSetPolicyResponse(session *models.Principal, params bucketApi.BucketSetPolicyParams) (*models.Bucket, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
// get updated bucket details and return it
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -565,45 +566,49 @@ func getBucketSetPolicyResponse(session *models.Principal, bucketName string, re
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
adminClient := AdminClient{Client: mAdmin}
|
||||
|
||||
bucketName := params.Name
|
||||
req := params.Body
|
||||
if err := setBucketAccessPolicy(ctx, minioClient, bucketName, *req.Access, req.Definition); err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// set bucket access policy
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, bucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return bucket, nil
|
||||
}
|
||||
|
||||
// putBucketTags sets tags for a bucket
|
||||
func getPutBucketTagsResponse(session *models.Principal, bucketName string, req *models.PutBucketTagsRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getPutBucketTagsResponse(session *models.Principal, params bucketApi.PutBucketTagsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
req := params.Body
|
||||
bucketName := params.BucketName
|
||||
|
||||
newTagSet, err := tags.NewTags(req.Tags, true)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
err = minioClient.SetBucketTagging(ctx, bucketName, newTagSet)
|
||||
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -615,20 +620,22 @@ func removeBucket(client MinioClient, bucketName string) error {
|
||||
|
||||
// getDeleteBucketResponse performs removeBucket() to delete a bucket
|
||||
func getDeleteBucketResponse(session *models.Principal, params bucketApi.DeleteBucketParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
if params.Name == "" {
|
||||
return prepareError(errBucketNameNotInRequest)
|
||||
return ErrorWithContext(ctx, ErrBucketNameNotInRequest)
|
||||
}
|
||||
bucketName := params.Name
|
||||
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
if err := removeBucket(minioClient, bucketName); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -638,8 +645,8 @@ func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdm
|
||||
var bucketAccess models.BucketAccess
|
||||
policyStr, err := client.getBucketPolicy(context.Background(), bucketName)
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
LogError("error getting bucket policy: %v", err)
|
||||
// we can tolerate this errors
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting bucket policy: %v", err))
|
||||
}
|
||||
|
||||
if policyStr == "" {
|
||||
@@ -658,8 +665,8 @@ func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdm
|
||||
}
|
||||
bucketTags, err := client.GetBucketTagging(ctx, bucketName)
|
||||
if err != nil {
|
||||
// we can tolerate this error
|
||||
LogError("error getting bucket tags: %v", err)
|
||||
// we can tolerate this errors
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting bucket tags: %v", err))
|
||||
}
|
||||
bucketDetails := &models.BucketDetails{}
|
||||
if bucketTags != nil {
|
||||
@@ -692,11 +699,11 @@ func getBucketInfo(ctx context.Context, client MinioClient, adminClient MinioAdm
|
||||
|
||||
// getBucketInfoResponse calls getBucketInfo() to get the bucket's info
|
||||
func getBucketInfoResponse(session *models.Principal, params bucketApi.BucketInfoParams) (*models.Bucket, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -704,7 +711,7 @@ func getBucketInfoResponse(session *models.Principal, params bucketApi.BucketInf
|
||||
|
||||
mAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -712,7 +719,7 @@ func getBucketInfoResponse(session *models.Principal, params bucketApi.BucketInf
|
||||
|
||||
bucket, err := getBucketInfo(ctx, minioClient, adminClient, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return bucket, nil
|
||||
|
||||
@@ -751,24 +758,24 @@ func enableBucketEncryption(ctx context.Context, client MinioClient, bucketName
|
||||
case models.BucketEncryptionTypeSseDashS3:
|
||||
config = sse.NewConfigurationSSES3()
|
||||
default:
|
||||
return errInvalidEncryptionAlgorithm
|
||||
return ErrInvalidEncryptionAlgorithm
|
||||
}
|
||||
return client.setBucketEncryption(ctx, bucketName, config)
|
||||
}
|
||||
|
||||
// enableBucketEncryptionResponse calls enableBucketEncryption() to create new encryption configuration for provided bucket name
|
||||
func enableBucketEncryptionResponse(session *models.Principal, params bucketApi.EnableBucketEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
if err := enableBucketEncryption(ctx, minioClient, params.BucketName, *params.Body.EncType, params.Body.KmsKeyID); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -780,17 +787,17 @@ func disableBucketEncryption(ctx context.Context, client MinioClient, bucketName
|
||||
|
||||
// disableBucketEncryptionResponse calls disableBucketEncryption()
|
||||
func disableBucketEncryptionResponse(session *models.Principal, params bucketApi.DisableBucketEncryptionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
if err := disableBucketEncryption(ctx, minioClient, params.BucketName); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -801,24 +808,24 @@ func getBucketEncryptionInfo(ctx context.Context, client MinioClient, bucketName
|
||||
return nil, err
|
||||
}
|
||||
if len(bucketInfo.Rules) == 0 {
|
||||
return nil, ErrorGeneric
|
||||
return nil, ErrDefault
|
||||
}
|
||||
return &models.BucketEncryptionInfo{Algorithm: bucketInfo.Rules[0].Apply.SSEAlgorithm, KmsMasterKeyID: bucketInfo.Rules[0].Apply.KmsMasterKeyID}, nil
|
||||
}
|
||||
|
||||
func getBucketEncryptionInfoResponse(session *models.Principal, params bucketApi.GetBucketEncryptionInfoParams) (*models.BucketEncryptionInfo, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
bucketInfo, err := getBucketEncryptionInfo(ctx, minioClient, params.BucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(errSSENotConfigured, err)
|
||||
return nil, ErrorWithContext(ctx, ErrSSENotConfigured, err)
|
||||
}
|
||||
return bucketInfo, nil
|
||||
}
|
||||
@@ -854,18 +861,18 @@ func setBucketRetentionConfig(ctx context.Context, client MinioClient, bucketNam
|
||||
}
|
||||
|
||||
func getSetBucketRetentionConfigResponse(session *models.Principal, params bucketApi.SetBucketRetentionConfigParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
err = setBucketRetentionConfig(ctx, minioClient, params.BucketName, *params.Body.Mode, *params.Body.Unit, params.Body.Validity)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -925,12 +932,13 @@ func getBucketRetentionConfig(ctx context.Context, client MinioClient, bucketNam
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func getBucketRetentionConfigResponse(session *models.Principal, bucketName string) (*models.GetBucketRetentionConfig, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketRetentionConfigResponse(session *models.Principal, params bucketApi.GetBucketRetentionConfigParams) (*models.GetBucketRetentionConfig, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucketName := params.BucketName
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// create a minioClient interface implementation
|
||||
@@ -939,19 +947,18 @@ func getBucketRetentionConfigResponse(session *models.Principal, bucketName stri
|
||||
|
||||
config, err := getBucketRetentionConfig(ctx, minioClient, bucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func getBucketObjectLockingResponse(session *models.Principal, bucketName string) (*models.BucketObLockingResponse, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getBucketObjectLockingResponse(session *models.Principal, params bucketApi.GetBucketObjectLockingStatusParams) (*models.BucketObLockingResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
bucketName := params.BucketName
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
LogError("error creating MinIO Client: %v", err)
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating MinIO Client: %v", err))
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -965,7 +972,7 @@ func getBucketObjectLockingResponse(session *models.Principal, bucketName string
|
||||
ObjectLockingEnabled: false,
|
||||
}, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
// serialize output
|
||||
@@ -975,21 +982,20 @@ func getBucketObjectLockingResponse(session *models.Principal, bucketName string
|
||||
}
|
||||
|
||||
func getBucketRewindResponse(session *models.Principal, params bucketApi.GetBucketRewindParams) (*models.RewindResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var prefix = ""
|
||||
if params.Prefix != nil {
|
||||
encodedPrefix := SanitizeEncodedPrefix(*params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, prefix)
|
||||
if err != nil {
|
||||
LogError("error creating S3Client: %v", err)
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, fmt.Errorf("error creating S3Client: %v", err))
|
||||
}
|
||||
|
||||
// create a mc S3Client interface implementation
|
||||
@@ -999,7 +1005,7 @@ func getBucketRewindResponse(session *models.Principal, params bucketApi.GetBuck
|
||||
parsedDate, errDate := time.Parse(time.RFC3339, params.Date)
|
||||
|
||||
if errDate != nil {
|
||||
return nil, prepareError(errDate)
|
||||
return nil, ErrorWithContext(ctx, errDate)
|
||||
}
|
||||
|
||||
var rewindItems []*models.RewindItem
|
||||
|
||||
@@ -39,14 +39,14 @@ func registerBucketEventsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// create bucket event
|
||||
api.BucketCreateBucketEventHandler = bucketApi.CreateBucketEventHandlerFunc(func(params bucketApi.CreateBucketEventParams, session *models.Principal) middleware.Responder {
|
||||
if err := getCreateBucketEventsResponse(session, params.BucketName, params.Body); err != nil {
|
||||
if err := getCreateBucketEventsResponse(session, params); err != nil {
|
||||
return bucketApi.NewCreateBucketEventDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewCreateBucketEventCreated()
|
||||
})
|
||||
// delete bucket event
|
||||
api.BucketDeleteBucketEventHandler = bucketApi.DeleteBucketEventHandlerFunc(func(params bucketApi.DeleteBucketEventParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteBucketEventsResponse(session, params.BucketName, params.Arn, params.Body.Events, params.Body.Prefix, params.Body.Suffix); err != nil {
|
||||
if err := getDeleteBucketEventsResponse(session, params); err != nil {
|
||||
return bucketApi.NewDeleteBucketEventDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return bucketApi.NewDeleteBucketEventNoContent()
|
||||
@@ -125,9 +125,11 @@ func listBucketEvents(client MinioClient, bucketName string) ([]*models.Notifica
|
||||
|
||||
// getListBucketsResponse performs listBucketEvents() and serializes it to the handler's output
|
||||
func getListBucketEventsResponse(session *models.Principal, params bucketApi.ListBucketEventsParams) (*models.ListBucketEventsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -135,7 +137,7 @@ func getListBucketEventsResponse(session *models.Principal, params bucketApi.Lis
|
||||
|
||||
bucketEvents, err := listBucketEvents(minioClient, params.BucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
listBucketsResponse := &models.ListBucketEventsResponse{
|
||||
@@ -175,19 +177,21 @@ func createBucketEvent(ctx context.Context, client MCClient, arn string, notific
|
||||
}
|
||||
|
||||
// getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification
|
||||
func getCreateBucketEventsResponse(session *models.Principal, bucketName string, eventReq *models.BucketEventRequest) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getCreateBucketEventsResponse(session *models.Principal, params bucketApi.CreateBucketEventParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucketName := params.BucketName
|
||||
eventReq := params.Body
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = createBucketEvent(ctx, mcClient, *eventReq.Configuration.Arn, eventReq.Configuration.Events, eventReq.Configuration.Prefix, eventReq.Configuration.Suffix, eventReq.IgnoreExisting)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -211,19 +215,24 @@ func joinNotificationEvents(events []models.NotificationEventType) string {
|
||||
}
|
||||
|
||||
// getDeleteBucketEventsResponse calls deleteBucketEventNotification() to delete a bucket event notification
|
||||
func getDeleteBucketEventsResponse(session *models.Principal, bucketName string, arn string, events []models.NotificationEventType, prefix, suffix *string) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getDeleteBucketEventsResponse(session *models.Principal, params bucketApi.DeleteBucketEventParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
bucketName := params.BucketName
|
||||
arn := params.Arn
|
||||
events := params.Body.Events
|
||||
prefix := params.Body.Prefix
|
||||
suffix := params.Body.Suffix
|
||||
s3Client, err := newS3BucketClient(session, bucketName, "")
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = deleteBucketEventNotification(ctx, mcClient, arn, events, prefix, suffix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -142,11 +142,11 @@ func getBucketLifecycle(ctx context.Context, client MinioClient, bucketName stri
|
||||
|
||||
// getBucketLifecycleResponse performs getBucketLifecycle() and serializes it to the handler's output
|
||||
func getBucketLifecycleResponse(session *models.Principal, params bucketApi.GetBucketLifecycleParams) (*models.BucketLifecycleResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -154,7 +154,7 @@ func getBucketLifecycleResponse(session *models.Principal, params bucketApi.GetB
|
||||
|
||||
bucketEvents, err := getBucketLifecycle(ctx, minioClient, params.BucketName)
|
||||
if err != nil {
|
||||
return nil, prepareError(errBucketLifeCycleNotConfigured, err)
|
||||
return nil, ErrorWithContext(ctx, ErrBucketLifeCycleNotConfigured, err)
|
||||
}
|
||||
return bucketEvents, nil
|
||||
}
|
||||
@@ -226,7 +226,7 @@ func addBucketLifecycle(ctx context.Context, client MinioClient, params bucketAp
|
||||
}
|
||||
|
||||
} else {
|
||||
// Non set, we return error
|
||||
// Non set, we return errors
|
||||
return errors.New("no valid configuration requested")
|
||||
}
|
||||
|
||||
@@ -241,11 +241,11 @@ func addBucketLifecycle(ctx context.Context, client MinioClient, params bucketAp
|
||||
|
||||
// getAddBucketLifecycleResponse returns the response of adding a bucket lifecycle response
|
||||
func getAddBucketLifecycleResponse(session *models.Principal, params bucketApi.AddBucketLifecycleParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -253,7 +253,7 @@ func getAddBucketLifecycleResponse(session *models.Principal, params bucketApi.A
|
||||
|
||||
err = addBucketLifecycle(ctx, minioClient, params)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -325,7 +325,7 @@ func editBucketLifecycle(ctx context.Context, client MinioClient, params bucketA
|
||||
}
|
||||
|
||||
} else {
|
||||
// Non set, we return error
|
||||
// Non set, we return errors
|
||||
return errors.New("no valid configuration requested")
|
||||
}
|
||||
|
||||
@@ -340,11 +340,11 @@ func editBucketLifecycle(ctx context.Context, client MinioClient, params bucketA
|
||||
|
||||
// getEditBucketLifecycleRule returns the response of bucket lifecycle tier edit
|
||||
func getEditBucketLifecycleRule(session *models.Principal, params bucketApi.UpdateBucketLifecycleParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -352,7 +352,7 @@ func getEditBucketLifecycleRule(session *models.Principal, params bucketApi.Upda
|
||||
|
||||
err = editBucketLifecycle(ctx, minioClient, params)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -394,11 +394,11 @@ func deleteBucketLifecycle(ctx context.Context, client MinioClient, params bucke
|
||||
|
||||
// getDeleteBucketLifecycleRule returns the response of bucket lifecycle tier delete
|
||||
func getDeleteBucketLifecycleRule(session *models.Principal, params bucketApi.DeleteBucketLifecycleRuleParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -406,7 +406,7 @@ func getDeleteBucketLifecycleRule(session *models.Principal, params bucketApi.De
|
||||
|
||||
err = deleteBucketLifecycle(ctx, minioClient, params)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -480,11 +480,11 @@ func addMultiBucketLifecycle(ctx context.Context, client MinioClient, params buc
|
||||
|
||||
// getAddMultiBucketLifecycleResponse returns the response of multibucket lifecycle assignment
|
||||
func getAddMultiBucketLifecycleResponse(session *models.Principal, params bucketApi.AddMultiBucketLifecycleParams) (*models.MultiLifecycleResult, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
|
||||
@@ -143,18 +143,17 @@ func TestMakeBucket(t *testing.T) {
|
||||
// mock minIO client
|
||||
minClient := minioClientMock{}
|
||||
function := "makeBucket()"
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
// Test-1: makeBucket() create a bucket
|
||||
// mock function response from makeBucketWithContext(ctx)
|
||||
minioMakeBucketWithContextMock = func(ctx context.Context, bucketName, location string, objectLock bool) error {
|
||||
return nil
|
||||
}
|
||||
if err := makeBucket(ctx, minClient, "bucktest1", true); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2 makeBucket() make sure errors are handled correctly when error on MakeBucketWithContext
|
||||
// Test-2 makeBucket() make sure errors are handled correctly when errors on MakeBucketWithContext
|
||||
minioMakeBucketWithContextMock = func(ctx context.Context, bucketName, location string, objectLock bool) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
@@ -175,10 +174,10 @@ func TestDeleteBucket(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
if err := removeBucket(minClient, "bucktest1"); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: removeBucket() make sure errors are handled correctly when error on DeleteBucket()
|
||||
// Test-2: removeBucket() make sure errors are handled correctly when errors on DeleteBucket()
|
||||
// mock function response from removeBucket(bucketName)
|
||||
minioRemoveBucketMock = func(bucketName string) error {
|
||||
return errors.New("error")
|
||||
@@ -193,8 +192,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
// mock minIO client
|
||||
minClient := minioClientMock{}
|
||||
adminClient := adminClientMock{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
function := "getBucketInfo()"
|
||||
|
||||
// Test-1: getBucketInfo() get a bucket with PRIVATE access
|
||||
@@ -248,7 +246,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
|
||||
bucketInfo, err := getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(outputExpected.Name, bucketInfo.Name)
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
@@ -272,7 +270,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(outputExpected.Name, bucketInfo.Name)
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
@@ -296,7 +294,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
}
|
||||
bucketInfo, err = getBucketInfo(ctx, minClient, adminClient, bucketToSet)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
assert.Equal(outputExpected.Name, bucketInfo.Name)
|
||||
assert.Equal(outputExpected.Access, bucketInfo.Access)
|
||||
@@ -304,7 +302,7 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert.Equal(outputExpected.Size, bucketInfo.Size)
|
||||
assert.Equal(outputExpected.Objects, bucketInfo.Objects)
|
||||
|
||||
// Test-4: getBucketInfo() returns an error while parsing invalid policy
|
||||
// Test-4: getBucketInfo() returns an errors while parsing invalid policy
|
||||
mockPolicy = "policyinvalid"
|
||||
minioGetBucketPolicyMock = func(bucketName string) (string, error) {
|
||||
return mockPolicy, nil
|
||||
@@ -321,14 +319,13 @@ func TestBucketInfo(t *testing.T) {
|
||||
assert.Equal("invalid character 'p' looking for beginning of value", err.Error())
|
||||
}
|
||||
|
||||
// Test-4: getBucketInfo() handle GetBucketPolicy error correctly
|
||||
// Test-4: getBucketInfo() handle GetBucketPolicy errors correctly
|
||||
// Test removed since we can tolerate this scenario now
|
||||
}
|
||||
|
||||
func TestSetBucketAccess(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
// mock minIO client
|
||||
minClient := minioClientMock{}
|
||||
|
||||
@@ -339,30 +336,30 @@ func TestSetBucketAccess(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", models.BucketAccessPUBLIC, ""); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-2: setBucketAccessPolicy() set private access
|
||||
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", models.BucketAccessPRIVATE, ""); err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
t.Errorf("Failed on %s:, errors occurred: %s", function, err.Error())
|
||||
}
|
||||
|
||||
// Test-3: setBucketAccessPolicy() set invalid access, expected error
|
||||
// Test-3: setBucketAccessPolicy() set invalid access, expected errors
|
||||
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", "other", ""); assert.Error(err) {
|
||||
assert.Equal("access: `other` not supported", err.Error())
|
||||
}
|
||||
|
||||
// Test-4: setBucketAccessPolicy() set access on empty bucket name, expected error
|
||||
// Test-4: setBucketAccessPolicy() set access on empty bucket name, expected errors
|
||||
if err := setBucketAccessPolicy(ctx, minClient, "", models.BucketAccessPRIVATE, ""); assert.Error(err) {
|
||||
assert.Equal("error: bucket name not present", err.Error())
|
||||
}
|
||||
|
||||
// Test-5: setBucketAccessPolicy() set empty access on bucket, expected error
|
||||
// Test-5: setBucketAccessPolicy() set empty access on bucket, expected errors
|
||||
if err := setBucketAccessPolicy(ctx, minClient, "bucktest1", "", ""); assert.Error(err) {
|
||||
assert.Equal("error: bucket access not present", err.Error())
|
||||
}
|
||||
|
||||
// Test-5: setBucketAccessPolicy() handle error on setPolicy call
|
||||
// Test-5: setBucketAccessPolicy() handle errors on setPolicy call
|
||||
minioSetBucketPolicyWithContextMock = func(ctx context.Context, bucketName, policy string) error {
|
||||
return errors.New("error")
|
||||
}
|
||||
@@ -373,8 +370,7 @@ func TestSetBucketAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_enableBucketEncryption(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
minClient := minioClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -410,7 +406,7 @@ func Test_enableBucketEncryption(t *testing.T) {
|
||||
bucketName: "test",
|
||||
encryptionType: "sse-s3",
|
||||
mockEnableBucketEncryptionFunc: func(ctx context.Context, bucketName string, config *sse.Configuration) error {
|
||||
return errorGenericInvalidSession
|
||||
return ErrInvalidSession
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -420,15 +416,14 @@ func Test_enableBucketEncryption(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
minioSetBucketEncryptionMock = tt.args.mockEnableBucketEncryptionFunc
|
||||
if err := enableBucketEncryption(tt.args.ctx, tt.args.client, tt.args.bucketName, tt.args.encryptionType, tt.args.kmsKeyID); (err != nil) != tt.wantErr {
|
||||
t.Errorf("enableBucketEncryption() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("enableBucketEncryption() errors = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_disableBucketEncryption(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
minClient := minioClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -460,7 +455,7 @@ func Test_disableBucketEncryption(t *testing.T) {
|
||||
client: minClient,
|
||||
bucketName: "test",
|
||||
mockBucketDisableFunc: func(ctx context.Context, bucketName string) error {
|
||||
return ErrorGeneric
|
||||
return ErrDefault
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -470,15 +465,14 @@ func Test_disableBucketEncryption(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
minioRemoveBucketEncryptionMock = tt.args.mockBucketDisableFunc
|
||||
if err := disableBucketEncryption(tt.args.ctx, tt.args.client, tt.args.bucketName); (err != nil) != tt.wantErr {
|
||||
t.Errorf("disableBucketEncryption() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("disableBucketEncryption() errors = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getBucketEncryptionInfo(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
minClient := minioClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -535,7 +529,7 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
|
||||
client: minClient,
|
||||
bucketName: "test",
|
||||
mockBucketEncryptionGet: func(ctx context.Context, bucketName string) (*sse.Configuration, error) {
|
||||
return nil, errSSENotConfigured
|
||||
return nil, ErrSSENotConfigured
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@@ -546,7 +540,7 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
|
||||
minioGetBucketEncryptionMock = tt.args.mockBucketEncryptionGet
|
||||
got, err := getBucketEncryptionInfo(tt.args.ctx, tt.args.client, tt.args.bucketName)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getBucketEncryptionInfo() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("getBucketEncryptionInfo() errors = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
@@ -558,8 +552,7 @@ func Test_getBucketEncryptionInfo(t *testing.T) {
|
||||
|
||||
func Test_SetBucketRetentionConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
minClient := minioClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -651,7 +644,7 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
|
||||
expectedError: errors.New("invalid retention unit"),
|
||||
},
|
||||
{
|
||||
name: "Handle error on objec lock function",
|
||||
name: "Handle errors on objec lock function",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: minClient,
|
||||
@@ -672,9 +665,9 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
|
||||
err := setBucketRetentionConfig(tt.args.ctx, tt.args.client, tt.args.bucketName, tt.args.mode, tt.args.unit, tt.args.validity)
|
||||
if tt.expectedError != nil {
|
||||
fmt.Println(t.Name())
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("setObjectRetention() error: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("setObjectRetention() errors: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
} else {
|
||||
assert.Nil(err, fmt.Sprintf("setBucketRetentionConfig() error: %v, wantErr: %v", err, tt.expectedError))
|
||||
assert.Nil(err, fmt.Sprintf("setBucketRetentionConfig() errors: %v, wantErr: %v", err, tt.expectedError))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -682,8 +675,7 @@ func Test_SetBucketRetentionConfig(t *testing.T) {
|
||||
|
||||
func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
minClient := minioClientMock{}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -751,7 +743,7 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
{
|
||||
// Description: if minio return NoSuchObjectLockConfiguration, don't panic
|
||||
// and return empty response
|
||||
name: "Handle NoLock Config error",
|
||||
name: "Handle NoLock Config errors",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: minClient,
|
||||
@@ -767,7 +759,7 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Return error on invalid mode",
|
||||
name: "Return errors on invalid mode",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: minClient,
|
||||
@@ -782,7 +774,7 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
expectedError: errors.New("invalid retention mode"),
|
||||
},
|
||||
{
|
||||
name: "Return error on invalid unit",
|
||||
name: "Return errors on invalid unit",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
client: minClient,
|
||||
@@ -805,9 +797,9 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
|
||||
if tt.expectedError != nil {
|
||||
fmt.Println(t.Name())
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("getBucketRetentionConfig() error: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("getBucketRetentionConfig() errors: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
} else {
|
||||
assert.Nil(err, fmt.Sprintf("getBucketRetentionConfig() error: %v, wantErr: %v", err, tt.expectedError))
|
||||
assert.Nil(err, fmt.Sprintf("getBucketRetentionConfig() errors: %v, wantErr: %v", err, tt.expectedError))
|
||||
if !reflect.DeepEqual(resp, tt.expectedResponse) {
|
||||
t.Errorf("getBucketRetentionConfig() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
return
|
||||
@@ -819,8 +811,7 @@ func Test_GetBucketRetentionConfig(t *testing.T) {
|
||||
|
||||
func Test_SetBucketVersioning(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := context.Background()
|
||||
errorMsg := "Error Message"
|
||||
minClient := s3ClientMock{}
|
||||
type args struct {
|
||||
@@ -874,7 +865,7 @@ func Test_SetBucketVersioning(t *testing.T) {
|
||||
|
||||
if tt.expectedError != nil {
|
||||
fmt.Println(t.Name())
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("getBucketRetentionConfig() error: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
assert.Equal(tt.expectedError.Error(), err.Error(), fmt.Sprintf("getBucketRetentionConfig() errors: `%s`, wantErr: `%s`", err, tt.expectedError))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1166,7 +1157,7 @@ func Test_getAccountBuckets(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
mockBucketList: madmin.AccountInfo{},
|
||||
mockError: errors.New("some error"),
|
||||
mockError: errors.New("some errors"),
|
||||
},
|
||||
want: []*models.Bucket{},
|
||||
wantErr: assert.Error,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -42,7 +43,9 @@ func registerLogSearchHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// getLogSearchResponse performs a query to Log Search if Enabled
|
||||
func getLogSearchResponse(session *models.Principal, params logApi.LogSearchParams) (*models.LogSearchResponse, *models.Error) {
|
||||
sessionResp, err := getSessionResponse(session)
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
sessionResp, err := getSessionResponse(ctx, session)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -80,31 +83,28 @@ func getLogSearchResponse(session *models.Principal, params logApi.LogSearchPara
|
||||
endpoint = fmt.Sprintf("%s&pageSize=%d", endpoint, *params.PageSize)
|
||||
endpoint = fmt.Sprintf("%s&pageNo=%d", endpoint, *params.PageNo)
|
||||
|
||||
return logSearch(endpoint)
|
||||
response, errLogSearch := logSearch(endpoint)
|
||||
if errLogSearch != nil {
|
||||
return nil, ErrorWithContext(ctx, errLogSearch)
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func logSearch(endpoint string) (*models.LogSearchResponse, *models.Error) {
|
||||
func logSearch(endpoint string) (*models.LogSearchResponse, error) {
|
||||
httpClnt := GetConsoleHTTPClient()
|
||||
resp, err := httpClnt.Get(endpoint)
|
||||
if err != nil {
|
||||
return nil, &models.Error{
|
||||
Code: int32(500),
|
||||
Message: swag.String("Log Search API not available."),
|
||||
DetailedMessage: swag.String("The Log Search API cannot be reached. Please review the URL and try again."),
|
||||
}
|
||||
return nil, fmt.Errorf("the Log Search API cannot be reached. Please review the URL and try again %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, &models.Error{
|
||||
Code: int32(resp.StatusCode),
|
||||
Message: swag.String(fmt.Sprintf("error retrieving logs: %s", http.StatusText(resp.StatusCode))),
|
||||
}
|
||||
return nil, fmt.Errorf("error retrieving logs: %s", http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
var results []map[string]interface{}
|
||||
if err = json.NewDecoder(resp.Body).Decode(&results); err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.LogSearchResponse{
|
||||
|
||||
@@ -17,16 +17,14 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/swag"
|
||||
"github.com/minio/console/models"
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLogSearch(t *testing.T) {
|
||||
@@ -41,7 +39,7 @@ func TestLogSearch(t *testing.T) {
|
||||
"request_id": "16595A4E30CCFE79",
|
||||
"user_agent": "MinIO (linux; amd64) madmin-go/0.0.1",
|
||||
"response_status": "OK",
|
||||
"response_status_code": 200,
|
||||
"response_status_code": float64(200),
|
||||
"request_content_length": nil,
|
||||
"response_content_length": nil,
|
||||
}, {
|
||||
@@ -54,13 +52,12 @@ func TestLogSearch(t *testing.T) {
|
||||
"request_id": "16595A4DA906FBA9",
|
||||
"user_agent": "Go-http-client/1.1",
|
||||
"response_status": "OK",
|
||||
"response_status_code": 200,
|
||||
"response_status_code": float64(200),
|
||||
"request_content_length": nil,
|
||||
"response_content_length": nil,
|
||||
},
|
||||
}
|
||||
|
||||
assert := asrt.New(t)
|
||||
type args struct {
|
||||
apiResponse string
|
||||
apiResponseCode int
|
||||
@@ -76,7 +73,7 @@ func TestLogSearch(t *testing.T) {
|
||||
name string
|
||||
args args
|
||||
expectedResponse *models.LogSearchResponse
|
||||
expectedError *models.Error
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "200 Success response",
|
||||
@@ -85,7 +82,7 @@ func TestLogSearch(t *testing.T) {
|
||||
apiResponseCode: 200,
|
||||
},
|
||||
expectedResponse: successfulResponse,
|
||||
expectedError: nil,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "500 unsuccessful response",
|
||||
@@ -94,10 +91,7 @@ func TestLogSearch(t *testing.T) {
|
||||
apiResponseCode: 500,
|
||||
},
|
||||
expectedResponse: nil,
|
||||
expectedError: &models.Error{
|
||||
Code: 500,
|
||||
Message: swag.String(fmt.Sprintf("error retrieving logs: %s", http.StatusText(500))),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -112,27 +106,33 @@ func TestLogSearch(t *testing.T) {
|
||||
|
||||
resp, err := logSearch(testRequest.URL)
|
||||
|
||||
if tt.expectedError != nil {
|
||||
assert.Equal(tt.expectedError.Code, err.Code, fmt.Sprintf("logSearch() error code: `%v`, wantErr: `%v`", err.Code, tt.expectedError))
|
||||
assert.Equal(tt.expectedError.Message, err.Message, fmt.Sprintf("logSearch() error message: `%v`, wantErr: `%v`", err.Message, tt.expectedError))
|
||||
} else {
|
||||
assert.Nil(err, fmt.Sprintf("logSearch() error: %v, wantErr: %v", err, tt.expectedError))
|
||||
buf1, err1 := tt.expectedResponse.MarshalBinary()
|
||||
buf2, err2 := resp.MarshalBinary()
|
||||
if err1 != err2 {
|
||||
t.Errorf("logSearch() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
return
|
||||
}
|
||||
h := sha256.New()
|
||||
h.Write(buf1)
|
||||
checkSum1 := fmt.Sprintf("%x\n", h.Sum(nil))
|
||||
h.Reset()
|
||||
h.Write(buf2)
|
||||
checkSum2 := fmt.Sprintf("%x\n", h.Sum(nil))
|
||||
if checkSum1 != checkSum2 {
|
||||
t.Errorf("logSearch() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
}
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("logSearch() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
if !reflect.DeepEqual(resp, tt.expectedResponse) {
|
||||
t.Errorf("\ngot: %d \nwant: %d", resp, tt.expectedResponse)
|
||||
}
|
||||
//if tt.wantErr {
|
||||
// assert.Equal(tt.expectedError.Code, err.Code, fmt.Sprintf("logSearch() error code: `%v`, wantErr: `%v`", err.Code, tt.expectedError))
|
||||
// assert.Equal(tt.expectedError.Message, err.Message, fmt.Sprintf("logSearch() error message: `%v`, wantErr: `%v`", err.Message, tt.expectedError))
|
||||
//} else {
|
||||
// assert.Nil(err, fmt.Sprintf("logSearch() error: %v, wantErr: %v", err, tt.expectedError))
|
||||
// buf1, err1 := tt.expectedResponse.MarshalBinary()
|
||||
// buf2, err2 := resp.MarshalBinary()
|
||||
// if err1 != err2 {
|
||||
// t.Errorf("logSearch() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
// return
|
||||
// }
|
||||
// h := sha256.New()
|
||||
// h.Write(buf1)
|
||||
// checkSum1 := fmt.Sprintf("%x\n", h.Sum(nil))
|
||||
// h.Reset()
|
||||
// h.Write(buf2)
|
||||
// checkSum2 := fmt.Sprintf("%x\n", h.Sum(nil))
|
||||
// if checkSum1 != checkSum2 {
|
||||
// t.Errorf("logSearch() resp: %v, expectedResponse: %v", resp, tt.expectedResponse)
|
||||
// }
|
||||
//}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
func registerLoginHandlers(api *operations.ConsoleAPI) {
|
||||
// GET login strategy
|
||||
api.AuthLoginDetailHandler = authApi.LoginDetailHandlerFunc(func(params authApi.LoginDetailParams) middleware.Responder {
|
||||
loginDetails, err := getLoginDetailsResponse(params.HTTPRequest)
|
||||
loginDetails, err := getLoginDetailsResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginDetailDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func registerLoginHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// POST login using user credentials
|
||||
api.AuthLoginHandler = authApi.LoginHandlerFunc(func(params authApi.LoginParams) middleware.Responder {
|
||||
loginResponse, err := getLoginResponse(params.Body)
|
||||
loginResponse, err := getLoginResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -57,7 +57,7 @@ func registerLoginHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// POST login using external IDP
|
||||
api.AuthLoginOauth2AuthHandler = authApi.LoginOauth2AuthHandlerFunc(func(params authApi.LoginOauth2AuthParams) middleware.Responder {
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params.HTTPRequest, params.Body)
|
||||
loginResponse, err := getLoginOauth2AuthResponse(params)
|
||||
if err != nil {
|
||||
return authApi.NewLoginOauth2AuthDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func login(credentials ConsoleCredentialsI, sessionFeatures *auth.SessionFeature
|
||||
token, err := auth.NewEncryptedTokenForClient(&tokens, credentials.GetAccountAccessKey(), sessionFeatures)
|
||||
if err != nil {
|
||||
LogError("error authenticating user: %v", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, ErrInvalidLogin
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
@@ -109,11 +109,14 @@ func getConsoleCredentials(accessKey, secretKey string) (*ConsoleCredentials, er
|
||||
}
|
||||
|
||||
// getLoginResponse performs login() and serializes it to the handler's output
|
||||
func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.Error) {
|
||||
func getLoginResponse(params authApi.LoginParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
lr := params.Body
|
||||
// prepare console credentials
|
||||
consoleCreds, err := getConsoleCredentials(*lr.AccessKey, *lr.SecretKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errInvalidCredentials, err)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidLogin, err)
|
||||
}
|
||||
sf := &auth.SessionFeatures{}
|
||||
if lr.Features != nil {
|
||||
@@ -121,7 +124,7 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.E
|
||||
}
|
||||
sessionID, err := login(consoleCreds, sf)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errInvalidCredentials, err)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidLogin, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -131,16 +134,18 @@ func getLoginResponse(lr *models.LoginRequest) (*models.LoginResponse, *models.E
|
||||
}
|
||||
|
||||
// getLoginDetailsResponse returns information regarding the Console authentication mechanism.
|
||||
func getLoginDetailsResponse(r *http.Request) (*models.LoginDetails, *models.Error) {
|
||||
func getLoginDetailsResponse(params authApi.LoginDetailParams) (*models.LoginDetails, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
loginStrategy := models.LoginDetailsLoginStrategyForm
|
||||
redirectURL := ""
|
||||
|
||||
r := params.HTTPRequest
|
||||
if oauth2.IsIDPEnabled() {
|
||||
loginStrategy = models.LoginDetailsLoginStrategyRedirect
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errOauth2Provider)
|
||||
return nil, ErrorWithContext(ctx, err, ErrOauth2Provider)
|
||||
}
|
||||
// Validate user against IDP
|
||||
identityProvider := &auth.IdentityProvider{Client: oauth2Client}
|
||||
@@ -159,26 +164,28 @@ func verifyUserAgainstIDP(ctx context.Context, provider auth.IdentityProviderI,
|
||||
userCredentials, err := provider.VerifyIdentity(ctx, code, state)
|
||||
if err != nil {
|
||||
LogError("error validating user identity against idp: %v", err)
|
||||
return nil, errInvalidCredentials
|
||||
return nil, ErrInvalidLogin
|
||||
}
|
||||
return userCredentials, nil
|
||||
}
|
||||
|
||||
func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthRequest) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getLoginOauth2AuthResponse(params authApi.LoginOauth2AuthParams) (*models.LoginResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
r := params.HTTPRequest
|
||||
lr := params.Body
|
||||
if oauth2.IsIDPEnabled() {
|
||||
// initialize new oauth2 client
|
||||
oauth2Client, err := oauth2.NewOauth2ProviderClient(nil, r, GetConsoleHTTPClient())
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// initialize new identity provider
|
||||
identityProvider := auth.IdentityProvider{Client: oauth2Client}
|
||||
// Validate user against IDP
|
||||
userCredentials, err := verifyUserAgainstIDP(ctx, identityProvider, *lr.Code, *lr.State)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// initialize admin client
|
||||
// login user against console and generate session token
|
||||
@@ -187,7 +194,7 @@ func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthReque
|
||||
AccountAccessKey: "",
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// serialize output
|
||||
loginResponse := &models.LoginResponse{
|
||||
@@ -195,5 +202,5 @@ func getLoginOauth2AuthResponse(r *http.Request, lr *models.LoginOauth2AuthReque
|
||||
}
|
||||
return loginResponse, nil
|
||||
}
|
||||
return nil, prepareError(ErrorGeneric)
|
||||
return nil, ErrorWithContext(ctx, ErrDefault)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func logout(credentials ConsoleCredentialsI) {
|
||||
credentials.Expire()
|
||||
}
|
||||
|
||||
// getLogoutResponse performs logout() and returns nil or error
|
||||
// getLogoutResponse performs logout() and returns nil or errors
|
||||
func getLogoutResponse(session *models.Principal) {
|
||||
creds := getConsoleCredentialsFromSession(session)
|
||||
credentials := ConsoleCredentials{ConsoleCredentials: creds}
|
||||
|
||||
@@ -79,13 +79,13 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
|
||||
// download object
|
||||
api.ObjectDownloadObjectHandler = objectApi.DownloadObjectHandlerFunc(func(params objectApi.DownloadObjectParams, session *models.Principal) middleware.Responder {
|
||||
isFolder := false
|
||||
|
||||
ctx := params.HTTPRequest.Context()
|
||||
var prefix string
|
||||
if params.Prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return objectApi.NewDownloadObjectDefault(int(400)).WithPayload(prepareError(err))
|
||||
return objectApi.NewDownloadObjectDefault(400).WithPayload(ErrorWithContext(ctx, err))
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
@@ -173,6 +173,8 @@ func registerObjectsHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// getListObjectsResponse returns a list of objects
|
||||
func getListObjectsResponse(session *models.Principal, params objectApi.ListObjectsParams) (*models.ListObjectsResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var prefix string
|
||||
var recursive bool
|
||||
var withVersions bool
|
||||
@@ -181,7 +183,7 @@ func getListObjectsResponse(session *models.Principal, params objectApi.ListObje
|
||||
encodedPrefix := SanitizeEncodedPrefix(*params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
@@ -196,19 +198,19 @@ func getListObjectsResponse(session *models.Principal, params objectApi.ListObje
|
||||
}
|
||||
// bucket request needed to proceed
|
||||
if params.BucketName == "" {
|
||||
return nil, prepareError(errBucketNameNotInRequest)
|
||||
return nil, ErrorWithContext(ctx, ErrBucketNameNotInRequest)
|
||||
}
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
|
||||
objs, err := listBucketObjects(params.HTTPRequest.Context(), minioClient, params.BucketName, prefix, recursive, withVersions, withMetadata)
|
||||
objs, err := listBucketObjects(ctx, minioClient, params.BucketName, prefix, recursive, withVersions, withMetadata)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
resp := &models.ListObjectsResponse{
|
||||
@@ -254,7 +256,7 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
if err != nil {
|
||||
errResp := minio.ToErrorResponse(probe.NewError(err).ToGoError())
|
||||
if errResp.Code != "InvalidRequest" && errResp.Code != "NoSuchObjectLockConfiguration" {
|
||||
LogError("error getting legal hold status for %s : %v", lsObj.VersionID, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting legal hold status for %s : %v", lsObj.VersionID, err))
|
||||
}
|
||||
} else {
|
||||
if legalHoldStatus != nil {
|
||||
@@ -266,7 +268,7 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
if err != nil {
|
||||
errResp := minio.ToErrorResponse(probe.NewError(err).ToGoError())
|
||||
if errResp.Code != "InvalidRequest" && errResp.Code != "NoSuchObjectLockConfiguration" {
|
||||
LogError("error getting retention status for %s : %v", lsObj.VersionID, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting retention status for %s : %v", lsObj.VersionID, err))
|
||||
}
|
||||
} else {
|
||||
if retention != nil && retUntilDate != nil {
|
||||
@@ -277,7 +279,7 @@ func listBucketObjects(ctx context.Context, client MinioClient, bucketName strin
|
||||
}
|
||||
tags, err := client.getObjectTagging(ctx, bucketName, lsObj.Key, minio.GetObjectTaggingOptions{VersionID: lsObj.VersionID})
|
||||
if err != nil {
|
||||
LogError("error getting object tags for %s : %v", lsObj.VersionID, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting object tags for %s : %v", lsObj.VersionID, err))
|
||||
} else {
|
||||
obj.Tags = tags.ToMap()
|
||||
}
|
||||
@@ -367,18 +369,17 @@ func parseRange(s string, size int64) ([]httpRange, error) {
|
||||
}
|
||||
|
||||
func getDownloadObjectResponse(session *models.Principal, params objectApi.DownloadObjectParams) (middleware.Responder, *models.Error) {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx := params.HTTPRequest.Context()
|
||||
var prefix string
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
if params.Prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
@@ -391,7 +392,7 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
||||
|
||||
resp, err := mClient.GetObject(ctx, params.BucketName, prefix, opts)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
return middleware.ResponderFunc(func(rw http.ResponseWriter, _ runtime.Producer) {
|
||||
@@ -414,17 +415,15 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
||||
stat, err := resp.Stat()
|
||||
if err != nil {
|
||||
minErr := minio.ToErrorResponse(err)
|
||||
// non-200 means we requested something wrong
|
||||
rw.WriteHeader(minErr.StatusCode)
|
||||
|
||||
LogError("Failed to get Stat() response from server for %s (version %s): %v", prefix, opts.VersionID, minErr.Error())
|
||||
ErrorWithContext(ctx, fmt.Errorf("Failed to get Stat() response from server for %s (version %s): %v", prefix, opts.VersionID, minErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// if we are getting a Range Request (video) handle that specially
|
||||
ranges, err := parseRange(params.HTTPRequest.Header.Get("Range"), stat.Size)
|
||||
if err != nil {
|
||||
LogError("Unable to parse range header input %s: %v", params.HTTPRequest.Header.Get("Range"), err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to parse range header input %s: %v", params.HTTPRequest.Header.Get("Range"), err))
|
||||
rw.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
@@ -454,7 +453,7 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
||||
|
||||
_, err = resp.Seek(start, io.SeekStart)
|
||||
if err != nil {
|
||||
LogError("Unable to seek at offset %d: %v", start, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to seek at offset %d: %v", start, err))
|
||||
rw.WriteHeader(400)
|
||||
return
|
||||
}
|
||||
@@ -468,21 +467,21 @@ func getDownloadObjectResponse(session *models.Principal, params objectApi.Downl
|
||||
rw.Header().Set("Content-Length", fmt.Sprintf("%d", length))
|
||||
_, err = io.Copy(rw, io.LimitReader(resp, length))
|
||||
if err != nil {
|
||||
LogError("Unable to write all data to client: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to write all data to client: %v", err))
|
||||
return
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
func getDownloadFolderResponse(session *models.Principal, params objectApi.DownloadObjectParams) (middleware.Responder, *models.Error) {
|
||||
ctx := context.Background()
|
||||
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var prefix string
|
||||
mClient, err := newMinioClient(session)
|
||||
if params.Prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
@@ -490,12 +489,12 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
|
||||
folders := strings.Split(prefix, "/")
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
minioClient := minioClient{client: mClient}
|
||||
objects, err := listBucketObjects(ctx, minioClient, params.BucketName, prefix, true, false, false)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
w := new(bytes.Buffer)
|
||||
zipw := zip.NewWriter(w)
|
||||
@@ -507,11 +506,11 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
|
||||
name := folder + objects[i].Name[len(prefix)-1:]
|
||||
object, err := mClient.GetObject(ctx, params.BucketName, objects[i].Name, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
f, err := zipw.Create(name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(object)
|
||||
@@ -530,7 +529,7 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
LogError("Unable to parse encoded prefix %s: %v", encodedPrefix, err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to parse encoded prefix %s: %v", encodedPrefix, err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -552,27 +551,27 @@ func getDownloadFolderResponse(session *models.Principal, params objectApi.Downl
|
||||
// Copy the stream
|
||||
_, err := io.Copy(rw, resp)
|
||||
if err != nil {
|
||||
LogError("Unable to write all the requested data: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("Unable to write all the requested data: %v", err))
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
// getDeleteObjectResponse returns whether there was an error on deletion of object
|
||||
// getDeleteObjectResponse returns whether there was an errors on deletion of object
|
||||
func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteObjectParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var prefix string
|
||||
if params.Path != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Path)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, prefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -596,19 +595,19 @@ func getDeleteObjectResponse(session *models.Principal, params objectApi.DeleteO
|
||||
|
||||
if allVersions && nonCurrentVersions {
|
||||
err := errors.New("cannot set delete all versions and delete non-current versions flags at the same time")
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, prefix, version, rec, allVersions, nonCurrentVersions)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDeleteMultiplePathsResponse returns whether there was an error on deletion of any object
|
||||
// getDeleteMultiplePathsResponse returns whether there was an errors on deletion of any object
|
||||
func getDeleteMultiplePathsResponse(session *models.Principal, params objectApi.DeleteMultipleObjectsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
var version string
|
||||
var allVersions bool
|
||||
@@ -622,14 +621,14 @@ func getDeleteMultiplePathsResponse(session *models.Principal, params objectApi.
|
||||
prefix := params.Files[i].Path
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, prefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
mcClient := mcClient{client: s3Client}
|
||||
err = deleteObjects(ctx, mcClient, params.BucketName, params.Files[i].Path, version, params.Files[i].Recursive, allVersions, false)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -680,7 +679,7 @@ OUTER_LOOP:
|
||||
switch content.Err.ToGoError().(type) {
|
||||
// ignore same as mc
|
||||
case mc.PathInsufficientPermission:
|
||||
// Ignore Permission error.
|
||||
// Ignore Permission errors.
|
||||
continue
|
||||
}
|
||||
close(contentCh)
|
||||
@@ -696,7 +695,7 @@ OUTER_LOOP:
|
||||
switch result.Err.ToGoError().(type) {
|
||||
// ignore same as mc
|
||||
case mc.PathInsufficientPermission:
|
||||
// Ignore Permission error.
|
||||
// Ignore Permission errors.
|
||||
continue
|
||||
}
|
||||
close(contentCh)
|
||||
@@ -712,7 +711,7 @@ OUTER_LOOP:
|
||||
switch result.Err.ToGoError().(type) {
|
||||
// ignore same as mc
|
||||
case mc.PathInsufficientPermission:
|
||||
// Ignore Permission error.
|
||||
// Ignore Permission errors.
|
||||
continue
|
||||
}
|
||||
return result.Err.Cause
|
||||
@@ -738,7 +737,7 @@ func deleteSingleObject(ctx context.Context, client MCClient, bucket, object str
|
||||
switch result.Err.ToGoError().(type) {
|
||||
// ignore same as mc
|
||||
case mc.PathInsufficientPermission:
|
||||
// Ignore Permission error.
|
||||
// Ignore Permission errors.
|
||||
continue
|
||||
}
|
||||
return result.Err.Cause
|
||||
@@ -767,17 +766,16 @@ func deleteNonCurrentVersions(ctx context.Context, client MCClient, bucket, path
|
||||
}
|
||||
|
||||
func getUploadObjectResponse(session *models.Principal, params objectApi.PostBucketsBucketNameObjectsUploadParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
minioClient := minioClient{client: mClient}
|
||||
if err := uploadFiles(ctx, minioClient, params); err != nil {
|
||||
return prepareError(err, ErrorGeneric)
|
||||
return ErrorWithContext(ctx, err, ErrDefault)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -832,20 +830,19 @@ func uploadFiles(ctx context.Context, client MinioClient, params objectApi.PostB
|
||||
|
||||
// getShareObjectResponse returns a share object url
|
||||
func getShareObjectResponse(session *models.Principal, params objectApi.ShareObjectParams) (*string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
var prefix string
|
||||
if params.Prefix != "" {
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
s3Client, err := newS3BucketClient(session, params.BucketName, prefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a mc S3Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -856,7 +853,7 @@ func getShareObjectResponse(session *models.Principal, params objectApi.ShareObj
|
||||
}
|
||||
url, err := getShareObjectURL(ctx, mcClient, params.VersionID, expireDuration)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
@@ -879,11 +876,10 @@ func getShareObjectURL(ctx context.Context, client MCClient, versionID string, d
|
||||
}
|
||||
|
||||
func getSetObjectLegalHoldResponse(session *models.Principal, params objectApi.PutObjectLegalHoldParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -893,13 +889,13 @@ func getSetObjectLegalHoldResponse(session *models.Principal, params objectApi.P
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
err = setObjectLegalHold(ctx, minioClient, params.BucketName, prefix, params.VersionID, *params.Body.Status)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -915,11 +911,10 @@ func setObjectLegalHold(ctx context.Context, client MinioClient, bucketName, pre
|
||||
}
|
||||
|
||||
func getSetObjectRetentionResponse(session *models.Principal, params objectApi.PutObjectRetentionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -929,13 +924,13 @@ func getSetObjectRetentionResponse(session *models.Principal, params objectApi.P
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
err = setObjectRetention(ctx, minioClient, params.BucketName, params.VersionID, prefix, params.Body)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -968,11 +963,10 @@ func setObjectRetention(ctx context.Context, client MinioClient, bucketName, ver
|
||||
}
|
||||
|
||||
func deleteObjectRetentionResponse(session *models.Principal, params objectApi.DeleteObjectRetentionParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -982,13 +976,13 @@ func deleteObjectRetentionResponse(session *models.Principal, params objectApi.D
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
err = deleteObjectRetention(ctx, minioClient, params.BucketName, prefix, params.VersionID)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1003,11 +997,10 @@ func deleteObjectRetention(ctx context.Context, client MinioClient, bucketName,
|
||||
}
|
||||
|
||||
func getPutObjectTagsResponse(session *models.Principal, params objectApi.PutObjectTagsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -1017,13 +1010,13 @@ func getPutObjectTagsResponse(session *models.Principal, params objectApi.PutObj
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
err = putObjectTags(ctx, minioClient, params.BucketName, prefix, params.VersionID, params.Body.Tags)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1041,11 +1034,10 @@ func putObjectTags(ctx context.Context, client MinioClient, bucketName, prefix,
|
||||
|
||||
// Restore Object Version
|
||||
func getPutObjectRestoreResponse(session *models.Principal, params objectApi.PutObjectRestoreParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -1056,14 +1048,14 @@ func getPutObjectRestoreResponse(session *models.Principal, params objectApi.Put
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
|
||||
err = restoreObject(ctx, minioClient, params.BucketName, prefix, params.VersionID)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1098,11 +1090,10 @@ func restoreObject(ctx context.Context, client MinioClient, bucketName, prefix,
|
||||
|
||||
// Metadata Response from minio-go API
|
||||
func getObjectMetadataResponse(session *models.Principal, params objectApi.GetObjectMetadataParams) (*models.Metadata, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
ctx := params.HTTPRequest.Context()
|
||||
mClient, err := newMinioClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a minioClient interface implementation
|
||||
// defining the client to be used
|
||||
@@ -1113,7 +1104,7 @@ func getObjectMetadataResponse(session *models.Principal, params objectApi.GetOb
|
||||
encodedPrefix := SanitizeEncodedPrefix(params.Prefix)
|
||||
decodedPrefix, err := base64.StdEncoding.DecodeString(encodedPrefix)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
prefix = string(decodedPrefix)
|
||||
}
|
||||
@@ -1121,7 +1112,7 @@ func getObjectMetadataResponse(session *models.Principal, params objectApi.GetOb
|
||||
objectInfo, err := getObjectInfo(ctx, minioClient, params.BucketName, prefix)
|
||||
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
metadata := &models.Metadata{ObjectMetadata: objectInfo.Metadata}
|
||||
|
||||
@@ -36,7 +36,7 @@ import (
|
||||
func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
// Create Service Account
|
||||
api.ServiceAccountCreateServiceAccountHandler = saApi.CreateServiceAccountHandlerFunc(func(params saApi.CreateServiceAccountParams, session *models.Principal) middleware.Responder {
|
||||
creds, err := getCreateServiceAccountResponse(session, params.Body)
|
||||
creds, err := getCreateServiceAccountResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewCreateServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Create User Service Account
|
||||
api.UserCreateAUserServiceAccountHandler = userApi.CreateAUserServiceAccountHandlerFunc(func(params userApi.CreateAUserServiceAccountParams, session *models.Principal) middleware.Responder {
|
||||
creds, err := getCreateAUserServiceAccountResponse(session, params.Body, params.Name)
|
||||
creds, err := getCreateAUserServiceAccountResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewCreateServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -52,14 +52,14 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// Create User Service Account
|
||||
api.UserCreateServiceAccountCredentialsHandler = userApi.CreateServiceAccountCredentialsHandlerFunc(func(params userApi.CreateServiceAccountCredentialsParams, session *models.Principal) middleware.Responder {
|
||||
creds, err := getCreateAUserServiceAccountCredsResponse(session, params.Body, params.Name)
|
||||
creds, err := getCreateAUserServiceAccountCredsResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewCreateServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return userApi.NewCreateServiceAccountCredentialsCreated().WithPayload(creds)
|
||||
})
|
||||
api.ServiceAccountCreateServiceAccountCredsHandler = saApi.CreateServiceAccountCredsHandlerFunc(func(params saApi.CreateServiceAccountCredsParams, session *models.Principal) middleware.Responder {
|
||||
creds, err := getCreateServiceAccountCredsResponse(session, params.Body)
|
||||
creds, err := getCreateServiceAccountCredsResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewCreateServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -67,7 +67,9 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
// List Service Accounts for User
|
||||
api.ServiceAccountListUserServiceAccountsHandler = saApi.ListUserServiceAccountsHandlerFunc(func(params saApi.ListUserServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(session, "")
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(ctx, session, "")
|
||||
if err != nil {
|
||||
return saApi.NewListUserServiceAccountsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -76,7 +78,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// Delete a User's service account
|
||||
api.ServiceAccountDeleteServiceAccountHandler = saApi.DeleteServiceAccountHandlerFunc(func(params saApi.DeleteServiceAccountParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteServiceAccountResponse(session, params.AccessKey); err != nil {
|
||||
if err := getDeleteServiceAccountResponse(session, params); err != nil {
|
||||
return saApi.NewDeleteServiceAccountDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return saApi.NewDeleteServiceAccountNoContent()
|
||||
@@ -84,7 +86,9 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// List Service Accounts for User
|
||||
api.UserListAUserServiceAccountsHandler = userApi.ListAUserServiceAccountsHandlerFunc(func(params userApi.ListAUserServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(session, params.Name)
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
serviceAccounts, err := getUserServiceAccountsResponse(ctx, session, params.Name)
|
||||
if err != nil {
|
||||
return saApi.NewListUserServiceAccountsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -92,7 +96,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
|
||||
api.ServiceAccountGetServiceAccountPolicyHandler = saApi.GetServiceAccountPolicyHandlerFunc(func(params saApi.GetServiceAccountPolicyParams, session *models.Principal) middleware.Responder {
|
||||
serviceAccounts, err := getServiceAccountPolicyResponse(session, params.AccessKey)
|
||||
serviceAccounts, err := getServiceAccountPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewGetServiceAccountPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -100,7 +104,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
})
|
||||
|
||||
api.ServiceAccountSetServiceAccountPolicyHandler = saApi.SetServiceAccountPolicyHandlerFunc(func(params saApi.SetServiceAccountPolicyParams, session *models.Principal) middleware.Responder {
|
||||
err := getSetServiceAccountPolicyResponse(session, params.AccessKey, *params.Policy.Policy)
|
||||
err := getSetServiceAccountPolicyResponse(session, params)
|
||||
if err != nil {
|
||||
return saApi.NewSetServiceAccountPolicyDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -109,7 +113,7 @@ func registerServiceAccountsHandlers(api *operations.ConsoleAPI) {
|
||||
|
||||
// Delete multiple service accounts
|
||||
api.ServiceAccountDeleteMultipleServiceAccountsHandler = saApi.DeleteMultipleServiceAccountsHandlerFunc(func(params saApi.DeleteMultipleServiceAccountsParams, session *models.Principal) middleware.Responder {
|
||||
if err := getDeleteMultipleServiceAccountsResponse(session, params.SelectedSA); err != nil {
|
||||
if err := getDeleteMultipleServiceAccountsResponse(session, params); err != nil {
|
||||
return saApi.NewDeleteMultipleServiceAccountsDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
return saApi.NewDeleteMultipleServiceAccountsNoContent()
|
||||
@@ -157,21 +161,21 @@ func createServiceAccountCreds(ctx context.Context, userClient MinioAdmin, polic
|
||||
// getCreateServiceAccountResponse creates a service account with the defined policy for the user that
|
||||
// is requestingit ,it first gets the credentials of the user and creates a client which is going to
|
||||
// make the call to create the Service Account
|
||||
func getCreateServiceAccountResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequest) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getCreateServiceAccountResponse(session *models.Principal, params saApi.CreateServiceAccountParams) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
|
||||
saCreds, err := createServiceAccount(ctx, userAdminClient, serviceAccount.Policy)
|
||||
saCreds, err := createServiceAccount(ctx, userAdminClient, params.Body.Policy)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
@@ -218,91 +222,88 @@ func createAUserServiceAccountCreds(ctx context.Context, userClient MinioAdmin,
|
||||
// getCreateServiceAccountResponse creates a service account with the defined policy for the user that
|
||||
// is requesting it ,it first gets the credentials of the user and creates a client which is going to
|
||||
// make the call to create the Service Account
|
||||
func getCreateAUserServiceAccountResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequest, user string) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getCreateAUserServiceAccountResponse(session *models.Principal, params userApi.CreateAUserServiceAccountParams) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
|
||||
saCreds, err := createAUserServiceAccount(ctx, userAdminClient, serviceAccount.Policy, user)
|
||||
saCreds, err := createAUserServiceAccount(ctx, userAdminClient, params.Body.Policy, params.Name)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
|
||||
// getCreateServiceAccountCredsResponse creates a service account with the defined policy for the user that
|
||||
// is requesting it, and with the credentials provided
|
||||
func getCreateAUserServiceAccountCredsResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequestCreds, user string) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getCreateAUserServiceAccountCredsResponse(session *models.Principal, params userApi.CreateServiceAccountCredentialsParams) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
|
||||
serviceAccount := params.Body
|
||||
user := params.Name
|
||||
if user == serviceAccount.AccessKey {
|
||||
return nil, prepareError(errors.New("Access Key already in use"))
|
||||
return nil, ErrorWithContext(ctx, errors.New("Access Key already in use"))
|
||||
}
|
||||
|
||||
accounts, err := userAdminClient.listServiceAccounts(ctx, user)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(accounts.Accounts); i++ {
|
||||
if accounts.Accounts[i] == serviceAccount.AccessKey {
|
||||
return nil, prepareError(errors.New("Access Key already in use"))
|
||||
return nil, ErrorWithContext(ctx, errors.New("Access Key already in use"))
|
||||
}
|
||||
}
|
||||
|
||||
saCreds, err := createAUserServiceAccountCreds(ctx, userAdminClient, serviceAccount.Policy, user, serviceAccount.AccessKey, serviceAccount.SecretKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
|
||||
func getCreateServiceAccountCredsResponse(session *models.Principal, serviceAccount *models.ServiceAccountRequestCreds) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getCreateServiceAccountCredsResponse(session *models.Principal, params saApi.CreateServiceAccountCredsParams) (*models.ServiceAccountCreds, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
serviceAccount := params.Body
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
|
||||
if session.AccountAccessKey == serviceAccount.AccessKey {
|
||||
return nil, prepareError(errors.New("Access Key already in use"))
|
||||
return nil, ErrorWithContext(ctx, errors.New("Access Key already in use"))
|
||||
}
|
||||
|
||||
accounts, err := userAdminClient.listServiceAccounts(ctx, "")
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
|
||||
for i := 0; i < len(accounts.Accounts); i++ {
|
||||
if accounts.Accounts[i] == serviceAccount.AccessKey {
|
||||
return nil, prepareError(errors.New("Access Key already in use"))
|
||||
return nil, ErrorWithContext(ctx, errors.New("Access Key already in use"))
|
||||
}
|
||||
}
|
||||
|
||||
saCreds, err := createServiceAccountCreds(ctx, userAdminClient, serviceAccount.Policy, serviceAccount.AccessKey, serviceAccount.SecretKey)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return saCreds, nil
|
||||
}
|
||||
@@ -322,13 +323,10 @@ func getUserServiceAccounts(ctx context.Context, userClient MinioAdmin, user str
|
||||
|
||||
// getUserServiceAccountsResponse authenticates the user and calls
|
||||
// getUserServiceAccounts to list the user's service accounts
|
||||
func getUserServiceAccountsResponse(session *models.Principal, user string) (models.ServiceAccounts, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
func getUserServiceAccountsResponse(ctx context.Context, session *models.Principal, user string) (models.ServiceAccounts, *models.Error) {
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -336,7 +334,7 @@ func getUserServiceAccountsResponse(session *models.Principal, user string) (mod
|
||||
|
||||
serviceAccounts, err := getUserServiceAccounts(ctx, userAdminClient, user)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return serviceAccounts, nil
|
||||
}
|
||||
@@ -347,20 +345,19 @@ func deleteServiceAccount(ctx context.Context, userClient MinioAdmin, accessKey
|
||||
}
|
||||
|
||||
// getDeleteServiceAccountResponse authenticates the user and calls deleteServiceAccount
|
||||
func getDeleteServiceAccountResponse(session *models.Principal, accessKey string) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getDeleteServiceAccountResponse(session *models.Principal, params saApi.DeleteServiceAccountParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
accessKey := params.AccessKey
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
|
||||
if err := deleteServiceAccount(ctx, userAdminClient, accessKey); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -381,13 +378,13 @@ func getServiceAccountPolicy(ctx context.Context, userClient MinioAdmin, accessK
|
||||
|
||||
// getServiceAccountPolicyResponse authenticates the user and calls
|
||||
// getServiceAccountPolicy to get the policy for a service account
|
||||
func getServiceAccountPolicyResponse(session *models.Principal, accessKey string) (string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getServiceAccountPolicyResponse(session *models.Principal, params saApi.GetServiceAccountPolicyParams) (string, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
accessKey := params.AccessKey
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return "", prepareError(err)
|
||||
return "", ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -395,7 +392,7 @@ func getServiceAccountPolicyResponse(session *models.Principal, accessKey string
|
||||
|
||||
serviceAccounts, err := getServiceAccountPolicy(ctx, userAdminClient, accessKey)
|
||||
if err != nil {
|
||||
return "", prepareError(err)
|
||||
return "", ErrorWithContext(ctx, err)
|
||||
}
|
||||
return serviceAccounts, nil
|
||||
}
|
||||
@@ -408,13 +405,14 @@ func setServiceAccountPolicy(ctx context.Context, userClient MinioAdmin, accessK
|
||||
|
||||
// getSetServiceAccountPolicyResponse authenticates the user and calls
|
||||
// getSetServiceAccountPolicy to set the policy for a service account
|
||||
func getSetServiceAccountPolicyResponse(session *models.Principal, accessKey string, policy string) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getSetServiceAccountPolicyResponse(session *models.Principal, params saApi.SetServiceAccountPolicyParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
|
||||
accessKey := params.AccessKey
|
||||
policy := *params.Policy.Policy
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
@@ -422,25 +420,26 @@ func getSetServiceAccountPolicyResponse(session *models.Principal, accessKey str
|
||||
|
||||
err = setServiceAccountPolicy(ctx, userAdminClient, accessKey, policy)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDeleteMultipleServiceAccountsResponse authenticates the user and calls deleteServiceAccount for each account listed in selectedSAs
|
||||
func getDeleteMultipleServiceAccountsResponse(session *models.Principal, selectedSAs []string) *models.Error {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getDeleteMultipleServiceAccountsResponse(session *models.Principal, params saApi.DeleteMultipleServiceAccountsParams) *models.Error {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
selectedSAs := params.SelectedSA
|
||||
userAdmin, err := NewMinioAdminClient(session)
|
||||
if err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
// create a MinIO user Admin Client interface implementation
|
||||
// defining the client to be used
|
||||
userAdminClient := AdminClient{Client: userAdmin}
|
||||
for _, sa := range selectedSAs {
|
||||
if err := deleteServiceAccount(ctx, userAdminClient, sa); err != nil {
|
||||
return prepareError(err)
|
||||
return ErrorWithContext(ctx, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -68,7 +68,7 @@ func isErasureMode() bool {
|
||||
func registerSessionHandlers(api *operations.ConsoleAPI) {
|
||||
// session check
|
||||
api.AuthSessionCheckHandler = authApi.SessionCheckHandlerFunc(func(params authApi.SessionCheckParams, session *models.Principal) middleware.Responder {
|
||||
sessionResp, err := getSessionResponse(session)
|
||||
sessionResp, err := getSessionResponse(params.HTTPRequest.Context(), session)
|
||||
if err != nil {
|
||||
return authApi.NewSessionCheckDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -91,12 +91,12 @@ func getClaimsFromToken(sessionToken string) (map[string]interface{}, error) {
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getSessionResponse(session *models.Principal) (*models.SessionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
func getSessionResponse(ctx context.Context, session *models.Principal) (*models.SessionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
// serialize output
|
||||
if session == nil {
|
||||
return nil, prepareError(errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, ErrInvalidSession)
|
||||
}
|
||||
tokenClaims, _ := getClaimsFromToken(session.STSSessionToken)
|
||||
|
||||
@@ -107,20 +107,20 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
|
||||
STSSessionToken: session.STSSessionToken,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidSession)
|
||||
}
|
||||
userAdminClient := AdminClient{Client: mAdminClient}
|
||||
// Obtain the current policy assigned to this user
|
||||
// necessary for generating the list of allowed endpoints
|
||||
accountInfo, err := getAccountInfo(ctx, userAdminClient)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidSession)
|
||||
|
||||
}
|
||||
rawPolicy := policies.ReplacePolicyVariables(tokenClaims, accountInfo)
|
||||
policy, err := minioIAMPolicy.ParseConfig(bytes.NewReader(rawPolicy))
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidSession)
|
||||
}
|
||||
currTime := time.Now().UTC()
|
||||
|
||||
@@ -143,7 +143,7 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
|
||||
|
||||
claims, err := getClaimsFromToken(session.STSSessionToken)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidSession)
|
||||
}
|
||||
|
||||
// Support all LDAP, JWT variables
|
||||
@@ -222,12 +222,12 @@ func getSessionResponse(session *models.Principal) (*models.SessionResponse, *mo
|
||||
}
|
||||
serializedPolicy, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return nil, prepareError(err, errorGenericInvalidSession)
|
||||
return nil, ErrorWithContext(ctx, err, ErrInvalidSession)
|
||||
}
|
||||
var sessionPolicy *models.IamPolicy
|
||||
err = json.Unmarshal(serializedPolicy, &sessionPolicy)
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
sessionResp := &models.SessionResponse{
|
||||
Features: getListOfEnabledFeatures(session),
|
||||
|
||||
159
restapi/user_session_test.go
Normal file
159
restapi/user_session_test.go
Normal file
@@ -0,0 +1,159 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/auth/idp/oauth2"
|
||||
"github.com/minio/console/pkg/auth/ldap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_getSessionResponse(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
session *models.Principal
|
||||
}
|
||||
ctx := context.Background()
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *models.SessionResponse
|
||||
wantErr bool
|
||||
preFunc func()
|
||||
postFunc func()
|
||||
}{
|
||||
{
|
||||
name: "empty session",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
session: nil,
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "malformed minio endpoint URL",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
session: &models.Principal{
|
||||
STSAccessKeyID: "",
|
||||
STSSecretAccessKey: "",
|
||||
STSSessionToken: "",
|
||||
AccountAccessKey: "",
|
||||
Hm: false,
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
preFunc: func() {
|
||||
os.Setenv(ConsoleMinIOServer, "malformed")
|
||||
},
|
||||
postFunc: func() {
|
||||
os.Unsetenv(ConsoleMinIOServer)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "malformed session",
|
||||
args: args{
|
||||
ctx: ctx,
|
||||
session: &models.Principal{
|
||||
STSAccessKeyID: "W257A03HTI7L30F7YCRD",
|
||||
STSSecretAccessKey: "g+QVorWQR8aSy+k3OHOoYn0qKpENld72faCMfYps",
|
||||
STSSessionToken: "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJXMjU3QTAzSFRJN0wzMEY3WUNSRCIsImV4cCI6MTY1MTAxNjU1OCwicGFyZW50IjoibWluaW8ifQ.uFFIIEQ6qM_QvMM297ODi_uK2IA1pwvsDbyBGErkQKqtbY_Ynte8GUkNsSHBEMCT9Fr7uUwaxK41kUqjtbqAwA",
|
||||
AccountAccessKey: "minio",
|
||||
Hm: false,
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.preFunc != nil {
|
||||
tt.preFunc()
|
||||
}
|
||||
session, err := getSessionResponse(tt.args.ctx, tt.args.session)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("getSessionResponse() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(session, tt.want) {
|
||||
t.Errorf("getSessionResponse() got = %v, want %v", session, tt.want)
|
||||
}
|
||||
if tt.postFunc != nil {
|
||||
tt.postFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getListOfEnabledFeatures(t *testing.T) {
|
||||
type args struct {
|
||||
session *models.Principal
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []string
|
||||
preFunc func()
|
||||
postFunc func()
|
||||
}{
|
||||
{
|
||||
name: "all features are enabled",
|
||||
args: args{
|
||||
session: &models.Principal{
|
||||
STSAccessKeyID: "",
|
||||
STSSecretAccessKey: "",
|
||||
STSSessionToken: "",
|
||||
AccountAccessKey: "",
|
||||
Hm: true,
|
||||
},
|
||||
},
|
||||
want: []string{"log-search", "oidc-idp", "external-idp", "ldap-idp", "external-idp", "hide-menu"},
|
||||
preFunc: func() {
|
||||
os.Setenv(ConsoleLogQueryURL, "http://logsearchapi:8080")
|
||||
os.Setenv(oauth2.ConsoleIDPURL, "http://external-idp.com")
|
||||
os.Setenv(oauth2.ConsoleIDPClientID, "eaeaeaeaeaea")
|
||||
os.Setenv(ldap.ConsoleLDAPEnabled, "on")
|
||||
},
|
||||
postFunc: func() {
|
||||
os.Unsetenv(ConsoleLogQueryURL)
|
||||
os.Unsetenv(oauth2.ConsoleIDPURL)
|
||||
os.Unsetenv(oauth2.ConsoleIDPClientID)
|
||||
os.Unsetenv(ldap.ConsoleLDAPEnabled)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.preFunc != nil {
|
||||
tt.preFunc()
|
||||
}
|
||||
assert.Equalf(t, tt.want, getListOfEnabledFeatures(tt.args.session), "getListOfEnabledFeatures(%v)", tt.args.session)
|
||||
if tt.postFunc != nil {
|
||||
tt.postFunc()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,12 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
xhttp "github.com/minio/console/pkg/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/console/pkg/utils"
|
||||
@@ -29,7 +32,7 @@ import (
|
||||
|
||||
func registerVersionHandlers(api *operations.ConsoleAPI) {
|
||||
api.SystemCheckMinIOVersionHandler = systemApi.CheckMinIOVersionHandlerFunc(func(params systemApi.CheckMinIOVersionParams) middleware.Responder {
|
||||
versionResponse, err := getVersionResponse()
|
||||
versionResponse, err := getVersionResponse(params)
|
||||
if err != nil {
|
||||
return systemApi.NewCheckMinIOVersionDefault(int(err.Code)).WithPayload(err)
|
||||
}
|
||||
@@ -38,13 +41,15 @@ func registerVersionHandlers(api *operations.ConsoleAPI) {
|
||||
}
|
||||
|
||||
// getSessionResponse parse the token of the current session and returns a list of allowed actions to render in the UI
|
||||
func getVersionResponse() (*models.CheckVersionResponse, *models.Error) {
|
||||
ver, err := utils.GetLatestMinIOImage(&utils.HTTPClient{
|
||||
func getVersionResponse(params systemApi.CheckMinIOVersionParams) (*models.CheckVersionResponse, *models.Error) {
|
||||
ctx, cancel := context.WithCancel(params.HTTPRequest.Context())
|
||||
defer cancel()
|
||||
ver, err := utils.GetLatestMinIOImage(&xhttp.Client{
|
||||
Client: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
}})
|
||||
if err != nil {
|
||||
return nil, prepareError(err)
|
||||
return nil, ErrorWithContext(ctx, err)
|
||||
}
|
||||
return &models.CheckVersionResponse{
|
||||
LatestVersion: *ver,
|
||||
|
||||
@@ -51,9 +51,13 @@ func startWatch(ctx context.Context, conn WSConn, wsc MCClient, options *watchOp
|
||||
}
|
||||
for _, event := range events {
|
||||
// Serialize message to be sent
|
||||
bytes, _ := json.Marshal(event)
|
||||
bytes, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
LogError("error on json.Marshal: %v", err)
|
||||
return err
|
||||
}
|
||||
// Send Message through websocket connection
|
||||
err := conn.writeMessage(websocket.TextMessage, bytes)
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
LogError("error writeMessage: %v", err)
|
||||
return err
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -62,3 +64,153 @@ func TestUniqueKeys(t *testing.T) {
|
||||
responseArray := UniqueKeys(exampleMixedArray)
|
||||
assert.ElementsMatchf(responseArray, exampleUniqueArray, "returned array doesn't contain the correct elements %s")
|
||||
}
|
||||
|
||||
func TestRandomCharStringWithAlphabet(t *testing.T) {
|
||||
type args struct {
|
||||
n int
|
||||
alphabet string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "generated random string has the right length",
|
||||
args: args{
|
||||
n: 10,
|
||||
alphabet: "A",
|
||||
},
|
||||
want: "AAAAAAAAAA",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, RandomCharStringWithAlphabet(tt.args.n, tt.args.alphabet), "RandomCharStringWithAlphabet(%v, %v)", tt.args.n, tt.args.alphabet)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSessionCookieForConsole(t *testing.T) {
|
||||
type args struct {
|
||||
token string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want http.Cookie
|
||||
}{
|
||||
{
|
||||
name: "session cookie has the right token an security configuration",
|
||||
args: args{
|
||||
token: "jwt-xxxxxxxxx",
|
||||
},
|
||||
want: http.Cookie{
|
||||
Path: "/",
|
||||
Value: "jwt-xxxxxxxxx",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
Name: "token",
|
||||
MaxAge: 3600,
|
||||
Expires: time.Now().Add(1 * time.Hour),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := NewSessionCookieForConsole(tt.args.token)
|
||||
assert.Equalf(t, tt.want.Value, got.Value, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
assert.Equalf(t, tt.want.Path, got.Path, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
assert.Equalf(t, tt.want.HttpOnly, got.HttpOnly, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
assert.Equalf(t, tt.want.Name, got.Name, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
assert.Equalf(t, tt.want.MaxAge, got.MaxAge, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
assert.Equalf(t, tt.want.SameSite, got.SameSite, "NewSessionCookieForConsole(%v)", tt.args.token)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpireSessionCookie(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
want http.Cookie
|
||||
}{
|
||||
{
|
||||
name: "cookie is expired correctly",
|
||||
want: http.Cookie{
|
||||
Name: "token",
|
||||
Value: "",
|
||||
MaxAge: -1,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ExpireSessionCookie()
|
||||
assert.Equalf(t, tt.want.Name, got.Name, "ExpireSessionCookie()")
|
||||
assert.Equalf(t, tt.want.Value, got.Value, "ExpireSessionCookie()")
|
||||
assert.Equalf(t, tt.want.MaxAge, got.MaxAge, "ExpireSessionCookie()")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizeEncodedPrefix(t *testing.T) {
|
||||
type args struct {
|
||||
rawPrefix string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "replace spaces with +",
|
||||
args: args{
|
||||
rawPrefix: "hello world",
|
||||
},
|
||||
want: "hello+world",
|
||||
},
|
||||
{
|
||||
name: "replace spaces with +",
|
||||
args: args{
|
||||
rawPrefix: " hello-world ",
|
||||
},
|
||||
want: "+++hello-world+++",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, SanitizeEncodedPrefix(tt.args.rawPrefix), "SanitizeEncodedPrefix(%v)", tt.args.rawPrefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isSafeToPreview(t *testing.T) {
|
||||
type args struct {
|
||||
str string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "mime type is safe to preview",
|
||||
args: args{
|
||||
str: "image/jpeg",
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "mime type is not safe to preview",
|
||||
args: args{
|
||||
str: "application/zip",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equalf(t, tt.want, isSafeToPreview(tt.args.str), "isSafeToPreview(%v)", tt.args.str)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@ package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/console/pkg/utils"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/minio/console/models"
|
||||
@@ -121,17 +124,25 @@ func (c wsConn) readMessage() (messageType int, p []byte, err error) {
|
||||
// on the path.
|
||||
// Request should come like ws://<host>:<port>/ws/<api>
|
||||
func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := req.Context()
|
||||
// Perform authentication before upgrading to a Websocket Connection
|
||||
// authenticate WS connection with Console
|
||||
session, err := auth.GetClaimsFromTokenInRequest(req)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
errors.ServeError(w, req, errors.New(http.StatusUnauthorized, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
//// DELETE ME !!!
|
||||
//upgrader.CheckOrigin = func(r *http.Request) bool {
|
||||
// return true
|
||||
//}
|
||||
|
||||
// upgrades the HTTP server connection to the WebSocket protocol.
|
||||
conn, err := upgrader.Upgrade(w, req, nil)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
errors.ServeError(w, req, err)
|
||||
return
|
||||
}
|
||||
@@ -141,6 +152,7 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
case strings.HasPrefix(wsPath, `/trace`):
|
||||
wsAdminClient, err := newWebSocketAdminClient(conn, session)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
@@ -172,11 +184,12 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
path: path,
|
||||
}
|
||||
|
||||
go wsAdminClient.trace(traceRequestItem)
|
||||
go wsAdminClient.trace(ctx, traceRequestItem)
|
||||
case strings.HasPrefix(wsPath, `/console`):
|
||||
|
||||
wsAdminClient, err := newWebSocketAdminClient(conn, session)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
@@ -187,60 +200,64 @@ func serveWS(w http.ResponseWriter, req *http.Request) {
|
||||
node: node,
|
||||
logType: logType,
|
||||
}
|
||||
go wsAdminClient.console(logRequestItem)
|
||||
go wsAdminClient.console(ctx, logRequestItem)
|
||||
case strings.HasPrefix(wsPath, `/health-info`):
|
||||
deadline, err := getHealthInfoOptionsFromReq(req)
|
||||
if err != nil {
|
||||
LogError("error getting health info options: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting health info options: %v", err))
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
wsAdminClient, err := newWebSocketAdminClient(conn, session)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
go wsAdminClient.healthInfo(deadline)
|
||||
go wsAdminClient.healthInfo(ctx, deadline)
|
||||
case strings.HasPrefix(wsPath, `/heal`):
|
||||
hOptions, err := getHealOptionsFromReq(req)
|
||||
if err != nil {
|
||||
LogError("error getting heal options: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting heal options: %v", err))
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
wsAdminClient, err := newWebSocketAdminClient(conn, session)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
go wsAdminClient.heal(hOptions)
|
||||
go wsAdminClient.heal(ctx, hOptions)
|
||||
case strings.HasPrefix(wsPath, `/watch`):
|
||||
wOptions, err := getWatchOptionsFromReq(req)
|
||||
if err != nil {
|
||||
LogError("error getting watch options: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting watch options: %v", err))
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
wsS3Client, err := newWebSocketS3Client(conn, session, wOptions.BucketName)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
go wsS3Client.watch(wOptions)
|
||||
go wsS3Client.watch(ctx, wOptions)
|
||||
case strings.HasPrefix(wsPath, `/speedtest`):
|
||||
speedtestOpts, err := getSpeedtestOptionsFromReq(req)
|
||||
if err != nil {
|
||||
LogError("error getting speedtest options: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error getting speedtest options: %v", err))
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
|
||||
wsAdminClient, err := newWebSocketAdminClient(conn, session)
|
||||
if err != nil {
|
||||
ErrorWithContext(ctx, err)
|
||||
closeWsConn(conn)
|
||||
return
|
||||
}
|
||||
go wsAdminClient.speedtest(speedtestOpts)
|
||||
go wsAdminClient.speedtest(ctx, speedtestOpts)
|
||||
|
||||
default:
|
||||
// path not found
|
||||
@@ -292,26 +309,54 @@ func newWebSocketS3Client(conn *websocket.Conn, claims *models.Principal, bucket
|
||||
// if the client sends a Close Message the context will be
|
||||
// canceled. If the connection is closed the goroutine inside
|
||||
// will return.
|
||||
func wsReadClientCtx(conn WSConn) context.Context {
|
||||
func wsReadClientCtx(parentContext context.Context, conn WSConn) context.Context {
|
||||
// a cancel context is needed to end all goroutines used
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
var requestID string
|
||||
var SessionID string
|
||||
var UserAgent string
|
||||
var Host string
|
||||
var RemoteHost string
|
||||
|
||||
if val, o := parentContext.Value(utils.ContextRequestID).(string); o {
|
||||
requestID = val
|
||||
}
|
||||
if val, o := parentContext.Value(utils.ContextRequestUserID).(string); o {
|
||||
SessionID = val
|
||||
}
|
||||
if val, o := parentContext.Value(utils.ContextRequestUserAgent).(string); o {
|
||||
UserAgent = val
|
||||
}
|
||||
if val, o := parentContext.Value(utils.ContextRequestHost).(string); o {
|
||||
Host = val
|
||||
}
|
||||
if val, o := parentContext.Value(utils.ContextRequestRemoteAddr).(string); o {
|
||||
RemoteHost = val
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestID, requestID)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestUserID, SessionID)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestUserAgent, UserAgent)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestHost, Host)
|
||||
ctx = context.WithValue(ctx, utils.ContextRequestRemoteAddr, RemoteHost)
|
||||
|
||||
go func() {
|
||||
defer cancel()
|
||||
for {
|
||||
_, _, err := conn.readMessage()
|
||||
if err != nil {
|
||||
// if error of type websocket.CloseError and is Unexpected
|
||||
// if errors of type websocket.CloseError and is Unexpected
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
|
||||
LogError("error unexpected CloseError on ReadMessage: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error unexpected CloseError on ReadMessage: %v", err))
|
||||
return
|
||||
}
|
||||
// Not all errors are of type websocket.CloseError.
|
||||
if _, ok := err.(*websocket.CloseError); !ok {
|
||||
LogError("error on ReadMessage: %v", err)
|
||||
ErrorWithContext(ctx, fmt.Errorf("error on ReadMessage: %v", err))
|
||||
return
|
||||
}
|
||||
// else is an expected Close Error
|
||||
LogError("closed conn.ReadMessage: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -327,7 +372,7 @@ func closeWsConn(conn *websocket.Conn) {
|
||||
|
||||
// trace serves madmin.ServiceTraceInfo
|
||||
// on a Websocket connection.
|
||||
func (wsc *wsAdminClient) trace(traceRequestItem TraceRequest) {
|
||||
func (wsc *wsAdminClient) trace(ctx context.Context, traceRequestItem TraceRequest) {
|
||||
defer func() {
|
||||
LogInfo("trace stopped")
|
||||
// close connection after return
|
||||
@@ -335,7 +380,7 @@ func (wsc *wsAdminClient) trace(traceRequestItem TraceRequest) {
|
||||
}()
|
||||
LogInfo("trace started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startTraceInfo(ctx, wsc.conn, wsc.client, traceRequestItem)
|
||||
|
||||
@@ -344,7 +389,7 @@ func (wsc *wsAdminClient) trace(traceRequestItem TraceRequest) {
|
||||
|
||||
// console serves madmin.GetLogs
|
||||
// on a Websocket connection.
|
||||
func (wsc *wsAdminClient) console(logRequestItem LogRequest) {
|
||||
func (wsc *wsAdminClient) console(ctx context.Context, logRequestItem LogRequest) {
|
||||
defer func() {
|
||||
LogInfo("console logs stopped")
|
||||
// close connection after return
|
||||
@@ -352,14 +397,14 @@ func (wsc *wsAdminClient) console(logRequestItem LogRequest) {
|
||||
}()
|
||||
LogInfo("console logs started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startConsoleLog(ctx, wsc.conn, wsc.client, logRequestItem)
|
||||
|
||||
sendWsCloseMessage(wsc.conn, err)
|
||||
}
|
||||
|
||||
func (wsc *wsS3Client) watch(params *watchOptions) {
|
||||
func (wsc *wsS3Client) watch(ctx context.Context, params *watchOptions) {
|
||||
defer func() {
|
||||
LogInfo("watch stopped")
|
||||
// close connection after return
|
||||
@@ -367,14 +412,14 @@ func (wsc *wsS3Client) watch(params *watchOptions) {
|
||||
}()
|
||||
LogInfo("watch started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startWatch(ctx, wsc.conn, wsc.client, params)
|
||||
|
||||
sendWsCloseMessage(wsc.conn, err)
|
||||
}
|
||||
|
||||
func (wsc *wsAdminClient) heal(opts *healOptions) {
|
||||
func (wsc *wsAdminClient) heal(ctx context.Context, opts *healOptions) {
|
||||
defer func() {
|
||||
LogInfo("heal stopped")
|
||||
// close connection after return
|
||||
@@ -382,14 +427,14 @@ func (wsc *wsAdminClient) heal(opts *healOptions) {
|
||||
}()
|
||||
LogInfo("heal started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startHeal(ctx, wsc.conn, wsc.client, opts)
|
||||
|
||||
sendWsCloseMessage(wsc.conn, err)
|
||||
}
|
||||
|
||||
func (wsc *wsAdminClient) healthInfo(deadline *time.Duration) {
|
||||
func (wsc *wsAdminClient) healthInfo(ctx context.Context, deadline *time.Duration) {
|
||||
defer func() {
|
||||
LogInfo("health info stopped")
|
||||
// close connection after return
|
||||
@@ -397,14 +442,14 @@ func (wsc *wsAdminClient) healthInfo(deadline *time.Duration) {
|
||||
}()
|
||||
LogInfo("health info started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startHealthInfo(ctx, wsc.conn, wsc.client, deadline)
|
||||
|
||||
sendWsCloseMessage(wsc.conn, err)
|
||||
}
|
||||
|
||||
func (wsc *wsAdminClient) speedtest(opts *madmin.SpeedtestOpts) {
|
||||
func (wsc *wsAdminClient) speedtest(ctx context.Context, opts *madmin.SpeedtestOpts) {
|
||||
defer func() {
|
||||
LogInfo("speedtest stopped")
|
||||
// close connection after return
|
||||
@@ -412,7 +457,7 @@ func (wsc *wsAdminClient) speedtest(opts *madmin.SpeedtestOpts) {
|
||||
}()
|
||||
LogInfo("speedtest started")
|
||||
|
||||
ctx := wsReadClientCtx(wsc.conn)
|
||||
ctx = wsReadClientCtx(ctx, wsc.conn)
|
||||
|
||||
err := startSpeedtest(ctx, wsc.conn, wsc.client, opts)
|
||||
|
||||
@@ -434,7 +479,7 @@ func sendWsCloseMessage(conn WSConn, err error) {
|
||||
return
|
||||
}
|
||||
// else, internal server error
|
||||
conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, ErrorGeneric.Error()))
|
||||
conn.writeMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseInternalServerErr, ErrDefault.Error()))
|
||||
return
|
||||
}
|
||||
// normal closure
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package restapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
@@ -50,7 +51,8 @@ func TestWSHandle(t *testing.T) {
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, &websocket.CloseError{Code: websocket.CloseAbnormalClosure, Text: ""}
|
||||
}
|
||||
ctx := wsReadClientCtx(mockWSConn)
|
||||
parentCtx := context.Background()
|
||||
ctx := wsReadClientCtx(parentCtx, mockWSConn)
|
||||
|
||||
<-ctx.Done()
|
||||
// closed ctx correctly
|
||||
@@ -59,7 +61,7 @@ func TestWSHandle(t *testing.T) {
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, errors.New("error")
|
||||
}
|
||||
ctx2 := wsReadClientCtx(mockWSConn)
|
||||
ctx2 := wsReadClientCtx(parentCtx, mockWSConn)
|
||||
<-ctx2.Done()
|
||||
// closed ctx correctly
|
||||
|
||||
@@ -67,7 +69,7 @@ func TestWSHandle(t *testing.T) {
|
||||
connReadMessageMock = func() (messageType int, p []byte, err error) {
|
||||
return 0, []byte{}, &websocket.CloseError{Code: websocket.CloseGoingAway, Text: ""}
|
||||
}
|
||||
ctx3 := wsReadClientCtx(mockWSConn)
|
||||
ctx3 := wsReadClientCtx(parentCtx, mockWSConn)
|
||||
<-ctx3.Done()
|
||||
// closed ctx correctly
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user