| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	crand "crypto/rand" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	"hash/crc32" | 
					
						
							| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	"regexp" | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 	"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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 	"github.com/dustin/go-humanize" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	"github.com/klauspost/compress/zip" | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/dsync" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/handlers" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger/message/log" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	iampolicy "github.com/minio/pkg/iam/policy" | 
					
						
							| 
									
										
										
										
											2021-06-15 05:54:37 +08:00
										 |  |  | 	xnet "github.com/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	"github.com/secure-io/sio-go" | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2021-09-24 03:57:21 +08:00
										 |  |  | 			err := AdminError{ | 
					
						
							|  |  |  | 				Code:       AdminUpdateApplyFailure, | 
					
						
							|  |  |  | 				Message:    nerr.Err.Error(), | 
					
						
							|  |  |  | 				StatusCode: http.StatusInternalServerError, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-29 06:04:43 +08:00
										 |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							| 
									
										
										
										
											2021-09-24 03:57:21 +08:00
										 |  |  | 			logger.LogIf(ctx, fmt.Errorf("server update failed with %w", err)) | 
					
						
							| 
									
										
										
										
											2020-07-23 23:03:31 +08:00
										 |  |  | 			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 { | 
					
						
							| 
									
										
										
										
											2021-09-24 03:57:21 +08:00
										 |  |  | 		logger.LogIf(ctx, fmt.Errorf("server update 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 { | 
					
						
							| 
									
										
										
										
											2021-08-28 04:07:55 +08:00
										 |  |  | 				if val, ok := entryMap[lockReqInfo.Name]; ok { | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 					val.ServerList = append(val.ServerList, peerLock.Addr) | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-08-28 04:07:55 +08:00
										 |  |  | 					entryMap[lockReqInfo.Name] = 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 | 
					
						
							| 
									
										
										
										
											2021-06-22 08:47:35 +08:00
										 |  |  | 	var lockers []dsync.NetLocker | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 	for _, path := range strings.Split(vars["paths"], ",") { | 
					
						
							|  |  |  | 		if path == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		args.Resources = append(args.Resources, path) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-22 08:47:35 +08:00
										 |  |  | 	for _, lks := range z.serverPools[0].erasureLockers { | 
					
						
							|  |  |  | 		lockers = append(lockers, lks...) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, locker := range lockers { | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | 		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
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if countStr := r.Form.Get("count"); countStr != "" { | 
					
						
							| 
									
										
										
										
											2020-06-20 21:33:01 +08:00
										 |  |  | 		var err error | 
					
						
							|  |  |  | 		count, err = strconv.Atoi(countStr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	stale := r.Form.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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	hip, errCode := extractHealInitParams(mux.Vars(r), r.Form, 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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-28 03:55:56 +08:00
										 |  |  | func (a adminAPIHandlers) SpeedtestHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "SpeedtestHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.HealAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !globalIsErasure { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	sizeStr := r.Form.Get(peerRESTSize) | 
					
						
							|  |  |  | 	durationStr := r.Form.Get(peerRESTDuration) | 
					
						
							|  |  |  | 	concurrentStr := r.Form.Get(peerRESTConcurrent) | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 	autotuneStr := r.Form.Get("autotune") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var autotune bool | 
					
						
							|  |  |  | 	if autotuneStr != "" { | 
					
						
							|  |  |  | 		autotune = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-28 03:55:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	size, err := strconv.Atoi(sizeStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		size = 64 * humanize.MiByte | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	concurrent, err := strconv.Atoi(concurrentStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		concurrent = 32 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	duration, err := time.ParseDuration(durationStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		duration = time.Second * 10 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 	deleteBucket := func() { | 
					
						
							|  |  |  | 		loc := pathJoin(minioMetaSpeedTestBucket, minioMetaSpeedTestBucketPrefix) | 
					
						
							|  |  |  | 		objectAPI.DeleteBucket(context.Background(), loc, DeleteBucketOptions{ | 
					
						
							|  |  |  | 			Force:      true, | 
					
						
							|  |  |  | 			NoRecreate: true, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 	keepAliveTicker := time.NewTicker(500 * time.Millisecond) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 	enc := json.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											2021-11-03 06:27:03 +08:00
										 |  |  | 	ch := speedTest(ctx, size, concurrent, duration, autotune) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		case <-keepAliveTicker.C: | 
					
						
							|  |  |  | 			// Write a blank entry to prevent client from disconnecting
 | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 			if err := enc.Encode(madmin.SpeedTestResult{}); err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 				return | 
					
						
							| 
									
										
										
										
											2021-11-03 06:27:03 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case result, ok := <-ch: | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 				deleteBucket() | 
					
						
							| 
									
										
										
										
											2021-11-03 06:27:03 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 			if err := enc.Encode(result); err != nil { | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 			w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2021-09-11 08:43:34 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-28 03:55:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-28 02:37:47 +08:00
										 |  |  | // Admin API errors
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	AdminUpdateUnexpectedFailure = "XMinioAdminUpdateUnexpectedFailure" | 
					
						
							|  |  |  | 	AdminUpdateURLNotReachable   = "XMinioAdminUpdateURLNotReachable" | 
					
						
							|  |  |  | 	AdminUpdateApplyFailure      = "XMinioAdminUpdateApplyFailure" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | // Returns true if the madmin.TraceInfo should be traced,
 | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | // false if certain conditions are not met.
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | // - input entry is not of the type *madmin.TraceInfo*
 | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | // - errOnly entries are to be traced, not status code 2xx, 3xx.
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | // - madmin.TraceInfo type is asked by opts
 | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | func mustTrace(entry interface{}, opts madmin.ServiceTraceOpts) (shouldTrace bool) { | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	trcInfo, ok := entry.(madmin.TraceInfo) | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 	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 { | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 		case madmin.TraceOS: | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 			latency = trcInfo.OSStats.Duration | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 		case madmin.TraceStorage: | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 			latency = trcInfo.StorageStats.Duration | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 		case madmin.TraceHTTP: | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 			latency = trcInfo.CallStats.Latency | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if latency < opts.Threshold { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	if opts.Internal && trcInfo.TraceType == madmin.TraceHTTP && HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath+SlashSeparator) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	if opts.S3 && trcInfo.TraceType == madmin.TraceHTTP && !HasPrefix(trcInfo.ReqInfo.Path, minioReservedBucketPath+SlashSeparator) { | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	if opts.Storage && trcInfo.TraceType == madmin.TraceStorage { | 
					
						
							| 
									
										
										
										
											2020-11-21 14:52:17 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 23:52:02 +08:00
										 |  |  | 	return opts.OS && trcInfo.TraceType == madmin.TraceOS | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func extractTraceOptions(r *http.Request) (opts madmin.ServiceTraceOpts, err error) { | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	q := r.Form | 
					
						
							| 
									
										
										
										
											2021-03-27 14:24:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-07-13 01:49:50 +08:00
										 |  |  | 			if len(traceCh) == 0 { | 
					
						
							|  |  |  | 				// Flush if nothing is queued
 | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 		case <-keepAliveTicker.C: | 
					
						
							| 
									
										
										
										
											2021-07-13 01:49:50 +08:00
										 |  |  | 			if len(traceCh) > 0 { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-01 02:08:39 +08:00
										 |  |  | 			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 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	node := r.Form.Get("node") | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 	// limit buffered console entries if client requested it.
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	limitStr := r.Form.Get("limit") | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 	limitLines, err := strconv.Atoi(limitStr) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		limitLines = 10 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	logKind := r.Form.Get("logType") | 
					
						
							| 
									
										
										
										
											2019-10-12 09:50:54 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-07-13 01:49:50 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if len(logCh) == 0 { | 
					
						
							|  |  |  | 				// Flush if nothing is queued
 | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case <-keepAliveTicker.C: | 
					
						
							| 
									
										
										
										
											2021-07-13 01:49:50 +08:00
										 |  |  | 			if len(logCh) > 0 { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-04 02:10:48 +08:00
										 |  |  | 			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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if err := GlobalKMS.CreateKey(r.Form.Get("key-id")); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-09 09:50:43 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-12 06:04:26 +08:00
										 |  |  | // KMSKeyStatusHandler - GET /minio/admin/v3/kms/status
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) KMSStatusHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "KMSStatus") | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.KMSKeyStatusAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if GlobalKMS == nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrKMSNotConfigured), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stat, err := GlobalKMS.Stat() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	status := madmin.KMSStatus{ | 
					
						
							|  |  |  | 		Name:         stat.Name, | 
					
						
							|  |  |  | 		DefaultKeyID: stat.DefaultKey, | 
					
						
							|  |  |  | 		Endpoints:    make(map[string]madmin.ItemState, len(stat.Endpoints)), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, endpoint := range stat.Endpoints { | 
					
						
							|  |  |  | 		status.Endpoints[endpoint] = madmin.ItemOnline // TODO(aead): Implement an online check for mTLS
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := json.Marshal(status) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), err.Error(), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, resp) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-05-18 22:22:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	keyID := r.Form.Get("key-id") | 
					
						
							| 
									
										
										
										
											2019-09-05 04:19:44 +08:00
										 |  |  | 	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-05-18 22:22:31 +08:00
										 |  |  | 	decryptedKey, err := GlobalKMS.DecryptKey(key.KeyID, key.Ciphertext, 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | func getServerInfo(ctx context.Context, r *http.Request) madmin.InfoMessage { | 
					
						
							|  |  |  | 	kmsStat := fetchKMSStatus() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ldap := madmin.LDAP{} | 
					
						
							|  |  |  | 	if globalLDAPConfig.Enabled { | 
					
						
							|  |  |  | 		ldapConn, err := globalLDAPConfig.Connect() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			ldap.Status = string(madmin.ItemOffline) | 
					
						
							|  |  |  | 		} else if ldapConn == nil { | 
					
						
							|  |  |  | 			ldap.Status = "Not Configured" | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			// Close ldap connection to avoid leaks.
 | 
					
						
							|  |  |  | 			ldapConn.Close() | 
					
						
							|  |  |  | 			ldap.Status = string(madmin.ItemOnline) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	log, audit := fetchLoggerInfo() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get the notification target info
 | 
					
						
							|  |  |  | 	notifyTarget := fetchLambdaInfo() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	local := getLocalServerProperty(globalEndpoints, r) | 
					
						
							|  |  |  | 	servers := globalNotificationSys.ServerInfo() | 
					
						
							|  |  |  | 	servers = append(servers, local) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	assignPoolNumbers(servers) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var backend interface{} | 
					
						
							|  |  |  | 	mode := madmin.ItemInitializing | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	buckets := madmin.Buckets{} | 
					
						
							|  |  |  | 	objects := madmin.Objects{} | 
					
						
							|  |  |  | 	usage := madmin.Usage{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI := newObjectLayerFn() | 
					
						
							|  |  |  | 	if objectAPI != nil { | 
					
						
							|  |  |  | 		mode = madmin.ItemOnline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// 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} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			buckets = madmin.Buckets{Error: err.Error()} | 
					
						
							|  |  |  | 			objects = madmin.Objects{Error: err.Error()} | 
					
						
							|  |  |  | 			usage = madmin.Usage{Error: err.Error()} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Fetching the backend information
 | 
					
						
							|  |  |  | 		backendInfo := objectAPI.BackendInfo() | 
					
						
							|  |  |  | 		if backendInfo.Type == madmin.Erasure { | 
					
						
							|  |  |  | 			// 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, | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	domain := globalDomainNames | 
					
						
							|  |  |  | 	services := madmin.Services{ | 
					
						
							|  |  |  | 		KMS:           kmsStat, | 
					
						
							|  |  |  | 		LDAP:          ldap, | 
					
						
							|  |  |  | 		Logger:        log, | 
					
						
							|  |  |  | 		Audit:         audit, | 
					
						
							|  |  |  | 		Notifications: notifyTarget, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return madmin.InfoMessage{ | 
					
						
							|  |  |  | 		Mode:         string(mode), | 
					
						
							|  |  |  | 		Domain:       domain, | 
					
						
							|  |  |  | 		Region:       globalServerRegion, | 
					
						
							|  |  |  | 		SQSARN:       globalNotificationSys.GetARNList(false), | 
					
						
							|  |  |  | 		DeploymentID: globalDeploymentID, | 
					
						
							|  |  |  | 		Buckets:      buckets, | 
					
						
							|  |  |  | 		Objects:      objects, | 
					
						
							|  |  |  | 		Usage:        usage, | 
					
						
							|  |  |  | 		Services:     services, | 
					
						
							|  |  |  | 		Backend:      backend, | 
					
						
							|  |  |  | 		Servers:      servers, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	query := r.Form | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 	healthInfo := madmin.HealthInfo{Version: madmin.HealthInfoVersion} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 	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
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 	deadline := 1 * time.Hour | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	if dstr := r.Form.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-30 11:55:21 +08:00
										 |  |  | 	lkctx, 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-30 11:55:21 +08:00
										 |  |  | 	defer nsLock.Unlock(lkctx.Cancel) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	hostAnonymizer := createHostAnonymizer() | 
					
						
							|  |  |  | 	// anonAddr - Anonymizes hosts in given input string.
 | 
					
						
							|  |  |  | 	anonAddr := func(addr string) string { | 
					
						
							|  |  |  | 		newAddr, found := hostAnonymizer[addr] | 
					
						
							|  |  |  | 		if found { | 
					
						
							|  |  |  | 			return newAddr | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		// If we reach here, it means that the given addr doesn't contain any of the hosts.
 | 
					
						
							|  |  |  | 		// Return it as is. Can happen for drive paths in non-distributed mode
 | 
					
						
							|  |  |  | 		return addr | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	// anonymizedAddr - Updated the addr of the node info with anonymized one
 | 
					
						
							|  |  |  | 	anonymizeAddr := func(info madmin.NodeInfo) { | 
					
						
							|  |  |  | 		info.SetAddr(anonAddr(info.GetAddr())) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getAndWriteCPUs := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("syscpu") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localCPUInfo := madmin.GetCPUs(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localCPUInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.CPUInfo = append(healthInfo.Sys.CPUInfo, localCPUInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerCPUInfo := globalNotificationSys.GetCPUs(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, cpuInfo := range peerCPUInfo { | 
					
						
							|  |  |  | 				anonymizeAddr(&cpuInfo) | 
					
						
							|  |  |  | 				healthInfo.Sys.CPUInfo = append(healthInfo.Sys.CPUInfo, cpuInfo) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWritePartitions := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("sysdrivehw") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localPartitions := madmin.GetPartitions(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localPartitions) | 
					
						
							|  |  |  | 			healthInfo.Sys.Partitions = append(healthInfo.Sys.Partitions, localPartitions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerPartitions := globalNotificationSys.GetPartitions(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, p := range peerPartitions { | 
					
						
							|  |  |  | 				anonymizeAddr(&p) | 
					
						
							|  |  |  | 				healthInfo.Sys.Partitions = append(healthInfo.Sys.Partitions, p) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWriteOSInfo := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("sysosinfo") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localOSInfo := madmin.GetOSInfo(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localOSInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.OSInfo = append(healthInfo.Sys.OSInfo, localOSInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerOSInfos := globalNotificationSys.GetOSInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, o := range peerOSInfos { | 
					
						
							|  |  |  | 				anonymizeAddr(&o) | 
					
						
							|  |  |  | 				healthInfo.Sys.OSInfo = append(healthInfo.Sys.OSInfo, o) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWriteMemInfo := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("sysmem") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localMemInfo := madmin.GetMemInfo(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localMemInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.MemInfo = append(healthInfo.Sys.MemInfo, localMemInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerMemInfos := globalNotificationSys.GetMemInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, m := range peerMemInfos { | 
					
						
							|  |  |  | 				anonymizeAddr(&m) | 
					
						
							|  |  |  | 				healthInfo.Sys.MemInfo = append(healthInfo.Sys.MemInfo, m) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-30 14:05:34 +08:00
										 |  |  | 	getAndWriteSysErrors := func() { | 
					
						
							|  |  |  | 		if query.Get(string(madmin.HealthDataTypeSysErrors)) == "true" { | 
					
						
							|  |  |  | 			localSysErrors := madmin.GetSysErrors(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localSysErrors) | 
					
						
							|  |  |  | 			healthInfo.Sys.SysErrs = append(healthInfo.Sys.SysErrs, localSysErrors) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerSysErrs := globalNotificationSys.GetSysErrors(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, se := range peerSysErrs { | 
					
						
							|  |  |  | 				anonymizeAddr(&se) | 
					
						
							|  |  |  | 				healthInfo.Sys.SysErrs = append(healthInfo.Sys.SysErrs, se) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 08:09:37 +08:00
										 |  |  | 	getAndWriteSysConfig := func() { | 
					
						
							|  |  |  | 		if query.Get(string(madmin.HealthDataTypeSysConfig)) == "true" { | 
					
						
							|  |  |  | 			localSysConfig := madmin.GetSysConfig(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localSysConfig) | 
					
						
							|  |  |  | 			healthInfo.Sys.SysConfig = append(healthInfo.Sys.SysConfig, localSysConfig) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerSysConfig := globalNotificationSys.GetSysConfig(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, sc := range peerSysConfig { | 
					
						
							|  |  |  | 				anonymizeAddr(&sc) | 
					
						
							|  |  |  | 				healthInfo.Sys.SysConfig = append(healthInfo.Sys.SysConfig, sc) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:58:40 +08:00
										 |  |  | 	getAndWriteSysServices := func() { | 
					
						
							|  |  |  | 		if query.Get(string(madmin.HealthDataTypeSysServices)) == "true" { | 
					
						
							|  |  |  | 			localSysServices := madmin.GetSysServices(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localSysServices) | 
					
						
							|  |  |  | 			healthInfo.Sys.SysServices = append(healthInfo.Sys.SysServices, localSysServices) | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			peerSysServices := globalNotificationSys.GetSysServices(deadlinedCtx) | 
					
						
							| 
									
										
										
										
											2021-08-25 08:09:37 +08:00
										 |  |  | 			for _, ss := range peerSysServices { | 
					
						
							|  |  |  | 				anonymizeAddr(&ss) | 
					
						
							|  |  |  | 				healthInfo.Sys.SysServices = append(healthInfo.Sys.SysServices, ss) | 
					
						
							| 
									
										
										
										
											2021-08-13 09:58:40 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	anonymizeCmdLine := func(cmdLine string) string { | 
					
						
							|  |  |  | 		if !globalIsDistErasure { | 
					
						
							|  |  |  | 			// FS mode - single server - hard code to `server1`
 | 
					
						
							| 
									
										
										
										
											2021-07-20 06:16:26 +08:00
										 |  |  | 			anonCmdLine := strings.Replace(cmdLine, globalLocalNodeName, "server1", -1) | 
					
						
							|  |  |  | 			return strings.Replace(anonCmdLine, globalMinioConsoleHost, "server1", -1) | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Server start command regex groups:
 | 
					
						
							|  |  |  | 		// 1 - minio server
 | 
					
						
							|  |  |  | 		// 2 - flags e.g. `--address :9000 --certs-dir /etc/minio/certs`
 | 
					
						
							|  |  |  | 		// 3 - pool args e.g. `https://node{01...16}.domain/data/disk{001...204} https://node{17...32}.domain/data/disk{001...204}`
 | 
					
						
							|  |  |  | 		re := regexp.MustCompile(`^(.*minio\s+server\s+)(--[^\s]+\s+[^\s]+\s+)*(.*)`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// stays unchanged in the anonymized version
 | 
					
						
							|  |  |  | 		cmdLineWithoutPools := re.ReplaceAllString(cmdLine, `$1$2`) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// to be anonymized
 | 
					
						
							|  |  |  | 		poolsArgs := re.ReplaceAllString(cmdLine, `$3`) | 
					
						
							|  |  |  | 		var anonPools []string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !(strings.Contains(poolsArgs, "{") && strings.Contains(poolsArgs, "}")) { | 
					
						
							|  |  |  | 			// No ellipses pattern. Anonymize host name from every pool arg
 | 
					
						
							|  |  |  | 			pools := strings.Fields(poolsArgs) | 
					
						
							|  |  |  | 			anonPools = make([]string, len(pools)) | 
					
						
							|  |  |  | 			for _, arg := range pools { | 
					
						
							|  |  |  | 				anonPools = append(anonPools, anonAddr(arg)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return cmdLineWithoutPools + strings.Join(anonPools, " ") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		// Ellipses pattern in pool args. Regex groups:
 | 
					
						
							|  |  |  | 		// 1 - server prefix
 | 
					
						
							|  |  |  | 		// 2 - number sequence for servers
 | 
					
						
							|  |  |  | 		// 3 - server suffix
 | 
					
						
							|  |  |  | 		// 4 - drive prefix (starting with /)
 | 
					
						
							|  |  |  | 		// 5 - number sequence for drives
 | 
					
						
							|  |  |  | 		// 6 - drive suffix
 | 
					
						
							|  |  |  | 		re = regexp.MustCompile(`([^\s^{]*)({\d+...\d+})?([^\s^{^/]*)(/[^\s^{]*)({\d+...\d+})?([^\s]*)`) | 
					
						
							|  |  |  | 		poolsMatches := re.FindAllStringSubmatch(poolsArgs, -1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		anonPools = make([]string, len(poolsMatches)) | 
					
						
							|  |  |  | 		idxMap := map[int]string{ | 
					
						
							|  |  |  | 			1: "spfx", | 
					
						
							|  |  |  | 			3: "ssfx", | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for pi, poolsMatch := range poolsMatches { | 
					
						
							|  |  |  | 			// Replace the server prefix/suffix with anonymized ones
 | 
					
						
							|  |  |  | 			for idx, lbl := range idxMap { | 
					
						
							|  |  |  | 				if len(poolsMatch[idx]) > 0 { | 
					
						
							|  |  |  | 					poolsMatch[idx] = fmt.Sprintf("%s%d", lbl, crc32.ChecksumIEEE([]byte(poolsMatch[idx]))) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Remove the original pools args present at index 0
 | 
					
						
							|  |  |  | 			anonPools[pi] = strings.Join(poolsMatch[1:], "") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return cmdLineWithoutPools + strings.Join(anonPools, " ") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	anonymizeProcInfo := func(p *madmin.ProcInfo) { | 
					
						
							|  |  |  | 		p.CmdLine = anonymizeCmdLine(p.CmdLine) | 
					
						
							|  |  |  | 		anonymizeAddr(p) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getAndWriteProcInfo := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("sysprocess") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localProcInfo := madmin.GetProcInfo(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeProcInfo(&localProcInfo) | 
					
						
							|  |  |  | 			healthInfo.Sys.ProcInfo = append(healthInfo.Sys.ProcInfo, localProcInfo) | 
					
						
							|  |  |  | 			peerProcInfos := globalNotificationSys.GetProcInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			for _, p := range peerProcInfos { | 
					
						
							|  |  |  | 				anonymizeProcInfo(&p) | 
					
						
							|  |  |  | 				healthInfo.Sys.ProcInfo = append(healthInfo.Sys.ProcInfo, p) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWriteMinioConfig := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("minioconfig") == "true" { | 
					
						
							|  |  |  | 			config, err := readServerConfig(ctx, objectAPI) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				healthInfo.Minio.Config = madmin.MinioConfig{ | 
					
						
							|  |  |  | 					Error: err.Error(), | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				healthInfo.Minio.Config = madmin.MinioConfig{ | 
					
						
							| 
									
										
										
										
											2021-06-03 23:15:44 +08:00
										 |  |  | 					Config: config.RedactSensitiveInfo(), | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWriteDrivePerfInfo := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("perfdrive") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localDPI := getDrivePerfInfos(deadlinedCtx, globalLocalNodeName) | 
					
						
							|  |  |  | 			anonymizeAddr(&localDPI) | 
					
						
							|  |  |  | 			healthInfo.Perf.Drives = append(healthInfo.Perf.Drives, localDPI) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 			perfCh := globalNotificationSys.GetDrivePerfInfos(deadlinedCtx) | 
					
						
							|  |  |  | 			for perfInfo := range perfCh { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 				anonymizeAddr(&perfInfo) | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 				healthInfo.Perf.Drives = append(healthInfo.Perf.Drives, perfInfo) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 				partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	anonymizeNetPerfInfo := func(npi *madmin.NetPerfInfo) { | 
					
						
							|  |  |  | 		anonymizeAddr(npi) | 
					
						
							|  |  |  | 		rps := npi.RemotePeers | 
					
						
							|  |  |  | 		for idx, peer := range rps { | 
					
						
							|  |  |  | 			anonymizeAddr(&peer) | 
					
						
							|  |  |  | 			rps[idx] = peer | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		npi.RemotePeers = rps | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-03-27 12:07:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	getAndWriteNetPerfInfo := func() { | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if globalIsDistErasure && query.Get("perfnet") == "true" { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			localNPI := globalNotificationSys.GetNetPerfInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			anonymizeNetPerfInfo(&localNPI) | 
					
						
							|  |  |  | 			healthInfo.Perf.Net = append(healthInfo.Perf.Net, localNPI) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 			netInfos := globalNotificationSys.DispatchNetPerfChan(deadlinedCtx) | 
					
						
							|  |  |  | 			for netInfo := range netInfos { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 				anonymizeNetPerfInfo(&netInfo) | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 				healthInfo.Perf.Net = append(healthInfo.Perf.Net, netInfo) | 
					
						
							|  |  |  | 				partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-05-23 08:56:45 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 			ppi := globalNotificationSys.GetParallelNetPerfInfo(deadlinedCtx) | 
					
						
							|  |  |  | 			anonymizeNetPerfInfo(&ppi) | 
					
						
							|  |  |  | 			healthInfo.Perf.NetParallel = ppi | 
					
						
							| 
									
										
										
										
											2020-11-21 04:52:53 +08:00
										 |  |  | 			partialWrite(healthInfo) | 
					
						
							| 
									
										
										
										
											2020-04-17 01:56:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	anonymizeNetwork := func(network map[string]string) map[string]string { | 
					
						
							|  |  |  | 		anonNetwork := map[string]string{} | 
					
						
							|  |  |  | 		for endpoint, status := range network { | 
					
						
							|  |  |  | 			anonEndpoint := anonAddr(endpoint) | 
					
						
							|  |  |  | 			anonNetwork[anonEndpoint] = status | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return anonNetwork | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	anonymizeDrives := func(drives []madmin.Disk) []madmin.Disk { | 
					
						
							|  |  |  | 		anonDrives := []madmin.Disk{} | 
					
						
							|  |  |  | 		for _, drive := range drives { | 
					
						
							|  |  |  | 			drive.Endpoint = anonAddr(drive.Endpoint) | 
					
						
							|  |  |  | 			anonDrives = append(anonDrives, drive) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return anonDrives | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(healthInfoCh) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		partialWrite(healthInfo) // Write first message with only version populated
 | 
					
						
							|  |  |  | 		getAndWriteCPUs() | 
					
						
							|  |  |  | 		getAndWritePartitions() | 
					
						
							|  |  |  | 		getAndWriteOSInfo() | 
					
						
							|  |  |  | 		getAndWriteMemInfo() | 
					
						
							|  |  |  | 		getAndWriteProcInfo() | 
					
						
							|  |  |  | 		getAndWriteMinioConfig() | 
					
						
							|  |  |  | 		getAndWriteDrivePerfInfo() | 
					
						
							|  |  |  | 		getAndWriteNetPerfInfo() | 
					
						
							| 
									
										
										
										
											2021-07-30 14:05:34 +08:00
										 |  |  | 		getAndWriteSysErrors() | 
					
						
							| 
									
										
										
										
											2021-08-13 09:58:40 +08:00
										 |  |  | 		getAndWriteSysServices() | 
					
						
							| 
									
										
										
										
											2021-08-25 08:09:37 +08:00
										 |  |  | 		getAndWriteSysConfig() | 
					
						
							| 
									
										
										
										
											2020-09-16 09:02:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 		if query.Get("minioinfo") == "true" { | 
					
						
							|  |  |  | 			infoMessage := getServerInfo(ctx, r) | 
					
						
							|  |  |  | 			servers := []madmin.ServerInfo{} | 
					
						
							|  |  |  | 			for _, server := range infoMessage.Servers { | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 				anonEndpoint := anonAddr(server.Endpoint) | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 				servers = append(servers, madmin.ServerInfo{ | 
					
						
							|  |  |  | 					State:      server.State, | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 					Endpoint:   anonEndpoint, | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 					Uptime:     server.Uptime, | 
					
						
							|  |  |  | 					Version:    server.Version, | 
					
						
							|  |  |  | 					CommitID:   server.CommitID, | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 					Network:    anonymizeNetwork(server.Network), | 
					
						
							|  |  |  | 					Drives:     anonymizeDrives(server.Disks), | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 					PoolNumber: server.PoolNumber, | 
					
						
							|  |  |  | 					MemStats: madmin.MemStats{ | 
					
						
							|  |  |  | 						Alloc:      server.MemStats.Alloc, | 
					
						
							|  |  |  | 						TotalAlloc: server.MemStats.TotalAlloc, | 
					
						
							|  |  |  | 						Mallocs:    server.MemStats.Mallocs, | 
					
						
							|  |  |  | 						Frees:      server.MemStats.Frees, | 
					
						
							|  |  |  | 						HeapAlloc:  server.MemStats.HeapAlloc, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-10-21 01:12:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 			healthInfo.Minio.Info = madmin.MinioInfo{ | 
					
						
							|  |  |  | 				Mode:         infoMessage.Mode, | 
					
						
							|  |  |  | 				Domain:       infoMessage.Domain, | 
					
						
							|  |  |  | 				Region:       infoMessage.Region, | 
					
						
							|  |  |  | 				SQSARN:       infoMessage.SQSARN, | 
					
						
							|  |  |  | 				DeploymentID: infoMessage.DeploymentID, | 
					
						
							|  |  |  | 				Buckets:      infoMessage.Buckets, | 
					
						
							|  |  |  | 				Objects:      infoMessage.Objects, | 
					
						
							|  |  |  | 				Usage:        infoMessage.Usage, | 
					
						
							|  |  |  | 				Services:     infoMessage.Services, | 
					
						
							|  |  |  | 				Backend:      infoMessage.Backend, | 
					
						
							|  |  |  | 				Servers:      servers, | 
					
						
							| 
									
										
										
										
											2021-10-21 01:12:01 +08:00
										 |  |  | 				TLS:          getTLSInfo(), | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			partialWrite(healthInfo) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-21 01:12:01 +08:00
										 |  |  | func getTLSInfo() madmin.TLSInfo { | 
					
						
							|  |  |  | 	tlsInfo := madmin.TLSInfo{ | 
					
						
							|  |  |  | 		TLSEnabled: globalIsTLS, | 
					
						
							|  |  |  | 		Certs:      []madmin.TLSCert{}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if globalIsTLS { | 
					
						
							|  |  |  | 		for _, c := range globalPublicCerts { | 
					
						
							|  |  |  | 			tlsInfo.Certs = append(tlsInfo.Certs, madmin.TLSCert{ | 
					
						
							|  |  |  | 				PubKeyAlgo:    c.PublicKeyAlgorithm.String(), | 
					
						
							|  |  |  | 				SignatureAlgo: c.SignatureAlgorithm.String(), | 
					
						
							|  |  |  | 				NotBefore:     c.NotBefore, | 
					
						
							|  |  |  | 				NotAfter:      c.NotAfter, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return tlsInfo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	bucketsRequestedString := r.Form.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
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	enc := json.NewEncoder(w) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-11-05 03:11:52 +08:00
										 |  |  | 			if err := enc.Encode(report); err != nil { | 
					
						
							| 
									
										
										
										
											2021-04-06 07:07:53 +08:00
										 |  |  | 				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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal API response
 | 
					
						
							| 
									
										
										
										
											2021-06-01 23:55:49 +08:00
										 |  |  | 	jsonBytes, err := json.Marshal(getServerInfo(ctx, r)) | 
					
						
							| 
									
										
										
										
											2019-12-12 06:27:03 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2021-10-28 22:35:28 +08:00
										 |  |  | 	for _, target := range logger.Targets() { | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 		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
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-28 22:35:28 +08:00
										 |  |  | 	for _, target := range logger.AuditTargets() { | 
					
						
							| 
									
										
										
										
											2020-10-03 07:19:44 +08:00
										 |  |  | 		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 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // getRawDataer provides an interface for getting raw FS files.
 | 
					
						
							|  |  |  | type getRawDataer interface { | 
					
						
							| 
									
										
										
										
											2021-10-22 02:20:13 +08:00
										 |  |  | 	GetRawData(ctx context.Context, volume, file string, fn func(r io.Reader, host string, disk string, filename string, info StatInfo) error) error | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // InspectDataHandler - GET /minio/admin/v3/inspect-data
 | 
					
						
							|  |  |  | // ----------
 | 
					
						
							|  |  |  | // Download file from all nodes in a zip format
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) InspectDataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "InspectData") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate request signature.
 | 
					
						
							|  |  |  | 	_, adminAPIErr := checkAdminRequestAuth(ctx, r, iampolicy.InspectDataAction, "") | 
					
						
							|  |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	o, ok := newObjectLayerFn().(getRawDataer) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	volume := r.Form.Get("volume") | 
					
						
							|  |  |  | 	file := r.Form.Get("file") | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 	if len(volume) == 0 { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidBucketName), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if len(file) == 0 { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 	file = strings.ReplaceAll(file, string(os.PathSeparator), "/") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Reject attempts to traverse parent or absolute paths.
 | 
					
						
							|  |  |  | 	if strings.Contains(file, "..") || strings.Contains(volume, "..") { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var key [32]byte | 
					
						
							|  |  |  | 	// MUST use crypto/rand
 | 
					
						
							|  |  |  | 	n, err := crand.Read(key[:]) | 
					
						
							|  |  |  | 	if err != nil || n != len(key) { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	stream, err := sio.AES_256_GCM.Stream(key[:]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Zero nonce, we only use each key once, and 32 bytes is plenty.
 | 
					
						
							|  |  |  | 	nonce := make([]byte, stream.NonceSize()) | 
					
						
							|  |  |  | 	encw := stream.EncryptWriter(w, nonce, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer encw.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Write a version for making *incompatible* changes.
 | 
					
						
							|  |  |  | 	// The AdminClient will reject any version it does not know.
 | 
					
						
							|  |  |  | 	w.Write([]byte{1}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Write key first (without encryption)
 | 
					
						
							|  |  |  | 	_, err = w.Write(key[:]) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInternalError), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize a zip writer which will provide a zipped content
 | 
					
						
							|  |  |  | 	// of profiling data of all nodes
 | 
					
						
							|  |  |  | 	zipWriter := zip.NewWriter(encw) | 
					
						
							|  |  |  | 	defer zipWriter.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-22 02:20:13 +08:00
										 |  |  | 	err = o.GetRawData(ctx, volume, file, func(r io.Reader, host, disk, filename string, si StatInfo) error { | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 		// Prefix host+disk
 | 
					
						
							|  |  |  | 		filename = path.Join(host, disk, filename) | 
					
						
							| 
									
										
										
										
											2021-10-22 02:20:13 +08:00
										 |  |  | 		if si.Dir { | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 			filename += "/" | 
					
						
							| 
									
										
										
										
											2021-10-22 02:20:13 +08:00
										 |  |  | 			si.Size = 0 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if si.Mode == 0 { | 
					
						
							|  |  |  | 			// Not, set it to default.
 | 
					
						
							|  |  |  | 			si.Mode = 0600 | 
					
						
							| 
									
										
										
										
											2021-10-02 02:50:00 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 		header, zerr := zip.FileInfoHeader(dummyFileInfo{ | 
					
						
							|  |  |  | 			name:    filename, | 
					
						
							| 
									
										
										
										
											2021-10-22 02:20:13 +08:00
										 |  |  | 			size:    si.Size, | 
					
						
							|  |  |  | 			mode:    os.FileMode(si.Mode), | 
					
						
							|  |  |  | 			modTime: si.ModTime, | 
					
						
							|  |  |  | 			isDir:   si.Dir, | 
					
						
							| 
									
										
										
										
											2021-07-10 02:29:16 +08:00
										 |  |  | 			sys:     nil, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		header.Method = zip.Deflate | 
					
						
							|  |  |  | 		zwriter, zerr := zipWriter.CreateHeader(header) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err = io.Copy(zwriter, r); err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	logger.LogIf(ctx, err) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func createHostAnonymizerForFSMode() map[string]string { | 
					
						
							|  |  |  | 	hostAnonymizer := map[string]string{ | 
					
						
							|  |  |  | 		globalLocalNodeName: "server1", | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	apiEndpoints := getAPIEndpoints() | 
					
						
							|  |  |  | 	for _, ep := range apiEndpoints { | 
					
						
							| 
									
										
										
										
											2021-08-03 12:50:20 +08:00
										 |  |  | 		if len(ep) == 0 { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if url, err := xnet.ParseHTTPURL(ep); err == nil { | 
					
						
							|  |  |  | 			// In FS mode the drive names don't include the host.
 | 
					
						
							|  |  |  | 			// So mapping just the host should be sufficient.
 | 
					
						
							|  |  |  | 			hostAnonymizer[url.Host] = "server1" | 
					
						
							| 
									
										
										
										
											2021-07-14 15:23:22 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return hostAnonymizer | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // anonymizeHost - Add entries related to given endpoint in the host anonymizer map
 | 
					
						
							|  |  |  | // The health report data can contain the hostname in various forms e.g. host, host:port,
 | 
					
						
							|  |  |  | // host:port/drivepath, full url (http://host:port/drivepath)
 | 
					
						
							|  |  |  | // The anonymizer map will have mappings for all these varients for efficiently replacing
 | 
					
						
							|  |  |  | // any of these strings to the anonymized versions at the time of health report generation.
 | 
					
						
							|  |  |  | func anonymizeHost(hostAnonymizer map[string]string, endpoint Endpoint, poolNum int, srvrNum int) { | 
					
						
							|  |  |  | 	if len(endpoint.Host) == 0 { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	currentURL := endpoint.String() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mapIfNotPresent - Maps the given key to the value only if the key is not present in the map
 | 
					
						
							|  |  |  | 	mapIfNotPresent := func(m map[string]string, key string, val string) { | 
					
						
							|  |  |  | 		_, found := m[key] | 
					
						
							|  |  |  | 		if !found { | 
					
						
							|  |  |  | 			m[key] = val | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, found := hostAnonymizer[currentURL] | 
					
						
							|  |  |  | 	if !found { | 
					
						
							|  |  |  | 		// In distributed setup, anonymized addr = 'poolNum.serverNum'
 | 
					
						
							|  |  |  | 		newHost := fmt.Sprintf("pool%d.server%d", poolNum, srvrNum) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Hostname
 | 
					
						
							|  |  |  | 		mapIfNotPresent(hostAnonymizer, endpoint.Hostname(), newHost) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		newHostPort := newHost | 
					
						
							|  |  |  | 		if len(endpoint.Port()) > 0 { | 
					
						
							|  |  |  | 			// Host + port
 | 
					
						
							|  |  |  | 			newHostPort = newHost + ":" + endpoint.Port() | 
					
						
							|  |  |  | 			mapIfNotPresent(hostAnonymizer, endpoint.Host, newHostPort) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		newHostPortPath := newHostPort | 
					
						
							|  |  |  | 		if len(endpoint.Path) > 0 { | 
					
						
							|  |  |  | 			// Host + port + path
 | 
					
						
							|  |  |  | 			currentHostPortPath := endpoint.Host + endpoint.Path | 
					
						
							|  |  |  | 			newHostPortPath = newHostPort + endpoint.Path | 
					
						
							|  |  |  | 			mapIfNotPresent(hostAnonymizer, currentHostPortPath, newHostPortPath) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Full url
 | 
					
						
							|  |  |  | 		hostAnonymizer[currentURL] = endpoint.Scheme + "://" + newHostPortPath | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // createHostAnonymizer - Creats a map of various strings to corresponding anonymized names
 | 
					
						
							|  |  |  | func createHostAnonymizer() map[string]string { | 
					
						
							|  |  |  | 	if !globalIsDistErasure { | 
					
						
							|  |  |  | 		return createHostAnonymizerForFSMode() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hostAnonymizer := map[string]string{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for poolIdx, pool := range globalEndpoints { | 
					
						
							|  |  |  | 		for srvrIdx, endpoint := range pool.Endpoints { | 
					
						
							|  |  |  | 			anonymizeHost(hostAnonymizer, endpoint, poolIdx+1, srvrIdx+1) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return hostAnonymizer | 
					
						
							|  |  |  | } |