diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index 149970fe3..c737fbf19 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -79,6 +79,208 @@ const ( mgmtForceStop = "forceStop" ) +// ServerUpdateV2Handler - POST /minio/admin/v3/update?updateURL={updateURL}&type=2 +// ---------- +// updates all minio servers and restarts them gracefully. +func (a adminAPIHandlers) ServerUpdateV2Handler(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + objectAPI, _ := validateAdminReq(ctx, w, r, policy.ServerUpdateAdminAction) + if objectAPI == nil { + return + } + + if globalInplaceUpdateDisabled || currentReleaseTime.IsZero() { + writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) + return + } + + vars := mux.Vars(r) + updateURL := vars["updateURL"] + dryRun := r.Form.Get("dry-run") == "true" + + mode := getMinioMode() + if updateURL == "" { + updateURL = minioReleaseInfoURL + if runtime.GOOS == globalWindowsOSName { + updateURL = minioReleaseWindowsInfoURL + } + } + + u, err := url.Parse(updateURL) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + content, err := downloadReleaseURL(u, updateTimeout, mode) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + sha256Sum, lrTime, releaseInfo, err := parseReleaseData(content) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo + // Download Binary Once + binC, bin, err := downloadBinary(u, mode) + if err != nil { + logger.LogIf(ctx, fmt.Errorf("server update failed with %w", err)) + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + updateStatus := madmin.ServerUpdateStatusV2{DryRun: dryRun} + peerResults := make(map[string]madmin.ServerPeerUpdateStatus) + + local := globalLocalNodeName + if local == "" { + local = "127.0.0.1" + } + + failedClients := make(map[int]struct{}) + + // Push binary to other servers + for idx, nerr := range globalNotificationSys.VerifyBinary(ctx, u, sha256Sum, releaseInfo, binC) { + if nerr.Err != nil { + peerResults[nerr.Host.String()] = madmin.ServerPeerUpdateStatus{ + Host: nerr.Host.String(), + Err: nerr.Err.Error(), + CurrentVersion: Version, + } + failedClients[idx] = struct{}{} + } else { + peerResults[nerr.Host.String()] = madmin.ServerPeerUpdateStatus{ + Host: nerr.Host.String(), + CurrentVersion: Version, + UpdatedVersion: lrTime.Format(minioReleaseTagTimeLayout), + } + } + } + + if lrTime.Sub(currentReleaseTime) > 0 { + if err = verifyBinary(u, sha256Sum, releaseInfo, mode, bytes.NewReader(bin)); err != nil { + peerResults[local] = madmin.ServerPeerUpdateStatus{ + Host: local, + Err: err.Error(), + CurrentVersion: Version, + } + } else { + peerResults[local] = madmin.ServerPeerUpdateStatus{ + Host: local, + CurrentVersion: Version, + UpdatedVersion: lrTime.Format(minioReleaseTagTimeLayout), + } + } + } else { + peerResults[local] = madmin.ServerPeerUpdateStatus{ + Host: local, + Err: fmt.Sprintf("server is already running the latest version: %s", Version), + CurrentVersion: Version, + } + } + + if !dryRun { + ng := WithNPeers(len(globalNotificationSys.peerClients)) + for idx, client := range globalNotificationSys.peerClients { + _, ok := failedClients[idx] + if ok { + continue + } + client := client + ng.Go(ctx, func() error { + return client.CommitBinary(ctx) + }, idx, *client.host) + } + + for _, nerr := range ng.Wait() { + if nerr.Err != nil { + prs, ok := peerResults[nerr.Host.String()] + if ok { + prs.Err = nerr.Err.Error() + peerResults[nerr.Host.String()] = prs + } else { + peerResults[nerr.Host.String()] = madmin.ServerPeerUpdateStatus{ + Host: nerr.Host.String(), + Err: nerr.Err.Error(), + CurrentVersion: Version, + UpdatedVersion: lrTime.Format(minioReleaseTagTimeLayout), + } + } + } + } + + prs := peerResults[local] + if prs.Err == "" { + if err = commitBinary(); err != nil { + prs.Err = err.Error() + } + peerResults[local] = prs + } + } + + prs, ok := peerResults[local] + if ok { + prs.WaitingDrives = waitingDrivesNode() + peerResults[local] = prs + } + + // Notify all other MinIO peers signal service. + ng := WithNPeers(len(globalNotificationSys.peerClients)) + for idx, client := range globalNotificationSys.peerClients { + _, ok := failedClients[idx] + if ok { + continue + } + client := client + ng.Go(ctx, func() error { + prs, ok := peerResults[client.String()] + if ok && prs.CurrentVersion != prs.UpdatedVersion && prs.UpdatedVersion != "" { + return client.SignalService(serviceRestart, "", dryRun) + } + return nil + }, idx, *client.host) + } + + for _, nerr := range ng.Wait() { + if nerr.Err != nil { + waitingDrives := map[string]madmin.DiskMetrics{} + jerr := json.Unmarshal([]byte(nerr.Err.Error()), &waitingDrives) + if jerr == nil { + prs, ok := peerResults[nerr.Host.String()] + if ok { + prs.WaitingDrives = waitingDrives + peerResults[nerr.Host.String()] = prs + } + continue + } + } + } + + for _, pr := range peerResults { + updateStatus.Results = append(updateStatus.Results, pr) + } + + // Marshal API response + jsonBytes, err := json.Marshal(updateStatus) + if err != nil { + writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) + return + } + + writeSuccessResponseJSON(w, jsonBytes) + + if !dryRun { + if lrTime.Sub(currentReleaseTime) > 0 { + globalServiceSignalCh <- serviceRestart + } + } +} + // ServerUpdateHandler - POST /minio/admin/v3/update?updateURL={updateURL} // ---------- // updates all minio servers and restarts them gracefully. @@ -90,7 +292,7 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req return } - if globalInplaceUpdateDisabled { + if globalInplaceUpdateDisabled || currentReleaseTime.IsZero() { // if MINIO_UPDATE=off - inplace update is disabled, mostly in containers. writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) return @@ -124,14 +326,7 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req return } - u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo - crTime, err := GetCurrentReleaseTime() - if err != nil { - writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) - return - } - - if lrTime.Sub(crTime) <= 0 { + if lrTime.Sub(currentReleaseTime) <= 0 { updateStatus := madmin.ServerUpdateStatus{ CurrentVersion: Version, UpdatedVersion: Version, @@ -148,6 +343,8 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req return } + u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo + // Download Binary Once binC, bin, err := downloadBinary(u, mode) if err != nil { @@ -279,14 +476,13 @@ func (a adminAPIHandlers) ServiceHandler(w http.ResponseWriter, r *http.Request) } type servicePeerResult struct { - Host string `json:"host"` - Err string `json:"err,omitempty"` - WaitingDrives map[string]DiskMetrics `json:"waitingDrives,omitempty"` + Host string `json:"host"` + Err string `json:"err,omitempty"` + WaitingDrives map[string]madmin.DiskMetrics `json:"waitingDrives,omitempty"` } type serviceResult struct { Action madmin.ServiceAction `json:"action"` - Forced bool `json:"forced"` DryRun bool `json:"dryRun"` Results []servicePeerResult `json:"results,omitempty"` } @@ -310,7 +506,6 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques vars := mux.Vars(r) action := vars["action"] dryRun := r.Form.Get("dry-run") == "true" - force := r.Form.Get("force") == "true" var serviceSig serviceSignal act := madmin.ServiceAction(action) @@ -343,27 +538,25 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques } // Notify all other MinIO peers signal service. - nerrs := globalNotificationSys.SignalServiceV2(serviceSig, dryRun, force) + nerrs := globalNotificationSys.SignalServiceV2(serviceSig, dryRun) srvResult := serviceResult{Action: act, Results: make([]servicePeerResult, 0, len(nerrs))} process := act == madmin.ServiceActionRestart || act == madmin.ServiceActionStop - var trigger bool if process { localhost := globalLocalNodeName if globalLocalNodeName == "" { localhost = "127.0.0.1" } - waitingDrives := canWeRestartNode() + waitingDrives := waitingDrivesNode() srvResult.Results = append(srvResult.Results, servicePeerResult{ Host: localhost, WaitingDrives: waitingDrives, }) - trigger = len(waitingDrives) == 0 || force } for _, nerr := range nerrs { if nerr.Err != nil && process { - waitingDrives := map[string]DiskMetrics{} + waitingDrives := map[string]madmin.DiskMetrics{} jerr := json.Unmarshal([]byte(nerr.Err.Error()), &waitingDrives) if jerr == nil { srvResult.Results = append(srvResult.Results, servicePeerResult{ @@ -383,7 +576,6 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques }) } - srvResult.Forced = force srvResult.DryRun = dryRun buf, err := json.Marshal(srvResult) @@ -401,7 +593,7 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques case serviceUnFreeze: unfreezeServices() case serviceRestart, serviceStop: - if !dryRun && trigger { + if !dryRun { globalServiceSignalCh <- serviceSig } } diff --git a/cmd/admin-router.go b/cmd/admin-router.go index cfbd3ae45..8bc44704a 100644 --- a/cmd/admin-router.go +++ b/cmd/admin-router.go @@ -157,10 +157,13 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) { // Restart and stop MinIO service type=2 adminRouter.Methods(http.MethodPost).Path(adminVersion+"/service").HandlerFunc(adminMiddleware(adminAPI.ServiceV2Handler, traceAllFlag)).Queries("action", "{action:.*}", "type", "2") - // Restart and stop MinIO service. + // Deprecated: Restart and stop MinIO service. adminRouter.Methods(http.MethodPost).Path(adminVersion+"/service").HandlerFunc(adminMiddleware(adminAPI.ServiceHandler, traceAllFlag)).Queries("action", "{action:.*}") - // Update MinIO servers. + // Update all MinIO servers type=2 + adminRouter.Methods(http.MethodPost).Path(adminVersion+"/update").HandlerFunc(adminMiddleware(adminAPI.ServerUpdateV2Handler, traceAllFlag)).Queries("updateURL", "{updateURL:.*}", "type", "2") + + // Deprecated: Update MinIO servers. adminRouter.Methods(http.MethodPost).Path(adminVersion+"/update").HandlerFunc(adminMiddleware(adminAPI.ServerUpdateHandler, traceAllFlag)).Queries("updateURL", "{updateURL:.*}") // Info operations diff --git a/cmd/common-main.go b/cmd/common-main.go index 42d93ea15..28f8eb24b 100644 --- a/cmd/common-main.go +++ b/cmd/common-main.go @@ -69,6 +69,8 @@ import ( // serverDebugLog will enable debug printing var serverDebugLog = env.Get("_MINIO_SERVER_DEBUG", config.EnableOff) == config.EnableOn +var currentReleaseTime time.Time + func init() { if runtime.GOOS == "windows" { if mousetrap.StartedByExplorer() { @@ -109,6 +111,8 @@ func init() { // another way to look at this is we are turning off retries. minio.MaxRetry = 1 madmin.MaxRetry = 1 + + currentReleaseTime, _ = GetCurrentReleaseTime() } const consolePrefix = "CONSOLE_" @@ -293,9 +297,7 @@ func checkUpdate(mode string) { return } - // Its OK to ignore any errors during doUpdate() here. - crTime, err := GetCurrentReleaseTime() - if err != nil { + if currentReleaseTime.IsZero() { return } @@ -306,8 +308,8 @@ func checkUpdate(mode string) { var older time.Duration var downloadURL string - if lrTime.After(crTime) { - older = lrTime.Sub(crTime) + if lrTime.After(currentReleaseTime) { + older = lrTime.Sub(currentReleaseTime) downloadURL = getDownloadURL(releaseTimeToReleaseTag(lrTime)) } @@ -316,7 +318,7 @@ func checkUpdate(mode string) { return } - logger.Info(prepareUpdateMessage("Run `mc admin update`", lrTime.Sub(crTime))) + logger.Info(prepareUpdateMessage("Run `mc admin update ALIAS`", lrTime.Sub(currentReleaseTime))) } func newConfigDir(dir string, dirSet bool, getDefaultDir func() string) (*ConfigDir, error) { diff --git a/cmd/notification.go b/cmd/notification.go index 1fea03dd8..13c304e4e 100644 --- a/cmd/notification.go +++ b/cmd/notification.go @@ -76,6 +76,17 @@ func WithNPeers(nerrs int) *NotificationGroup { return &NotificationGroup{errs: make([]NotificationPeerErr, nerrs), workers: wk, retryCount: 3} } +// WithNPeersThrottled returns a new NotificationGroup with length of errs slice upto nerrs, +// upon Wait() errors are returned collected from all tasks, optionally allows for X workers +// only "per" parallel task. +func WithNPeersThrottled(nerrs, wks int) *NotificationGroup { + if nerrs <= 0 { + nerrs = 1 + } + wk, _ := workers.New(wks) + return &NotificationGroup{errs: make([]NotificationPeerErr, nerrs), workers: wk, retryCount: 3} +} + // WithRetries sets the retry count for all function calls from the Go method. func (g *NotificationGroup) WithRetries(retryCount int) *NotificationGroup { if g != nil { @@ -366,7 +377,7 @@ func (sys *NotificationSys) VerifyBinary(ctx context.Context, u *url.URL, sha256 maxWorkers = len(sys.peerClients) } - ng := WithNPeers(maxWorkers) + ng := WithNPeersThrottled(len(sys.peerClients), maxWorkers) for idx, client := range sys.peerClients { if client == nil { continue @@ -403,7 +414,7 @@ func (sys *NotificationSys) SignalConfigReload(subSys string) []NotificationPeer } client := client ng.Go(GlobalContext, func() error { - return client.SignalService(serviceReloadDynamic, subSys, false, true) + return client.SignalService(serviceReloadDynamic, subSys, false) }, idx, *client.host) } return ng.Wait() @@ -419,14 +430,14 @@ func (sys *NotificationSys) SignalService(sig serviceSignal) []NotificationPeerE client := client ng.Go(GlobalContext, func() error { // force == true preserves the current behavior - return client.SignalService(sig, "", false, true) + return client.SignalService(sig, "", false) }, idx, *client.host) } return ng.Wait() } // SignalServiceV2 - calls signal service RPC call on all peers with v2 API -func (sys *NotificationSys) SignalServiceV2(sig serviceSignal, dryRun, force bool) []NotificationPeerErr { +func (sys *NotificationSys) SignalServiceV2(sig serviceSignal, dryRun bool) []NotificationPeerErr { ng := WithNPeers(len(sys.peerClients)) for idx, client := range sys.peerClients { if client == nil { @@ -434,7 +445,7 @@ func (sys *NotificationSys) SignalServiceV2(sig serviceSignal, dryRun, force boo } client := client ng.Go(GlobalContext, func() error { - return client.SignalService(sig, "", dryRun, force) + return client.SignalService(sig, "", dryRun) }, idx, *client.host) } return ng.Wait() @@ -1318,7 +1329,7 @@ func (sys *NotificationSys) ServiceFreeze(ctx context.Context, freeze bool) []No } client := client ng.Go(GlobalContext, func() error { - return client.SignalService(serviceSig, "", false, true) + return client.SignalService(serviceSig, "", false) }, idx, *client.host) } nerrs := ng.Wait() diff --git a/cmd/peer-rest-client.go b/cmd/peer-rest-client.go index 8bd195f6b..fcffc5bdf 100644 --- a/cmd/peer-rest-client.go +++ b/cmd/peer-rest-client.go @@ -502,11 +502,10 @@ func (client *peerRESTClient) CommitBinary(ctx context.Context) error { } // SignalService - sends signal to peer nodes. -func (client *peerRESTClient) SignalService(sig serviceSignal, subSys string, dryRun, force bool) error { +func (client *peerRESTClient) SignalService(sig serviceSignal, subSys string, dryRun bool) error { values := make(url.Values) values.Set(peerRESTSignal, strconv.Itoa(int(sig))) values.Set(peerRESTDryRun, strconv.FormatBool(dryRun)) - values.Set(peerRESTForce, strconv.FormatBool(force)) values.Set(peerRESTSubSys, subSys) respBody, err := client.call(peerRESTMethodSignalService, values, nil, -1) if err != nil { diff --git a/cmd/peer-rest-common.go b/cmd/peer-rest-common.go index 4c5030023..6a51534ee 100644 --- a/cmd/peer-rest-common.go +++ b/cmd/peer-rest-common.go @@ -108,7 +108,6 @@ const ( peerRESTStartRebalance = "start-rebalance" peerRESTMetrics = "metrics" peerRESTDryRun = "dry-run" - peerRESTForce = "force" peerRESTURL = "url" peerRESTSha256Sum = "sha256sum" diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 4cbef208d..92ea4f50c 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -844,6 +844,7 @@ func (s *peerRESTServer) VerifyBinaryHandler(w http.ResponseWriter, r *http.Requ s.writeErrorResponse(w, err) return } + sha256Sum, err := hex.DecodeString(r.Form.Get(peerRESTSha256Sum)) if err != nil { s.writeErrorResponse(w, err) @@ -851,6 +852,17 @@ func (s *peerRESTServer) VerifyBinaryHandler(w http.ResponseWriter, r *http.Requ } releaseInfo := r.Form.Get(peerRESTReleaseInfo) + lrTime, err := releaseInfoToReleaseTime(releaseInfo) + if err != nil { + s.writeErrorResponse(w, err) + return + } + + if lrTime.Sub(currentReleaseTime) <= 0 { + s.writeErrorResponse(w, fmt.Errorf("server is already running the latest version: %s", Version)) + return + } + zr, err := zstd.NewReader(r.Body) if err != nil { s.writeErrorResponse(w, err) @@ -879,16 +891,19 @@ func (s *peerRESTServer) CommitBinaryHandler(w http.ResponseWriter, r *http.Requ var errUnsupportedSignal = fmt.Errorf("unsupported signal") -func canWeRestartNode() map[string]DiskMetrics { +func waitingDrivesNode() map[string]madmin.DiskMetrics { errs := make([]error, len(globalLocalDrives)) infos := make([]DiskInfo, len(globalLocalDrives)) for i, drive := range globalLocalDrives { infos[i], errs[i] = drive.DiskInfo(GlobalContext, DiskInfoOptions{}) } - infoMaps := make(map[string]DiskMetrics) + infoMaps := make(map[string]madmin.DiskMetrics) for i := range infos { if infos[i].Metrics.TotalWaiting >= 1 && errors.Is(errs[i], errFaultyDisk) { - infoMaps[infos[i].Endpoint] = infos[i].Metrics + infoMaps[infos[i].Endpoint] = madmin.DiskMetrics{ + TotalTokens: infos[i].Metrics.TotalTokens, + TotalWaiting: infos[i].Metrics.TotalWaiting, + } } } return infoMaps @@ -916,9 +931,8 @@ func (s *peerRESTServer) SignalServiceHandler(w http.ResponseWriter, r *http.Req switch signal { case serviceRestart, serviceStop: dryRun := r.Form.Get("dry-run") == "true" // This is only supported for `restart/stop` - force := r.Form.Get("force") == "true" - waitingDisks := canWeRestartNode() + waitingDisks := waitingDrivesNode() if len(waitingDisks) > 0 { buf, err := json.Marshal(waitingDisks) if err != nil { @@ -926,10 +940,6 @@ func (s *peerRESTServer) SignalServiceHandler(w http.ResponseWriter, r *http.Req return } s.writeErrorResponse(w, errors.New(string(buf))) - // if its forced we signal the process anyway. - if !force { - return - } } if !dryRun { globalServiceSignalCh <- signal diff --git a/cmd/update.go b/cmd/update.go index 313db811e..73c22141a 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -364,6 +364,25 @@ func downloadReleaseURL(u *url.URL, timeout time.Duration, mode string) (content return string(contentBytes), nil } +func releaseInfoToReleaseTime(releaseInfo string) (releaseTime time.Time, err error) { + // Split release of style minio.RELEASE.2019-08-21T19-40-07Z. + nfields := strings.SplitN(releaseInfo, ".", 2) + if len(nfields) != 2 { + err = fmt.Errorf("Unknown release information `%s`", releaseInfo) + return releaseTime, err + } + if nfields[0] != "minio" { + err = fmt.Errorf("Unknown release `%s`", releaseInfo) + return releaseTime, err + } + + releaseTime, err = releaseTagToReleaseTime(nfields[1]) + if err != nil { + err = fmt.Errorf("Unknown release tag format. %w", err) + } + return releaseTime, err +} + // parseReleaseData - parses release info file content fetched from // official minio download server. // @@ -396,22 +415,7 @@ func parseReleaseData(data string) (sha256Sum []byte, releaseTime time.Time, rel releaseInfo = fields[1] - // Split release of style minio.RELEASE.2019-08-21T19-40-07Z. - nfields := strings.SplitN(releaseInfo, ".", 2) - if len(nfields) != 2 { - err = fmt.Errorf("Unknown release information `%s`", releaseInfo) - return sha256Sum, releaseTime, releaseInfo, err - } - if nfields[0] != "minio" { - err = fmt.Errorf("Unknown release `%s`", releaseInfo) - return sha256Sum, releaseTime, releaseInfo, err - } - - releaseTime, err = releaseTagToReleaseTime(nfields[1]) - if err != nil { - err = fmt.Errorf("Unknown release tag format. %w", err) - } - + releaseTime, err = releaseInfoToReleaseTime(releaseInfo) return sha256Sum, releaseTime, releaseInfo, err } diff --git a/go.mod b/go.mod index 13e03d00d..b9252d8e0 100644 --- a/go.mod +++ b/go.mod @@ -51,7 +51,7 @@ require ( github.com/minio/dperf v0.5.3 github.com/minio/highwayhash v1.0.2 github.com/minio/kes-go v0.2.0 - github.com/minio/madmin-go/v3 v3.0.40-0.20240119195114-66fab65f959f + github.com/minio/madmin-go/v3 v3.0.40 github.com/minio/minio-go/v7 v7.0.66 github.com/minio/mux v1.9.0 github.com/minio/pkg/v2 v2.0.8 diff --git a/go.sum b/go.sum index c9a8e09fb..c294f8dde 100644 --- a/go.sum +++ b/go.sum @@ -443,8 +443,8 @@ github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= github.com/minio/kes-go v0.2.0 h1:HA33arq9s3MErbsj3PAXFVfFo4U4yw7lTKQ5kWFrpCA= github.com/minio/kes-go v0.2.0/go.mod h1:VorHLaIYis9/MxAHAtXN4d8PUMNKhIxTIlvFt0hBOEo= -github.com/minio/madmin-go/v3 v3.0.40-0.20240119195114-66fab65f959f h1:clgtVs6KUJTtKb4Xghq35gyJM/m10IwEmgfb4Do6BuY= -github.com/minio/madmin-go/v3 v3.0.40-0.20240119195114-66fab65f959f/go.mod h1:4QN2NftLSV7MdlT50dkrenOMmNVHluxTvlqJou3hte8= +github.com/minio/madmin-go/v3 v3.0.40 h1:e4bfbRYgzv+Sl13ek6CM1la0PMLfxfgmKSLjBBDil0I= +github.com/minio/madmin-go/v3 v3.0.40/go.mod h1:4QN2NftLSV7MdlT50dkrenOMmNVHluxTvlqJou3hte8= github.com/minio/mc v0.0.0-20240111054932-d4305a5bf95e h1:vKnv5aBTcAAnDGYeJW/SPieXCerp/7MIYxuEUYt7iOE= github.com/minio/mc v0.0.0-20240111054932-d4305a5bf95e/go.mod h1:wFVJTmLJniMFDkcvPP0h/KvCxK+MiA2rc6q7KUefN28= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=