| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	"crypto/subtle" | 
					
						
							| 
									
										
										
										
											2020-02-02 09:45:29 +08:00
										 |  |  | 	"crypto/tls" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 	"math/rand" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2019-10-23 13:59:13 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/config" | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/crypto" | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2020-03-21 06:13:41 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger/message/log" | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/dsync" | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	iampolicy "github.com/minio/minio/pkg/iam/policy" | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/kms" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 	trace "github.com/minio/minio/pkg/trace" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	maxEConfigJSONSize = 262272 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | // Only valid query params for mgmt admin APIs.
 | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2020-09-24 23:40:21 +08:00
										 |  |  | 	mgmtBucket      = "bucket" | 
					
						
							|  |  |  | 	mgmtPrefix      = "prefix" | 
					
						
							|  |  |  | 	mgmtClientToken = "clientToken" | 
					
						
							|  |  |  | 	mgmtForceStart  = "forceStart" | 
					
						
							|  |  |  | 	mgmtForceStop   = "forceStop" | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 00:51:11 +08:00
										 |  |  | func updateServer(u *url.URL, sha256Sum []byte, lrTime time.Time, releaseInfo string, mode string) (us madmin.ServerUpdateStatus, err error) { | 
					
						
							|  |  |  | 	if err = doUpdate(u, lrTime, sha256Sum, releaseInfo, mode); err != nil { | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		return us, err | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	us.CurrentVersion = Version | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	us.UpdatedVersion = lrTime.Format(minioReleaseTagTimeLayout) | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	return us, nil | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // ServerUpdateHandler - POST /minio/admin/v3/update?updateURL={updateURL}
 | 
					
						
							| 
									
										
										
										
											2016-12-21 10:49:48 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | // updates all minio servers and restarts them gracefully.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ServerUpdateHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ServerUpdate") | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ServerUpdateAdminAction) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	if globalInplaceUpdateDisabled { | 
					
						
							| 
									
										
										
										
											2020-10-25 13:39:44 +08:00
										 |  |  | 		// if MINIO_UPDATE=off - inplace update is disabled, mostly in containers.
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	updateURL := vars["updateURL"] | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	mode := getMinioMode() | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	if updateURL == "" { | 
					
						
							|  |  |  | 		updateURL = minioReleaseInfoURL | 
					
						
							|  |  |  | 		if runtime.GOOS == globalWindowsOSName { | 
					
						
							|  |  |  | 			updateURL = minioReleaseWindowsInfoURL | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 00:51:11 +08:00
										 |  |  | 	sha256Sum, lrTime, releaseInfo, err := parseReleaseData(content) | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 00:51:11 +08:00
										 |  |  | 	u.Path = path.Dir(u.Path) + SlashSeparator + releaseInfo | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	crTime, err := GetCurrentReleaseTime() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-13 06:18:59 +08:00
										 |  |  | 	if lrTime.Sub(crTime) <= 0 { | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 		updateStatus := madmin.ServerUpdateStatus{ | 
					
						
							|  |  |  | 			CurrentVersion: Version, | 
					
						
							|  |  |  | 			UpdatedVersion: Version, | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 		// Marshal API response
 | 
					
						
							|  |  |  | 		jsonBytes, err := json.Marshal(updateStatus) | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 		writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 00:51:11 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.ServerUpdate(ctx, u, sha256Sum, lrTime, releaseInfo) { | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 			err = fmt.Errorf("Server update failed, please do not restart the servers yet: failed with %w", nerr.Err) | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 00:51:11 +08:00
										 |  |  | 	updateStatus, err := updateServer(u, sha256Sum, lrTime, releaseInfo, mode) | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 		err = fmt.Errorf("Server update failed, please do not restart the servers yet: failed with %w", err) | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2016-12-21 10:05:25 +08:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	// Marshal API response
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(updateStatus) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 	// Notify all other MinIO peers signal service.
 | 
					
						
							|  |  |  | 	for _, nerr := range globalNotificationSys.SignalService(serviceRestart) { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	globalServiceSignalCh <- serviceRestart | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | // ServiceHandler - POST /minio/admin/v3/service?action={action}
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // restarts/stops minio server gracefully. In a distributed setup,
 | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | func (a adminAPIHandlers) ServiceHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "Service") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	action := vars["action"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var serviceSig serviceSignal | 
					
						
							|  |  |  | 	switch madmin.ServiceAction(action) { | 
					
						
							|  |  |  | 	case madmin.ServiceActionRestart: | 
					
						
							|  |  |  | 		serviceSig = serviceRestart | 
					
						
							|  |  |  | 	case madmin.ServiceActionStop: | 
					
						
							|  |  |  | 		serviceSig = serviceStop | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 		logger.LogIf(ctx, fmt.Errorf("Unrecognized service action %s requested", action), logger.Application) | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 	var objectAPI ObjectLayer | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	switch serviceSig { | 
					
						
							|  |  |  | 	case serviceRestart: | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 		objectAPI, _ = validateAdminReq(ctx, w, r, iampolicy.ServiceRestartAdminAction) | 
					
						
							| 
									
										
										
										
											2020-12-05 01:32:35 +08:00
										 |  |  | 	case serviceStop: | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 		objectAPI, _ = validateAdminReq(ctx, w, r, iampolicy.ServiceStopAdminAction) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  | 	// Notify all other MinIO peers signal service.
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.SignalService(serviceSig) { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	// Reply to the client before restarting, stopping MinIO server.
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	globalServiceSignalCh <- serviceSig | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | // ServerProperties holds some server information such as, version, region
 | 
					
						
							|  |  |  | // uptime, etc..
 | 
					
						
							|  |  |  | type ServerProperties struct { | 
					
						
							| 
									
										
										
										
											2019-12-12 09:56:02 +08:00
										 |  |  | 	Uptime       int64    `json:"uptime"` | 
					
						
							|  |  |  | 	Version      string   `json:"version"` | 
					
						
							|  |  |  | 	CommitID     string   `json:"commitID"` | 
					
						
							|  |  |  | 	DeploymentID string   `json:"deploymentID"` | 
					
						
							|  |  |  | 	Region       string   `json:"region"` | 
					
						
							|  |  |  | 	SQSARN       []string `json:"sqsARN"` | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerConnStats holds transferred bytes from/to the server
 | 
					
						
							|  |  |  | type ServerConnStats struct { | 
					
						
							|  |  |  | 	TotalInputBytes  uint64 `json:"transferred"` | 
					
						
							|  |  |  | 	TotalOutputBytes uint64 `json:"received"` | 
					
						
							|  |  |  | 	Throughput       uint64 `json:"throughput,omitempty"` | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	S3InputBytes     uint64 `json:"transferredS3"` | 
					
						
							|  |  |  | 	S3OutputBytes    uint64 `json:"receivedS3"` | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // ServerHTTPAPIStats holds total number of HTTP operations from/to the server,
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | // including the average duration the call was spent.
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | type ServerHTTPAPIStats struct { | 
					
						
							|  |  |  | 	APIStats map[string]int `json:"apiStats"` | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerHTTPStats holds all type of http operations performed to/from the server
 | 
					
						
							|  |  |  | // including their average execution time.
 | 
					
						
							|  |  |  | type ServerHTTPStats struct { | 
					
						
							| 
									
										
										
										
											2021-03-31 14:19:36 +08:00
										 |  |  | 	S3RequestsInQueue      int32              `json:"s3RequestsInQueue"` | 
					
						
							|  |  |  | 	CurrentS3Requests      ServerHTTPAPIStats `json:"currentS3Requests"` | 
					
						
							|  |  |  | 	TotalS3Requests        ServerHTTPAPIStats `json:"totalS3Requests"` | 
					
						
							|  |  |  | 	TotalS3Errors          ServerHTTPAPIStats `json:"totalS3Errors"` | 
					
						
							|  |  |  | 	TotalS3Canceled        ServerHTTPAPIStats `json:"totalS3Canceled"` | 
					
						
							|  |  |  | 	TotalS3RejectedAuth    uint64             `json:"totalS3RejectedAuth"` | 
					
						
							|  |  |  | 	TotalS3RejectedTime    uint64             `json:"totalS3RejectedTime"` | 
					
						
							|  |  |  | 	TotalS3RejectedHeader  uint64             `json:"totalS3RejectedHeader"` | 
					
						
							|  |  |  | 	TotalS3RejectedInvalid uint64             `json:"totalS3RejectedInvalid"` | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // StorageInfoHandler - GET /minio/admin/v3/storageinfo
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // Get server information
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) StorageInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "StorageInfo") | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 10:47:52 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.StorageInfoAdminAction) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-29 04:03:04 +08:00
										 |  |  | 	// ignores any errors here.
 | 
					
						
							| 
									
										
										
										
											2021-01-05 01:42:09 +08:00
										 |  |  | 	storageInfo, _ := objectAPI.StorageInfo(ctx) | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 	// Collect any disk healing.
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	healing, _ := getAggregatedBackgroundHealState(ctx, nil) | 
					
						
							| 
									
										
										
										
											2020-09-29 10:39:32 +08:00
										 |  |  | 	healDisks := make(map[string]struct{}, len(healing.HealDisks)) | 
					
						
							|  |  |  | 	for _, disk := range healing.HealDisks { | 
					
						
							|  |  |  | 		healDisks[disk] = struct{}{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// find all disks which belong to each respective endpoints
 | 
					
						
							|  |  |  | 	for i, disk := range storageInfo.Disks { | 
					
						
							|  |  |  | 		if _, ok := healDisks[disk.Endpoint]; ok { | 
					
						
							|  |  |  | 			storageInfo.Disks[i].Healing = true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-23 12:01:14 +08:00
										 |  |  | 	// Marshal API response
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(storageInfo) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Reply with storage information (across nodes in a
 | 
					
						
							|  |  |  | 	// distributed setup) as json.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // DataUsageInfoHandler - GET /minio/admin/v3/datausage
 | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // Get server/cluster data usage info
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) DataUsageInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "DataUsageInfo") | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 10:47:52 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.DataUsageInfoAdminAction) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dataUsageInfo, err := loadDataUsageFromBackend(ctx, objectAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dataUsageInfoJSON, err := json.Marshal(dataUsageInfo) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-22 06:07:49 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, dataUsageInfoJSON) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | func lriToLockEntry(l lockRequesterInfo, resource, server string) *madmin.LockEntry { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	entry := &madmin.LockEntry{ | 
					
						
							|  |  |  | 		Timestamp:  l.Timestamp, | 
					
						
							|  |  |  | 		Resource:   resource, | 
					
						
							|  |  |  | 		ServerList: []string{server}, | 
					
						
							|  |  |  | 		Source:     l.Source, | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		Owner:      l.Owner, | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		ID:         l.UID, | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | 		Quorum:     l.Quorum, | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	if l.Writer { | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 		entry.Type = "WRITE" | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 		entry.Type = "READ" | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return entry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | func topLockEntries(peerLocks []*PeerLocks, stale bool) madmin.LockEntries { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	entryMap := make(map[string]*madmin.LockEntry) | 
					
						
							|  |  |  | 	for _, peerLock := range peerLocks { | 
					
						
							|  |  |  | 		if peerLock == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 		for k, v := range peerLock.Locks { | 
					
						
							|  |  |  | 			for _, lockReqInfo := range v { | 
					
						
							|  |  |  | 				if val, ok := entryMap[lockReqInfo.UID]; ok { | 
					
						
							|  |  |  | 					val.ServerList = append(val.ServerList, peerLock.Addr) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					entryMap[lockReqInfo.UID] = lriToLockEntry(lockReqInfo, k, peerLock.Addr) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	var lockEntries madmin.LockEntries | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	for _, v := range entryMap { | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 		if stale { | 
					
						
							|  |  |  | 			lockEntries = append(lockEntries, *v) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(v.ServerList) >= v.Quorum { | 
					
						
							|  |  |  | 			lockEntries = append(lockEntries, *v) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	sort.Sort(lockEntries) | 
					
						
							|  |  |  | 	return lockEntries | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PeerLocks holds server information result of one node
 | 
					
						
							|  |  |  | type PeerLocks struct { | 
					
						
							|  |  |  | 	Addr  string | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	Locks map[string][]lockRequesterInfo | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | // ForceUnlockHandler force unlocks requested resource
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ForceUnlockHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ForceUnlock") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ForceUnlockAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	z, ok := objectAPI.(*erasureServerPools) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var args dsync.LockArgs | 
					
						
							|  |  |  | 	lockersMap := make(map[string]dsync.NetLocker) | 
					
						
							|  |  |  | 	for _, path := range strings.Split(vars["paths"], ",") { | 
					
						
							|  |  |  | 		if path == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		args.Resources = append(args.Resources, path) | 
					
						
							|  |  |  | 		lockers, _ := z.serverPools[0].getHashedSet(path).getLockers() | 
					
						
							|  |  |  | 		for _, locker := range lockers { | 
					
						
							|  |  |  | 			if locker != nil { | 
					
						
							|  |  |  | 				lockersMap[locker.String()] = locker | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, locker := range lockersMap { | 
					
						
							|  |  |  | 		locker.ForceUnlock(ctx, args) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | // TopLocksHandler Get list of locks in use
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "TopLocks") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 10:47:52 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.TopLocksAdminAction) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 	count := 10 // by default list only top 10 entries
 | 
					
						
							|  |  |  | 	if countStr := r.URL.Query().Get("count"); countStr != "" { | 
					
						
							|  |  |  | 		var err error | 
					
						
							|  |  |  | 		count, err = strconv.Atoi(countStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	stale := r.URL.Query().Get("stale") == "true" // list also stale locks
 | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	peerLocks := globalNotificationSys.GetLocks(ctx, r) | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | 	topLocks := topLockEntries(peerLocks, stale) | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal API response upto requested count.
 | 
					
						
							|  |  |  | 	if len(topLocks) > count && count > 0 { | 
					
						
							|  |  |  | 		topLocks = topLocks[:count] | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(topLocks) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Reply with storage information (across nodes in a
 | 
					
						
							|  |  |  | 	// distributed setup) as json.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | // StartProfilingResult contains the status of the starting
 | 
					
						
							|  |  |  | // profiling action in a given server
 | 
					
						
							|  |  |  | type StartProfilingResult struct { | 
					
						
							|  |  |  | 	NodeName string `json:"nodeName"` | 
					
						
							|  |  |  | 	Success  bool   `json:"success"` | 
					
						
							|  |  |  | 	Error    string `json:"error"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // StartProfilingHandler - POST /minio/admin/v3/profiling/start?profilerType={profilerType}
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2018-09-27 12:02:05 +08:00
										 |  |  | // Enable server profiling
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | func (a adminAPIHandlers) StartProfilingHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "StartProfiling") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 	// Validate request signature.
 | 
					
						
							|  |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ProfilingAdminAction, "") | 
					
						
							|  |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if globalNotificationSys == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	profiles := strings.Split(vars["profilerType"], ",") | 
					
						
							| 
									
										
										
										
											2021-03-27 02:37:58 +08:00
										 |  |  | 	thisAddr, err := xnet.ParseHost(globalLocalNodeName) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	globalProfilerMu.Lock() | 
					
						
							|  |  |  | 	defer globalProfilerMu.Unlock() | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	if globalProfiler == nil { | 
					
						
							|  |  |  | 		globalProfiler = make(map[string]minioProfiler, 10) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Stop profiler of all types if already running
 | 
					
						
							|  |  |  | 	for k, v := range globalProfiler { | 
					
						
							|  |  |  | 		for _, p := range profiles { | 
					
						
							|  |  |  | 			if p == k { | 
					
						
							|  |  |  | 				v.Stop() | 
					
						
							|  |  |  | 				delete(globalProfiler, k) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start profiling on remote servers.
 | 
					
						
							|  |  |  | 	var hostErrs []NotificationPeerErr | 
					
						
							|  |  |  | 	for _, profiler := range profiles { | 
					
						
							|  |  |  | 		hostErrs = append(hostErrs, globalNotificationSys.StartProfiling(profiler)...) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Start profiling locally as well.
 | 
					
						
							|  |  |  | 		prof, err := startProfiler(profiler) | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			hostErrs = append(hostErrs, NotificationPeerErr{ | 
					
						
							|  |  |  | 				Host: *thisAddr, | 
					
						
							|  |  |  | 				Err:  err, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 			globalProfiler[profiler] = prof | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 			hostErrs = append(hostErrs, NotificationPeerErr{ | 
					
						
							|  |  |  | 				Host: *thisAddr, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var startProfilingResult []StartProfilingResult | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, nerr := range hostErrs { | 
					
						
							|  |  |  | 		result := StartProfilingResult{NodeName: nerr.Host.String()} | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			result.Error = nerr.Err.Error() | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 			result.Success = true | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		startProfilingResult = append(startProfilingResult, result) | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create JSON result and send it to the client
 | 
					
						
							|  |  |  | 	startProfilingResultInBytes, err := json.Marshal(startProfilingResult) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-11 09:19:58 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, startProfilingResultInBytes) | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // dummyFileInfo represents a dummy representation of a profile data file
 | 
					
						
							|  |  |  | // present only in memory, it helps to generate the zip stream.
 | 
					
						
							|  |  |  | type dummyFileInfo struct { | 
					
						
							|  |  |  | 	name    string | 
					
						
							|  |  |  | 	size    int64 | 
					
						
							|  |  |  | 	mode    os.FileMode | 
					
						
							|  |  |  | 	modTime time.Time | 
					
						
							|  |  |  | 	isDir   bool | 
					
						
							|  |  |  | 	sys     interface{} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (f dummyFileInfo) Name() string       { return f.name } | 
					
						
							|  |  |  | func (f dummyFileInfo) Size() int64        { return f.size } | 
					
						
							|  |  |  | func (f dummyFileInfo) Mode() os.FileMode  { return f.mode } | 
					
						
							|  |  |  | func (f dummyFileInfo) ModTime() time.Time { return f.modTime } | 
					
						
							|  |  |  | func (f dummyFileInfo) IsDir() bool        { return f.isDir } | 
					
						
							|  |  |  | func (f dummyFileInfo) Sys() interface{}   { return f.sys } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // DownloadProfilingHandler - POST /minio/admin/v3/profiling/download
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // Download profiling information of all nodes in a zip format
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) DownloadProfilingHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "DownloadProfiling") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 	// Validate request signature.
 | 
					
						
							|  |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ProfilingAdminAction, "") | 
					
						
							|  |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if globalNotificationSys == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	if !globalNotificationSys.DownloadProfilingData(ctx, w) { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminProfilerNotEnabled), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-28 01:34:37 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | type healInitParams struct { | 
					
						
							|  |  |  | 	bucket, objPrefix     string | 
					
						
							|  |  |  | 	hs                    madmin.HealOpts | 
					
						
							|  |  |  | 	clientToken           string | 
					
						
							|  |  |  | 	forceStart, forceStop bool | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | // extractHealInitParams - Validates params for heal init API.
 | 
					
						
							|  |  |  | func extractHealInitParams(vars map[string]string, qParms url.Values, r io.Reader) (hip healInitParams, err APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2020-09-24 23:40:21 +08:00
										 |  |  | 	hip.bucket = vars[mgmtBucket] | 
					
						
							|  |  |  | 	hip.objPrefix = vars[mgmtPrefix] | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	if hip.bucket == "" { | 
					
						
							|  |  |  | 		if hip.objPrefix != "" { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			// Bucket is required if object-prefix is given
 | 
					
						
							|  |  |  | 			err = ErrHealMissingBucket | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	} else if isReservedOrInvalidBucket(hip.bucket, false) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		err = ErrInvalidBucketName | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// empty prefix is valid.
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	if !IsValidObjectPrefix(hip.objPrefix) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		err = ErrInvalidObjectName | 
					
						
							| 
									
										
										
										
											2018-01-12 02:21:41 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:40:21 +08:00
										 |  |  | 	if len(qParms[mgmtClientToken]) > 0 { | 
					
						
							|  |  |  | 		hip.clientToken = qParms[mgmtClientToken][0] | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-24 23:40:21 +08:00
										 |  |  | 	if _, ok := qParms[mgmtForceStart]; ok { | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 		hip.forceStart = true | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-24 23:40:21 +08:00
										 |  |  | 	if _, ok := qParms[mgmtForceStop]; ok { | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 		hip.forceStop = true | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Invalid request conditions:
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//   Cannot have both forceStart and forceStop in the same
 | 
					
						
							|  |  |  | 	//   request; If clientToken is provided, request can only be
 | 
					
						
							|  |  |  | 	//   to continue receiving logs, so it cannot be start or
 | 
					
						
							|  |  |  | 	//   stop;
 | 
					
						
							|  |  |  | 	if (hip.forceStart && hip.forceStop) || | 
					
						
							|  |  |  | 		(hip.clientToken != "" && (hip.forceStart || hip.forceStop)) { | 
					
						
							|  |  |  | 		err = ErrInvalidRequest | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// ignore body if clientToken is provided
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	if hip.clientToken == "" { | 
					
						
							|  |  |  | 		jerr := json.NewDecoder(r).Decode(&hip.hs) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if jerr != nil { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:30:02 +08:00
										 |  |  | 			logger.LogIf(GlobalContext, jerr, logger.Application) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			err = ErrRequestBodyParse | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-15 01:28:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	err = ErrNone | 
					
						
							|  |  |  | 	return | 
					
						
							| 
									
										
										
										
											2017-04-01 08:55:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // HealHandler - POST /minio/admin/v3/heal/
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // -----------
 | 
					
						
							|  |  |  | // Start heal processing and return heal status items.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // On a successful heal sequence start, a unique client token is
 | 
					
						
							|  |  |  | // returned. Subsequent requests to this endpoint providing the client
 | 
					
						
							|  |  |  | // token will receive heal status records from the running heal
 | 
					
						
							|  |  |  | // sequence.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // If no client token is provided, and a heal sequence is in progress
 | 
					
						
							|  |  |  | // an error is returned with information about the running heal
 | 
					
						
							|  |  |  | // sequence. However, if the force-start flag is provided, the server
 | 
					
						
							|  |  |  | // aborts the running heal sequence and starts a new one.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) HealHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-07-21 09:46:32 +08:00
										 |  |  | 	ctx := newContext(r, w, "Heal") | 
					
						
							| 
									
										
										
										
											2018-03-15 03:01:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.HealAdminAction) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-12 02:21:41 +08:00
										 |  |  | 	// Check if this setup has an erasure coded backend.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if !globalIsErasure { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrHealNotImplemented), r.URL) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:11:05 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	hip, errCode := extractHealInitParams(mux.Vars(r), r.URL.Query(), r.Body) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if errCode != ErrNone { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(errCode), r.URL) | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-04 10:27:13 +08:00
										 |  |  | 	// Analyze the heal token and route the request accordingly
 | 
					
						
							|  |  |  | 	token, success := proxyRequestByToken(ctx, w, r, hip.clientToken) | 
					
						
							|  |  |  | 	if success { | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2020-07-04 02:53:03 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-04 10:27:13 +08:00
										 |  |  | 	hip.clientToken = token | 
					
						
							|  |  |  | 	// if request was not successful, try this server locally if token
 | 
					
						
							|  |  |  | 	// is not found the call will fail anyways. if token is empty
 | 
					
						
							|  |  |  | 	// try this server to generate a new token.
 | 
					
						
							| 
									
										
										
										
											2020-07-04 02:53:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	type healResp struct { | 
					
						
							|  |  |  | 		respBytes []byte | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		apiErr    APIError | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		errBody   string | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Define a closure to start sending whitespace to client
 | 
					
						
							|  |  |  | 	// after 10s unless a response item comes in
 | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	keepConnLive := func(w http.ResponseWriter, r *http.Request, respCh chan healResp) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		ticker := time.NewTicker(time.Second * 10) | 
					
						
							| 
									
										
										
										
											2018-05-05 01:43:20 +08:00
										 |  |  | 		defer ticker.Stop() | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		started := false | 
					
						
							|  |  |  | 	forLoop: | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 			case <-r.Context().Done(): | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			case <-ticker.C: | 
					
						
							|  |  |  | 				if !started { | 
					
						
							|  |  |  | 					// Start writing response to client
 | 
					
						
							|  |  |  | 					started = true | 
					
						
							|  |  |  | 					setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 					setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 					// Set 200 OK status
 | 
					
						
							|  |  |  | 					w.WriteHeader(200) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Send whitespace and keep connection open
 | 
					
						
							| 
									
										
										
										
											2019-07-12 04:19:25 +08:00
										 |  |  | 				w.Write([]byte(" ")) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			case hr := <-respCh: | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 				switch hr.apiErr { | 
					
						
							|  |  |  | 				case noError: | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 					if started { | 
					
						
							|  |  |  | 						w.Write(hr.respBytes) | 
					
						
							|  |  |  | 						w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						writeSuccessResponseJSON(w, hr.respBytes) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 				default: | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 					var errorRespJSON []byte | 
					
						
							|  |  |  | 					if hr.errBody == "" { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 						errorRespJSON = encodeResponseJSON(getAPIErrorResponse(ctx, hr.apiErr, | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 							r.URL.Path, w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2019-07-02 03:22:01 +08:00
										 |  |  | 							globalDeploymentID)) | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 					} else { | 
					
						
							|  |  |  | 						errorRespJSON = encodeResponseJSON(APIErrorResponse{ | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 							Code:      hr.apiErr.Code, | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 							Message:   hr.errBody, | 
					
						
							|  |  |  | 							Resource:  r.URL.Path, | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 							RequestID: w.Header().Get(xhttp.AmzRequestID), | 
					
						
							| 
									
										
										
										
											2019-07-02 03:22:01 +08:00
										 |  |  | 							HostID:    globalDeploymentID, | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 						}) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if !started { | 
					
						
							|  |  |  | 						setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2019-07-03 13:34:32 +08:00
										 |  |  | 						w.Header().Set(xhttp.ContentType, string(mimeJSON)) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 						w.WriteHeader(hr.apiErr.HTTPStatusCode) | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 					w.Write(errorRespJSON) | 
					
						
							|  |  |  | 					w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				break forLoop | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	healPath := pathJoin(hip.bucket, hip.objPrefix) | 
					
						
							|  |  |  | 	if hip.clientToken == "" && !hip.forceStart && !hip.forceStop { | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 		nh, exists := globalAllHealState.getHealSequence(healPath) | 
					
						
							|  |  |  | 		if exists && !nh.hasEnded() && len(nh.currentStatus.Items) > 0 { | 
					
						
							| 
									
										
										
										
											2020-07-04 10:27:13 +08:00
										 |  |  | 			clientToken := nh.clientToken | 
					
						
							|  |  |  | 			if globalIsDistErasure { | 
					
						
							|  |  |  | 				clientToken = fmt.Sprintf("%s@%d", nh.clientToken, GetProxyEndpointLocalIndex(globalProxyEndpoints)) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 			b, err := json.Marshal(madmin.HealStartSuccess{ | 
					
						
							| 
									
										
										
										
											2020-07-04 10:27:13 +08:00
										 |  |  | 				ClientToken:   clientToken, | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 				ClientAddress: nh.clientAddress, | 
					
						
							|  |  |  | 				StartTime:     nh.startTime, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 				writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Client token not specified but a heal sequence exists on a path,
 | 
					
						
							|  |  |  | 			// Send the token back to client.
 | 
					
						
							|  |  |  | 			writeSuccessResponseJSON(w, b) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	if hip.clientToken != "" && !hip.forceStart && !hip.forceStop { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		// Since clientToken is given, fetch heal status from running
 | 
					
						
							|  |  |  | 		// heal sequence.
 | 
					
						
							|  |  |  | 		respBytes, errCode := globalAllHealState.PopHealStatusJSON( | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 			healPath, hip.clientToken) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if errCode != ErrNone { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(errCode), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			writeSuccessResponseJSON(w, respBytes) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 		return | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	respCh := make(chan healResp) | 
					
						
							|  |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	case hip.forceStop: | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 		go func() { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 			respBytes, apiErr := globalAllHealState.stopHealSequence(healPath) | 
					
						
							|  |  |  | 			hr := healResp{respBytes: respBytes, apiErr: apiErr} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 			respCh <- hr | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2019-08-30 04:53:27 +08:00
										 |  |  | 	case hip.clientToken == "": | 
					
						
							| 
									
										
										
										
											2020-06-30 04:07:26 +08:00
										 |  |  | 		nh := newHealSequence(GlobalContext, hip.bucket, hip.objPrefix, handlers.GetSourceIP(r), hip.hs, hip.forceStart) | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 		go func() { | 
					
						
							| 
									
										
										
										
											2020-11-11 01:02:06 +08:00
										 |  |  | 			respBytes, apiErr, errMsg := globalAllHealState.LaunchNewHealSequence(nh, objectAPI) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 			hr := healResp{respBytes, apiErr, errMsg} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 			respCh <- hr | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Due to the force-starting functionality, the Launch
 | 
					
						
							|  |  |  | 	// call above can take a long time - to keep the
 | 
					
						
							|  |  |  | 	// connection alive, we start sending whitespace
 | 
					
						
							| 
									
										
										
										
											2020-05-04 13:35:40 +08:00
										 |  |  | 	keepConnLive(w, r, respCh) | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | // getAggregatedBackgroundHealState returns the heal state of disks.
 | 
					
						
							|  |  |  | // If no ObjectLayer is provided no set status is returned.
 | 
					
						
							|  |  |  | func getAggregatedBackgroundHealState(ctx context.Context, o ObjectLayer) (madmin.BgHealState, error) { | 
					
						
							|  |  |  | 	// Get local heal status first
 | 
					
						
							|  |  |  | 	bgHealStates, ok := getBackgroundHealStatus(ctx, o) | 
					
						
							| 
									
										
										
										
											2020-08-08 10:43:06 +08:00
										 |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 		return bgHealStates, errServerNotInitialized | 
					
						
							| 
									
										
										
										
											2020-08-08 10:43:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if globalIsDistErasure { | 
					
						
							| 
									
										
										
										
											2019-06-26 07:42:24 +08:00
										 |  |  | 		// Get heal status from other peers
 | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 		peersHealStates, nerrs := globalNotificationSys.BackgroundHealStatus() | 
					
						
							| 
									
										
										
										
											2020-09-03 13:54:56 +08:00
										 |  |  | 		var errCount int | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 		for _, nerr := range nerrs { | 
					
						
							|  |  |  | 			if nerr.Err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2020-09-03 13:54:56 +08:00
										 |  |  | 				errCount++ | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-03 13:54:56 +08:00
										 |  |  | 		if errCount == len(nerrs) { | 
					
						
							|  |  |  | 			return madmin.BgHealState{}, fmt.Errorf("all remote servers failed to report heal status, cluster is unhealthy") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 		bgHealStates.Merge(peersHealStates...) | 
					
						
							| 
									
										
										
										
											2019-06-26 07:42:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	return bgHealStates, nil | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) BackgroundHealStatusHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "HealBackgroundStatus") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.HealAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if this setup has an erasure coded backend.
 | 
					
						
							|  |  |  | 	if !globalIsErasure { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrHealNotImplemented), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	aggregateHealStateResult, err := getAggregatedBackgroundHealState(r.Context(), objectAPI) | 
					
						
							| 
									
										
										
										
											2020-09-03 13:54:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-08 04:22:53 +08:00
										 |  |  | 	if err := json.NewEncoder(w).Encode(aggregateHealStateResult); err != nil { | 
					
						
							| 
									
										
										
										
											2019-06-26 07:42:24 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request, action iampolicy.AdminAction) (ObjectLayer, auth.Credentials) { | 
					
						
							|  |  |  | 	var cred auth.Credentials | 
					
						
							|  |  |  | 	var adminAPIErr APIErrorCode | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	// Get current object layer instance.
 | 
					
						
							| 
									
										
										
										
											2020-10-10 00:59:52 +08:00
										 |  |  | 	objectAPI := newObjectLayerFn() | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	if objectAPI == nil || globalNotificationSys == nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 		return nil, cred | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate request signature.
 | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 	cred, adminAPIErr = checkAdminRequestAuth(ctx, r, action, "") | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 		return nil, cred | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-19 18:03:18 +08:00
										 |  |  | 	return objectAPI, cred | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | // AdminError - is a generic error for all admin APIs.
 | 
					
						
							|  |  |  | type AdminError struct { | 
					
						
							|  |  |  | 	Code       string | 
					
						
							|  |  |  | 	Message    string | 
					
						
							|  |  |  | 	StatusCode int | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ae AdminError) Error() string { | 
					
						
							|  |  |  | 	return ae.Message | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Admin API errors
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	AdminUpdateUnexpectedFailure = "XMinioAdminUpdateUnexpectedFailure" | 
					
						
							|  |  |  | 	AdminUpdateURLNotReachable   = "XMinioAdminUpdateURLNotReachable" | 
					
						
							|  |  |  | 	AdminUpdateApplyFailure      = "XMinioAdminUpdateApplyFailure" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | // toAdminAPIErrCode - converts errErasureWriteQuorum error to admin API
 | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | // specific error.
 | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | func toAdminAPIErrCode(ctx context.Context, err error) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	switch err { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	case errErasureWriteQuorum: | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		return ErrAdminConfigNoQuorum | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 		return toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | func toAdminAPIErr(ctx context.Context, err error) APIError { | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return noError | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-30 15:04:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var apiErr APIError | 
					
						
							|  |  |  | 	switch e := err.(type) { | 
					
						
							| 
									
										
										
										
											2020-01-04 03:28:52 +08:00
										 |  |  | 	case iampolicy.Error: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioMalformedIAMPolicy", | 
					
						
							|  |  |  | 			Description:    e.Error(), | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-30 15:04:39 +08:00
										 |  |  | 	case config.Error: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           "XMinioConfigError", | 
					
						
							|  |  |  | 			Description:    e.Error(), | 
					
						
							|  |  |  | 			HTTPStatusCode: http.StatusBadRequest, | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-10-30 15:04:39 +08:00
										 |  |  | 	case AdminError: | 
					
						
							|  |  |  | 		apiErr = APIError{ | 
					
						
							|  |  |  | 			Code:           e.Code, | 
					
						
							|  |  |  | 			Description:    e.Message, | 
					
						
							|  |  |  | 			HTTPStatusCode: e.StatusCode, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2020-06-12 05:11:30 +08:00
										 |  |  | 		switch { | 
					
						
							|  |  |  | 		case errors.Is(err, errConfigNotFound): | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioConfigError", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusNotFound, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-12 05:11:30 +08:00
										 |  |  | 		case errors.Is(err, errIAMActionNotAllowed): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioIAMActionNotAllowed", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusForbidden, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errIAMNotInitialized): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioIAMNotInitialized", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusServiceUnavailable, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-07-09 09:50:43 +08:00
										 |  |  | 		case errors.Is(err, crypto.ErrKESKeyExists): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioKMSKeyExists", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusConflict, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-04-20 01:30:42 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Tier admin API errors
 | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierNameEmpty): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierNameEmpty", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierInvalidConfig): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInvalidConfig", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierInvalidConfigVersion): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInvalidConfigVersion", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, madmin.ErrTierTypeUnsupported): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierTypeUnsupported", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errTierBackendInUse): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierBackendInUse", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusConflict, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errors.Is(err, errTierInsufficientCreds): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInsufficientCreds", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case errIsTierPermError(err): | 
					
						
							|  |  |  | 			apiErr = APIError{ | 
					
						
							|  |  |  | 				Code:           "XMinioAdminTierInsufficientPermissions", | 
					
						
							|  |  |  | 				Description:    err.Error(), | 
					
						
							|  |  |  | 				HTTPStatusCode: http.StatusBadRequest, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-06-12 05:11:30 +08:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2020-04-15 02:28:56 +08:00
										 |  |  | 			apiErr = errorCodes.ToAPIErrWithErr(toAdminAPIErrCode(ctx, err), err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 	return apiErr | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | // Returns true if the trace.Info should be traced,
 | 
					
						
							|  |  |  | // false if certain conditions are not met.
 | 
					
						
							|  |  |  | // - input entry is not of the type *trace.Info*
 | 
					
						
							|  |  |  | // - errOnly entries are to be traced, not status code 2xx, 3xx.
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | // - trace.Info type is asked by opts
 | 
					
						
							|  |  |  | func mustTrace(entry interface{}, opts madmin.ServiceTraceOpts) (shouldTrace bool) { | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 	trcInfo, ok := entry.(trace.Info) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-21 14:52:17 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	// Override shouldTrace decision with errOnly filtering
 | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if shouldTrace && opts.OnlyErrors { | 
					
						
							|  |  |  | 			shouldTrace = trcInfo.RespInfo.StatusCode >= http.StatusBadRequest | 
					
						
							| 
									
										
										
										
											2020-11-21 14:52:17 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.Threshold > 0 { | 
					
						
							|  |  |  | 		var latency time.Duration | 
					
						
							|  |  |  | 		switch trcInfo.TraceType { | 
					
						
							|  |  |  | 		case trace.OS: | 
					
						
							|  |  |  | 			latency = trcInfo.OSStats.Duration | 
					
						
							|  |  |  | 		case trace.Storage: | 
					
						
							|  |  |  | 			latency = trcInfo.StorageStats.Duration | 
					
						
							|  |  |  | 		case trace.HTTP: | 
					
						
							|  |  |  | 			latency = trcInfo.CallStats.Latency | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if latency < opts.Threshold { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.Internal && trcInfo.TraceType == trace.HTTP && HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath+SlashSeparator) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.S3 && trcInfo.TraceType == trace.HTTP && !HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath+SlashSeparator) { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.Storage && trcInfo.TraceType == trace.Storage { | 
					
						
							| 
									
										
										
										
											2020-11-21 14:52:17 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	return opts.OS && trcInfo.TraceType == trace.OS | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractTraceOptions(r *http.Request) (opts madmin.ServiceTraceOpts, err error) { | 
					
						
							|  |  |  | 	q := r.URL.Query() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	opts.OnlyErrors = q.Get("err") == "true" | 
					
						
							|  |  |  | 	opts.S3 = q.Get("s3") == "true" | 
					
						
							|  |  |  | 	opts.Internal = q.Get("internal") == "true" | 
					
						
							|  |  |  | 	opts.Storage = q.Get("storage") == "true" | 
					
						
							|  |  |  | 	opts.OS = q.Get("os") == "true" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Support deprecated 'all' query
 | 
					
						
							|  |  |  | 	if q.Get("all") == "true" { | 
					
						
							|  |  |  | 		opts.S3 = true | 
					
						
							|  |  |  | 		opts.Internal = true | 
					
						
							|  |  |  | 		opts.Storage = true | 
					
						
							|  |  |  | 		opts.OS = true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if t := q.Get("threshold"); t != "" { | 
					
						
							|  |  |  | 		d, err := time.ParseDuration(t) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return opts, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		opts.Threshold = d | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	return | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // TraceHandler - POST /minio/admin/v3/trace
 | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // The handler sends http trace to the connected HTTP client.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) TraceHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "HTTPTrace") | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-22 07:47:51 +08:00
										 |  |  | 	// Validate request signature.
 | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.TraceAdminAction, "") | 
					
						
							| 
									
										
										
										
											2019-06-22 07:47:51 +08:00
										 |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	traceOpts, err := extractTraceOptions(r) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 	// Trace Publisher and peer-trace-client uses nonblocking send and hence does not wait for slow receivers.
 | 
					
						
							|  |  |  | 	// Use buffered channel to take care of burst sends or slow w.Write()
 | 
					
						
							|  |  |  | 	traceCh := make(chan interface{}, 4000) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	peers, _ := newPeerRestClients(globalEndpoints) | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 	globalTrace.Subscribe(traceCh, ctx.Done(), func(entry interface{}) bool { | 
					
						
							|  |  |  | 		return mustTrace(entry, traceOpts) | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, peer := range peers { | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 		if peer == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 		peer.Trace(traceCh, ctx.Done(), traceOpts) | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 	keepAliveTicker := time.NewTicker(500 * time.Millisecond) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 	enc := json.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case entry := <-traceCh: | 
					
						
							| 
									
										
										
										
											2019-06-27 13:41:12 +08:00
										 |  |  | 			if err := enc.Encode(entry); err != nil { | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 		case <-keepAliveTicker.C: | 
					
						
							|  |  |  | 			if _, err := w.Write([]byte(" ")); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2019-06-09 06:54:41 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // The handler sends console logs to the connected HTTP client.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ConsoleLog") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 10:47:52 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ConsoleLogAdminAction) | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	node := r.URL.Query().Get("node") | 
					
						
							|  |  |  | 	// limit buffered console entries if client requested it.
 | 
					
						
							|  |  |  | 	limitStr := r.URL.Query().Get("limit") | 
					
						
							|  |  |  | 	limitLines, err := strconv.Atoi(limitStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		limitLines = 10 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	logKind := r.URL.Query().Get("logType") | 
					
						
							|  |  |  | 	if logKind == "" { | 
					
						
							|  |  |  | 		logKind = string(logger.All) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	logKind = strings.ToUpper(logKind) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 	// Avoid reusing tcp connection if read timeout is hit
 | 
					
						
							|  |  |  | 	// This is needed to make r.Context().Done() work as
 | 
					
						
							|  |  |  | 	// expected in case of read timeout
 | 
					
						
							| 
									
										
										
										
											2020-09-02 07:58:13 +08:00
										 |  |  | 	w.Header().Set("Connection", "close") | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	logCh := make(chan interface{}, 4000) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-29 00:18:35 +08:00
										 |  |  | 	peers, _ := newPeerRestClients(globalEndpoints) | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	globalConsoleSys.Subscribe(logCh, ctx.Done(), node, limitLines, logKind, nil) | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, peer := range peers { | 
					
						
							| 
									
										
										
										
											2019-11-10 01:27:23 +08:00
										 |  |  | 		if peer == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-09-06 06:51:27 +08:00
										 |  |  | 		if node == "" || strings.EqualFold(peer.host.Name, node) { | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 			peer.ConsoleLog(logCh, ctx.Done()) | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	enc := json.NewEncoder(w) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	keepAliveTicker := time.NewTicker(500 * time.Millisecond) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case entry := <-logCh: | 
					
						
							| 
									
										
										
										
											2020-03-21 06:13:41 +08:00
										 |  |  | 			log, ok := entry.(log.Info) | 
					
						
							|  |  |  | 			if ok && log.SendLog(node, logKind) { | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 				if err := enc.Encode(log); err != nil { | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case <-keepAliveTicker.C: | 
					
						
							|  |  |  | 			if _, err := w.Write([]byte(" ")); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		case <-ctx.Done(): | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-09 09:50:43 +08:00
										 |  |  | // KMSCreateKeyHandler - POST /minio/admin/v3/kms/key/create?key-id=<master-key-id>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) KMSCreateKeyHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "KMSCreateKey") | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-09 09:50:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSCreateKeyAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if GlobalKMS == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := GlobalKMS.CreateKey(r.URL.Query().Get("key-id")); err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // KMSKeyStatusHandler - GET /minio/admin/v3/kms/key/status?key-id=<master-key-id>
 | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | func (a adminAPIHandlers) KMSKeyStatusHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 	ctx := newContext(r, w, "KMSKeyStatus") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-27 10:47:52 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSKeyStatusAdminAction) | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if GlobalKMS == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	stat, err := GlobalKMS.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	keyID := r.URL.Query().Get("key-id") | 
					
						
							|  |  |  | 	if keyID == "" { | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 		keyID = stat.DefaultKey | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	var response = madmin.KMSKeyStatus{ | 
					
						
							|  |  |  | 		KeyID: keyID, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	kmsContext := kms.Context{"MinIO admin API": "KMSKeyStatusHandler"} // Context for a test key operation
 | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	// 1. Generate a new key using the KMS.
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	key, err := GlobalKMS.GenerateKey(keyID, kmsContext) | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		response.EncryptionErr = err.Error() | 
					
						
							|  |  |  | 		resp, err := json.Marshal(response) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, resp) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 01:17:35 +08:00
										 |  |  | 	// 2. Verify that we can indeed decrypt the (encrypted) key
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	decryptedKey, err := GlobalKMS.DecryptKey(key.KeyID, key.Plaintext, kmsContext) | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		response.DecryptionErr = err.Error() | 
					
						
							|  |  |  | 		resp, err := json.Marshal(response) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, resp) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-06 01:17:35 +08:00
										 |  |  | 	// 3. Compare generated key with decrypted key
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	if subtle.ConstantTimeCompare(key.Plaintext, decryptedKey) != 1 { | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 		response.DecryptionErr = "The generated and the decrypted data key do not match" | 
					
						
							|  |  |  | 		resp, err := json.Marshal(response) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, resp) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := json.Marshal(response) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, resp) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-10-03 22:48:38 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | // HealthInfoHandler - GET /minio/admin/v3/healthinfo
 | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | // Get server health info
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) HealthInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "HealthInfo") | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.HealthInfoAdminAction) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 	query := r.URL.Query() | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 	healthInfo := madmin.HealthInfo{} | 
					
						
							|  |  |  | 	healthInfoCh := make(chan madmin.HealthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-31 16:15:21 +08:00
										 |  |  | 	enc := json.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 	partialWrite := func(oinfo madmin.HealthInfo) { | 
					
						
							|  |  |  | 		healthInfoCh <- oinfo | 
					
						
							| 
									
										
										
										
											2020-04-15 02:48:32 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	setCommonHeaders(w) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:45:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	setEventStreamHeaders(w) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	w.WriteHeader(http.StatusOK) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	errResp := func(err error) { | 
					
						
							|  |  |  | 		errorResponse := getAPIErrorResponse(ctx, toAdminAPIErr(ctx, err), r.URL.String(), | 
					
						
							|  |  |  | 			w.Header().Get(xhttp.AmzRequestID), globalDeploymentID) | 
					
						
							|  |  |  | 		encodedErrorResponse := encodeResponse(errorResponse) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 		healthInfo.Error = string(encodedErrorResponse) | 
					
						
							|  |  |  | 		logger.LogIf(ctx, enc.Encode(healthInfo)) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-15 02:48:32 +08:00
										 |  |  | 	deadline := 3600 * time.Second | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	if dstr := r.URL.Query().Get("deadline"); dstr != "" { | 
					
						
							| 
									
										
										
										
											2020-04-15 02:48:32 +08:00
										 |  |  | 		var err error | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		deadline, err = time.ParseDuration(dstr) | 
					
						
							| 
									
										
										
										
											2020-04-15 02:48:32 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			errResp(err) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-28 07:12:50 +08:00
										 |  |  | 	deadlinedCtx, deadlineCancel := context.WithTimeout(ctx, deadline) | 
					
						
							|  |  |  | 	defer deadlineCancel() | 
					
						
							| 
									
										
										
										
											2020-04-15 02:48:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 	nsLock := objectAPI.NewNSLock(minioMetaBucket, "health-check-in-progress") | 
					
						
							| 
									
										
										
										
											2021-04-28 07:12:50 +08:00
										 |  |  | 	ctx, cancel, err := nsLock.GetLock(ctx, newDynamicTimeout(deadline, deadline)) | 
					
						
							| 
									
										
										
										
											2021-03-04 10:36:43 +08:00
										 |  |  | 	if err != nil { // returns a locked lock
 | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		errResp(err) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-28 07:12:50 +08:00
										 |  |  | 	defer nsLock.Unlock(cancel) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 		defer close(healthInfoCh) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if cpu := query.Get("syscpu"); cpu == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			cpuInfo := getLocalCPUInfo(deadlinedCtx, r) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Sys.CPUInfo = append(healthInfo.Sys.CPUInfo, cpuInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.CPUInfo = append(healthInfo.Sys.CPUInfo, globalNotificationSys.CPUInfo(deadlinedCtx)...) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if diskHw := query.Get("sysdiskhw"); diskHw == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			diskHwInfo := getLocalDiskHwInfo(deadlinedCtx, r) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Sys.DiskHwInfo = append(healthInfo.Sys.DiskHwInfo, diskHwInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.DiskHwInfo = append(healthInfo.Sys.DiskHwInfo, globalNotificationSys.DiskHwInfo(deadlinedCtx)...) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if osInfo := query.Get("sysosinfo"); osInfo == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			osInfo := getLocalOsInfo(deadlinedCtx, r) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Sys.OsInfo = append(healthInfo.Sys.OsInfo, osInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.OsInfo = append(healthInfo.Sys.OsInfo, globalNotificationSys.OsInfo(deadlinedCtx)...) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if mem := query.Get("sysmem"); mem == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			memInfo := getLocalMemInfo(deadlinedCtx, r) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Sys.MemInfo = append(healthInfo.Sys.MemInfo, memInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.MemInfo = append(healthInfo.Sys.MemInfo, globalNotificationSys.MemInfo(deadlinedCtx)...) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if proc := query.Get("sysprocess"); proc == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			procInfo := getLocalProcInfo(deadlinedCtx, r) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Sys.ProcInfo = append(healthInfo.Sys.ProcInfo, procInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.ProcInfo = append(healthInfo.Sys.ProcInfo, globalNotificationSys.ProcInfo(deadlinedCtx)...) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if config := query.Get("minioconfig"); config == "true" { | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 			cfg, err := readServerConfig(ctx, objectAPI) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Minio.Config = cfg | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if drive := query.Get("perfdrive"); drive == "true" { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			// Get drive perf details from local server's drive(s)
 | 
					
						
							|  |  |  | 			drivePerfSerial := getLocalDrives(deadlinedCtx, false, globalEndpoints, r) | 
					
						
							|  |  |  | 			drivePerfParallel := getLocalDrives(deadlinedCtx, true, globalEndpoints, r) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			errStr := "" | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			if drivePerfSerial.Error != "" { | 
					
						
							|  |  |  | 				errStr = "serial: " + drivePerfSerial.Error | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			if drivePerfParallel.Error != "" { | 
					
						
							|  |  |  | 				errStr = errStr + " parallel: " + drivePerfParallel.Error | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			driveInfo := madmin.ServerDrivesInfo{ | 
					
						
							|  |  |  | 				Addr:     drivePerfSerial.Addr, | 
					
						
							|  |  |  | 				Serial:   drivePerfSerial.Serial, | 
					
						
							|  |  |  | 				Parallel: drivePerfParallel.Parallel, | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 				Error:    errStr, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Perf.DriveInfo = append(healthInfo.Perf.DriveInfo, driveInfo) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Notify all other MinIO peers to report drive perf numbers
 | 
					
						
							|  |  |  | 			driveInfos := globalNotificationSys.DrivePerfInfoChan(deadlinedCtx) | 
					
						
							|  |  |  | 			for obd := range driveInfos { | 
					
						
							|  |  |  | 				healthInfo.Perf.DriveInfo = append(healthInfo.Perf.DriveInfo, obd) | 
					
						
							|  |  |  | 				partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 		if net := query.Get("perfnet"); net == "true" && globalIsDistErasure { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Perf.Net = append(healthInfo.Perf.Net, globalNotificationSys.NetInfo(deadlinedCtx)) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			netInfos := globalNotificationSys.DispatchNetPerfChan(deadlinedCtx) | 
					
						
							|  |  |  | 			for netInfo := range netInfos { | 
					
						
							|  |  |  | 				healthInfo.Perf.Net = append(healthInfo.Perf.Net, netInfo) | 
					
						
							|  |  |  | 				partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			healthInfo.Perf.NetParallel = globalNotificationSys.NetPerfParallelInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-07 05:29:50 +08:00
										 |  |  | 	ticker := time.NewTicker(5 * time.Second) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	defer ticker.Stop() | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 		case oinfo, ok := <-healthInfoCh: | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			logger.LogIf(ctx, enc.Encode(oinfo)) | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case <-ticker.C: | 
					
						
							|  |  |  | 			if _, err := w.Write([]byte(" ")); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case <-deadlinedCtx.Done(): | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | // BandwidthMonitorHandler - GET /minio/admin/v3/bandwidth
 | 
					
						
							|  |  |  | // ----------
 | 
					
						
							|  |  |  | // Get bandwidth consumption information
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) BandwidthMonitorHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "BandwidthMonitor") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	// Validate request signature.
 | 
					
						
							| 
									
										
										
										
											2020-12-19 03:51:15 +08:00
										 |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.BandwidthMonitorAction, "") | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 	rnd := rand.New(rand.NewSource(time.Now().UnixNano())) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	setEventStreamHeaders(w) | 
					
						
							| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | 	reportCh := make(chan madmin.BucketBandwidthReport) | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 	keepAliveTicker := time.NewTicker(500 * time.Millisecond) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	bucketsRequestedString := r.URL.Query().Get("buckets") | 
					
						
							| 
									
										
										
										
											2020-10-13 00:04:55 +08:00
										 |  |  | 	bucketsRequested := strings.Split(bucketsRequestedString, ",") | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 		defer close(reportCh) | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-ctx.Done(): | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 			case reportCh <- globalNotificationSys.GetBandwidthReports(ctx, bucketsRequested...): | 
					
						
							|  |  |  | 				time.Sleep(time.Duration(rnd.Float64() * float64(2*time.Second))) | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 		case report, ok := <-reportCh: | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err := json.NewEncoder(w).Encode(report); err != nil { | 
					
						
							|  |  |  | 				writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-12-04 03:34:42 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case <-keepAliveTicker.C: | 
					
						
							|  |  |  | 			if _, err := w.Write([]byte(" ")); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-10-10 11:36:00 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:30:59 +08:00
										 |  |  | // ServerInfoHandler - GET /minio/admin/v3/info
 | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // Get server information
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ServerInfo") | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-05-12 01:34:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 	// Validate request signature.
 | 
					
						
							|  |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.ServerInfoAdminAction, "") | 
					
						
							|  |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-30 09:55:37 +08:00
										 |  |  | 	kmsStat := fetchKMSStatus() | 
					
						
							| 
									
										
										
										
											2019-12-13 01:58:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	ldap := madmin.LDAP{} | 
					
						
							|  |  |  | 	if globalLDAPConfig.Enabled { | 
					
						
							|  |  |  | 		ldapConn, err := globalLDAPConfig.Connect() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			ldap.Status = string(madmin.ItemOffline) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} else if ldapConn == nil { | 
					
						
							|  |  |  | 			ldap.Status = "Not Configured" | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-03-12 14:13:32 +08:00
										 |  |  | 			// Close ldap connection to avoid leaks.
 | 
					
						
							|  |  |  | 			ldapConn.Close() | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			ldap.Status = string(madmin.ItemOnline) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 	log, audit := fetchLoggerInfo() | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get the notification target info
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 	notifyTarget := fetchLambdaInfo() | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	local := getLocalServerProperty(globalEndpoints, r) | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 	servers := globalNotificationSys.ServerInfo() | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 	servers = append(servers, local) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-02 00:09:43 +08:00
										 |  |  | 	assignPoolNumbers(servers) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	var backend interface{} | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 	mode := madmin.ItemInitializing | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	buckets := madmin.Buckets{} | 
					
						
							|  |  |  | 	objects := madmin.Objects{} | 
					
						
							|  |  |  | 	usage := madmin.Usage{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI := newObjectLayerFn() | 
					
						
							|  |  |  | 	if objectAPI != nil { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 		mode = madmin.ItemOnline | 
					
						
							| 
									
										
										
										
											2021-03-17 11:06:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 		// Load data usage
 | 
					
						
							|  |  |  | 		dataUsageInfo, err := loadDataUsageFromBackend(ctx, objectAPI) | 
					
						
							|  |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			buckets = madmin.Buckets{Count: dataUsageInfo.BucketsCount} | 
					
						
							|  |  |  | 			objects = madmin.Objects{Count: dataUsageInfo.ObjectsTotalCount} | 
					
						
							|  |  |  | 			usage = madmin.Usage{Size: dataUsageInfo.ObjectsTotalSize} | 
					
						
							| 
									
										
										
										
											2021-02-10 01:52:38 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			buckets = madmin.Buckets{Error: err.Error()} | 
					
						
							|  |  |  | 			objects = madmin.Objects{Error: err.Error()} | 
					
						
							|  |  |  | 			usage = madmin.Usage{Error: err.Error()} | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Fetching the backend information
 | 
					
						
							|  |  |  | 		backendInfo := objectAPI.BackendInfo() | 
					
						
							| 
									
										
										
										
											2021-03-05 06:36:23 +08:00
										 |  |  | 		if backendInfo.Type == madmin.Erasure { | 
					
						
							| 
									
										
										
										
											2020-12-22 01:35:19 +08:00
										 |  |  | 			// Calculate the number of online/offline disks of all nodes
 | 
					
						
							|  |  |  | 			var allDisks []madmin.Disk | 
					
						
							|  |  |  | 			for _, s := range servers { | 
					
						
							|  |  |  | 				allDisks = append(allDisks, s.Disks...) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			onlineDisks, offlineDisks := getOnlineOfflineDisksStats(allDisks) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			backend = madmin.ErasureBackend{ | 
					
						
							|  |  |  | 				Type:             madmin.ErasureType, | 
					
						
							|  |  |  | 				OnlineDisks:      onlineDisks.Sum(), | 
					
						
							|  |  |  | 				OfflineDisks:     offlineDisks.Sum(), | 
					
						
							|  |  |  | 				StandardSCParity: backendInfo.StandardSCParity, | 
					
						
							|  |  |  | 				RRSCParity:       backendInfo.RRSCParity, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			backend = madmin.FSBackend{ | 
					
						
							|  |  |  | 				Type: madmin.FsType, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	domain := globalDomainNames | 
					
						
							|  |  |  | 	services := madmin.Services{ | 
					
						
							| 
									
										
										
										
											2021-01-30 09:55:37 +08:00
										 |  |  | 		KMS:           kmsStat, | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		LDAP:          ldap, | 
					
						
							|  |  |  | 		Logger:        log, | 
					
						
							|  |  |  | 		Audit:         audit, | 
					
						
							|  |  |  | 		Notifications: notifyTarget, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-19 00:59:45 +08:00
										 |  |  | 	infoMsg := madmin.InfoMessage{ | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 		Mode:         string(mode), | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		Domain:       domain, | 
					
						
							|  |  |  | 		Region:       globalServerRegion, | 
					
						
							| 
									
										
										
										
											2020-04-22 00:38:32 +08:00
										 |  |  | 		SQSARN:       globalNotificationSys.GetARNList(false), | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		DeploymentID: globalDeploymentID, | 
					
						
							|  |  |  | 		Buckets:      buckets, | 
					
						
							|  |  |  | 		Objects:      objects, | 
					
						
							|  |  |  | 		Usage:        usage, | 
					
						
							|  |  |  | 		Services:     services, | 
					
						
							|  |  |  | 		Backend:      backend, | 
					
						
							|  |  |  | 		Servers:      servers, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal API response
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(infoMsg) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 	// Reply with storage information (across nodes in a
 | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	// distributed setup) as json.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-02 00:09:43 +08:00
										 |  |  | func assignPoolNumbers(servers []madmin.ServerProperties) { | 
					
						
							|  |  |  | 	for i := range servers { | 
					
						
							|  |  |  | 		for idx, ge := range globalEndpoints { | 
					
						
							|  |  |  | 			for _, endpoint := range ge.Endpoints { | 
					
						
							|  |  |  | 				if servers[i].Endpoint == endpoint.Host { | 
					
						
							|  |  |  | 					servers[i].PoolNumber = idx + 1 | 
					
						
							|  |  |  | 				} else if host, err := xnet.ParseHost(servers[i].Endpoint); err == nil { | 
					
						
							|  |  |  | 					if host.Name == endpoint.Hostname() { | 
					
						
							|  |  |  | 						servers[i].PoolNumber = idx + 1 | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | func fetchLambdaInfo() []map[string][]madmin.TargetIDStatus { | 
					
						
							| 
									
										
										
										
											2020-04-15 02:19:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-01 20:20:04 +08:00
										 |  |  | 	lambdaMap := make(map[string][]madmin.TargetIDStatus) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 	for _, tgt := range globalConfigTargetList.Targets() { | 
					
						
							|  |  |  | 		targetIDStatus := make(map[string]madmin.Status) | 
					
						
							|  |  |  | 		active, _ := tgt.IsActive() | 
					
						
							|  |  |  | 		targetID := tgt.ID() | 
					
						
							|  |  |  | 		if active { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			targetIDStatus[targetID.ID] = madmin.Status{Status: string(madmin.ItemOnline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			targetIDStatus[targetID.ID] = madmin.Status{Status: string(madmin.ItemOffline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		list := lambdaMap[targetID.Name] | 
					
						
							|  |  |  | 		list = append(list, targetIDStatus) | 
					
						
							|  |  |  | 		lambdaMap[targetID.Name] = list | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, tgt := range globalEnvTargetList.Targets() { | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		targetIDStatus := make(map[string]madmin.Status) | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 		active, _ := tgt.IsActive() | 
					
						
							|  |  |  | 		targetID := tgt.ID() | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		if active { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			targetIDStatus[targetID.ID] = madmin.Status{Status: string(madmin.ItemOnline)} | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 			targetIDStatus[targetID.ID] = madmin.Status{Status: string(madmin.ItemOffline)} | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		list := lambdaMap[targetID.Name] | 
					
						
							|  |  |  | 		list = append(list, targetIDStatus) | 
					
						
							|  |  |  | 		lambdaMap[targetID.Name] = list | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	notify := make([]map[string][]madmin.TargetIDStatus, len(lambdaMap)) | 
					
						
							|  |  |  | 	counter := 0 | 
					
						
							|  |  |  | 	for key, value := range lambdaMap { | 
					
						
							|  |  |  | 		v := make(map[string][]madmin.TargetIDStatus) | 
					
						
							|  |  |  | 		v[key] = value | 
					
						
							|  |  |  | 		notify[counter] = v | 
					
						
							|  |  |  | 		counter++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return notify | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-30 09:55:37 +08:00
										 |  |  | // fetchKMSStatus fetches KMS-related status information.
 | 
					
						
							|  |  |  | func fetchKMSStatus() madmin.KMS { | 
					
						
							|  |  |  | 	kmsStat := madmin.KMS{} | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	if GlobalKMS == nil { | 
					
						
							| 
									
										
										
										
											2021-01-30 09:55:37 +08:00
										 |  |  | 		kmsStat.Status = "disabled" | 
					
						
							|  |  |  | 		return kmsStat | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 	stat, err := GlobalKMS.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 		kmsStat.Status = string(madmin.ItemOffline) | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 		return kmsStat | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(stat.Endpoints) == 0 { | 
					
						
							|  |  |  | 		kmsStat.Status = stat.Name | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 		return kmsStat | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if err := checkConnection(stat.Endpoints[0], 15*time.Second); err != nil { | 
					
						
							|  |  |  | 		kmsStat.Status = string(madmin.ItemOffline) | 
					
						
							|  |  |  | 		return kmsStat | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	kmsStat.Status = string(madmin.ItemOnline) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 	kmsContext := kms.Context{"MinIO admin API": "ServerInfoHandler"} // Context for a test key operation
 | 
					
						
							|  |  |  | 	// 1. Generate a new key using the KMS.
 | 
					
						
							|  |  |  | 	key, err := GlobalKMS.GenerateKey("", kmsContext) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		kmsStat.Encrypt = fmt.Sprintf("Encryption failed: %v", err) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		kmsStat.Encrypt = "success" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-15 23:47:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-22 23:45:30 +08:00
										 |  |  | 	// 2. Verify that we can indeed decrypt the (encrypted) key
 | 
					
						
							|  |  |  | 	decryptedKey, err := GlobalKMS.DecryptKey(key.KeyID, key.Ciphertext, kmsContext) | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case err != nil: | 
					
						
							|  |  |  | 		kmsStat.Decrypt = fmt.Sprintf("Decryption failed: %v", err) | 
					
						
							|  |  |  | 	case subtle.ConstantTimeCompare(key.Plaintext, decryptedKey) != 1: | 
					
						
							|  |  |  | 		kmsStat.Decrypt = "Decryption failed: decrypted key does not match generated key" | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		kmsStat.Decrypt = "success" | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-01-30 09:55:37 +08:00
										 |  |  | 	return kmsStat | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // fetchLoggerDetails return log info
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | func fetchLoggerInfo() ([]madmin.Logger, []madmin.Audit) { | 
					
						
							|  |  |  | 	var loggerInfo []madmin.Logger | 
					
						
							|  |  |  | 	var auditloggerInfo []madmin.Audit | 
					
						
							|  |  |  | 	for _, target := range logger.Targets { | 
					
						
							|  |  |  | 		if target.Endpoint() != "" { | 
					
						
							|  |  |  | 			tgt := target.String() | 
					
						
							|  |  |  | 			err := checkConnection(target.Endpoint(), 15*time.Second) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				mapLog := make(map[string]madmin.Status) | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 				mapLog[tgt] = madmin.Status{Status: string(madmin.ItemOnline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 				loggerInfo = append(loggerInfo, mapLog) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				mapLog := make(map[string]madmin.Status) | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 				mapLog[tgt] = madmin.Status{Status: string(madmin.ItemOffline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 				loggerInfo = append(loggerInfo, mapLog) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 	for _, target := range logger.AuditTargets { | 
					
						
							|  |  |  | 		if target.Endpoint() != "" { | 
					
						
							|  |  |  | 			tgt := target.String() | 
					
						
							|  |  |  | 			err := checkConnection(target.Endpoint(), 15*time.Second) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			if err == nil { | 
					
						
							|  |  |  | 				mapAudit := make(map[string]madmin.Status) | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 				mapAudit[tgt] = madmin.Status{Status: string(madmin.ItemOnline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 				auditloggerInfo = append(auditloggerInfo, mapAudit) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				mapAudit := make(map[string]madmin.Status) | 
					
						
							| 
									
										
										
										
											2021-03-03 09:28:04 +08:00
										 |  |  | 				mapAudit[tgt] = madmin.Status{Status: string(madmin.ItemOffline)} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 				auditloggerInfo = append(auditloggerInfo, mapAudit) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return loggerInfo, auditloggerInfo | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // checkConnection - ping an endpoint , return err in case of no connection
 | 
					
						
							| 
									
										
										
										
											2020-02-02 09:45:29 +08:00
										 |  |  | func checkConnection(endpointStr string, timeout time.Duration) error { | 
					
						
							| 
									
										
										
										
											2020-04-29 04:57:56 +08:00
										 |  |  | 	ctx, cancel := context.WithTimeout(GlobalContext, timeout) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-05 05:55:53 +08:00
										 |  |  | 	client := &http.Client{Transport: &http.Transport{ | 
					
						
							|  |  |  | 		Proxy:                 http.ProxyFromEnvironment, | 
					
						
							|  |  |  | 		DialContext:           xhttp.NewCustomDialContext(timeout), | 
					
						
							|  |  |  | 		ResponseHeaderTimeout: 5 * time.Second, | 
					
						
							|  |  |  | 		TLSHandshakeTimeout:   5 * time.Second, | 
					
						
							|  |  |  | 		ExpectContinueTimeout: 5 * time.Second, | 
					
						
							|  |  |  | 		TLSClientConfig:       &tls.Config{RootCAs: globalRootCAs}, | 
					
						
							|  |  |  | 		// Go net/http automatically unzip if content-type is
 | 
					
						
							|  |  |  | 		// gzip disable this feature, as we are always interested
 | 
					
						
							|  |  |  | 		// in raw stream.
 | 
					
						
							|  |  |  | 		DisableCompression: true, | 
					
						
							|  |  |  | 	}} | 
					
						
							|  |  |  | 	defer client.CloseIdleConnections() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-09 05:22:04 +08:00
										 |  |  | 	req, err := http.NewRequestWithContext(ctx, http.MethodHead, endpointStr, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := client.Do(req) | 
					
						
							| 
									
										
										
										
											2020-04-29 04:57:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-29 04:57:56 +08:00
										 |  |  | 	defer xhttp.DrainBody(resp.Body) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } |