mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
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:
parent
d0283ff354
commit
88837fb753
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -108,7 +108,6 @@ const (
|
||||
peerRESTStartRebalance = "start-rebalance"
|
||||
peerRESTMetrics = "metrics"
|
||||
peerRESTDryRun = "dry-run"
|
||||
peerRESTForce = "force"
|
||||
|
||||
peerRESTURL = "url"
|
||||
peerRESTSha256Sum = "sha256sum"
|
||||
|
@ -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
|
||||
|
@ -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.<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
|
||||
// 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.<hotfix>
|
||||
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
|
||||
}
|
||||
|
||||
|
2
go.mod
2
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
|
||||
|
4
go.sum
4
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=
|
||||
|
Loading…
Reference in New Issue
Block a user