From ae654831aa6e676639d75e2e0c4e946dbdbd22ba Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Fri, 20 Mar 2020 15:00:44 -0700 Subject: [PATCH] Add madmin package context support (#9172) This is to improve responsiveness for all admin API operations and allowing callers to cancel any on-going admin operations, if they happen to be waiting too long. --- cmd/admin-handlers.go | 4 +- cmd/consolelogger.go | 6 +- cmd/logger/message/log/entry.go | 8 ++ pkg/madmin/api-log-entry.go | 52 ++++++++++++ pkg/madmin/api-log.go | 11 ++- pkg/madmin/api.go | 57 ++++++------- pkg/madmin/config-commands.go | 10 ++- pkg/madmin/config-help-commands.go | 5 +- pkg/madmin/config-history-commands.go | 14 ++-- pkg/madmin/config-kv-commands.go | 14 ++-- pkg/madmin/examples/accounting-usage-info.go | 3 +- .../add-service-account-and-policy.go | 3 +- pkg/madmin/examples/add-user-and-policy.go | 7 +- pkg/madmin/examples/cpu-load-info.go | 3 +- pkg/madmin/examples/data-usage-info.go | 3 +- pkg/madmin/examples/drives-perf-info.go | 3 +- pkg/madmin/examples/get-service-account.go | 3 +- pkg/madmin/examples/heal-bucket.go | 5 +- pkg/madmin/examples/heal-buckets-list.go | 3 +- pkg/madmin/examples/heal-format.go | 5 +- pkg/madmin/examples/heal-object.go | 5 +- pkg/madmin/examples/heal-status.go | 3 +- pkg/madmin/examples/hw-cpu-info.go | 3 +- pkg/madmin/examples/hw-network-info.go | 3 +- pkg/madmin/examples/kms-status.go | 3 +- pkg/madmin/examples/lock-clear.go | 3 +- pkg/madmin/examples/mem-usage-info.go | 3 +- pkg/madmin/examples/net-perf-info.go | 3 +- pkg/madmin/examples/profiling.go | 5 +- pkg/madmin/examples/server-info.go | 3 +- pkg/madmin/examples/service-restart.go | 3 +- pkg/madmin/examples/service-trace.go | 3 +- pkg/madmin/examples/storage-info.go | 3 +- pkg/madmin/examples/top-locks.go | 3 +- pkg/madmin/group-commands.go | 17 ++-- pkg/madmin/hardware-commands.go | 25 +++--- pkg/madmin/heal-commands.go | 22 +++--- pkg/madmin/info-commands.go | 79 +++++++++++-------- pkg/madmin/kms-commands.go | 5 +- pkg/madmin/policy-commands.go | 21 ++--- pkg/madmin/profiling-commands.go | 23 +++--- pkg/madmin/retry.go | 12 +-- pkg/madmin/service-commands.go | 27 ++++--- pkg/madmin/top-commands.go | 9 ++- pkg/madmin/transport.go | 60 ++++++++++++++ pkg/madmin/update-commands.go | 13 +-- pkg/madmin/user-commands.go | 33 ++++---- 47 files changed, 398 insertions(+), 213 deletions(-) create mode 100644 pkg/madmin/api-log-entry.go create mode 100644 pkg/madmin/transport.go diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 62e68fb42..e3e00f81f 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1436,7 +1436,7 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque OffDisks += v } - backend = madmin.XlBackend{ + backend = madmin.XLBackend{ Type: madmin.ErasureType, OnlineDisks: OnDisks, OfflineDisks: OffDisks, @@ -1446,7 +1446,7 @@ func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Reque RRSCParity: storageInfo.Backend.RRSCParity, } } else { - backend = madmin.FsBackend{ + backend = madmin.FSBackend{ Type: madmin.FsType, } } diff --git a/cmd/consolelogger.go b/cmd/consolelogger.go index d2f74eb51..1a21fd709 100644 --- a/cmd/consolelogger.go +++ b/cmd/consolelogger.go @@ -118,12 +118,12 @@ func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh chan s // Send log message 'e' to console and publish to console // log pubsub system func (sys *HTTPConsoleLoggerSys) Send(e interface{}, logKind string) error { - var lg madmin.LogInfo + var lg log.Info switch e := e.(type) { case log.Entry: - lg = madmin.LogInfo{Entry: e, NodeName: sys.nodeName} + lg = log.Info{Entry: e, NodeName: sys.nodeName} case string: - lg = madmin.LogInfo{ConsoleMsg: e, NodeName: sys.nodeName} + lg = log.Info{ConsoleMsg: e, NodeName: sys.nodeName} } sys.pubsub.Publish(lg) diff --git a/cmd/logger/message/log/entry.go b/cmd/logger/message/log/entry.go index becfe89d0..1309f041a 100644 --- a/cmd/logger/message/log/entry.go +++ b/cmd/logger/message/log/entry.go @@ -50,3 +50,11 @@ type Entry struct { Message string `json:"message,omitempty"` Trace *Trace `json:"error,omitempty"` } + +// Info holds console log messages +type Info struct { + Entry + ConsoleMsg string + NodeName string `json:"node"` + Err error `json:"-"` +} diff --git a/pkg/madmin/api-log-entry.go b/pkg/madmin/api-log-entry.go new file mode 100644 index 000000000..85bd3cb38 --- /dev/null +++ b/pkg/madmin/api-log-entry.go @@ -0,0 +1,52 @@ +/* + * MinIO Cloud Storage, (C) 2020 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package madmin + +// Args - defines the arguments for the API. +type logArgs struct { + Bucket string `json:"bucket,omitempty"` + Object string `json:"object,omitempty"` + Metadata map[string]string `json:"metadata,omitempty"` +} + +// Trace - defines the trace. +type logTrace struct { + Message string `json:"message,omitempty"` + Source []string `json:"source,omitempty"` + Variables map[string]string `json:"variables,omitempty"` +} + +// API - defines the api type and its args. +type logAPI struct { + Name string `json:"name,omitempty"` + Args *logArgs `json:"args,omitempty"` +} + +// Entry - defines fields and values of each log entry. +type logEntry struct { + DeploymentID string `json:"deploymentid,omitempty"` + Level string `json:"level"` + LogKind string `json:"errKind"` + Time string `json:"time"` + API *logAPI `json:"api,omitempty"` + RemoteHost string `json:"remotehost,omitempty"` + Host string `json:"host,omitempty"` + RequestID string `json:"requestID,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + Message string `json:"message,omitempty"` + Trace *logTrace `json:"error,omitempty"` +} diff --git a/pkg/madmin/api-log.go b/pkg/madmin/api-log.go index e126812fe..7c1facbf0 100644 --- a/pkg/madmin/api-log.go +++ b/pkg/madmin/api-log.go @@ -17,18 +17,17 @@ package madmin import ( + "context" "encoding/json" "net/http" "net/url" "strconv" "strings" - - "github.com/minio/minio/cmd/logger/message/log" ) // LogInfo holds console log messages type LogInfo struct { - log.Entry + logEntry ConsoleMsg string NodeName string `json:"node"` Err error `json:"-"` @@ -42,7 +41,7 @@ func (l LogInfo) SendLog(node, logKind string) bool { } // GetLogs - listen on console log messages. -func (adm AdminClient) GetLogs(node string, lineCnt int, logKind string, doneCh <-chan struct{}) <-chan LogInfo { +func (adm AdminClient) GetLogs(ctx context.Context, node string, lineCnt int, logKind string) <-chan LogInfo { logCh := make(chan LogInfo, 1) // Only success, start a routine to start reading line by line. @@ -58,7 +57,7 @@ func (adm AdminClient) GetLogs(node string, lineCnt int, logKind string, doneCh queryValues: urlValues, } // Execute GET to call log handler - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) if err != nil { closeResponse(resp) return @@ -75,7 +74,7 @@ func (adm AdminClient) GetLogs(node string, lineCnt int, logKind string, doneCh break } select { - case <-doneCh: + case <-ctx.Done(): return case logCh <- info: } diff --git a/pkg/madmin/api.go b/pkg/madmin/api.go index 49922de48..2bc3df177 100644 --- a/pkg/madmin/api.go +++ b/pkg/madmin/api.go @@ -18,7 +18,9 @@ package madmin import ( "bytes" + "context" "encoding/hex" + "errors" "fmt" "io" "io/ioutil" @@ -109,7 +111,7 @@ func privateNew(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Ad endpointURL: *endpointURL, // Instantiate http client and bucket location cache. httpClient: &http.Client{ - Transport: http.DefaultTransport, + Transport: DefaultTransport(secure), }, // Introduce a new locked random seed. random: rand.New(&lockedRandSource{src: rand.NewSource(time.Now().UTC().UnixNano())}), @@ -263,38 +265,19 @@ func (adm AdminClient) dumpHTTP(req *http.Request, resp *http.Response) error { // do - execute http request. func (adm AdminClient) do(req *http.Request) (*http.Response, error) { - var resp *http.Response - var err error - // Do the request in a loop in case of 307 http is met since golang still doesn't - // handle properly this situation (https://github.com/golang/go/issues/7912) - for { - resp, err = adm.httpClient.Do(req) - if err != nil { - // Close idle connections upon error. - adm.httpClient.CloseIdleConnections() - - // Handle this specifically for now until future Golang - // versions fix this issue properly. - urlErr, ok := err.(*url.Error) - if ok && strings.Contains(urlErr.Err.Error(), "EOF") { + resp, err := adm.httpClient.Do(req) + if err != nil { + // Handle this specifically for now until future Golang versions fix this issue properly. + if urlErr, ok := err.(*url.Error); ok { + if strings.Contains(urlErr.Err.Error(), "EOF") { return nil, &url.Error{ Op: urlErr.Op, URL: urlErr.URL, - Err: fmt.Errorf("Connection closed by foreign host %s", urlErr.URL), + Err: errors.New("Connection closed by foreign host " + urlErr.URL + ". Retry again."), } } - return nil, err - } - // Redo the request with the new redirect url if http 307 is returned, quit the loop otherwise - if resp != nil && resp.StatusCode == http.StatusTemporaryRedirect { - newURL, uErr := url.Parse(resp.Header.Get("Location")) - if uErr != nil { - break - } - req.URL = newURL - } else { - break } + return nil, err } // Response cannot be non-nil, report if its the case. @@ -323,16 +306,23 @@ var successStatus = []int{ // executeMethod - instantiates a given method, and retries the // request upon any error up to maxRetries attempts in a binomially // delayed manner using a standard back off algorithm. -func (adm AdminClient) executeMethod(method string, reqData requestData) (res *http.Response, err error) { +func (adm AdminClient) executeMethod(ctx context.Context, method string, reqData requestData) (res *http.Response, err error) { var reqRetry = MaxRetry // Indicates how many times we can retry the request - // Create a done channel to control 'ListObjects' go routine. - doneCh := make(chan struct{}, 1) + defer func() { + if err != nil { + // close idle connections before returning, upon error. + adm.httpClient.CloseIdleConnections() + } + }() + + // Create cancel context to control 'newRetryTimer' go routine. + retryCtx, cancel := context.WithCancel(ctx) // Indicate to our routine to exit cleanly upon return. - defer close(doneCh) + defer cancel() - for range adm.newRetryTimer(reqRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter, doneCh) { + for range adm.newRetryTimer(retryCtx, reqRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter) { // Instantiate a new request. var req *http.Request req, err = adm.newRequest(method, reqData) @@ -340,6 +330,9 @@ func (adm AdminClient) executeMethod(method string, reqData requestData) (res *h return nil, err } + // Add context to request + req = req.WithContext(ctx) + // Initiate the request. res, err = adm.do(req) if err != nil { diff --git a/pkg/madmin/config-commands.go b/pkg/madmin/config-commands.go index 6f7170248..9c43f1ab5 100644 --- a/pkg/madmin/config-commands.go +++ b/pkg/madmin/config-commands.go @@ -19,14 +19,16 @@ package madmin import ( "bytes" + "context" "io" "net/http" ) // GetConfig - returns the config.json of a minio setup, incoming data is encrypted. -func (adm *AdminClient) GetConfig() ([]byte, error) { +func (adm *AdminClient) GetConfig(ctx context.Context) ([]byte, error) { // Execute GET on /minio/admin/v2/config to get config of a setup. - resp, err := adm.executeMethod(http.MethodGet, + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{relPath: adminAPIPrefix + "/config"}) defer closeResponse(resp) if err != nil { @@ -41,7 +43,7 @@ func (adm *AdminClient) GetConfig() ([]byte, error) { } // SetConfig - set config supplied as config.json for the setup. -func (adm *AdminClient) SetConfig(config io.Reader) (err error) { +func (adm *AdminClient) SetConfig(ctx context.Context, config io.Reader) (err error) { const maxConfigJSONSize = 256 * 1024 // 256KiB // Read configuration bytes @@ -65,7 +67,7 @@ func (adm *AdminClient) SetConfig(config io.Reader) (err error) { } // Execute PUT on /minio/admin/v2/config to set config. - resp, err := adm.executeMethod(http.MethodPut, reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { diff --git a/pkg/madmin/config-help-commands.go b/pkg/madmin/config-help-commands.go index 77f3c42d6..f97e3b45b 100644 --- a/pkg/madmin/config-help-commands.go +++ b/pkg/madmin/config-help-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "net/http" "net/url" @@ -45,7 +46,7 @@ type HelpKV struct { type HelpKVS []HelpKV // HelpConfigKV - return help for a given sub-system. -func (adm *AdminClient) HelpConfigKV(subSys, key string, envOnly bool) (Help, error) { +func (adm *AdminClient) HelpConfigKV(ctx context.Context, subSys, key string, envOnly bool) (Help, error) { v := url.Values{} v.Set("subSys", subSys) v.Set("key", key) @@ -59,7 +60,7 @@ func (adm *AdminClient) HelpConfigKV(subSys, key string, envOnly bool) (Help, er } // Execute GET on /minio/admin/v2/help-config-kv - resp, err := adm.executeMethod(http.MethodGet, reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) if err != nil { return Help{}, err } diff --git a/pkg/madmin/config-history-commands.go b/pkg/madmin/config-history-commands.go index 4d4522944..9e145219c 100644 --- a/pkg/madmin/config-history-commands.go +++ b/pkg/madmin/config-history-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "net/http" "net/url" @@ -28,7 +29,7 @@ import ( // ClearConfigHistoryKV - clears the config entry represented by restoreID. // optionally allows setting `all` as a special keyword to automatically // erase all config set history entires. -func (adm *AdminClient) ClearConfigHistoryKV(restoreID string) (err error) { +func (adm *AdminClient) ClearConfigHistoryKV(ctx context.Context, restoreID string) (err error) { v := url.Values{} v.Set("restoreId", restoreID) reqData := requestData{ @@ -37,7 +38,7 @@ func (adm *AdminClient) ClearConfigHistoryKV(restoreID string) (err error) { } // Execute DELETE on /minio/admin/v2/clear-config-history-kv - resp, err := adm.executeMethod(http.MethodDelete, reqData) + resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) defer closeResponse(resp) if err != nil { @@ -53,7 +54,7 @@ func (adm *AdminClient) ClearConfigHistoryKV(restoreID string) (err error) { // RestoreConfigHistoryKV - Restore a previous config set history. // Input is a unique id which represents the previous setting. -func (adm *AdminClient) RestoreConfigHistoryKV(restoreID string) (err error) { +func (adm *AdminClient) RestoreConfigHistoryKV(ctx context.Context, restoreID string) (err error) { v := url.Values{} v.Set("restoreId", restoreID) reqData := requestData{ @@ -62,7 +63,7 @@ func (adm *AdminClient) RestoreConfigHistoryKV(restoreID string) (err error) { } // Execute PUT on /minio/admin/v2/set-config-kv to set config key/value. - resp, err := adm.executeMethod(http.MethodPut, reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -90,7 +91,7 @@ func (ch ConfigHistoryEntry) CreateTimeFormatted() string { } // ListConfigHistoryKV - lists a slice of ConfigHistoryEntries sorted by createTime. -func (adm *AdminClient) ListConfigHistoryKV(count int) ([]ConfigHistoryEntry, error) { +func (adm *AdminClient) ListConfigHistoryKV(ctx context.Context, count int) ([]ConfigHistoryEntry, error) { if count == 0 { count = 10 } @@ -98,7 +99,8 @@ func (adm *AdminClient) ListConfigHistoryKV(count int) ([]ConfigHistoryEntry, er v.Set("count", strconv.Itoa(count)) // Execute GET on /minio/admin/v2/list-config-history-kv - resp, err := adm.executeMethod(http.MethodGet, + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ relPath: adminAPIPrefix + "/list-config-history-kv", queryValues: v, diff --git a/pkg/madmin/config-kv-commands.go b/pkg/madmin/config-kv-commands.go index 54e9e344b..aa6b4cad2 100644 --- a/pkg/madmin/config-kv-commands.go +++ b/pkg/madmin/config-kv-commands.go @@ -18,12 +18,13 @@ package madmin import ( + "context" "net/http" "net/url" ) // DelConfigKV - delete key from server config. -func (adm *AdminClient) DelConfigKV(k string) (err error) { +func (adm *AdminClient) DelConfigKV(ctx context.Context, k string) (err error) { econfigBytes, err := EncryptData(adm.secretAccessKey, []byte(k)) if err != nil { return err @@ -35,7 +36,7 @@ func (adm *AdminClient) DelConfigKV(k string) (err error) { } // Execute DELETE on /minio/admin/v2/del-config-kv to delete config key. - resp, err := adm.executeMethod(http.MethodDelete, reqData) + resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) defer closeResponse(resp) if err != nil { @@ -50,7 +51,7 @@ func (adm *AdminClient) DelConfigKV(k string) (err error) { } // SetConfigKV - set key value config to server. -func (adm *AdminClient) SetConfigKV(kv string) (err error) { +func (adm *AdminClient) SetConfigKV(ctx context.Context, kv string) (err error) { econfigBytes, err := EncryptData(adm.secretAccessKey, []byte(kv)) if err != nil { return err @@ -62,7 +63,7 @@ func (adm *AdminClient) SetConfigKV(kv string) (err error) { } // Execute PUT on /minio/admin/v2/set-config-kv to set config key/value. - resp, err := adm.executeMethod(http.MethodPut, reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -77,12 +78,13 @@ func (adm *AdminClient) SetConfigKV(kv string) (err error) { } // GetConfigKV - returns the key, value of the requested key, incoming data is encrypted. -func (adm *AdminClient) GetConfigKV(key string) (Targets, error) { +func (adm *AdminClient) GetConfigKV(ctx context.Context, key string) (Targets, error) { v := url.Values{} v.Set("key", key) // Execute GET on /minio/admin/v2/get-config-kv?key={key} to get value of key. - resp, err := adm.executeMethod(http.MethodGet, + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ relPath: adminAPIPrefix + "/get-config-kv", queryValues: v, diff --git a/pkg/madmin/examples/accounting-usage-info.go b/pkg/madmin/examples/accounting-usage-info.go index ca2a38466..b4a22e6f9 100644 --- a/pkg/madmin/examples/accounting-usage-info.go +++ b/pkg/madmin/examples/accounting-usage-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - accountingUsageInfo, err := madmClnt.AccountingUsageInfo() + accountingUsageInfo, err := madmClnt.AccountingUsageInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/add-service-account-and-policy.go b/pkg/madmin/examples/add-service-account-and-policy.go index 214811815..4c3c0c678 100644 --- a/pkg/madmin/examples/add-service-account-and-policy.go +++ b/pkg/madmin/examples/add-service-account-and-policy.go @@ -20,6 +20,7 @@ package main import ( + "context" "fmt" "log" @@ -43,7 +44,7 @@ func main() { // Create policy policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::testbucket/*"],"Sid": ""}]}` - creds, err := madmClnt.AddServiceAccount("parentuser", policy) + creds, err := madmClnt.AddServiceAccount(context.Background(), "parentuser", policy) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/add-user-and-policy.go b/pkg/madmin/examples/add-user-and-policy.go index fa1535451..f611bbc10 100644 --- a/pkg/madmin/examples/add-user-and-policy.go +++ b/pkg/madmin/examples/add-user-and-policy.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -39,18 +40,18 @@ func main() { log.Fatalln(err) } - if err = madmClnt.AddUser("newuser", "newstrongpassword"); err != nil { + if err = madmClnt.AddUser(context.Background(), "newuser", "newstrongpassword"); err != nil { log.Fatalln(err) } // Create policy policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}` - if err = madmClnt.AddCannedPolicy("get-only", policy); err != nil { + if err = madmClnt.AddCannedPolicy(context.Background(), "get-only", policy); err != nil { log.Fatalln(err) } - if err = madmClnt.SetUserPolicy("newuser", "get-only"); err != nil { + if err = madmClnt.SetUserPolicy(context.Background(), "newuser", "get-only"); err != nil { log.Fatalln(err) } } diff --git a/pkg/madmin/examples/cpu-load-info.go b/pkg/madmin/examples/cpu-load-info.go index f8c24d671..d05c925b4 100644 --- a/pkg/madmin/examples/cpu-load-info.go +++ b/pkg/madmin/examples/cpu-load-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.ServerCPULoadInfo() + st, err := madmClnt.ServerCPULoadInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/data-usage-info.go b/pkg/madmin/examples/data-usage-info.go index 0f3b1d33a..19a7a0019 100644 --- a/pkg/madmin/examples/data-usage-info.go +++ b/pkg/madmin/examples/data-usage-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - dataUsageInfo, err := madmClnt.DataUsageInfo() + dataUsageInfo, err := madmClnt.DataUsageInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/drives-perf-info.go b/pkg/madmin/examples/drives-perf-info.go index 1839d92c5..d8003cdb8 100644 --- a/pkg/madmin/examples/drives-perf-info.go +++ b/pkg/madmin/examples/drives-perf-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.ServerDrivesPerfInfo(madmin.DefaultDrivePerfSize) + st, err := madmClnt.ServerDrivesPerfInfo(context.Background(), madmin.DefaultDrivePerfSize) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/get-service-account.go b/pkg/madmin/examples/get-service-account.go index 93303bd4a..e2a1f3544 100644 --- a/pkg/madmin/examples/get-service-account.go +++ b/pkg/madmin/examples/get-service-account.go @@ -20,6 +20,7 @@ package main import ( + "context" "fmt" "log" @@ -40,7 +41,7 @@ func main() { log.Fatalln(err) } - creds, err := madmClnt.GetServiceAccount("service-account-access-key") + creds, err := madmClnt.GetServiceAccount(context.Background(), "service-account-access-key") if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/heal-bucket.go b/pkg/madmin/examples/heal-bucket.go index 26a971b05..24f7cdd03 100644 --- a/pkg/madmin/examples/heal-bucket.go +++ b/pkg/madmin/examples/heal-bucket.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -38,7 +39,7 @@ func main() { // Heal bucket mybucket - dry run isDryRun := true - err = madmClnt.HealBucket("mybucket", isDryRun) + err = madmClnt.HealBucket(context.Background(), "mybucket", isDryRun) if err != nil { log.Fatalln(err) @@ -46,7 +47,7 @@ func main() { // Heal bucket mybucket - for real this time. isDryRun := false - err = madmClnt.HealBucket("mybucket", isDryRun) + err = madmClnt.HealBucket(context.Background(), "mybucket", isDryRun) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/heal-buckets-list.go b/pkg/madmin/examples/heal-buckets-list.go index 68089e659..8c05ac7bc 100644 --- a/pkg/madmin/examples/heal-buckets-list.go +++ b/pkg/madmin/examples/heal-buckets-list.go @@ -20,6 +20,7 @@ package main */ import ( + "context" "fmt" "log" @@ -39,7 +40,7 @@ func main() { } // List buckets that need healing - healBucketsList, err := madmClnt.ListBucketsHeal() + healBucketsList, err := madmClnt.ListBucketsHeal(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/heal-format.go b/pkg/madmin/examples/heal-format.go index c48ffdd0a..2edfa1893 100644 --- a/pkg/madmin/examples/heal-format.go +++ b/pkg/madmin/examples/heal-format.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -41,14 +42,14 @@ func main() { // Attempt healing format in dry-run mode. isDryRun := true - err = madmClnt.HealFormat(isDryRun) + err = madmClnt.HealFormat(context.Background(), isDryRun) if err != nil { log.Fatalln(err) } // Perform actual healing of format. isDryRun = false - err = madmClnt.HealFormat(isDryRun) + err = madmClnt.HealFormat(context.Background(), isDryRun) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/heal-object.go b/pkg/madmin/examples/heal-object.go index b765d8288..13f2e4e3c 100644 --- a/pkg/madmin/examples/heal-object.go +++ b/pkg/madmin/examples/heal-object.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -41,14 +42,14 @@ func main() { // Heal object mybucket/myobject - dry run. isDryRun := true - _, err = madmClnt.HealObject("mybucket", "myobject", isDryRun) + _, err = madmClnt.HealObject(context.Background(), "mybucket", "myobject", isDryRun) if err != nil { log.Fatalln(err) } // Heal object mybucket/myobject - this time for real. isDryRun = false - healResult, err := madmClnt.HealObject("mybucket", "myobject", isDryRun) + healResult, err := madmClnt.HealObject(context.Background(), "mybucket", "myobject", isDryRun) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/heal-status.go b/pkg/madmin/examples/heal-status.go index 125550133..ad8e5900d 100644 --- a/pkg/madmin/examples/heal-status.go +++ b/pkg/madmin/examples/heal-status.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - healStatusResult, err := madmClnt.BackgroundHealStatus() + healStatusResult, err := madmClnt.BackgroundHealStatus(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/hw-cpu-info.go b/pkg/madmin/examples/hw-cpu-info.go index 00241ba59..88b242c6b 100644 --- a/pkg/madmin/examples/hw-cpu-info.go +++ b/pkg/madmin/examples/hw-cpu-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -35,7 +36,7 @@ func main() { if err != nil { log.Fatalln(err) } - st, err := madmClnt.ServerCPUHardwareInfo() + st, err := madmClnt.ServerCPUHardwareInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/hw-network-info.go b/pkg/madmin/examples/hw-network-info.go index a15062497..63ca845a0 100644 --- a/pkg/madmin/examples/hw-network-info.go +++ b/pkg/madmin/examples/hw-network-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -35,7 +36,7 @@ func main() { if err != nil { log.Fatalln(err) } - st, err := madmClnt.ServerNetworkHardwareInfo() + st, err := madmClnt.ServerNetworkHardwareInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/kms-status.go b/pkg/madmin/examples/kms-status.go index 136558fe6..a18bad58f 100644 --- a/pkg/madmin/examples/kms-status.go +++ b/pkg/madmin/examples/kms-status.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - status, err := madmClnt.GetKeyStatus("") // empty string refers to the default master key + status, err := madmClnt.GetKeyStatus(context.Background(), "") // empty string refers to the default master key if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/lock-clear.go b/pkg/madmin/examples/lock-clear.go index 2a7f9cb8b..04ffaac9a 100644 --- a/pkg/madmin/examples/lock-clear.go +++ b/pkg/madmin/examples/lock-clear.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "time" @@ -39,7 +40,7 @@ func main() { // Clear locks held on mybucket/myprefix for longer than 30s. olderThan := time.Duration(30 * time.Second) - locksCleared, err := madmClnt.ClearLocks("mybucket", "myprefix", olderThan) + locksCleared, err := madmClnt.ClearLocks(context.Background(), "mybucket", "myprefix", olderThan) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/mem-usage-info.go b/pkg/madmin/examples/mem-usage-info.go index 847324fb2..2fc2f59f4 100644 --- a/pkg/madmin/examples/mem-usage-info.go +++ b/pkg/madmin/examples/mem-usage-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.ServerMemUsageInfo() + st, err := madmClnt.ServerMemUsageInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/net-perf-info.go b/pkg/madmin/examples/net-perf-info.go index 85da2ffcd..887ed0ed1 100644 --- a/pkg/madmin/examples/net-perf-info.go +++ b/pkg/madmin/examples/net-perf-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.NetPerfInfo(madmin.DefaultNetPerfSize) + st, err := madmClnt.NetPerfInfo(context.Background(), madmin.DefaultNetPerfSize) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/profiling.go b/pkg/madmin/examples/profiling.go index 1ce784250..a04c25b28 100644 --- a/pkg/madmin/examples/profiling.go +++ b/pkg/madmin/examples/profiling.go @@ -20,6 +20,7 @@ package main import ( + "context" "io" "log" "os" @@ -45,7 +46,7 @@ func main() { profiler := madmin.ProfilerCPU log.Println("Starting " + profiler + " profiling..") - startResults, err := madmClnt.StartProfiling(profiler) + startResults, err := madmClnt.StartProfiling(context.Background(), profiler) if err != nil { log.Fatalln(err) } @@ -63,7 +64,7 @@ func main() { log.Println("Stopping profiling..") - profilingData, err := madmClnt.DownloadProfilingData() + profilingData, err := madmClnt.DownloadProfilingData(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/server-info.go b/pkg/madmin/examples/server-info.go index 009dbdda7..6e54f433c 100644 --- a/pkg/madmin/examples/server-info.go +++ b/pkg/madmin/examples/server-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.ServerInfo() + st, err := madmClnt.ServerInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/service-restart.go b/pkg/madmin/examples/service-restart.go index 8273e0bf7..0586d3deb 100644 --- a/pkg/madmin/examples/service-restart.go +++ b/pkg/madmin/examples/service-restart.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - err = madmClnt.ServiceRestart() + err = madmClnt.ServiceRestart(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/service-trace.go b/pkg/madmin/examples/service-trace.go index dae05b434..99144a133 100644 --- a/pkg/madmin/examples/service-trace.go +++ b/pkg/madmin/examples/service-trace.go @@ -20,6 +20,7 @@ package main import ( + "context" "fmt" "log" @@ -43,7 +44,7 @@ func main() { // in the minio cluster. allTrace := false errTrace := false - traceCh := madmClnt.ServiceTrace(allTrace, errTrace, doneCh) + traceCh := madmClnt.ServiceTrace(context.Background(), allTrace, errTrace, doneCh) for traceInfo := range traceCh { if traceInfo.Err != nil { fmt.Println(traceInfo.Err) diff --git a/pkg/madmin/examples/storage-info.go b/pkg/madmin/examples/storage-info.go index 0a6346664..5441e278d 100644 --- a/pkg/madmin/examples/storage-info.go +++ b/pkg/madmin/examples/storage-info.go @@ -20,6 +20,7 @@ package main import ( + "context" "log" "github.com/minio/minio/pkg/madmin" @@ -36,7 +37,7 @@ func main() { log.Fatalln(err) } - st, err := madmClnt.StorageInfo() + st, err := madmClnt.StorageInfo(context.Background()) if err != nil { log.Fatalln(err) } diff --git a/pkg/madmin/examples/top-locks.go b/pkg/madmin/examples/top-locks.go index baf48143d..93f6911c7 100644 --- a/pkg/madmin/examples/top-locks.go +++ b/pkg/madmin/examples/top-locks.go @@ -20,6 +20,7 @@ package main import ( + "context" "encoding/json" "log" @@ -37,7 +38,7 @@ func main() { log.Fatalln(err) } - locks, err := madmClnt.TopLocks() + locks, err := madmClnt.TopLocks(context.Background()) if err != nil { log.Fatalf("failed due to: %v", err) } diff --git a/pkg/madmin/group-commands.go b/pkg/madmin/group-commands.go index faf6a60d0..a2f689146 100644 --- a/pkg/madmin/group-commands.go +++ b/pkg/madmin/group-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -34,7 +35,7 @@ type GroupAddRemove struct { // UpdateGroupMembers - adds/removes users to/from a group. Server // creates the group as needed. Group is removed if remove request is // made on empty group. -func (adm *AdminClient) UpdateGroupMembers(g GroupAddRemove) error { +func (adm *AdminClient) UpdateGroupMembers(ctx context.Context, g GroupAddRemove) error { data, err := json.Marshal(g) if err != nil { return err @@ -46,7 +47,7 @@ func (adm *AdminClient) UpdateGroupMembers(g GroupAddRemove) error { } // Execute PUT on /minio/admin/v2/update-group-members - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -70,7 +71,7 @@ type GroupDesc struct { } // GetGroupDescription - fetches information on a group. -func (adm *AdminClient) GetGroupDescription(group string) (*GroupDesc, error) { +func (adm *AdminClient) GetGroupDescription(ctx context.Context, group string) (*GroupDesc, error) { v := url.Values{} v.Set("group", group) reqData := requestData{ @@ -78,7 +79,7 @@ func (adm *AdminClient) GetGroupDescription(group string) (*GroupDesc, error) { queryValues: v, } - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { return nil, err @@ -102,12 +103,12 @@ func (adm *AdminClient) GetGroupDescription(group string) (*GroupDesc, error) { } // ListGroups - lists all groups names present on the server. -func (adm *AdminClient) ListGroups() ([]string, error) { +func (adm *AdminClient) ListGroups(ctx context.Context) ([]string, error) { reqData := requestData{ relPath: adminAPIPrefix + "/groups", } - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { return nil, err @@ -140,7 +141,7 @@ const ( ) // SetGroupStatus - sets the status of a group. -func (adm *AdminClient) SetGroupStatus(group string, status GroupStatus) error { +func (adm *AdminClient) SetGroupStatus(ctx context.Context, group string, status GroupStatus) error { v := url.Values{} v.Set("group", group) v.Set("status", string(status)) @@ -150,7 +151,7 @@ func (adm *AdminClient) SetGroupStatus(group string, status GroupStatus) error { queryValues: v, } - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { return err diff --git a/pkg/madmin/hardware-commands.go b/pkg/madmin/hardware-commands.go index 32312e4ad..ccbea08f6 100644 --- a/pkg/madmin/hardware-commands.go +++ b/pkg/madmin/hardware-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net" @@ -47,13 +48,15 @@ type ServerCPUHardwareInfo struct { } // ServerCPUHardwareInfo - Returns cpu hardware information -func (adm *AdminClient) ServerCPUHardwareInfo() ([]ServerCPUHardwareInfo, error) { +func (adm *AdminClient) ServerCPUHardwareInfo(ctx context.Context) ([]ServerCPUHardwareInfo, error) { v := url.Values{} v.Set(HARDWARE, string(CPU)) - resp, err := adm.executeMethod("GET", requestData{ - relPath: adminAPIPrefix + "/hardware", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ + relPath: adminAPIPrefix + "/hardware", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { @@ -88,13 +91,15 @@ type ServerNetworkHardwareInfo struct { } // ServerNetworkHardwareInfo - Returns network hardware information -func (adm *AdminClient) ServerNetworkHardwareInfo() ([]ServerNetworkHardwareInfo, error) { +func (adm *AdminClient) ServerNetworkHardwareInfo(ctx context.Context) ([]ServerNetworkHardwareInfo, error) { v := url.Values{} v.Set(HARDWARE, string(NETWORK)) - resp, err := adm.executeMethod("GET", requestData{ - relPath: "/v1/hardware", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ + relPath: "/v1/hardware", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { diff --git a/pkg/madmin/heal-commands.go b/pkg/madmin/heal-commands.go index 0b9bdf554..33d6bbe5c 100644 --- a/pkg/madmin/heal-commands.go +++ b/pkg/madmin/heal-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "fmt" "io/ioutil" @@ -195,8 +196,8 @@ func (hri *HealResultItem) GetOnlineCounts() (b, a int) { // forceStart and forceStop are mutually exclusive, you can either // set one of them to 'true'. If both are set 'forceStart' will be // honored. -func (adm *AdminClient) Heal(bucket, prefix string, healOpts HealOpts, - clientToken string, forceStart, forceStop bool) ( +func (adm *AdminClient) Heal(ctx context.Context, bucket, prefix string, + healOpts HealOpts, clientToken string, forceStart, forceStop bool) ( healStart HealStartSuccess, healTaskStatus HealTaskStatus, err error) { if forceStart && forceStop { @@ -227,11 +228,12 @@ func (adm *AdminClient) Heal(bucket, prefix string, healOpts HealOpts, queryVals.Set("forceStop", "true") } - resp, err := adm.executeMethod("POST", requestData{ - relPath: path, - content: body, - queryValues: queryVals, - }) + resp, err := adm.executeMethod(ctx, + http.MethodPost, requestData{ + relPath: path, + content: body, + queryValues: queryVals, + }) defer closeResponse(resp) if err != nil { return healStart, healTaskStatus, err @@ -279,9 +281,11 @@ type BgHealState struct { // BackgroundHealStatus returns the background heal status of the // current server or cluster. -func (adm *AdminClient) BackgroundHealStatus() (BgHealState, error) { +func (adm *AdminClient) BackgroundHealStatus(ctx context.Context) (BgHealState, error) { // Execute POST request to background heal status api - resp, err := adm.executeMethod("POST", requestData{relPath: adminAPIPrefix + "/background-heal/status"}) + resp, err := adm.executeMethod(ctx, + http.MethodPost, + requestData{relPath: adminAPIPrefix + "/background-heal/status"}) if err != nil { return BgHealState{}, err } diff --git a/pkg/madmin/info-commands.go b/pkg/madmin/info-commands.go index 4c85a4e85..67b5bd9c5 100644 --- a/pkg/madmin/info-commands.go +++ b/pkg/madmin/info-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "errors" "io/ioutil" @@ -114,8 +115,8 @@ func (d1 BackendDisks) Merge(d2 BackendDisks) BackendDisks { // StorageInfo - Connect to a minio server and call Storage Info Management API // to fetch server's information represented by StorageInfo structure -func (adm *AdminClient) StorageInfo() (StorageInfo, error) { - resp, err := adm.executeMethod("GET", requestData{relPath: adminAPIPrefix + "/storageinfo"}) +func (adm *AdminClient) StorageInfo(ctx context.Context) (StorageInfo, error) { + resp, err := adm.executeMethod(ctx, http.MethodGet, requestData{relPath: adminAPIPrefix + "/storageinfo"}) defer closeResponse(resp) if err != nil { return StorageInfo{}, err @@ -178,8 +179,8 @@ type DataUsageInfo struct { } // DataUsageInfo - returns data usage of the current object API -func (adm *AdminClient) DataUsageInfo() (DataUsageInfo, error) { - resp, err := adm.executeMethod("GET", requestData{relPath: adminAPIPrefix + "/datausageinfo"}) +func (adm *AdminClient) DataUsageInfo(ctx context.Context) (DataUsageInfo, error) { + resp, err := adm.executeMethod(ctx, http.MethodGet, requestData{relPath: adminAPIPrefix + "/datausageinfo"}) defer closeResponse(resp) if err != nil { return DataUsageInfo{}, err @@ -222,8 +223,8 @@ type BucketAccountingUsage struct { // AccountingUsageInfo returns the accounting usage info, currently it returns // the type of access of different accounts to the different buckets. -func (adm *AdminClient) AccountingUsageInfo() (map[string]BucketAccountingUsage, error) { - resp, err := adm.executeMethod(http.MethodGet, requestData{relPath: adminAPIPrefix + "/accountingusageinfo"}) +func (adm *AdminClient) AccountingUsageInfo(ctx context.Context) (map[string]BucketAccountingUsage, error) { + resp, err := adm.executeMethod(ctx, http.MethodGet, requestData{relPath: adminAPIPrefix + "/accountingusageinfo"}) defer closeResponse(resp) if err != nil { return nil, err @@ -260,16 +261,18 @@ type ServerDrivesPerfInfo struct { } // ServerDrivesPerfInfo - Returns drive's read and write performance information -func (adm *AdminClient) ServerDrivesPerfInfo(size int64) ([]ServerDrivesPerfInfo, error) { +func (adm *AdminClient) ServerDrivesPerfInfo(ctx context.Context, size int64) ([]ServerDrivesPerfInfo, error) { v := url.Values{} v.Set("perfType", string("drive")) v.Set("size", strconv.FormatInt(size, 10)) - resp, err := adm.executeMethod("GET", requestData{ - relPath: adminAPIPrefix + "/performance", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, http.MethodGet, + requestData{ + relPath: adminAPIPrefix + "/performance", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { @@ -307,13 +310,15 @@ type ServerCPULoadInfo struct { } // ServerCPULoadInfo - Returns cpu utilization information -func (adm *AdminClient) ServerCPULoadInfo() ([]ServerCPULoadInfo, error) { +func (adm *AdminClient) ServerCPULoadInfo(ctx context.Context) ([]ServerCPULoadInfo, error) { v := url.Values{} v.Set("perfType", string("cpu")) - resp, err := adm.executeMethod("GET", requestData{ - relPath: adminAPIPrefix + "/performance", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ + relPath: adminAPIPrefix + "/performance", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { @@ -351,13 +356,16 @@ type ServerMemUsageInfo struct { } // ServerMemUsageInfo - Returns mem utilization information -func (adm *AdminClient) ServerMemUsageInfo() ([]ServerMemUsageInfo, error) { +func (adm *AdminClient) ServerMemUsageInfo(ctx context.Context) ([]ServerMemUsageInfo, error) { v := url.Values{} v.Set("perfType", string("mem")) - resp, err := adm.executeMethod("GET", requestData{ - relPath: adminAPIPrefix + "/performance", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, + requestData{ + relPath: adminAPIPrefix + "/performance", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { @@ -393,16 +401,19 @@ type NetPerfInfo struct { } // NetPerfInfo - Returns network performance information of all cluster nodes. -func (adm *AdminClient) NetPerfInfo(size int) (map[string][]NetPerfInfo, error) { +func (adm *AdminClient) NetPerfInfo(ctx context.Context, size int) (map[string][]NetPerfInfo, error) { v := url.Values{} v.Set("perfType", "net") if size > 0 { v.Set("size", strconv.Itoa(size)) } - resp, err := adm.executeMethod("GET", requestData{ - relPath: adminAPIPrefix + "/performance", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, + requestData{ + relPath: adminAPIPrefix + "/performance", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { @@ -509,13 +520,13 @@ const ( ErasureType = backendType("Erasure") ) -// FsBackend contains specific FS storage information -type FsBackend struct { +// FSBackend contains specific FS storage information +type FSBackend struct { Type backendType `json:"backendType,omitempty"` } -// XlBackend contains specific erasure storage information -type XlBackend struct { +// XLBackend contains specific erasure storage information +type XLBackend struct { Type backendType `json:"backendType,omitempty"` OnlineDisks int `json:"onlineDisks,omitempty"` OfflineDisks int `json:"offlineDisks,omitempty"` @@ -557,9 +568,11 @@ type Disk struct { // ServerInfo - Connect to a minio server and call Server Admin Info Management API // to fetch server's information represented by infoMessage structure -func (adm *AdminClient) ServerInfo() (InfoMessage, error) { - - resp, err := adm.executeMethod("GET", requestData{relPath: adminAPIPrefix + "/info"}) +func (adm *AdminClient) ServerInfo(ctx context.Context) (InfoMessage, error) { + resp, err := adm.executeMethod(ctx, + http.MethodGet, + requestData{relPath: adminAPIPrefix + "/info"}, + ) defer closeResponse(resp) if err != nil { return InfoMessage{}, err diff --git a/pkg/madmin/kms-commands.go b/pkg/madmin/kms-commands.go index 775383714..7e3cf0322 100644 --- a/pkg/madmin/kms-commands.go +++ b/pkg/madmin/kms-commands.go @@ -17,6 +17,7 @@ package madmin import ( + "context" "encoding/json" "net/http" "net/url" @@ -25,7 +26,7 @@ import ( // GetKeyStatus requests status information about the key referenced by keyID // from the KMS connected to a MinIO by performing a Admin-API request. // It basically hits the `/minio/admin/v2/kms/key/status` API endpoint. -func (adm *AdminClient) GetKeyStatus(keyID string) (*KMSKeyStatus, error) { +func (adm *AdminClient) GetKeyStatus(ctx context.Context, keyID string) (*KMSKeyStatus, error) { // GET /minio/admin/v2/kms/key/status?key-id= qv := url.Values{} qv.Set("key-id", keyID) @@ -34,7 +35,7 @@ func (adm *AdminClient) GetKeyStatus(keyID string) (*KMSKeyStatus, error) { queryValues: qv, } - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) if err != nil { return nil, err } diff --git a/pkg/madmin/policy-commands.go b/pkg/madmin/policy-commands.go index 58db87437..0bdb4dede 100644 --- a/pkg/madmin/policy-commands.go +++ b/pkg/madmin/policy-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -25,7 +26,7 @@ import ( ) // InfoCannedPolicy - expand canned policy into JSON structure. -func (adm *AdminClient) InfoCannedPolicy(policyName string) ([]byte, error) { +func (adm *AdminClient) InfoCannedPolicy(ctx context.Context, policyName string) ([]byte, error) { queryValues := url.Values{} queryValues.Set("name", policyName) @@ -35,7 +36,7 @@ func (adm *AdminClient) InfoCannedPolicy(policyName string) ([]byte, error) { } // Execute GET on /minio/admin/v2/info-canned-policy - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { @@ -50,13 +51,13 @@ func (adm *AdminClient) InfoCannedPolicy(policyName string) ([]byte, error) { } // ListCannedPolicies - list all configured canned policies. -func (adm *AdminClient) ListCannedPolicies() (map[string][]byte, error) { +func (adm *AdminClient) ListCannedPolicies(ctx context.Context) (map[string][]byte, error) { reqData := requestData{ relPath: adminAPIPrefix + "/list-canned-policies", } // Execute GET on /minio/admin/v2/list-canned-policies - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { @@ -81,7 +82,7 @@ func (adm *AdminClient) ListCannedPolicies() (map[string][]byte, error) { } // RemoveCannedPolicy - remove a policy for a canned. -func (adm *AdminClient) RemoveCannedPolicy(policyName string) error { +func (adm *AdminClient) RemoveCannedPolicy(ctx context.Context, policyName string) error { queryValues := url.Values{} queryValues.Set("name", policyName) @@ -91,7 +92,7 @@ func (adm *AdminClient) RemoveCannedPolicy(policyName string) error { } // Execute DELETE on /minio/admin/v2/remove-canned-policy to remove policy. - resp, err := adm.executeMethod("DELETE", reqData) + resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) defer closeResponse(resp) if err != nil { @@ -106,7 +107,7 @@ func (adm *AdminClient) RemoveCannedPolicy(policyName string) error { } // AddCannedPolicy - adds a policy for a canned. -func (adm *AdminClient) AddCannedPolicy(policyName, policy string) error { +func (adm *AdminClient) AddCannedPolicy(ctx context.Context, policyName, policy string) error { queryValues := url.Values{} queryValues.Set("name", policyName) @@ -117,7 +118,7 @@ func (adm *AdminClient) AddCannedPolicy(policyName, policy string) error { } // Execute PUT on /minio/admin/v2/add-canned-policy to set policy. - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -132,7 +133,7 @@ func (adm *AdminClient) AddCannedPolicy(policyName, policy string) error { } // SetPolicy - sets the policy for a user or a group. -func (adm *AdminClient) SetPolicy(policyName, entityName string, isGroup bool) error { +func (adm *AdminClient) SetPolicy(ctx context.Context, policyName, entityName string, isGroup bool) error { queryValues := url.Values{} queryValues.Set("policyName", policyName) queryValues.Set("userOrGroup", entityName) @@ -148,7 +149,7 @@ func (adm *AdminClient) SetPolicy(policyName, entityName string, isGroup bool) e } // Execute PUT on /minio/admin/v2/set-user-or-group-policy to set policy. - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { return err diff --git a/pkg/madmin/profiling-commands.go b/pkg/madmin/profiling-commands.go index 716c65fec..84dc96baf 100644 --- a/pkg/madmin/profiling-commands.go +++ b/pkg/madmin/profiling-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "errors" "fmt" @@ -52,13 +53,15 @@ type StartProfilingResult struct { // StartProfiling makes an admin call to remotely start profiling on a standalone // server or the whole cluster in case of a distributed setup. -func (adm *AdminClient) StartProfiling(profiler ProfilerType) ([]StartProfilingResult, error) { +func (adm *AdminClient) StartProfiling(ctx context.Context, profiler ProfilerType) ([]StartProfilingResult, error) { v := url.Values{} v.Set("profilerType", string(profiler)) - resp, err := adm.executeMethod("POST", requestData{ - relPath: adminAPIPrefix + "/profiling/start", - queryValues: v, - }) + resp, err := adm.executeMethod(ctx, + http.MethodPost, requestData{ + relPath: adminAPIPrefix + "/profiling/start", + queryValues: v, + }, + ) defer closeResponse(resp) if err != nil { return nil, err @@ -84,11 +87,13 @@ func (adm *AdminClient) StartProfiling(profiler ProfilerType) ([]StartProfilingR // DownloadProfilingData makes an admin call to download profiling data of a standalone // server or of the whole cluster in case of a distributed setup. -func (adm *AdminClient) DownloadProfilingData() (io.ReadCloser, error) { +func (adm *AdminClient) DownloadProfilingData(ctx context.Context) (io.ReadCloser, error) { path := fmt.Sprintf(adminAPIPrefix + "/profiling/download") - resp, err := adm.executeMethod("GET", requestData{ - relPath: path, - }) + resp, err := adm.executeMethod(ctx, + http.MethodGet, requestData{ + relPath: path, + }, + ) if err != nil { closeResponse(resp) diff --git a/pkg/madmin/retry.go b/pkg/madmin/retry.go index 8ad347ab2..099de381a 100644 --- a/pkg/madmin/retry.go +++ b/pkg/madmin/retry.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "math/rand" "net" "net/http" @@ -68,7 +69,7 @@ func (r *lockedRandSource) Seed(seed int64) { // newRetryTimer creates a timer with exponentially increasing // delays until the maximum retry attempts are reached. -func (adm AdminClient) newRetryTimer(maxRetry int, unit time.Duration, cap time.Duration, jitter float64, doneCh chan struct{}) <-chan int { +func (adm AdminClient) newRetryTimer(ctx context.Context, maxRetry int, unit time.Duration, cap time.Duration, jitter float64) <-chan int { attemptCh := make(chan int) // computes the exponential backoff duration according to @@ -96,14 +97,15 @@ func (adm AdminClient) newRetryTimer(maxRetry int, unit time.Duration, cap time. go func() { defer close(attemptCh) for i := 0; i < maxRetry; i++ { - select { // Attempts start from 1. - case attemptCh <- i + 1: - case <-doneCh: + attemptCh <- i + 1 + + select { + case <-time.After(exponentialBackoffWait(i)): + case <-ctx.Done(): // Stop the routine. return } - time.Sleep(exponentialBackoffWait(i)) } }() return attemptCh diff --git a/pkg/madmin/service-commands.go b/pkg/madmin/service-commands.go index 68068b313..77417ac77 100644 --- a/pkg/madmin/service-commands.go +++ b/pkg/madmin/service-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "net/http" "net/url" @@ -27,13 +28,13 @@ import ( ) // ServiceRestart - restarts the MinIO cluster -func (adm *AdminClient) ServiceRestart() error { - return adm.serviceCallAction(ServiceActionRestart) +func (adm *AdminClient) ServiceRestart(ctx context.Context) error { + return adm.serviceCallAction(ctx, ServiceActionRestart) } // ServiceStop - stops the MinIO cluster -func (adm *AdminClient) ServiceStop() error { - return adm.serviceCallAction(ServiceActionStop) +func (adm *AdminClient) ServiceStop(ctx context.Context) error { + return adm.serviceCallAction(ctx, ServiceActionStop) } // ServiceAction - type to restrict service-action values @@ -47,15 +48,17 @@ const ( ) // serviceCallAction - call service restart/update/stop API. -func (adm *AdminClient) serviceCallAction(action ServiceAction) error { +func (adm *AdminClient) serviceCallAction(ctx context.Context, action ServiceAction) error { queryValues := url.Values{} queryValues.Set("action", string(action)) // Request API to Restart server - resp, err := adm.executeMethod("POST", requestData{ - relPath: adminAPIPrefix + "/service", - queryValues: queryValues, - }) + resp, err := adm.executeMethod(ctx, + http.MethodPost, requestData{ + relPath: adminAPIPrefix + "/service", + queryValues: queryValues, + }, + ) defer closeResponse(resp) if err != nil { return err @@ -75,7 +78,7 @@ type ServiceTraceInfo struct { } // ServiceTrace - listen on http trace notifications. -func (adm AdminClient) ServiceTrace(allTrace, errTrace bool, doneCh <-chan struct{}) <-chan ServiceTraceInfo { +func (adm AdminClient) ServiceTrace(ctx context.Context, allTrace, errTrace bool) <-chan ServiceTraceInfo { traceInfoCh := make(chan ServiceTraceInfo) // Only success, start a routine to start reading line by line. go func(traceInfoCh chan<- ServiceTraceInfo) { @@ -89,7 +92,7 @@ func (adm AdminClient) ServiceTrace(allTrace, errTrace bool, doneCh <-chan struc queryValues: urlValues, } // Execute GET to call trace handler - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) if err != nil { closeResponse(resp) return @@ -107,7 +110,7 @@ func (adm AdminClient) ServiceTrace(allTrace, errTrace bool, doneCh <-chan struc break } select { - case <-doneCh: + case <-ctx.Done(): return case traceInfoCh <- ServiceTraceInfo{Trace: info}: } diff --git a/pkg/madmin/top-commands.go b/pkg/madmin/top-commands.go index 4b080ebef..fed76b5ea 100644 --- a/pkg/madmin/top-commands.go +++ b/pkg/madmin/top-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -53,11 +54,13 @@ func (l LockEntries) Swap(i, j int) { } // TopLocks - returns the oldest locks in a minio setup. -func (adm *AdminClient) TopLocks() (LockEntries, error) { +func (adm *AdminClient) TopLocks(ctx context.Context) (LockEntries, error) { // Execute GET on /minio/admin/v2/top/locks // to get the oldest locks in a minio setup. - resp, err := adm.executeMethod("GET", - requestData{relPath: adminAPIPrefix + "/top/locks"}) + resp, err := adm.executeMethod(ctx, + http.MethodGet, + requestData{relPath: adminAPIPrefix + "/top/locks"}, + ) defer closeResponse(resp) if err != nil { return nil, err diff --git a/pkg/madmin/transport.go b/pkg/madmin/transport.go new file mode 100644 index 000000000..ff9be7412 --- /dev/null +++ b/pkg/madmin/transport.go @@ -0,0 +1,60 @@ +/* + * MinIO Cloud Storage, (C) 2020 MinIO, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package madmin + +import ( + "crypto/tls" + "net" + "net/http" + "time" +) + +// DefaultTransport - this default transport is similar to +// http.DefaultTransport but with additional param DisableCompression +// is set to true to avoid decompressing content with 'gzip' encoding. +var DefaultTransport = func(secure bool) http.RoundTripper { + tr := &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: 5 * time.Second, + KeepAlive: 15 * time.Second, + }).DialContext, + MaxIdleConns: 1024, + MaxIdleConnsPerHost: 1024, + ResponseHeaderTimeout: 60 * time.Second, + IdleConnTimeout: 60 * time.Second, + TLSHandshakeTimeout: 10 * time.Second, + ExpectContinueTimeout: 1 * time.Second, + // Set this value so that the underlying transport round-tripper + // doesn't try to auto decode the body of objects with + // content-encoding set to `gzip`. + // + // Refer: + // https://golang.org/src/net/http/transport.go?h=roundTrip#L1843 + DisableCompression: true, + } + + if secure { + tr.TLSClientConfig = &tls.Config{ + // Can't use SSLv3 because of POODLE and BEAST + // Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher + // Can't use TLSv1.1 because of RC4 cipher usage + MinVersion: tls.VersionTLS12, + } + } + return tr +} diff --git a/pkg/madmin/update-commands.go b/pkg/madmin/update-commands.go index 286a0c360..0139af8ba 100644 --- a/pkg/madmin/update-commands.go +++ b/pkg/madmin/update-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -32,15 +33,17 @@ type ServerUpdateStatus struct { // ServerUpdate - updates and restarts the MinIO cluster to latest version. // optionally takes an input URL to specify a custom update binary link -func (adm *AdminClient) ServerUpdate(updateURL string) (us ServerUpdateStatus, err error) { +func (adm *AdminClient) ServerUpdate(ctx context.Context, updateURL string) (us ServerUpdateStatus, err error) { queryValues := url.Values{} queryValues.Set("updateURL", updateURL) // Request API to Restart server - resp, err := adm.executeMethod("POST", requestData{ - relPath: adminAPIPrefix + "/update", - queryValues: queryValues, - }) + resp, err := adm.executeMethod(ctx, + http.MethodPost, requestData{ + relPath: adminAPIPrefix + "/update", + queryValues: queryValues, + }, + ) defer closeResponse(resp) if err != nil { return us, err diff --git a/pkg/madmin/user-commands.go b/pkg/madmin/user-commands.go index 1ec006204..ce6eb37a5 100644 --- a/pkg/madmin/user-commands.go +++ b/pkg/madmin/user-commands.go @@ -18,6 +18,7 @@ package madmin import ( + "context" "encoding/json" "io/ioutil" "net/http" @@ -44,7 +45,7 @@ type UserInfo struct { } // RemoveUser - remove a user. -func (adm *AdminClient) RemoveUser(accessKey string) error { +func (adm *AdminClient) RemoveUser(ctx context.Context, accessKey string) error { queryValues := url.Values{} queryValues.Set("accessKey", accessKey) @@ -54,7 +55,7 @@ func (adm *AdminClient) RemoveUser(accessKey string) error { } // Execute DELETE on /minio/admin/v2/remove-user to remove a user. - resp, err := adm.executeMethod("DELETE", reqData) + resp, err := adm.executeMethod(ctx, http.MethodDelete, reqData) defer closeResponse(resp) if err != nil { @@ -69,13 +70,13 @@ func (adm *AdminClient) RemoveUser(accessKey string) error { } // ListUsers - list all users. -func (adm *AdminClient) ListUsers() (map[string]UserInfo, error) { +func (adm *AdminClient) ListUsers(ctx context.Context) (map[string]UserInfo, error) { reqData := requestData{ relPath: adminAPIPrefix + "/list-users", } // Execute GET on /minio/admin/v2/list-users - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { @@ -100,7 +101,7 @@ func (adm *AdminClient) ListUsers() (map[string]UserInfo, error) { } // GetUserInfo - get info on a user -func (adm *AdminClient) GetUserInfo(name string) (u UserInfo, err error) { +func (adm *AdminClient) GetUserInfo(ctx context.Context, name string) (u UserInfo, err error) { queryValues := url.Values{} queryValues.Set("accessKey", name) @@ -110,7 +111,7 @@ func (adm *AdminClient) GetUserInfo(name string) (u UserInfo, err error) { } // Execute GET on /minio/admin/v2/user-info - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { @@ -134,7 +135,7 @@ func (adm *AdminClient) GetUserInfo(name string) (u UserInfo, err error) { } // SetUser - sets a user info. -func (adm *AdminClient) SetUser(accessKey, secretKey string, status AccountStatus) error { +func (adm *AdminClient) SetUser(ctx context.Context, accessKey, secretKey string, status AccountStatus) error { if !auth.IsAccessKeyValid(accessKey) { return auth.ErrInvalidAccessKeyLength @@ -166,7 +167,7 @@ func (adm *AdminClient) SetUser(accessKey, secretKey string, status AccountStatu } // Execute PUT on /minio/admin/v2/add-user to set a user. - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -181,12 +182,12 @@ func (adm *AdminClient) SetUser(accessKey, secretKey string, status AccountStatu } // AddUser - adds a user. -func (adm *AdminClient) AddUser(accessKey, secretKey string) error { - return adm.SetUser(accessKey, secretKey, AccountEnabled) +func (adm *AdminClient) AddUser(ctx context.Context, accessKey, secretKey string) error { + return adm.SetUser(ctx, accessKey, secretKey, AccountEnabled) } // SetUserStatus - adds a status for a user. -func (adm *AdminClient) SetUserStatus(accessKey string, status AccountStatus) error { +func (adm *AdminClient) SetUserStatus(ctx context.Context, accessKey string, status AccountStatus) error { queryValues := url.Values{} queryValues.Set("accessKey", accessKey) queryValues.Set("status", string(status)) @@ -197,7 +198,7 @@ func (adm *AdminClient) SetUserStatus(accessKey string, status AccountStatus) er } // Execute PUT on /minio/admin/v2/set-user-status to set status. - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { @@ -224,7 +225,7 @@ type AddServiceAccountResp struct { // AddServiceAccount - creates a new service account belonging to the given parent user // while restricting the service account permission by the given policy document. -func (adm *AdminClient) AddServiceAccount(parentUser string, policy string) (auth.Credentials, error) { +func (adm *AdminClient) AddServiceAccount(ctx context.Context, parentUser string, policy string) (auth.Credentials, error) { if !auth.IsAccessKeyValid(parentUser) { return auth.Credentials{}, auth.ErrInvalidAccessKeyLength @@ -249,7 +250,7 @@ func (adm *AdminClient) AddServiceAccount(parentUser string, policy string) (aut } // Execute PUT on /minio/admin/v2/add-service-account to set a user. - resp, err := adm.executeMethod("PUT", reqData) + resp, err := adm.executeMethod(ctx, http.MethodPut, reqData) defer closeResponse(resp) if err != nil { return auth.Credentials{}, err @@ -272,7 +273,7 @@ func (adm *AdminClient) AddServiceAccount(parentUser string, policy string) (aut } // GetServiceAccount returns the credential of the service account -func (adm *AdminClient) GetServiceAccount(serviceAccountAccessKey string) (auth.Credentials, error) { +func (adm *AdminClient) GetServiceAccount(ctx context.Context, serviceAccountAccessKey string) (auth.Credentials, error) { if !auth.IsAccessKeyValid(serviceAccountAccessKey) { return auth.Credentials{}, auth.ErrInvalidAccessKeyLength @@ -287,7 +288,7 @@ func (adm *AdminClient) GetServiceAccount(serviceAccountAccessKey string) (auth. } // Execute GET on /minio/admin/v2/get-service-account to set a user. - resp, err := adm.executeMethod("GET", reqData) + resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) defer closeResponse(resp) if err != nil { return auth.Credentials{}, err