add new update v2 that updates per node, allows idempotent behavior (#18859)

add new update v2 that updates per node, allows idempotent behavior

new API ensures that

- binary is correct and can be downloaded checksummed verified
- committed to actual path
- restart returns back the relevant waiting drives
This commit is contained in:
Harshavardhana 2024-01-26 08:40:13 -08:00 committed by GitHub
parent d0283ff354
commit 88837fb753
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 286 additions and 66 deletions

View File

@ -79,6 +79,208 @@ const (
mgmtForceStop = "forceStop" 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} // ServerUpdateHandler - POST /minio/admin/v3/update?updateURL={updateURL}
// ---------- // ----------
// updates all minio servers and restarts them gracefully. // updates all minio servers and restarts them gracefully.
@ -90,7 +292,7 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req
return return
} }
if globalInplaceUpdateDisabled { if globalInplaceUpdateDisabled || currentReleaseTime.IsZero() {
// if MINIO_UPDATE=off - inplace update is disabled, mostly in containers. // if MINIO_UPDATE=off - inplace update is disabled, mostly in containers.
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL)
return return
@ -124,14 +326,7 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req
return return
} }
u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo if lrTime.Sub(currentReleaseTime) <= 0 {
crTime, err := GetCurrentReleaseTime()
if err != nil {
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
return
}
if lrTime.Sub(crTime) <= 0 {
updateStatus := madmin.ServerUpdateStatus{ updateStatus := madmin.ServerUpdateStatus{
CurrentVersion: Version, CurrentVersion: Version,
UpdatedVersion: Version, UpdatedVersion: Version,
@ -148,6 +343,8 @@ func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Req
return return
} }
u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo
// Download Binary Once // Download Binary Once
binC, bin, err := downloadBinary(u, mode) binC, bin, err := downloadBinary(u, mode)
if err != nil { if err != nil {
@ -281,12 +478,11 @@ func (a adminAPIHandlers) ServiceHandler(w http.ResponseWriter, r *http.Request)
type servicePeerResult struct { type servicePeerResult struct {
Host string `json:"host"` Host string `json:"host"`
Err string `json:"err,omitempty"` Err string `json:"err,omitempty"`
WaitingDrives map[string]DiskMetrics `json:"waitingDrives,omitempty"` WaitingDrives map[string]madmin.DiskMetrics `json:"waitingDrives,omitempty"`
} }
type serviceResult struct { type serviceResult struct {
Action madmin.ServiceAction `json:"action"` Action madmin.ServiceAction `json:"action"`
Forced bool `json:"forced"`
DryRun bool `json:"dryRun"` DryRun bool `json:"dryRun"`
Results []servicePeerResult `json:"results,omitempty"` Results []servicePeerResult `json:"results,omitempty"`
} }
@ -310,7 +506,6 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques
vars := mux.Vars(r) vars := mux.Vars(r)
action := vars["action"] action := vars["action"]
dryRun := r.Form.Get("dry-run") == "true" dryRun := r.Form.Get("dry-run") == "true"
force := r.Form.Get("force") == "true"
var serviceSig serviceSignal var serviceSig serviceSignal
act := madmin.ServiceAction(action) 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. // 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))} srvResult := serviceResult{Action: act, Results: make([]servicePeerResult, 0, len(nerrs))}
process := act == madmin.ServiceActionRestart || act == madmin.ServiceActionStop process := act == madmin.ServiceActionRestart || act == madmin.ServiceActionStop
var trigger bool
if process { if process {
localhost := globalLocalNodeName localhost := globalLocalNodeName
if globalLocalNodeName == "" { if globalLocalNodeName == "" {
localhost = "127.0.0.1" localhost = "127.0.0.1"
} }
waitingDrives := canWeRestartNode() waitingDrives := waitingDrivesNode()
srvResult.Results = append(srvResult.Results, servicePeerResult{ srvResult.Results = append(srvResult.Results, servicePeerResult{
Host: localhost, Host: localhost,
WaitingDrives: waitingDrives, WaitingDrives: waitingDrives,
}) })
trigger = len(waitingDrives) == 0 || force
} }
for _, nerr := range nerrs { for _, nerr := range nerrs {
if nerr.Err != nil && process { if nerr.Err != nil && process {
waitingDrives := map[string]DiskMetrics{} waitingDrives := map[string]madmin.DiskMetrics{}
jerr := json.Unmarshal([]byte(nerr.Err.Error()), &waitingDrives) jerr := json.Unmarshal([]byte(nerr.Err.Error()), &waitingDrives)
if jerr == nil { if jerr == nil {
srvResult.Results = append(srvResult.Results, servicePeerResult{ 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 srvResult.DryRun = dryRun
buf, err := json.Marshal(srvResult) buf, err := json.Marshal(srvResult)
@ -401,7 +593,7 @@ func (a adminAPIHandlers) ServiceV2Handler(w http.ResponseWriter, r *http.Reques
case serviceUnFreeze: case serviceUnFreeze:
unfreezeServices() unfreezeServices()
case serviceRestart, serviceStop: case serviceRestart, serviceStop:
if !dryRun && trigger { if !dryRun {
globalServiceSignalCh <- serviceSig globalServiceSignalCh <- serviceSig
} }
} }

View File

@ -157,10 +157,13 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
// Restart and stop MinIO service type=2 // Restart and stop MinIO service type=2
adminRouter.Methods(http.MethodPost).Path(adminVersion+"/service").HandlerFunc(adminMiddleware(adminAPI.ServiceV2Handler, traceAllFlag)).Queries("action", "{action:.*}", "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:.*}") 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:.*}") adminRouter.Methods(http.MethodPost).Path(adminVersion+"/update").HandlerFunc(adminMiddleware(adminAPI.ServerUpdateHandler, traceAllFlag)).Queries("updateURL", "{updateURL:.*}")
// Info operations // Info operations

View File

@ -69,6 +69,8 @@ import (
// serverDebugLog will enable debug printing // serverDebugLog will enable debug printing
var serverDebugLog = env.Get("_MINIO_SERVER_DEBUG", config.EnableOff) == config.EnableOn var serverDebugLog = env.Get("_MINIO_SERVER_DEBUG", config.EnableOff) == config.EnableOn
var currentReleaseTime time.Time
func init() { func init() {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
if mousetrap.StartedByExplorer() { if mousetrap.StartedByExplorer() {
@ -109,6 +111,8 @@ func init() {
// another way to look at this is we are turning off retries. // another way to look at this is we are turning off retries.
minio.MaxRetry = 1 minio.MaxRetry = 1
madmin.MaxRetry = 1 madmin.MaxRetry = 1
currentReleaseTime, _ = GetCurrentReleaseTime()
} }
const consolePrefix = "CONSOLE_" const consolePrefix = "CONSOLE_"
@ -293,9 +297,7 @@ func checkUpdate(mode string) {
return return
} }
// Its OK to ignore any errors during doUpdate() here. if currentReleaseTime.IsZero() {
crTime, err := GetCurrentReleaseTime()
if err != nil {
return return
} }
@ -306,8 +308,8 @@ func checkUpdate(mode string) {
var older time.Duration var older time.Duration
var downloadURL string var downloadURL string
if lrTime.After(crTime) { if lrTime.After(currentReleaseTime) {
older = lrTime.Sub(crTime) older = lrTime.Sub(currentReleaseTime)
downloadURL = getDownloadURL(releaseTimeToReleaseTag(lrTime)) downloadURL = getDownloadURL(releaseTimeToReleaseTag(lrTime))
} }
@ -316,7 +318,7 @@ func checkUpdate(mode string) {
return 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) { func newConfigDir(dir string, dirSet bool, getDefaultDir func() string) (*ConfigDir, error) {

View File

@ -76,6 +76,17 @@ func WithNPeers(nerrs int) *NotificationGroup {
return &NotificationGroup{errs: make([]NotificationPeerErr, nerrs), workers: wk, retryCount: 3} 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. // WithRetries sets the retry count for all function calls from the Go method.
func (g *NotificationGroup) WithRetries(retryCount int) *NotificationGroup { func (g *NotificationGroup) WithRetries(retryCount int) *NotificationGroup {
if g != nil { if g != nil {
@ -366,7 +377,7 @@ func (sys *NotificationSys) VerifyBinary(ctx context.Context, u *url.URL, sha256
maxWorkers = len(sys.peerClients) maxWorkers = len(sys.peerClients)
} }
ng := WithNPeers(maxWorkers) ng := WithNPeersThrottled(len(sys.peerClients), maxWorkers)
for idx, client := range sys.peerClients { for idx, client := range sys.peerClients {
if client == nil { if client == nil {
continue continue
@ -403,7 +414,7 @@ func (sys *NotificationSys) SignalConfigReload(subSys string) []NotificationPeer
} }
client := client client := client
ng.Go(GlobalContext, func() error { ng.Go(GlobalContext, func() error {
return client.SignalService(serviceReloadDynamic, subSys, false, true) return client.SignalService(serviceReloadDynamic, subSys, false)
}, idx, *client.host) }, idx, *client.host)
} }
return ng.Wait() return ng.Wait()
@ -419,14 +430,14 @@ func (sys *NotificationSys) SignalService(sig serviceSignal) []NotificationPeerE
client := client client := client
ng.Go(GlobalContext, func() error { ng.Go(GlobalContext, func() error {
// force == true preserves the current behavior // force == true preserves the current behavior
return client.SignalService(sig, "", false, true) return client.SignalService(sig, "", false)
}, idx, *client.host) }, idx, *client.host)
} }
return ng.Wait() return ng.Wait()
} }
// SignalServiceV2 - calls signal service RPC call on all peers with v2 API // 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)) ng := WithNPeers(len(sys.peerClients))
for idx, client := range sys.peerClients { for idx, client := range sys.peerClients {
if client == nil { if client == nil {
@ -434,7 +445,7 @@ func (sys *NotificationSys) SignalServiceV2(sig serviceSignal, dryRun, force boo
} }
client := client client := client
ng.Go(GlobalContext, func() error { ng.Go(GlobalContext, func() error {
return client.SignalService(sig, "", dryRun, force) return client.SignalService(sig, "", dryRun)
}, idx, *client.host) }, idx, *client.host)
} }
return ng.Wait() return ng.Wait()
@ -1318,7 +1329,7 @@ func (sys *NotificationSys) ServiceFreeze(ctx context.Context, freeze bool) []No
} }
client := client client := client
ng.Go(GlobalContext, func() error { ng.Go(GlobalContext, func() error {
return client.SignalService(serviceSig, "", false, true) return client.SignalService(serviceSig, "", false)
}, idx, *client.host) }, idx, *client.host)
} }
nerrs := ng.Wait() nerrs := ng.Wait()

View File

@ -502,11 +502,10 @@ func (client *peerRESTClient) CommitBinary(ctx context.Context) error {
} }
// SignalService - sends signal to peer nodes. // 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 := make(url.Values)
values.Set(peerRESTSignal, strconv.Itoa(int(sig))) values.Set(peerRESTSignal, strconv.Itoa(int(sig)))
values.Set(peerRESTDryRun, strconv.FormatBool(dryRun)) values.Set(peerRESTDryRun, strconv.FormatBool(dryRun))
values.Set(peerRESTForce, strconv.FormatBool(force))
values.Set(peerRESTSubSys, subSys) values.Set(peerRESTSubSys, subSys)
respBody, err := client.call(peerRESTMethodSignalService, values, nil, -1) respBody, err := client.call(peerRESTMethodSignalService, values, nil, -1)
if err != nil { if err != nil {

View File

@ -108,7 +108,6 @@ const (
peerRESTStartRebalance = "start-rebalance" peerRESTStartRebalance = "start-rebalance"
peerRESTMetrics = "metrics" peerRESTMetrics = "metrics"
peerRESTDryRun = "dry-run" peerRESTDryRun = "dry-run"
peerRESTForce = "force"
peerRESTURL = "url" peerRESTURL = "url"
peerRESTSha256Sum = "sha256sum" peerRESTSha256Sum = "sha256sum"

View File

@ -844,6 +844,7 @@ func (s *peerRESTServer) VerifyBinaryHandler(w http.ResponseWriter, r *http.Requ
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
return return
} }
sha256Sum, err := hex.DecodeString(r.Form.Get(peerRESTSha256Sum)) sha256Sum, err := hex.DecodeString(r.Form.Get(peerRESTSha256Sum))
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
@ -851,6 +852,17 @@ func (s *peerRESTServer) VerifyBinaryHandler(w http.ResponseWriter, r *http.Requ
} }
releaseInfo := r.Form.Get(peerRESTReleaseInfo) 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) zr, err := zstd.NewReader(r.Body)
if err != nil { if err != nil {
s.writeErrorResponse(w, err) s.writeErrorResponse(w, err)
@ -879,16 +891,19 @@ func (s *peerRESTServer) CommitBinaryHandler(w http.ResponseWriter, r *http.Requ
var errUnsupportedSignal = fmt.Errorf("unsupported signal") var errUnsupportedSignal = fmt.Errorf("unsupported signal")
func canWeRestartNode() map[string]DiskMetrics { func waitingDrivesNode() map[string]madmin.DiskMetrics {
errs := make([]error, len(globalLocalDrives)) errs := make([]error, len(globalLocalDrives))
infos := make([]DiskInfo, len(globalLocalDrives)) infos := make([]DiskInfo, len(globalLocalDrives))
for i, drive := range globalLocalDrives { for i, drive := range globalLocalDrives {
infos[i], errs[i] = drive.DiskInfo(GlobalContext, DiskInfoOptions{}) infos[i], errs[i] = drive.DiskInfo(GlobalContext, DiskInfoOptions{})
} }
infoMaps := make(map[string]DiskMetrics) infoMaps := make(map[string]madmin.DiskMetrics)
for i := range infos { for i := range infos {
if infos[i].Metrics.TotalWaiting >= 1 && errors.Is(errs[i], errFaultyDisk) { 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 return infoMaps
@ -916,9 +931,8 @@ func (s *peerRESTServer) SignalServiceHandler(w http.ResponseWriter, r *http.Req
switch signal { switch signal {
case serviceRestart, serviceStop: case serviceRestart, serviceStop:
dryRun := r.Form.Get("dry-run") == "true" // This is only supported for `restart/stop` 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 { if len(waitingDisks) > 0 {
buf, err := json.Marshal(waitingDisks) buf, err := json.Marshal(waitingDisks)
if err != nil { if err != nil {
@ -926,10 +940,6 @@ func (s *peerRESTServer) SignalServiceHandler(w http.ResponseWriter, r *http.Req
return return
} }
s.writeErrorResponse(w, errors.New(string(buf))) s.writeErrorResponse(w, errors.New(string(buf)))
// if its forced we signal the process anyway.
if !force {
return
}
} }
if !dryRun { if !dryRun {
globalServiceSignalCh <- signal globalServiceSignalCh <- signal

View File

@ -364,6 +364,25 @@ func downloadReleaseURL(u *url.URL, timeout time.Duration, mode string) (content
return string(contentBytes), nil return string(contentBytes), nil
} }
func releaseInfoToReleaseTime(releaseInfo string) (releaseTime time.Time, err error) {
// Split release of style minio.RELEASE.2019-08-21T19-40-07Z.<hotfix>
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 // parseReleaseData - parses release info file content fetched from
// official minio download server. // official minio download server.
// //
@ -396,22 +415,7 @@ func parseReleaseData(data string) (sha256Sum []byte, releaseTime time.Time, rel
releaseInfo = fields[1] releaseInfo = fields[1]
// Split release of style minio.RELEASE.2019-08-21T19-40-07Z.<hotfix> releaseTime, err = releaseInfoToReleaseTime(releaseInfo)
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)
}
return sha256Sum, releaseTime, releaseInfo, err return sha256Sum, releaseTime, releaseInfo, err
} }

2
go.mod
View File

@ -51,7 +51,7 @@ require (
github.com/minio/dperf v0.5.3 github.com/minio/dperf v0.5.3
github.com/minio/highwayhash v1.0.2 github.com/minio/highwayhash v1.0.2
github.com/minio/kes-go v0.2.0 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/minio-go/v7 v7.0.66
github.com/minio/mux v1.9.0 github.com/minio/mux v1.9.0
github.com/minio/pkg/v2 v2.0.8 github.com/minio/pkg/v2 v2.0.8

4
go.sum
View File

@ -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/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 h1:HA33arq9s3MErbsj3PAXFVfFo4U4yw7lTKQ5kWFrpCA=
github.com/minio/kes-go v0.2.0/go.mod h1:VorHLaIYis9/MxAHAtXN4d8PUMNKhIxTIlvFt0hBOEo= 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 h1:e4bfbRYgzv+Sl13ek6CM1la0PMLfxfgmKSLjBBDil0I=
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/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 h1:vKnv5aBTcAAnDGYeJW/SPieXCerp/7MIYxuEUYt7iOE=
github.com/minio/mc v0.0.0-20240111054932-d4305a5bf95e/go.mod h1:wFVJTmLJniMFDkcvPP0h/KvCxK+MiA2rc6q7KUefN28= 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= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=