diff --git a/.gitignore b/.gitignore
index cbe51798e..c144586a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,7 @@ vendor/
# Ignore executables
target/
+!pkg/logger/target/
console
!console/
diff --git a/cluster/config.go b/cluster/config.go
index 96ac280ff..fb7e10799 100644
--- a/cluster/config.go
+++ b/cluster/config.go
@@ -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,
},
diff --git a/cmd/console/app_commands.go b/cmd/console/app_commands.go
index fa355e65c..517900034 100644
--- a/cmd/console/app_commands.go
+++ b/cmd/console/app_commands.go
@@ -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)
diff --git a/cmd/console/app_commands_noop.go b/cmd/console/app_commands_noop.go
index 576dea507..6d9161143 100644
--- a/cmd/console/app_commands_noop.go
+++ b/cmd/console/app_commands_noop.go
@@ -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)
diff --git a/cmd/console/operator.go b/cmd/console/operator.go
index 138eb6d00..36a16590b 100644
--- a/cmd/console/operator.go
+++ b/cmd/console/operator.go
@@ -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"}
diff --git a/go.mod b/go.mod
index e3225b314..8740ca367 100644
--- a/go.mod
+++ b/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
diff --git a/go.sum b/go.sum
index 0cd336daa..e8ebfc420 100644
--- a/go.sum
+++ b/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=
diff --git a/operatorapi/auth/operator.go b/operatorapi/auth/operator.go
index fbd66466b..3fa74053c 100644
--- a/operatorapi/auth/operator.go
+++ b/operatorapi/auth/operator.go
@@ -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
}
diff --git a/operatorapi/configure_operator.go b/operatorapi/configure_operator.go
index b78d904c6..191774c6a 100644
--- a/operatorapi/configure_operator.go
+++ b/operatorapi/configure_operator.go
@@ -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(),
diff --git a/operatorapi/error.go b/operatorapi/error.go
deleted file mode 100644
index c3031cc60..000000000
--- a/operatorapi/error.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/operatorapi/login.go b/operatorapi/login.go
index 8693dc7e5..49c18b5ea 100644
--- a/operatorapi/login.go
+++ b/operatorapi/login.go
@@ -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{
diff --git a/operatorapi/namespaces.go b/operatorapi/namespaces.go
index 3c91ef10e..3a37fa2b8 100644
--- a/operatorapi/namespaces.go
+++ b/operatorapi/namespaces.go
@@ -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
diff --git a/operatorapi/nodes.go b/operatorapi/nodes.go
index 86b56a13c..ef7402bba 100644
--- a/operatorapi/nodes.go
+++ b/operatorapi/nodes.go
@@ -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
}
diff --git a/operatorapi/parity.go b/operatorapi/parity.go
index b3450d25a..73069eaeb 100644
--- a/operatorapi/parity.go
+++ b/operatorapi/parity.go
@@ -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
}
diff --git a/operatorapi/resource_quota.go b/operatorapi/resource_quota.go
index a6c0729fc..1f947b9f4 100644
--- a/operatorapi/resource_quota.go
+++ b/operatorapi/resource_quota.go
@@ -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
}
diff --git a/operatorapi/session.go b/operatorapi/session.go
index f9a6a7c14..1c6374590 100644
--- a/operatorapi/session.go
+++ b/operatorapi/session.go
@@ -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,
diff --git a/operatorapi/tenant_add.go b/operatorapi/tenant_add.go
index 5ba5dbdd3..9d6bc75df 100644
--- a/operatorapi/tenant_add.go
+++ b/operatorapi/tenant_add.go
@@ -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{
diff --git a/operatorapi/tenant_get.go b/operatorapi/tenant_get.go
index e12857d3b..e45dcecf4 100644
--- a/operatorapi/tenant_get.go
+++ b/operatorapi/tenant_get.go
@@ -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"
diff --git a/operatorapi/tenant_update.go b/operatorapi/tenant_update.go
index daba71228..f08b0d401 100644
--- a/operatorapi/tenant_update.go
+++ b/operatorapi/tenant_update.go
@@ -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
diff --git a/operatorapi/tenants.go b/operatorapi/tenants.go
index e99c6ae58..0fe9332ac 100644
--- a/operatorapi/tenants.go
+++ b/operatorapi/tenants.go
@@ -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
diff --git a/operatorapi/tenants_helper.go b/operatorapi/tenants_helper.go
index 616af379d..db6c27b89 100644
--- a/operatorapi/tenants_helper.go
+++ b/operatorapi/tenants_helper.go
@@ -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
diff --git a/operatorapi/tenants_test.go b/operatorapi/tenants_test.go
index 7be351040..332e917f3 100644
--- a/operatorapi/tenants_test.go
+++ b/operatorapi/tenants_test.go
@@ -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)
diff --git a/operatorapi/version.go b/operatorapi/version.go
index 0e9c027be..31339eb21 100644
--- a/operatorapi/version.go
+++ b/operatorapi/version.go
@@ -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,
diff --git a/operatorapi/volumes.go b/operatorapi/volumes.go
index 22e727536..d281fb3be 100644
--- a/operatorapi/volumes.go
+++ b/operatorapi/volumes.go
@@ -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++ {
diff --git a/pkg/auth/token.go b/pkg/auth/token.go
index 1cda95da1..5ca4d9448 100644
--- a/pkg/auth/token.go
+++ b/pkg/auth/token.go
@@ -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 (
diff --git a/pkg/certs/certs.go b/pkg/certs/certs.go
index 858275df5..7f8cbb607 100644
--- a/pkg/certs/certs.go
+++ b/pkg/certs/certs.go
@@ -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
+}
diff --git a/pkg/http/headers.go b/pkg/http/headers.go
new file mode 100644
index 000000000..e864edbc3
--- /dev/null
+++ b/pkg/http/headers.go
@@ -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 .
+
+package http
+
+// Standard S3 HTTP response constants
+const (
+ ETag = "ETag"
+ ContentType = "Content-Type"
+)
diff --git a/pkg/utils/http_client.go b/pkg/http/http.go
similarity index 52%
rename from pkg/utils/http_client.go
rename to pkg/http/http.go
index 4dff79349..84cb3ad19 100644
--- a/pkg/utils/http_client.go
+++ b/pkg/http/http.go
@@ -14,40 +14,61 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
-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)
+ }
+}
diff --git a/pkg/logger/audit.go b/pkg/logger/audit.go
new file mode 100644
index 000000000..a5e07380d
--- /dev/null
+++ b/pkg/logger/audit.go
@@ -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 .
+
+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 - 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 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)
+ }
+ }
+}
diff --git a/pkg/logger/color/color.go b/pkg/logger/color/color.go
new file mode 100644
index 000000000..7c1d60c85
--- /dev/null
+++ b/pkg/logger/color/color.go
@@ -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 .
+
+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
+ }()
+)
diff --git a/pkg/logger/config.go b/pkg/logger/config.go
new file mode 100644
index 000000000..4da4c0be5
--- /dev/null
+++ b/pkg/logger/config.go
@@ -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 .
+
+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()
+}
diff --git a/pkg/logger/config/bool-flag.go b/pkg/logger/config/bool-flag.go
new file mode 100644
index 000000000..6f9eb96ab
--- /dev/null
+++ b/pkg/logger/config/bool-flag.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/config/bool-flag_test.go b/pkg/logger/config/bool-flag_test.go
new file mode 100644
index 000000000..d850f1ebe
--- /dev/null
+++ b/pkg/logger/config/bool-flag_test.go
@@ -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 .
+
+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 = , got = %v", err)
+ }
+ if testCase.expectedErr && err == nil {
+ t.Fatalf("error: expected error, got = ")
+ }
+ 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 = , got = %v", err)
+ }
+ if testCase.expectedErr && err == nil {
+ t.Fatalf("error: expected error, got = ")
+ }
+ if err == nil && testCase.expectedResult != bf {
+ t.Fatalf("result: expected: %v, got: %v", testCase.expectedResult, bf)
+ }
+ }
+}
diff --git a/pkg/logger/config/certs.go b/pkg/logger/config/certs.go
new file mode 100644
index 000000000..3221c9ac9
--- /dev/null
+++ b/pkg/logger/config/certs.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/config/config.go b/pkg/logger/config/config.go
new file mode 100644
index 000000000..a862dc374
--- /dev/null
+++ b/pkg/logger/config/config.go
@@ -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 .
+
+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"
+)
diff --git a/pkg/logger/console.go b/pkg/logger/console.go
new file mode 100644
index 000000000..ee9c44caf
--- /dev/null
+++ b/pkg/logger/console.go
@@ -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 .
+
+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...)
+}
diff --git a/pkg/logger/const.go b/pkg/logger/const.go
new file mode 100644
index 000000000..978d0c612
--- /dev/null
+++ b/pkg/logger/const.go
@@ -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 .
+
+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
+)
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
new file mode 100644
index 000000000..db01e25e5
--- /dev/null
+++ b/pkg/logger/logger.go
@@ -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 .
+
+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, ""
+ // and also skip traces with function name that starts with "runtime."
+ if !strings.HasPrefix(file, "") &&
+ !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
+}
diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go
new file mode 100644
index 000000000..624adc137
--- /dev/null
+++ b/pkg/logger/logger_test.go
@@ -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 .
+
+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)
+ }
+ })
+ }
+}
diff --git a/pkg/logger/logonce.go b/pkg/logger/logonce.go
new file mode 100644
index 000000000..1db87c283
--- /dev/null
+++ b/pkg/logger/logonce.go
@@ -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 .
+
+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...)
+}
diff --git a/pkg/logger/message/audit/entry.go b/pkg/logger/message/audit/entry.go
new file mode 100644
index 000000000..43148375f
--- /dev/null
+++ b/pkg/logger/message/audit/entry.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/message/audit/entry_test.go b/pkg/logger/message/audit/entry_test.go
new file mode 100644
index 000000000..3da4bdbdb
--- /dev/null
+++ b/pkg/logger/message/audit/entry_test.go
@@ -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 .
+
+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()
+ }
+ })
+ }
+}
diff --git a/pkg/logger/message/log/entry.go b/pkg/logger/message/log/entry.go
new file mode 100644
index 000000000..0177fd9dd
--- /dev/null
+++ b/pkg/logger/message/log/entry.go
@@ -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 .
+
+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"`
+}
diff --git a/pkg/logger/reqinfo.go b/pkg/logger/reqinfo.go
new file mode 100644
index 000000000..58b5ca0cc
--- /dev/null
+++ b/pkg/logger/reqinfo.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/target/http/http.go b/pkg/logger/target/http/http.go
new file mode 100644
index 000000000..4dec59e23
--- /dev/null
+++ b/pkg/logger/target/http/http.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/target/types/types.go b/pkg/logger/target/types/types.go
new file mode 100644
index 000000000..92ef0a04a
--- /dev/null
+++ b/pkg/logger/target/types/types.go
@@ -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 .
+
+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
+)
diff --git a/pkg/logger/targets.go b/pkg/logger/targets.go
new file mode 100644
index 000000000..6be0618ce
--- /dev/null
+++ b/pkg/logger/targets.go
@@ -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 .
+
+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
+}
diff --git a/pkg/logger/utils.go b/pkg/logger/utils.go
new file mode 100644
index 000000000..87b58a11a
--- /dev/null
+++ b/pkg/logger/utils.go
@@ -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 .
+
+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")
+ }
+}
diff --git a/pkg/subnet/subnet.go b/pkg/subnet/subnet.go
index d9fe00f03..2b90a1777 100644
--- a/pkg/subnet/subnet.go
+++ b/pkg/subnet/subnet.go
@@ -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)
diff --git a/pkg/subnet/utils.go b/pkg/subnet/utils.go
index 811ce9e50..496c3f576 100644
--- a/pkg/subnet/utils.go
+++ b/pkg/subnet/utils.go
@@ -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
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
new file mode 100644
index 000000000..ddee049e2
--- /dev/null
+++ b/pkg/utils/utils.go
@@ -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 .
+
+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")
diff --git a/pkg/utils/version.go b/pkg/utils/version.go
index 10498da75..5eb514ec4 100644
--- a/pkg/utils/version.go
+++ b/pkg/utils/version.go
@@ -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
diff --git a/restapi/admin_arns.go b/restapi/admin_arns.go
index 0ab0b85f9..645831261 100644
--- a/restapi/admin_arns.go
+++ b/restapi/admin_arns.go
@@ -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
}
diff --git a/restapi/admin_config.go b/restapi/admin_config.go
index 728d9536e..d39e96080 100644
--- a/restapi/admin_config.go
+++ b/restapi/admin_config.go
@@ -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
diff --git a/restapi/admin_groups.go b/restapi/admin_groups.go
index c7b75e31c..8fbc5c5cd 100644
--- a/restapi/admin_groups.go
+++ b/restapi/admin_groups.go
@@ -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,
diff --git a/restapi/admin_heal.go b/restapi/admin_heal.go
index edd76f553..3eadfc1aa 100644
--- a/restapi/admin_heal.go
+++ b/restapi/admin_heal.go
@@ -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)
diff --git a/restapi/admin_info.go b/restapi/admin_info.go
index 91693f2b9..83a310864 100644
--- a/restapi/admin_info.go
+++ b/restapi/admin_info.go
@@ -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
}
diff --git a/restapi/admin_inspect.go b/restapi/admin_inspect.go
index 8e740f79d..95acb3f59 100644
--- a/restapi/admin_inspect.go
+++ b/restapi/admin_inspect.go
@@ -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
}
diff --git a/restapi/admin_nodes.go b/restapi/admin_nodes.go
index 72d2d7c5b..5c486ef7f 100644
--- a/restapi/admin_nodes.go
+++ b/restapi/admin_nodes.go
@@ -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
diff --git a/restapi/admin_notification_endpoints.go b/restapi/admin_notification_endpoints.go
index 9ab9c5f49..1eefb092d 100644
--- a/restapi/admin_notification_endpoints.go
+++ b/restapi/admin_notification_endpoints.go
@@ -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
}
diff --git a/restapi/admin_policies.go b/restapi/admin_policies.go
index f45b818f0..79fd8f14d 100644
--- a/restapi/admin_policies.go
+++ b/restapi/admin_policies.go
@@ -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
}
diff --git a/restapi/admin_policies_test.go b/restapi/admin_policies_test.go
index d92f15613..dc7010881 100644
--- a/restapi/admin_policies_test.go
+++ b/restapi/admin_policies_test.go
@@ -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)
}
})
diff --git a/restapi/admin_profiling.go b/restapi/admin_profiling.go
index 9103f92f9..91f764237 100644
--- a/restapi/admin_profiling.go
+++ b/restapi/admin_profiling.go
@@ -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
}
diff --git a/restapi/admin_remote_buckets.go b/restapi/admin_remote_buckets.go
index 37ac73071..d4956bd07 100644
--- a/restapi/admin_remote_buckets.go
+++ b/restapi/admin_remote_buckets.go
@@ -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
diff --git a/restapi/admin_replication_status.go b/restapi/admin_replication_status.go
index b9eacda43..b50f600e0 100644
--- a/restapi/admin_replication_status.go
+++ b/restapi/admin_replication_status.go
@@ -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
}
diff --git a/restapi/admin_service.go b/restapi/admin_service.go
index 0e767e1b1..dd2b1cac9 100644
--- a/restapi/admin_service.go
+++ b/restapi/admin_service.go
@@ -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
}
diff --git a/restapi/admin_service_test.go b/restapi/admin_service_test.go
index a61f83b6e..20d5af1f2 100644
--- a/restapi/admin_service_test.go
+++ b/restapi/admin_service_test.go
@@ -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
}
diff --git a/restapi/admin_site_replication.go b/restapi/admin_site_replication.go
index 83d7c0f6f..de6c0ce60 100644
--- a/restapi/admin_site_replication.go
+++ b/restapi/admin_site_replication.go
@@ -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
}
diff --git a/restapi/admin_subnet.go b/restapi/admin_subnet.go
index c46e3dfee..5d0f17881 100644
--- a/restapi/admin_subnet.go
+++ b/restapi/admin_subnet.go
@@ -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,
diff --git a/restapi/admin_tiers.go b/restapi/admin_tiers.go
index 697004c83..dc51214c6 100644
--- a/restapi/admin_tiers.go
+++ b/restapi/admin_tiers.go
@@ -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
}
diff --git a/restapi/admin_users.go b/restapi/admin_users.go
index 4b61be325..39179fc93 100644
--- a/restapi/admin_users.go
+++ b/restapi/admin_users.go
@@ -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
}
diff --git a/restapi/admin_users_test.go b/restapi/admin_users_test.go
index 019ec9140..f9896f973 100644
--- a/restapi/admin_users_test.go
+++ b/restapi/admin_users_test.go
@@ -525,7 +525,7 @@ func TestListUsersWithAccessToBucket(t *testing.T) {
}
return mockResponse, nil
}
- return nil, ErrorGeneric
+ return nil, ErrDefault
}
type args struct {
bucket string
diff --git a/restapi/client.go b/restapi/client.go
index bdb2f7f64..5078b2ae2 100644
--- a/restapi/client.go
+++ b/restapi/client.go
@@ -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
}
diff --git a/restapi/client_test.go b/restapi/client_test.go
index 5f61cb80e..52780e669 100644
--- a/restapi/client_test.go
+++ b/restapi/client_test.go
@@ -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 {
diff --git a/restapi/configure_console.go b/restapi/configure_console.go
index b91fefc2a..340301d34 100644
--- a/restapi/configure_console.go
+++ b/restapi/configure_console.go
@@ -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)
}
diff --git a/restapi/error.go b/restapi/error.go
deleted file mode 100644
index ee11422e2..000000000
--- a/restapi/error.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/restapi/errors.go b/restapi/errors.go
new file mode 100644
index 000000000..707a2e4c7
--- /dev/null
+++ b/restapi/errors.go
@@ -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 .
+
+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...)
+}
diff --git a/restapi/errors_test.go b/restapi/errors_test.go
new file mode 100644
index 000000000..561693627
--- /dev/null
+++ b/restapi/errors_test.go
@@ -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 .
+
+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)
+ })
+ }
+}
diff --git a/restapi/logs.go b/restapi/logs.go
index 8222ec552..38ce72cee 100644
--- a/restapi/logs.go
+++ b/restapi/logs.go
@@ -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
diff --git a/restapi/tls.go b/restapi/tls.go
index 56c630fc1..1f2077d33 100644
--- a/restapi/tls.go
+++ b/restapi/tls.go
@@ -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,
diff --git a/restapi/user_account.go b/restapi/user_account.go
index 00ff98018..d8f81645a 100644
--- a/restapi/user_account.go
+++ b/restapi/user_account.go
@@ -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{
diff --git a/restapi/user_account_test.go b/restapi/user_account_test.go
index 1d3e3006c..8f7e95948 100644
--- a/restapi/user_account_test.go
+++ b/restapi/user_account_test.go
@@ -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,
diff --git a/restapi/user_bucket_quota.go b/restapi/user_bucket_quota.go
index ca5b4500c..2fb66431d 100644
--- a/restapi/user_bucket_quota.go
+++ b/restapi/user_bucket_quota.go
@@ -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),
diff --git a/restapi/user_buckets.go b/restapi/user_buckets.go
index e29b7a740..4fd4a1212 100644
--- a/restapi/user_buckets.go
+++ b/restapi/user_buckets.go
@@ -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
diff --git a/restapi/user_buckets_events.go b/restapi/user_buckets_events.go
index 27dd3e895..64cef6053 100644
--- a/restapi/user_buckets_events.go
+++ b/restapi/user_buckets_events.go
@@ -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
}
diff --git a/restapi/user_buckets_lifecycle.go b/restapi/user_buckets_lifecycle.go
index 6460acaa0..fa3f13938 100644
--- a/restapi/user_buckets_lifecycle.go
+++ b/restapi/user_buckets_lifecycle.go
@@ -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
diff --git a/restapi/user_buckets_test.go b/restapi/user_buckets_test.go
index 813e81fb0..27dec79a3 100644
--- a/restapi/user_buckets_test.go
+++ b/restapi/user_buckets_test.go
@@ -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,
diff --git a/restapi/user_log_search.go b/restapi/user_log_search.go
index 2ddc41457..4f9c831d5 100644
--- a/restapi/user_log_search.go
+++ b/restapi/user_log_search.go
@@ -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{
diff --git a/restapi/user_log_search_test.go b/restapi/user_log_search_test.go
index 0ea4032a1..a311f27e9 100644
--- a/restapi/user_log_search_test.go
+++ b/restapi/user_log_search_test.go
@@ -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)
+ // }
+ //}
})
}
}
diff --git a/restapi/user_login.go b/restapi/user_login.go
index ab76f3dad..9b046a0e1 100644
--- a/restapi/user_login.go
+++ b/restapi/user_login.go
@@ -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)
}
diff --git a/restapi/user_logout.go b/restapi/user_logout.go
index 2b34cfdcd..d5a9fd043 100644
--- a/restapi/user_logout.go
+++ b/restapi/user_logout.go
@@ -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}
diff --git a/restapi/user_objects.go b/restapi/user_objects.go
index 3452f17c1..e3772ca20 100644
--- a/restapi/user_objects.go
+++ b/restapi/user_objects.go
@@ -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}
diff --git a/restapi/user_service_accounts.go b/restapi/user_service_accounts.go
index ba294831b..78f810b7f 100644
--- a/restapi/user_service_accounts.go
+++ b/restapi/user_service_accounts.go
@@ -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
diff --git a/restapi/user_session.go b/restapi/user_session.go
index 0dafa5569..a5cd20eac 100644
--- a/restapi/user_session.go
+++ b/restapi/user_session.go
@@ -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),
diff --git a/restapi/user_session_test.go b/restapi/user_session_test.go
new file mode 100644
index 000000000..f9ba3396c
--- /dev/null
+++ b/restapi/user_session_test.go
@@ -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 .
+
+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()
+ }
+ })
+ }
+}
diff --git a/restapi/user_version.go b/restapi/user_version.go
index 4f3ba9a12..093e1c2a2 100644
--- a/restapi/user_version.go
+++ b/restapi/user_version.go
@@ -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,
diff --git a/restapi/user_watch.go b/restapi/user_watch.go
index a92902b31..b09e2d2dc 100644
--- a/restapi/user_watch.go
+++ b/restapi/user_watch.go
@@ -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
diff --git a/restapi/utils_test.go b/restapi/utils_test.go
index 897f42cae..28046b8d9 100644
--- a/restapi/utils_test.go
+++ b/restapi/utils_test.go
@@ -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)
+ })
+ }
+}
diff --git a/restapi/ws_handle.go b/restapi/ws_handle.go
index b6fd5a62a..af7baa13d 100644
--- a/restapi/ws_handle.go
+++ b/restapi/ws_handle.go
@@ -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://:/ws/
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
diff --git a/restapi/ws_handle_test.go b/restapi/ws_handle_test.go
index 595e85e08..9fcbe3726 100644
--- a/restapi/ws_handle_test.go
+++ b/restapi/ws_handle_test.go
@@ -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
}