| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2016, 2017, 2018, 2019 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	"encoding/base64" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	"github.com/tidwall/gjson" | 
					
						
							|  |  |  | 	"github.com/tidwall/sjson" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/cpu" | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/disk" | 
					
						
							| 
									
										
										
										
											2018-03-03 07:23:04 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/handlers" | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/iam/policy" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/mem" | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							| 
									
										
										
										
											2018-04-14 06:14:19 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/quick" | 
					
						
							| 
									
										
										
										
											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-01-18 02:02:58 +08:00
										 |  |  | // Type-safe query params.
 | 
					
						
							|  |  |  | type mgmtQueryKey string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 ( | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	mgmtBucket      mgmtQueryKey = "bucket" | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	mgmtPrefix                   = "prefix" | 
					
						
							|  |  |  | 	mgmtClientToken              = "clientToken" | 
					
						
							|  |  |  | 	mgmtForceStart               = "forceStart" | 
					
						
							|  |  |  | 	mgmtForceStop                = "forceStop" | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	// This struct literal represents the Admin API version that
 | 
					
						
							|  |  |  | 	// the server uses.
 | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	adminAPIVersionInfo = madmin.AdminAPIVersionInfo{ | 
					
						
							|  |  |  | 		Version: "1", | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // VersionHandler - GET /minio/admin/version
 | 
					
						
							|  |  |  | // -----------
 | 
					
						
							|  |  |  | // Returns Administration API version
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) VersionHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "Version") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(adminAPIVersionInfo) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // ServiceStatusHandler - GET /minio/admin/v1/service
 | 
					
						
							| 
									
										
										
										
											2016-12-21 10:49:48 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // Returns server version and uptime.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "ServiceStatus") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Fetch server version
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	serverVersion := madmin.ServerVersion{ | 
					
						
							|  |  |  | 		Version:  Version, | 
					
						
							|  |  |  | 		CommitID: CommitID, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	// Fetch uptimes from all peers and pick the latest.
 | 
					
						
							|  |  |  | 	uptime := getPeerUptimes(globalNotificationSys.ServerInfo(ctx)) | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 	// Create API response
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	serverStatus := madmin.ServiceStatus{ | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 		ServerVersion: serverVersion, | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 		Uptime:        uptime, | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal API response
 | 
					
						
							|  |  |  | 	jsonBytes, err := json.Marshal(serverStatus) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 	// Reply with storage information (across nodes in a
 | 
					
						
							|  |  |  | 	// distributed setup) as json.
 | 
					
						
							| 
									
										
										
										
											2017-01-06 16:37:00 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // ServiceStopNRestartHandler - POST /minio/admin/v1/service
 | 
					
						
							|  |  |  | // Body: {"action": <restart-action>}
 | 
					
						
							| 
									
										
										
										
											2016-12-21 10:49:48 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // Restarts/Stops minio server gracefully. In a distributed setup,
 | 
					
						
							|  |  |  | // restarts all the servers in the cluster.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ServiceStopNRestartHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "ServiceStopNRestart") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	var sa madmin.ServiceAction | 
					
						
							|  |  |  | 	err := json.NewDecoder(r.Body).Decode(&sa) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrRequestBodyParse), r.URL) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	var serviceSig serviceSignal | 
					
						
							|  |  |  | 	switch sa.Action { | 
					
						
							|  |  |  | 	case madmin.ServiceActionValueRestart: | 
					
						
							|  |  |  | 		serviceSig = serviceRestart | 
					
						
							|  |  |  | 	case madmin.ServiceActionValueStop: | 
					
						
							|  |  |  | 		serviceSig = serviceStop | 
					
						
							|  |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 		logger.LogIf(ctx, errors.New("Invalid service action received")) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Reply to the client before restarting minio server.
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	// Notify all other Minio peers signal service.
 | 
					
						
							|  |  |  | 	for _, nerr := range globalNotificationSys.SignalService(serviceSig) { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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 { | 
					
						
							|  |  |  | 	Uptime   time.Duration `json:"uptime"` | 
					
						
							|  |  |  | 	Version  string        `json:"version"` | 
					
						
							|  |  |  | 	CommitID string        `json:"commitID"` | 
					
						
							|  |  |  | 	Region   string        `json:"region"` | 
					
						
							|  |  |  | 	SQSARN   []string      `json:"sqsARN"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerConnStats holds transferred bytes from/to the server
 | 
					
						
							|  |  |  | type ServerConnStats struct { | 
					
						
							|  |  |  | 	TotalInputBytes  uint64 `json:"transferred"` | 
					
						
							|  |  |  | 	TotalOutputBytes uint64 `json:"received"` | 
					
						
							|  |  |  | 	Throughput       uint64 `json:"throughput,omitempty"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | // ServerHTTPMethodStats holds total number of HTTP operations from/to the server,
 | 
					
						
							|  |  |  | // including the average duration the call was spent.
 | 
					
						
							|  |  |  | type ServerHTTPMethodStats struct { | 
					
						
							|  |  |  | 	Count       uint64 `json:"count"` | 
					
						
							|  |  |  | 	AvgDuration string `json:"avgDuration"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerHTTPStats holds all type of http operations performed to/from the server
 | 
					
						
							|  |  |  | // including their average execution time.
 | 
					
						
							|  |  |  | type ServerHTTPStats struct { | 
					
						
							|  |  |  | 	TotalHEADStats     ServerHTTPMethodStats `json:"totalHEADs"` | 
					
						
							|  |  |  | 	SuccessHEADStats   ServerHTTPMethodStats `json:"successHEADs"` | 
					
						
							|  |  |  | 	TotalGETStats      ServerHTTPMethodStats `json:"totalGETs"` | 
					
						
							|  |  |  | 	SuccessGETStats    ServerHTTPMethodStats `json:"successGETs"` | 
					
						
							|  |  |  | 	TotalPUTStats      ServerHTTPMethodStats `json:"totalPUTs"` | 
					
						
							|  |  |  | 	SuccessPUTStats    ServerHTTPMethodStats `json:"successPUTs"` | 
					
						
							|  |  |  | 	TotalPOSTStats     ServerHTTPMethodStats `json:"totalPOSTs"` | 
					
						
							|  |  |  | 	SuccessPOSTStats   ServerHTTPMethodStats `json:"successPOSTs"` | 
					
						
							|  |  |  | 	TotalDELETEStats   ServerHTTPMethodStats `json:"totalDELETEs"` | 
					
						
							|  |  |  | 	SuccessDELETEStats ServerHTTPMethodStats `json:"successDELETEs"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | // ServerInfoData holds storage, connections and other
 | 
					
						
							|  |  |  | // information of a given server.
 | 
					
						
							|  |  |  | type ServerInfoData struct { | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	StorageInfo StorageInfo      `json:"storage"` | 
					
						
							|  |  |  | 	ConnStats   ServerConnStats  `json:"network"` | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	HTTPStats   ServerHTTPStats  `json:"http"` | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	Properties  ServerProperties `json:"server"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | // ServerInfo holds server information result of one node
 | 
					
						
							|  |  |  | type ServerInfo struct { | 
					
						
							| 
									
										
										
										
											2017-05-15 22:28:47 +08:00
										 |  |  | 	Error string          `json:"error"` | 
					
						
							|  |  |  | 	Addr  string          `json:"addr"` | 
					
						
							|  |  |  | 	Data  *ServerInfoData `json:"data"` | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // ServerInfoHandler - GET /minio/admin/v1/info
 | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | // ----------
 | 
					
						
							|  |  |  | // Get server information
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func (a adminAPIHandlers) ServerInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	ctx := newContext(r, w, "ServerInfo") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) | 
					
						
							|  |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	serverInfo := globalNotificationSys.ServerInfo(ctx) | 
					
						
							| 
									
										
										
										
											2019-01-17 04:49:20 +08:00
										 |  |  | 	// Once we have received all the ServerInfo from peers
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	// add the local peer server info as well.
 | 
					
						
							|  |  |  | 	serverInfo = append(serverInfo, ServerInfo{ | 
					
						
							|  |  |  | 		Addr: thisAddr.String(), | 
					
						
							|  |  |  | 		Data: &ServerInfoData{ | 
					
						
							|  |  |  | 			StorageInfo: objectAPI.StorageInfo(ctx), | 
					
						
							|  |  |  | 			ConnStats:   globalConnStats.toServerConnStats(), | 
					
						
							|  |  |  | 			HTTPStats:   globalHTTPStats.toServerHTTPStats(), | 
					
						
							|  |  |  | 			Properties: ServerProperties{ | 
					
						
							|  |  |  | 				Uptime:   UTCNow().Sub(globalBootTime), | 
					
						
							|  |  |  | 				Version:  Version, | 
					
						
							|  |  |  | 				CommitID: CommitID, | 
					
						
							|  |  |  | 				SQSARN:   globalNotificationSys.GetARNList(), | 
					
						
							|  |  |  | 				Region:   globalServerConfig.GetRegion(), | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	// Marshal API response
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	jsonBytes, err := json.Marshal(serverInfo) | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-16 02:45:45 +08:00
										 |  |  | 	// Reply with storage information (across nodes in a
 | 
					
						
							|  |  |  | 	// distributed setup) as json.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | // ServerDrivesPerfInfo holds information about address, performance
 | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | // of all drives on one server. It also reports any errors if encountered
 | 
					
						
							|  |  |  | // while trying to reach this server.
 | 
					
						
							|  |  |  | type ServerDrivesPerfInfo struct { | 
					
						
							|  |  |  | 	Addr  string             `json:"addr"` | 
					
						
							|  |  |  | 	Error string             `json:"error,omitempty"` | 
					
						
							|  |  |  | 	Perf  []disk.Performance `json:"perf"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | // ServerCPULoadInfo holds informantion about cpu utilization
 | 
					
						
							|  |  |  | // of one minio node. It also reports any errors if encountered
 | 
					
						
							|  |  |  | // while trying to reach this server.
 | 
					
						
							|  |  |  | type ServerCPULoadInfo struct { | 
					
						
							| 
									
										
										
										
											2019-01-30 15:17:32 +08:00
										 |  |  | 	Addr         string     `json:"addr"` | 
					
						
							|  |  |  | 	Error        string     `json:"error,omitempty"` | 
					
						
							|  |  |  | 	Load         []cpu.Load `json:"load"` | 
					
						
							|  |  |  | 	HistoricLoad []cpu.Load `json:"historicLoad"` | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ServerMemUsageInfo holds informantion about memory utilization
 | 
					
						
							|  |  |  | // of one minio node. It also reports any errors if encountered
 | 
					
						
							|  |  |  | // while trying to reach this server.
 | 
					
						
							|  |  |  | type ServerMemUsageInfo struct { | 
					
						
							| 
									
										
										
										
											2019-01-30 15:17:32 +08:00
										 |  |  | 	Addr          string      `json:"addr"` | 
					
						
							|  |  |  | 	Error         string      `json:"error,omitempty"` | 
					
						
							|  |  |  | 	Usage         []mem.Usage `json:"usage"` | 
					
						
							|  |  |  | 	HistoricUsage []mem.Usage `json:"historicUsage"` | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | // PerfInfoHandler - GET /minio/admin/v1/performance?perfType={perfType}
 | 
					
						
							|  |  |  | // ----------
 | 
					
						
							|  |  |  | // Get all performance information based on input type
 | 
					
						
							|  |  |  | // Supported types = drive
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) PerfInfoHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "PerfInfo") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	perfType := vars["perfType"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if perfType == "drive" { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		info := objectAPI.StorageInfo(ctx) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 		if !(info.Backend.Type == BackendFS || info.Backend.Type == BackendErasure) { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 		// Get drive performance details from local server's drive(s)
 | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 		dp := localEndpointsDrivePerf(globalEndpoints) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Notify all other Minio peers to report drive performance numbers
 | 
					
						
							|  |  |  | 		dps := globalNotificationSys.DrivePerfInfo() | 
					
						
							|  |  |  | 		dps = append(dps, dp) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Marshal API response
 | 
					
						
							|  |  |  | 		jsonBytes, err := json.Marshal(dps) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Reply with performance information (across nodes in a
 | 
					
						
							|  |  |  | 		// distributed setup) as json.
 | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 	} else if perfType == "cpu" { | 
					
						
							|  |  |  | 		// Get CPU load details from local server's cpu(s)
 | 
					
						
							|  |  |  | 		cpu := localEndpointsCPULoad(globalEndpoints) | 
					
						
							|  |  |  | 		// Notify all other Minio peers to report cpu load numbers
 | 
					
						
							|  |  |  | 		cpus := globalNotificationSys.CPULoadInfo() | 
					
						
							|  |  |  | 		cpus = append(cpus, cpu) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Marshal API response
 | 
					
						
							|  |  |  | 		jsonBytes, err := json.Marshal(cpus) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Reply with cpu load information (across nodes in a
 | 
					
						
							|  |  |  | 		// distributed setup) as json.
 | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							|  |  |  | 	} else if perfType == "mem" { | 
					
						
							|  |  |  | 		// Get mem usage details from local server(s)
 | 
					
						
							|  |  |  | 		m := localEndpointsMemUsage(globalEndpoints) | 
					
						
							|  |  |  | 		// Notify all other Minio peers to report mem usage numbers
 | 
					
						
							|  |  |  | 		mems := globalNotificationSys.MemUsageInfo() | 
					
						
							|  |  |  | 		mems = append(mems, m) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Marshal API response
 | 
					
						
							|  |  |  | 		jsonBytes, err := json.Marshal(mems) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-10 11:04:19 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Reply with mem usage information (across nodes in a
 | 
					
						
							|  |  |  | 		// distributed setup) as json.
 | 
					
						
							|  |  |  | 		writeSuccessResponseJSON(w, jsonBytes) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2019-01-01 01:46:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | func newLockEntry(l lockRequesterInfo, resource, server string) *madmin.LockEntry { | 
					
						
							|  |  |  | 	entry := &madmin.LockEntry{Timestamp: l.Timestamp, Resource: resource, ServerList: []string{server}, Owner: l.Node, Source: l.Source, ID: l.UID} | 
					
						
							|  |  |  | 	if l.Writer { | 
					
						
							|  |  |  | 		entry.Type = "Write" | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		entry.Type = "Read" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return entry | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func topLockEntries(peerLocks []*PeerLocks) madmin.LockEntries { | 
					
						
							|  |  |  | 	const listCount int = 10 | 
					
						
							|  |  |  | 	entryMap := make(map[string]*madmin.LockEntry) | 
					
						
							|  |  |  | 	for _, peerLock := range peerLocks { | 
					
						
							|  |  |  | 		if peerLock == nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for k, v := range peerLock.Locks { | 
					
						
							|  |  |  | 			for _, lockReqInfo := range v { | 
					
						
							|  |  |  | 				if val, ok := entryMap[lockReqInfo.UID]; ok { | 
					
						
							|  |  |  | 					val.ServerList = append(val.ServerList, peerLock.Addr) | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					entryMap[lockReqInfo.UID] = newLockEntry(lockReqInfo, k, peerLock.Addr) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var lockEntries = make(madmin.LockEntries, 0) | 
					
						
							|  |  |  | 	for _, v := range entryMap { | 
					
						
							|  |  |  | 		lockEntries = append(lockEntries, *v) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Sort(lockEntries) | 
					
						
							|  |  |  | 	if len(lockEntries) > listCount { | 
					
						
							|  |  |  | 		lockEntries = lockEntries[:listCount] | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return lockEntries | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PeerLocks holds server information result of one node
 | 
					
						
							|  |  |  | type PeerLocks struct { | 
					
						
							|  |  |  | 	Addr  string | 
					
						
							|  |  |  | 	Locks GetLocksResp | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TopLocksHandler Get list of locks in use
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) TopLocksHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "TopLocks") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 	// Method only allowed in Distributed XL mode.
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if !globalIsDistXL { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2019-02-13 20:59:36 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:22:14 +08:00
										 |  |  | 	thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	peerLocks := globalNotificationSys.GetLocks(ctx) | 
					
						
							|  |  |  | 	// Once we have received all the locks currently used from peers
 | 
					
						
							|  |  |  | 	// add the local peer locks list as well.
 | 
					
						
							|  |  |  | 	localLocks := globalLockServer.ll.DupLockMap() | 
					
						
							|  |  |  | 	peerLocks = append(peerLocks, &PeerLocks{ | 
					
						
							|  |  |  | 		Addr:  thisAddr.String(), | 
					
						
							|  |  |  | 		Locks: localLocks, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	topLocks := topLockEntries(peerLocks) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Marshal API response
 | 
					
						
							|  |  |  | 	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"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-27 12:02:05 +08:00
										 |  |  | // StartProfilingHandler - POST /minio/admin/v1/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") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2018-09-27 12:02:05 +08:00
										 |  |  | 	profiler := vars["profilerType"] | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	thisAddr, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start profiling on remote servers.
 | 
					
						
							|  |  |  | 	hostErrs := globalNotificationSys.StartProfiling(profiler) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start profiling locally as well.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		if globalProfiler != nil { | 
					
						
							|  |  |  | 			globalProfiler.Stop() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		prof, err := startProfiler(profiler, "") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			hostErrs = append(hostErrs, NotificationPeerErr{ | 
					
						
							|  |  |  | 				Host: *thisAddr, | 
					
						
							|  |  |  | 				Err:  err, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			globalProfiler = prof | 
					
						
							|  |  |  | 			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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-19 07:46:35 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, []byte(startProfilingResultInBytes)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DownloadProfilingHandler - POST /minio/admin/v1/profiling/download
 | 
					
						
							|  |  |  | // ----------
 | 
					
						
							|  |  |  | // 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") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // extractHealInitParams - Validates params for heal init API.
 | 
					
						
							|  |  |  | func extractHealInitParams(r *http.Request) (bucket, objPrefix string, | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	hs madmin.HealOpts, clientToken string, forceStart bool, forceStop bool, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	err APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket = vars[string(mgmtBucket)] | 
					
						
							|  |  |  | 	objPrefix = vars[string(mgmtPrefix)] | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	if bucket == "" { | 
					
						
							|  |  |  | 		if objPrefix != "" { | 
					
						
							|  |  |  | 			// Bucket is required if object-prefix is given
 | 
					
						
							|  |  |  | 			err = ErrHealMissingBucket | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	} else if !IsValidBucketName(bucket) { | 
					
						
							|  |  |  | 		err = ErrInvalidBucketName | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// empty prefix is valid.
 | 
					
						
							|  |  |  | 	if !IsValidObjectPrefix(objPrefix) { | 
					
						
							|  |  |  | 		err = ErrInvalidObjectName | 
					
						
							| 
									
										
										
										
											2018-01-12 02:21:41 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	qParms := r.URL.Query() | 
					
						
							|  |  |  | 	if len(qParms[string(mgmtClientToken)]) > 0 { | 
					
						
							|  |  |  | 		clientToken = qParms[string(mgmtClientToken)][0] | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	if _, ok := qParms[string(mgmtForceStart)]; ok { | 
					
						
							|  |  |  | 		forceStart = true | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	if _, ok := qParms[string(mgmtForceStop)]; ok { | 
					
						
							|  |  |  | 		forceStop = true | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// ignore body if clientToken is provided
 | 
					
						
							|  |  |  | 	if clientToken == "" { | 
					
						
							|  |  |  | 		jerr := json.NewDecoder(r.Body).Decode(&hs) | 
					
						
							|  |  |  | 		if jerr != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			logger.LogIf(context.Background(), jerr) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // HealHandler - POST /minio/admin/v1/heal/
 | 
					
						
							|  |  |  | // -----------
 | 
					
						
							|  |  |  | // 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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	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.
 | 
					
						
							|  |  |  | 	if !globalIsXL { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrHealNotImplemented), r.URL) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:11:05 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	bucket, objPrefix, hs, clientToken, forceStart, forceStop, errCode := extractHealInitParams(r) | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
 | 
					
						
							|  |  |  | 	keepConnLive := func(w http.ResponseWriter, respCh chan healResp) { | 
					
						
							|  |  |  | 		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 { | 
					
						
							|  |  |  | 			case <-ticker.C: | 
					
						
							|  |  |  | 				if !started { | 
					
						
							|  |  |  | 					// Start writing response to client
 | 
					
						
							|  |  |  | 					started = true | 
					
						
							|  |  |  | 					setCommonHeaders(w) | 
					
						
							|  |  |  | 					w.Header().Set("Content-Type", string(mimeJSON)) | 
					
						
							|  |  |  | 					// Set 200 OK status
 | 
					
						
							|  |  |  | 					w.WriteHeader(200) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Send whitespace and keep connection open
 | 
					
						
							|  |  |  | 				w.Write([]byte("\n\r")) | 
					
						
							|  |  |  | 				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, | 
					
						
							|  |  |  | 							r.URL.Path, w.Header().Get(responseRequestIDKey), | 
					
						
							|  |  |  | 							w.Header().Get(responseDeploymentIDKey))) | 
					
						
							| 
									
										
										
										
											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, | 
					
						
							|  |  |  | 							RequestID: w.Header().Get(responseRequestIDKey), | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 							HostID:    w.Header().Get(responseDeploymentIDKey), | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 						}) | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if !started { | 
					
						
							|  |  |  | 						setCommonHeaders(w) | 
					
						
							|  |  |  | 						w.Header().Set("Content-Type", 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 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// find number of disks in the setup
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	info := objectAPI.StorageInfo(ctx) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	numDisks := info.Backend.OfflineDisks + info.Backend.OnlineDisks | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	healPath := pathJoin(bucket, objPrefix) | 
					
						
							|  |  |  | 	if clientToken == "" && !forceStart && !forceStop { | 
					
						
							|  |  |  | 		nh, exists := globalAllHealState.getHealSequence(healPath) | 
					
						
							|  |  |  | 		if exists && !nh.hasEnded() && len(nh.currentStatus.Items) > 0 { | 
					
						
							|  |  |  | 			b, err := json.Marshal(madmin.HealStartSuccess{ | 
					
						
							|  |  |  | 				ClientToken:   nh.clientToken, | 
					
						
							|  |  |  | 				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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 	if clientToken != "" && !forceStart && !forceStop { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		// Since clientToken is given, fetch heal status from running
 | 
					
						
							|  |  |  | 		// heal sequence.
 | 
					
						
							|  |  |  | 		respBytes, errCode := globalAllHealState.PopHealStatusJSON( | 
					
						
							| 
									
										
										
										
											2018-11-05 11:24:16 +08:00
										 |  |  | 			healPath, 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 { | 
					
						
							|  |  |  | 	case forceStop: | 
					
						
							|  |  |  | 		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 | 
					
						
							|  |  |  | 		}() | 
					
						
							|  |  |  | 	case clientToken == "": | 
					
						
							|  |  |  | 		nh := newHealSequence(bucket, objPrefix, handlers.GetSourceIP(r), numDisks, hs, forceStart) | 
					
						
							|  |  |  | 		go func() { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 			respBytes, apiErr, errMsg := globalAllHealState.LaunchNewHealSequence(nh) | 
					
						
							|  |  |  | 			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
 | 
					
						
							|  |  |  | 	keepConnLive(w, respCh) | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // GetConfigHandler - GET /minio/admin/v1/config
 | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | // Get config.json of this minio setup.
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func (a adminAPIHandlers) GetConfigHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	ctx := newContext(r, w, "GetConfigHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	config, err := readServerConfig(ctx, objectAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 10:09:12 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	configData, err := json.MarshalIndent(config, "", "\t") | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	password := config.GetCredential().SecretKey | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	econfigData, err := madmin.EncryptData(password, configData) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, econfigData) | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | // Disable tidwall json array notation in JSON key path so
 | 
					
						
							|  |  |  | // users can set json with a key as a number.
 | 
					
						
							|  |  |  | // In tidwall json, notify.webhook.0 = val means { "notify" : { "webhook" : [val] }}
 | 
					
						
							|  |  |  | // In Minio, notify.webhook.0 = val means { "notify" : { "webhook" : {"0" : val}}}
 | 
					
						
							|  |  |  | func normalizeJSONKey(input string) (key string) { | 
					
						
							|  |  |  | 	subKeys := strings.Split(input, ".") | 
					
						
							|  |  |  | 	for i, k := range subKeys { | 
					
						
							|  |  |  | 		if i > 0 { | 
					
						
							|  |  |  | 			key += "." | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err := strconv.Atoi(k); err == nil { | 
					
						
							|  |  |  | 			key += ":" + k | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			key += k | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | func validateAdminReq(ctx context.Context, w http.ResponseWriter, r *http.Request) ObjectLayer { | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	// Get current object layer instance.
 | 
					
						
							|  |  |  | 	objectAPI := newObjectLayerFn() | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	if objectAPI == nil || globalNotificationSys == nil || globalIAMSys == nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate request signature.
 | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 	adminAPIErr := checkAdminRequestAuthType(ctx, r, "") | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if adminAPIErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(adminAPIErr), r.URL) | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return objectAPI | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetConfigHandler - GET /minio/admin/v1/config-keys
 | 
					
						
							|  |  |  | // Get some keys in config.json of this minio setup.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) GetConfigKeysHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "GetConfigKeysHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var keys []string | 
					
						
							|  |  |  | 	queries := r.URL.Query() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for k := range queries { | 
					
						
							|  |  |  | 		keys = append(keys, k) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	config, err := readServerConfig(ctx, objectAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configData, err := json.Marshal(config) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configStr := string(configData) | 
					
						
							|  |  |  | 	newConfigStr := `{}` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, key := range keys { | 
					
						
							|  |  |  | 		// sjson.Set does not return an error if key is empty
 | 
					
						
							|  |  |  | 		// we should check by ourselves here
 | 
					
						
							|  |  |  | 		if key == "" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		val := gjson.Get(configStr, key) | 
					
						
							| 
									
										
										
										
											2018-10-03 06:21:34 +08:00
										 |  |  | 		if j, ierr := sjson.Set(newConfigStr, normalizeJSONKey(key), val.Value()); ierr == nil { | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 			newConfigStr = j | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	password := config.GetCredential().SecretKey | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	econfigData, err := madmin.EncryptData(password, []byte(newConfigStr)) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, []byte(econfigData)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | // toAdminAPIErrCode - converts errXLWriteQuorum error to admin API
 | 
					
						
							|  |  |  | // specific error.
 | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | func toAdminAPIErrCode(ctx context.Context, err error) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	switch err { | 
					
						
							|  |  |  | 	case errXLWriteQuorum: | 
					
						
							|  |  |  | 		return ErrAdminConfigNoQuorum | 
					
						
							| 
									
										
										
										
											2018-08-07 01:26:40 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 		return toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | func toAdminAPIErr(ctx context.Context, err error) APIError { | 
					
						
							|  |  |  | 	return errorCodes.ToAPIErr(toAdminAPIErrCode(ctx, err)) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | // RemoveUser - DELETE /minio/admin/v1/remove-user?accessKey=<access_key>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) RemoveUser(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "RemoveUser") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	accessKey := vars["accessKey"] | 
					
						
							|  |  |  | 	if err := globalIAMSys.DeleteUser(accessKey); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | // ListUsers - GET /minio/admin/v1/list-users
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ListUsers(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ListUsers") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	allCredentials, err := globalIAMSys.ListUsers() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := json.Marshal(allCredentials) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							|  |  |  | 	econfigData, err := madmin.EncryptData(password, data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, econfigData) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | // SetUserStatus - PUT /minio/admin/v1/set-user-status?accessKey=<access_key>&status=[enabled|disabled]
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) SetUserStatus(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "SetUserStatus") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	accessKey := vars["accessKey"] | 
					
						
							|  |  |  | 	status := vars["status"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Custom IAM policies not allowed for admin user.
 | 
					
						
							|  |  |  | 	if accessKey == globalServerConfig.GetCredential().AccessKey { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := globalIAMSys.SetUserStatus(accessKey, madmin.AccountStatus(status)); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Notify all other Minio peers to reload users
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadUsers() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-17 05:55:23 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | // AddUser - PUT /minio/admin/v1/add-user?accessKey=<access_key>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) AddUser(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "AddUser") | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	accessKey := vars["accessKey"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Custom IAM policies not allowed for admin user.
 | 
					
						
							|  |  |  | 	if accessKey == globalServerConfig.GetCredential().AccessKey { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAddUserInvalidArgument), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		// More than maxConfigSize bytes were available
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							|  |  |  | 	configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	var uinfo madmin.UserInfo | 
					
						
							|  |  |  | 	if err = json.Unmarshal(configBytes, &uinfo); err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-13 15:18:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if err = globalIAMSys.SetUser(accessKey, uinfo); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Notify all other Minio peers to reload users
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadUsers() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListCannedPolicies - GET /minio/admin/v1/list-canned-policies
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ListCannedPolicies(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ListCannedPolicies") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	policies, err := globalIAMSys.ListCannedPolicies() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if err = json.NewEncoder(w).Encode(policies); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	w.(http.Flusher).Flush() | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | // RemoveCannedPolicy - DELETE /minio/admin/v1/remove-canned-policy?name=<policy_name>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) RemoveCannedPolicy(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "RemoveCannedPolicy") | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 	policyName := vars["name"] | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 	if err := globalIAMSys.DeleteCannedPolicy(policyName); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Notify all other Minio peers to reload users
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadUsers() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AddCannedPolicy - PUT /minio/admin/v1/add-canned-policy?name=<policy_name>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) AddCannedPolicy(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "AddCannedPolicy") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	policyName := vars["name"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Error out if Content-Length is missing.
 | 
					
						
							|  |  |  | 	if r.ContentLength <= 0 { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Error out if Content-Length is beyond allowed size.
 | 
					
						
							|  |  |  | 	if r.ContentLength > maxBucketPolicySize { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrEntityTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	iamPolicy, err := iampolicy.ParseConfig(io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Version in policy must not be empty
 | 
					
						
							|  |  |  | 	if iamPolicy.Version == "" { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPolicy), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 	if err = globalIAMSys.SetCannedPolicy(policyName, *iamPolicy); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Notify all other Minio peers to reload users
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadUsers() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetUserPolicy - PUT /minio/admin/v1/set-user-policy?accessKey=<access_key>&name=<policy_name>
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) SetUserPolicy(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "SetUserPolicy") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	accessKey := vars["accessKey"] | 
					
						
							|  |  |  | 	policyName := vars["name"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Custom IAM policies not allowed for admin user.
 | 
					
						
							|  |  |  | 	if accessKey == globalServerConfig.GetCredential().AccessKey { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := globalIAMSys.SetUserPolicy(accessKey, policyName); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-17 03:48:19 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Notify all other Minio peers to reload users
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadUsers() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-12-19 06:39:21 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetConfigHandler - PUT /minio/admin/v1/config
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "SetConfigHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 { | 
					
						
							|  |  |  | 		// More than maxConfigSize bytes were available
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Validate JSON provided in the request body: check the
 | 
					
						
							|  |  |  | 	// client has not sent JSON objects with duplicate keys.
 | 
					
						
							| 
									
										
										
										
											2018-04-14 06:14:19 +08:00
										 |  |  | 	if err = quick.CheckDuplicateKeys(string(configBytes)); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	var config serverConfig | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if err = json.Unmarshal(configBytes, &config); err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2017-09-08 02:16:13 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 	// If credentials for the server are provided via environment,
 | 
					
						
							|  |  |  | 	// then credentials in the provided configuration must match.
 | 
					
						
							| 
									
										
										
										
											2017-09-08 02:16:13 +08:00
										 |  |  | 	if globalIsEnvCreds { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		if !globalServerConfig.GetCredential().Equal(config.Credential) { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminCredentialsMismatch), r.URL) | 
					
						
							| 
									
										
										
										
											2017-09-08 02:16:13 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	if err = config.Validate(); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-07-19 02:22:29 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-23 02:51:26 +08:00
										 |  |  | 	if err = config.TestNotificationTargets(); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-10-23 02:51:26 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if err = saveServerConfig(ctx, objectAPI, &config); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	// Reply to the client before restarting minio server.
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | func convertValueType(elem []byte, jsonType gjson.Type) (interface{}, error) { | 
					
						
							|  |  |  | 	str := string(elem) | 
					
						
							|  |  |  | 	switch jsonType { | 
					
						
							|  |  |  | 	case gjson.False, gjson.True: | 
					
						
							|  |  |  | 		return strconv.ParseBool(str) | 
					
						
							|  |  |  | 	case gjson.JSON: | 
					
						
							|  |  |  | 		return gjson.Parse(str).Value(), nil | 
					
						
							|  |  |  | 	case gjson.String: | 
					
						
							|  |  |  | 		return str, nil | 
					
						
							|  |  |  | 	case gjson.Number: | 
					
						
							|  |  |  | 		return strconv.ParseFloat(str, 64) | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return nil, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // SetConfigKeysHandler - PUT /minio/admin/v1/config-keys
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) SetConfigKeysHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "SetConfigKeysHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Deny if WORM is enabled
 | 
					
						
							|  |  |  | 	if globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Load config
 | 
					
						
							|  |  |  | 	configStruct, err := readServerConfig(ctx, objectAPI) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Convert config to json bytes
 | 
					
						
							|  |  |  | 	configBytes, err := json.Marshal(configStruct) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configStr := string(configBytes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	queries := r.URL.Query() | 
					
						
							|  |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Set key values in the JSON config
 | 
					
						
							|  |  |  | 	for k := range queries { | 
					
						
							|  |  |  | 		// Decode encrypted data associated to the current key
 | 
					
						
							|  |  |  | 		encryptedElem, dErr := base64.StdEncoding.DecodeString(queries.Get(k)) | 
					
						
							|  |  |  | 		if dErr != nil { | 
					
						
							|  |  |  | 			reqInfo := (&logger.ReqInfo{}).AppendTags("key", k) | 
					
						
							|  |  |  | 			ctx = logger.SetReqInfo(ctx, reqInfo) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, dErr) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), dErr.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		elem, dErr := madmin.DecryptData(password, bytes.NewBuffer([]byte(encryptedElem))) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		if dErr != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, dErr) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), dErr.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		// Calculate the type of the current key from the
 | 
					
						
							|  |  |  | 		// original config json
 | 
					
						
							|  |  |  | 		jsonFieldType := gjson.Get(configStr, k).Type | 
					
						
							|  |  |  | 		// Convert passed value to json filed type
 | 
					
						
							|  |  |  | 		val, cErr := convertValueType(elem, jsonFieldType) | 
					
						
							|  |  |  | 		if cErr != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), cErr.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Set the key/value in the new json document
 | 
					
						
							|  |  |  | 		if s, sErr := sjson.Set(configStr, normalizeJSONKey(k), val); sErr == nil { | 
					
						
							|  |  |  | 			configStr = s | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configBytes = []byte(configStr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Validate config
 | 
					
						
							|  |  |  | 	var config serverConfig | 
					
						
							|  |  |  | 	if err = json.Unmarshal(configBytes, &config); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = config.Validate(); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = config.TestNotificationTargets(); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// If credentials for the server are provided via environment,
 | 
					
						
							|  |  |  | 	// then credentials in the provided configuration must match.
 | 
					
						
							|  |  |  | 	if globalIsEnvCreds { | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 		if !globalServerConfig.GetCredential().Equal(config.Credential) { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminCredentialsMismatch), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err = saveServerConfig(ctx, objectAPI, &config); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Send success response
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | // UpdateAdminCredsHandler - POST /minio/admin/v1/config/credential
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // ----------
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | // Update admin credentials in a minio server
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) UpdateAdminCredentialsHandler(w http.ResponseWriter, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	r *http.Request) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	ctx := newContext(r, w, "UpdateCredentialsHandler") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 17:25:52 +08:00
										 |  |  | 	objectAPI := validateAdminReq(ctx, w, r) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Avoid setting new credentials when they are already passed
 | 
					
						
							|  |  |  | 	// by the environment. Deny if WORM is enabled.
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if globalIsEnvCreds || globalWORMEnabled { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMethodNotAllowed), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	if r.ContentLength > maxEConfigJSONSize || r.ContentLength == -1 { | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 		// More than maxConfigSize bytes were available
 | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigTooLarge), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeCustomErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), err.Error(), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Decode request body
 | 
					
						
							|  |  |  | 	var req madmin.SetCredsReq | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	if err = json.Unmarshal(configBytes, &req); err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrRequestBodyParse), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	creds, err := auth.CreateCredentials(req.AccessKey, req.SecretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 00:15:54 +08:00
										 |  |  | 	// Acquire lock before updating global configuration.
 | 
					
						
							|  |  |  | 	globalServerConfigMu.Lock() | 
					
						
							|  |  |  | 	defer globalServerConfigMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Update local credentials in memory.
 | 
					
						
							|  |  |  | 	globalServerConfig.SetCredential(creds) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	// Set active creds.
 | 
					
						
							|  |  |  | 	globalActiveCred = creds | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 	if err = saveServerConfig(ctx, objectAPI, globalServerConfig); err != nil { | 
					
						
							| 
									
										
										
										
											2019-02-14 08:07:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	// Notify all other Minio peers to update credentials
 | 
					
						
							| 
									
										
										
										
											2019-01-14 14:44:20 +08:00
										 |  |  | 	for _, nerr := range globalNotificationSys.LoadCredentials() { | 
					
						
							|  |  |  | 		if nerr.Err != nil { | 
					
						
							|  |  |  | 			logger.GetReqInfo(ctx).SetTags("peerAddress", nerr.Host.String()) | 
					
						
							|  |  |  | 			logger.LogIf(ctx, nerr.Err) | 
					
						
							| 
									
										
										
										
											2018-09-06 23:03:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-06-06 16:51:56 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 	// Reply to the client before restarting minio server.
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | } |