| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2016, 2017, 2018 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-01-18 02:02:58 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/http/httptest" | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2018-09-28 08:16:30 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	"testing" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	"github.com/gorilla/mux" | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/auth" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	configJSON = []byte(`{ | 
					
						
							| 
									
										
										
										
											2018-11-30 13:16:17 +08:00
										 |  |  |   "version": "33", | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  |   "credential": { | 
					
						
							|  |  |  |     "accessKey": "minio", | 
					
						
							|  |  |  |     "secretKey": "minio123" | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "region": "us-east-1", | 
					
						
							|  |  |  |   "worm": "off", | 
					
						
							|  |  |  |   "storageclass": { | 
					
						
							|  |  |  |     "standard": "", | 
					
						
							|  |  |  |     "rrs": "" | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "cache": { | 
					
						
							|  |  |  |     "drives": [], | 
					
						
							|  |  |  |     "expiry": 90, | 
					
						
							|  |  |  |     "maxuse": 80, | 
					
						
							|  |  |  |     "exclude": [] | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "kms": { | 
					
						
							|  |  |  |     "vault": { | 
					
						
							|  |  |  |       "endpoint": "", | 
					
						
							|  |  |  |       "auth": { | 
					
						
							|  |  |  |         "type": "", | 
					
						
							|  |  |  |         "approle": { | 
					
						
							|  |  |  |           "id": "", | 
					
						
							|  |  |  |           "secret": "" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       "key-id": { | 
					
						
							|  |  |  |         "name": "", | 
					
						
							|  |  |  |         "version": 0 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "notify": { | 
					
						
							|  |  |  |     "amqp": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "url": "", | 
					
						
							|  |  |  |         "exchange": "", | 
					
						
							|  |  |  |         "routingKey": "", | 
					
						
							|  |  |  |         "exchangeType": "", | 
					
						
							|  |  |  |         "deliveryMode": 0, | 
					
						
							|  |  |  |         "mandatory": false, | 
					
						
							|  |  |  |         "immediate": false, | 
					
						
							|  |  |  |         "durable": false, | 
					
						
							|  |  |  |         "internal": false, | 
					
						
							|  |  |  |         "noWait": false, | 
					
						
							|  |  |  |         "autoDeleted": false | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "elasticsearch": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "format": "namespace", | 
					
						
							|  |  |  |         "url": "", | 
					
						
							|  |  |  |         "index": "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "kafka": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "brokers": null, | 
					
						
							|  |  |  |         "topic": "", | 
					
						
							|  |  |  |         "tls": { | 
					
						
							|  |  |  |           "enable": false, | 
					
						
							|  |  |  |           "skipVerify": false, | 
					
						
							|  |  |  |           "clientAuth": 0 | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         "sasl": { | 
					
						
							|  |  |  |           "enable": false, | 
					
						
							|  |  |  |           "username": "", | 
					
						
							|  |  |  |           "password": "" | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "mqtt": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "broker": "", | 
					
						
							|  |  |  |         "topic": "", | 
					
						
							|  |  |  |         "qos": 0, | 
					
						
							|  |  |  |         "clientId": "", | 
					
						
							|  |  |  |         "username": "", | 
					
						
							|  |  |  |         "password": "", | 
					
						
							|  |  |  |         "reconnectInterval": 0, | 
					
						
							|  |  |  |         "keepAliveInterval": 0 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "mysql": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "format": "namespace", | 
					
						
							|  |  |  |         "dsnString": "", | 
					
						
							|  |  |  |         "table": "", | 
					
						
							|  |  |  |         "host": "", | 
					
						
							|  |  |  |         "port": "", | 
					
						
							|  |  |  |         "user": "", | 
					
						
							|  |  |  |         "password": "", | 
					
						
							|  |  |  |         "database": "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "nats": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "address": "", | 
					
						
							|  |  |  |         "subject": "", | 
					
						
							|  |  |  |         "username": "", | 
					
						
							|  |  |  |         "password": "", | 
					
						
							|  |  |  |         "token": "", | 
					
						
							|  |  |  |         "secure": false, | 
					
						
							|  |  |  |         "pingInterval": 0, | 
					
						
							|  |  |  |         "streaming": { | 
					
						
							|  |  |  |           "enable": false, | 
					
						
							|  |  |  |           "clusterID": "", | 
					
						
							|  |  |  |           "async": false, | 
					
						
							|  |  |  |           "maxPubAcksInflight": 0 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-11-08 02:23:13 +08:00
										 |  |  | 	}, | 
					
						
							|  |  |  |     "nsq": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "nsqdAddress": "", | 
					
						
							|  |  |  |         "topic": "", | 
					
						
							|  |  |  |         "tls": { | 
					
						
							|  |  |  | 			"enable": false, | 
					
						
							|  |  |  | 			"skipVerify": false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  |     }, | 
					
						
							|  |  |  |     "postgresql": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "format": "namespace", | 
					
						
							|  |  |  |         "connectionString": "", | 
					
						
							|  |  |  |         "table": "", | 
					
						
							|  |  |  |         "host": "", | 
					
						
							|  |  |  |         "port": "", | 
					
						
							|  |  |  |         "user": "", | 
					
						
							|  |  |  |         "password": "", | 
					
						
							|  |  |  |         "database": "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "redis": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "format": "namespace", | 
					
						
							|  |  |  |         "address": "", | 
					
						
							|  |  |  |         "password": "", | 
					
						
							|  |  |  |         "key": "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "webhook": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enable": false, | 
					
						
							|  |  |  |         "endpoint": "" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "logger": { | 
					
						
							|  |  |  |     "console": { | 
					
						
							|  |  |  |       "enabled": true | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |     "http": { | 
					
						
							|  |  |  |       "1": { | 
					
						
							|  |  |  |         "enabled": false, | 
					
						
							|  |  |  |         "endpoint": "https://username:password@example.com/api" | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "compress": { | 
					
						
							|  |  |  |     "enabled": false, | 
					
						
							|  |  |  |     "extensions":[".txt",".log",".csv",".json"], | 
					
						
							|  |  |  |     "mime-types":["text/csv","text/plain","application/json"] | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "openid": { | 
					
						
							|  |  |  |     "jwks": { | 
					
						
							|  |  |  |       "url": "" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   "policy": { | 
					
						
							|  |  |  |     "opa": { | 
					
						
							|  |  |  |       "url": "", | 
					
						
							|  |  |  |       "authToken": "" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | `) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | // adminXLTestBed - encapsulates subsystems that need to be setup for
 | 
					
						
							|  |  |  | // admin-handler unit tests.
 | 
					
						
							|  |  |  | type adminXLTestBed struct { | 
					
						
							|  |  |  | 	configPath string | 
					
						
							|  |  |  | 	xlDirs     []string | 
					
						
							|  |  |  | 	objLayer   ObjectLayer | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	router     *mux.Router | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // prepareAdminXLTestBed - helper function that setups a single-node
 | 
					
						
							|  |  |  | // XL backend for admin-handler tests.
 | 
					
						
							|  |  |  | func prepareAdminXLTestBed() (*adminXLTestBed, error) { | 
					
						
							|  |  |  | 	// reset global variables to start afresh.
 | 
					
						
							|  |  |  | 	resetTestGlobals() | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	// Initializing objectLayer for HealFormatHandler.
 | 
					
						
							|  |  |  | 	objLayer, xlDirs, xlErr := initTestXLObjLayer() | 
					
						
							|  |  |  | 	if xlErr != nil { | 
					
						
							|  |  |  | 		return nil, xlErr | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	// Initialize minio server config.
 | 
					
						
							|  |  |  | 	if err := newTestConfig(globalMinioDefaultRegion, objLayer); err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 	// Initialize boot time
 | 
					
						
							| 
									
										
										
										
											2017-03-19 02:28:41 +08:00
										 |  |  | 	globalBootTime = UTCNow() | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalEndpoints = mustGetNewEndpointList(xlDirs...) | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Set globalIsXL to indicate that the setup uses an erasure
 | 
					
						
							|  |  |  | 	// code backend.
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	globalIsXL = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// initialize NSLock.
 | 
					
						
							|  |  |  | 	isDistXL := false | 
					
						
							|  |  |  | 	initNSLock(isDistXL) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Init global heal state
 | 
					
						
							|  |  |  | 	initAllHealState(globalIsXL) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-10 09:50:31 +08:00
										 |  |  | 	globalNotificationSys = NewNotificationSys(globalServerConfig, globalEndpoints) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:03:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-25 06:53:30 +08:00
										 |  |  | 	// Create new policy system.
 | 
					
						
							|  |  |  | 	globalPolicySys = NewPolicySys() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	// Setup admin mgmt REST API handlers.
 | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminRouter := mux.NewRouter() | 
					
						
							| 
									
										
										
										
											2018-12-19 05:03:26 +08:00
										 |  |  | 	registerAdminRouter(adminRouter, true) | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return &adminXLTestBed{ | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		xlDirs:   xlDirs, | 
					
						
							|  |  |  | 		objLayer: objLayer, | 
					
						
							|  |  |  | 		router:   adminRouter, | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TearDown - method that resets the test bed for subsequent unit
 | 
					
						
							|  |  |  | // tests to start afresh.
 | 
					
						
							|  |  |  | func (atb *adminXLTestBed) TearDown() { | 
					
						
							|  |  |  | 	removeRoots(atb.xlDirs) | 
					
						
							|  |  |  | 	resetTestGlobals() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func (atb *adminXLTestBed) GenerateHealTestData(t *testing.T) { | 
					
						
							|  |  |  | 	// Create an object myobject under bucket mybucket.
 | 
					
						
							|  |  |  | 	bucketName := "mybucket" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	err := atb.objLayer.MakeBucketWithLocation(context.Background(), bucketName, "") | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to make bucket %s - %v", bucketName, | 
					
						
							|  |  |  | 			err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create some objects
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		objName := "myobject" | 
					
						
							|  |  |  | 		for i := 0; i < 10; i++ { | 
					
						
							|  |  |  | 			objectName := fmt.Sprintf("%s-%d", objName, i) | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 			_, err = atb.objLayer.PutObject(context.Background(), bucketName, objectName, | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  | 				mustGetPutObjReader(t, bytes.NewReader([]byte("hello")), | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 					int64(len("hello")), "", ""), nil, ObjectOptions{}) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				t.Fatalf("Failed to create %s - %v", objectName, | 
					
						
							|  |  |  | 					err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// create a multipart upload (incomplete)
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		objName := "mpObject" | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 		uploadID, err := atb.objLayer.NewMultipartUpload(context.Background(), bucketName, | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 			objName, nil, ObjectOptions{}) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("mp new error: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 		_, err = atb.objLayer.PutObjectPart(context.Background(), bucketName, objName, | 
					
						
							| 
									
										
										
										
											2018-11-15 09:36:41 +08:00
										 |  |  | 			uploadID, 3, mustGetPutObjReader(t, bytes.NewReader( | 
					
						
							| 
									
										
										
										
											2018-09-11 00:42:43 +08:00
										 |  |  | 				[]byte("hello")), int64(len("hello")), "", ""), ObjectOptions{}) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("mp put error: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (atb *adminXLTestBed) CleanupHealTestData(t *testing.T) { | 
					
						
							|  |  |  | 	bucketName := "mybucket" | 
					
						
							|  |  |  | 	objName := "myobject" | 
					
						
							|  |  |  | 	for i := 0; i < 10; i++ { | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 		atb.objLayer.DeleteObject(context.Background(), bucketName, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			fmt.Sprintf("%s-%d", objName, i)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-16 04:27:16 +08:00
										 |  |  | 	atb.objLayer.DeleteBucket(context.Background(), bucketName) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | // initTestObjLayer - Helper function to initialize an XL-based object
 | 
					
						
							|  |  |  | // layer and set globalObjectAPI.
 | 
					
						
							|  |  |  | func initTestXLObjLayer() (ObjectLayer, []string, error) { | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	xlDirs, err := getRandomDisks(16) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	endpoints := mustGetNewEndpointList(xlDirs...) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	format, err := waitForFormatXL(context.Background(), true, endpoints, 1, 16) | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		removeRoots(xlDirs) | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-07 03:52:56 +08:00
										 |  |  | 	globalPolicySys = NewPolicySys() | 
					
						
							| 
									
										
										
										
											2018-02-16 09:45:57 +08:00
										 |  |  | 	objLayer, err := newXLSets(endpoints, format, 1, 16) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	// Make objLayer available to all internal services via globalObjectAPI.
 | 
					
						
							|  |  |  | 	globalObjLayerMutex.Lock() | 
					
						
							|  |  |  | 	globalObjectAPI = objLayer | 
					
						
							|  |  |  | 	globalObjLayerMutex.Unlock() | 
					
						
							|  |  |  | 	return objLayer, xlDirs, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func TestAdminVersionHandler(t *testing.T) { | 
					
						
							|  |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	req, err := newTestRequest("GET", "/minio/admin/version", 0, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to construct request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cred := globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 	err = signRequestV4(req, cred.AccessKey, cred.SecretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to sign request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	if http.StatusOK != rec.Code { | 
					
						
							|  |  |  | 		t.Errorf("Unexpected status code - got %d but expected %d", | 
					
						
							|  |  |  | 			rec.Code, http.StatusOK) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var result madmin.AdminAPIVersionInfo | 
					
						
							|  |  |  | 	err = json.NewDecoder(rec.Body).Decode(&result) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Errorf("json parse err: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if result != adminAPIVersionInfo { | 
					
						
							|  |  |  | 		t.Errorf("unexpected version: %v", result) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // cmdType - Represents different service subcomands like status, stop
 | 
					
						
							|  |  |  | // and restart.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | type cmdType int | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	statusCmd cmdType = iota | 
					
						
							|  |  |  | 	restartCmd | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	stopCmd | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	setCreds | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // String - String representation for cmdType
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func (c cmdType) String() string { | 
					
						
							|  |  |  | 	switch c { | 
					
						
							|  |  |  | 	case statusCmd: | 
					
						
							|  |  |  | 		return "status" | 
					
						
							|  |  |  | 	case restartCmd: | 
					
						
							|  |  |  | 		return "restart" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	case stopCmd: | 
					
						
							|  |  |  | 		return "stop" | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	case setCreds: | 
					
						
							|  |  |  | 		return "set-credentials" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // apiMethod - Returns the HTTP method corresponding to the admin REST
 | 
					
						
							|  |  |  | // API for a given cmdType value.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func (c cmdType) apiMethod() string { | 
					
						
							|  |  |  | 	switch c { | 
					
						
							|  |  |  | 	case statusCmd: | 
					
						
							|  |  |  | 		return "GET" | 
					
						
							|  |  |  | 	case restartCmd: | 
					
						
							|  |  |  | 		return "POST" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	case stopCmd: | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		return "POST" | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	case setCreds: | 
					
						
							|  |  |  | 		return "PUT" | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return "GET" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | // apiEndpoint - Return endpoint for each admin REST API mapped to a
 | 
					
						
							|  |  |  | // command here.
 | 
					
						
							|  |  |  | func (c cmdType) apiEndpoint() string { | 
					
						
							|  |  |  | 	switch c { | 
					
						
							|  |  |  | 	case statusCmd, restartCmd, stopCmd: | 
					
						
							|  |  |  | 		return "/minio/admin/v1/service" | 
					
						
							|  |  |  | 	case setCreds: | 
					
						
							|  |  |  | 		return "/minio/admin/v1/config/credential" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return "" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // toServiceSignal - Helper function that translates a given cmdType
 | 
					
						
							|  |  |  | // value to its corresponding serviceSignal value.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func (c cmdType) toServiceSignal() serviceSignal { | 
					
						
							|  |  |  | 	switch c { | 
					
						
							|  |  |  | 	case statusCmd: | 
					
						
							|  |  |  | 		return serviceStatus | 
					
						
							|  |  |  | 	case restartCmd: | 
					
						
							|  |  |  | 		return serviceRestart | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	case stopCmd: | 
					
						
							|  |  |  | 		return serviceStop | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return serviceStatus | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func (c cmdType) toServiceActionValue() madmin.ServiceActionValue { | 
					
						
							|  |  |  | 	switch c { | 
					
						
							|  |  |  | 	case restartCmd: | 
					
						
							|  |  |  | 		return madmin.ServiceActionValueRestart | 
					
						
							|  |  |  | 	case stopCmd: | 
					
						
							|  |  |  | 		return madmin.ServiceActionValueStop | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return madmin.ServiceActionValueStop | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // testServiceSignalReceiver - Helper function that simulates a
 | 
					
						
							|  |  |  | // go-routine waiting on service signal.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func testServiceSignalReceiver(cmd cmdType, t *testing.T) { | 
					
						
							|  |  |  | 	expectedCmd := cmd.toServiceSignal() | 
					
						
							|  |  |  | 	serviceCmd := <-globalServiceSignalCh | 
					
						
							|  |  |  | 	if serviceCmd != expectedCmd { | 
					
						
							|  |  |  | 		t.Errorf("Expected service command %v but received %v", expectedCmd, serviceCmd) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // getServiceCmdRequest - Constructs a management REST API request for service
 | 
					
						
							|  |  |  | // subcommands for a given cmdType value.
 | 
					
						
							| 
									
										
										
										
											2017-11-01 02:54:32 +08:00
										 |  |  | func getServiceCmdRequest(cmd cmdType, cred auth.Credentials, body []byte) (*http.Request, error) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	req, err := newTestRequest(cmd.apiMethod(), cmd.apiEndpoint(), 0, nil) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	// Set body
 | 
					
						
							|  |  |  | 	req.Body = ioutil.NopCloser(bytes.NewReader(body)) | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	req.ContentLength = int64(len(body)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	// Set sha-sum header
 | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	req.Header.Set("X-Amz-Content-Sha256", getSHA256Hash(body)) | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// management REST API uses signature V4 for authentication.
 | 
					
						
							| 
									
										
										
										
											2016-12-27 02:21:23 +08:00
										 |  |  | 	err = signRequestV4(req, cred.AccessKey, cred.SecretKey) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return req, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // testServicesCmdHandler - parametrizes service subcommand tests on
 | 
					
						
							|  |  |  | // cmdType value.
 | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | func testServicesCmdHandler(cmd cmdType, t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-23 16:32:55 +08:00
										 |  |  | 	defer adminTestBed.TearDown() | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 	// Initialize admin peers to make admin RPC calls. Note: In a
 | 
					
						
							|  |  |  | 	// single node setup, this degenerates to a simple function
 | 
					
						
							|  |  |  | 	// call under the hood.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalMinioAddr = "127.0.0.1:9000" | 
					
						
							|  |  |  | 	initGlobalAdminPeers(mustGetNewEndpointList("http://127.0.0.1:9000/d1")) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-28 08:16:30 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	// Setting up a go routine to simulate ServerRouter's
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	// handleServiceSignals for stop and restart commands.
 | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 	if cmd == restartCmd { | 
					
						
							| 
									
										
										
										
											2018-09-28 08:16:30 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							|  |  |  | 		go func() { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			testServiceSignalReceiver(cmd, t) | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	credentials := globalServerConfig.GetCredential() | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	body, err := json.Marshal(madmin.ServiceAction{ | 
					
						
							| 
									
										
										
										
											2018-11-15 02:23:44 +08:00
										 |  |  | 		Action: cmd.toServiceActionValue()}) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("JSONify error: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	req, err := getServiceCmdRequest(cmd, credentials, body) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to build service status request %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if cmd == statusCmd { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		expectedInfo := madmin.ServiceStatus{ | 
					
						
							|  |  |  | 			ServerVersion: madmin.ServerVersion{Version: Version, CommitID: CommitID}, | 
					
						
							| 
									
										
										
										
											2017-01-24 00:56:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		receivedInfo := madmin.ServiceStatus{} | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 		if jsonErr := json.Unmarshal(rec.Body.Bytes(), &receivedInfo); jsonErr != nil { | 
					
						
							|  |  |  | 			t.Errorf("Failed to unmarshal StorageInfo - %v", jsonErr) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-02-08 16:13:02 +08:00
										 |  |  | 		if expectedInfo.ServerVersion != receivedInfo.ServerVersion { | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 			t.Errorf("Expected storage info and received storage info differ, %v %v", expectedInfo, receivedInfo) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if rec.Code != http.StatusOK { | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | 		resp, _ := ioutil.ReadAll(rec.Body) | 
					
						
							|  |  |  | 		t.Errorf("Expected to receive %d status code but received %d. Body (%s)", | 
					
						
							|  |  |  | 			http.StatusOK, rec.Code, string(resp)) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-28 08:16:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Wait until testServiceSignalReceiver() called in a goroutine quits.
 | 
					
						
							|  |  |  | 	wg.Wait() | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // Test for service status management REST API.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func TestServiceStatusHandler(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 	testServicesCmdHandler(statusCmd, t) | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | // Test for service restart management REST API.
 | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | func TestServiceRestartHandler(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 	testServicesCmdHandler(restartCmd, t) | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | // Test for service set creds management REST API.
 | 
					
						
							| 
									
										
										
										
											2017-01-18 06:25:59 +08:00
										 |  |  | func TestServiceSetCreds(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize admin peers to make admin RPC calls. Note: In a
 | 
					
						
							|  |  |  | 	// single node setup, this degenerates to a simple function
 | 
					
						
							|  |  |  | 	// call under the hood.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalMinioAddr = "127.0.0.1:9000" | 
					
						
							|  |  |  | 	initGlobalAdminPeers(mustGetNewEndpointList("http://127.0.0.1:9000/d1")) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	credentials := globalServerConfig.GetCredential() | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		AccessKey          string | 
					
						
							|  |  |  | 		SecretKey          string | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 		EnvKeysSet         bool | 
					
						
							|  |  |  | 		ExpectedStatusCode int | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// Bad secret key
 | 
					
						
							|  |  |  | 		{"minio", "minio", false, http.StatusBadRequest}, | 
					
						
							|  |  |  | 		// Bad  secret key set from the env
 | 
					
						
							|  |  |  | 		{"minio", "minio", true, http.StatusMethodNotAllowed}, | 
					
						
							|  |  |  | 		// Good keys set from the env
 | 
					
						
							|  |  |  | 		{"minio", "minio123", true, http.StatusMethodNotAllowed}, | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		// Successful operation should be the last one to
 | 
					
						
							|  |  |  | 		// not change server credentials during tests.
 | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 		{"minio", "minio123", false, http.StatusOK}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		// Set or unset environement keys
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		globalIsEnvCreds = testCase.EnvKeysSet | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Construct setCreds request body
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		body, err := json.Marshal(madmin.SetCredsReq{ | 
					
						
							|  |  |  | 			AccessKey: testCase.AccessKey, | 
					
						
							|  |  |  | 			SecretKey: testCase.SecretKey}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("JSONify err: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		ebody, err := madmin.EncryptData(credentials.SecretKey, body) | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 		// Construct setCreds request
 | 
					
						
							| 
									
										
										
										
											2018-08-18 09:51:34 +08:00
										 |  |  | 		req, err := getServiceCmdRequest(setCreds, credentials, ebody) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Failed to build service status request %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Execute request
 | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Check if the http code response is expected
 | 
					
						
							|  |  |  | 		if rec.Code != testCase.ExpectedStatusCode { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Wrong status code, expected = %d, found = %d", i+1, testCase.ExpectedStatusCode, rec.Code) | 
					
						
							|  |  |  | 			resp, _ := ioutil.ReadAll(rec.Body) | 
					
						
							|  |  |  | 			t.Errorf("Expected to receive %d status code but received %d. Body (%s)", | 
					
						
							|  |  |  | 				http.StatusOK, rec.Code, string(resp)) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// If we got 200 OK, check if new credentials are really set
 | 
					
						
							|  |  |  | 		if rec.Code == http.StatusOK { | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 			cred := globalServerConfig.GetCredential() | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			if cred.AccessKey != testCase.AccessKey { | 
					
						
							|  |  |  | 				t.Errorf("Test %d: Wrong access key, expected = %s, found = %s", i+1, testCase.AccessKey, cred.AccessKey) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 			if cred.SecretKey != testCase.SecretKey { | 
					
						
							|  |  |  | 				t.Errorf("Test %d: Wrong secret key, expected = %s, found = %s", i+1, testCase.SecretKey, cred.SecretKey) | 
					
						
							| 
									
										
										
										
											2017-01-25 00:08:36 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-12-16 14:26:15 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-04 15:39:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | // buildAdminRequest - helper function to build an admin API request.
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func buildAdminRequest(queryVal url.Values, method, path string, | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	contentLength int64, bodySeeker io.ReadSeeker) (*http.Request, error) { | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	req, err := newTestRequest(method, | 
					
						
							|  |  |  | 		"/minio/admin/v1"+path+"?"+queryVal.Encode(), | 
					
						
							|  |  |  | 		contentLength, bodySeeker) | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-30 05:12:47 +08:00
										 |  |  | 	cred := globalServerConfig.GetCredential() | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	err = signRequestV4(req, cred.AccessKey, cred.SecretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2017-03-18 00:25:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return req, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | // TestGetConfigHandler - test for GetConfigHandler.
 | 
					
						
							|  |  |  | func TestGetConfigHandler(t *testing.T) { | 
					
						
							|  |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize admin peers to make admin RPC calls.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalMinioAddr = "127.0.0.1:9000" | 
					
						
							|  |  |  | 	initGlobalAdminPeers(mustGetNewEndpointList("http://127.0.0.1:9000/d1")) | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Prepare query params for get-config mgmt REST API.
 | 
					
						
							|  |  |  | 	queryVal := url.Values{} | 
					
						
							|  |  |  | 	queryVal.Set("config", "") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	req, err := buildAdminRequest(queryVal, http.MethodGet, "/config", 0, nil) | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to construct get-config object request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2017-02-21 04:58:50 +08:00
										 |  |  | 	if rec.Code != http.StatusOK { | 
					
						
							|  |  |  | 		t.Errorf("Expected to succeed but failed with %d", rec.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestSetConfigHandler - test for SetConfigHandler.
 | 
					
						
							|  |  |  | func TestSetConfigHandler(t *testing.T) { | 
					
						
							|  |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize admin peers to make admin RPC calls.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalMinioAddr = "127.0.0.1:9000" | 
					
						
							|  |  |  | 	initGlobalAdminPeers(mustGetNewEndpointList("http://127.0.0.1:9000/d1")) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Prepare query params for set-config mgmt REST API.
 | 
					
						
							|  |  |  | 	queryVal := url.Values{} | 
					
						
							|  |  |  | 	queryVal.Set("config", "") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	password := globalServerConfig.GetCredential().SecretKey | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 	econfigJSON, err := madmin.EncryptData(password, configJSON) | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	req, err := buildAdminRequest(queryVal, http.MethodPut, "/config", | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		int64(len(econfigJSON)), bytes.NewReader(econfigJSON)) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		t.Fatalf("Failed to construct set-config object request - %v", err) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	if rec.Code != http.StatusOK { | 
					
						
							| 
									
										
										
										
											2018-11-08 02:23:13 +08:00
										 |  |  | 		t.Errorf("Expected to succeed but failed with %d, body: %s", rec.Code, rec.Body) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 	// Check that a very large config file returns an error.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// Make a large enough config string
 | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 		invalidCfg := []byte(strings.Repeat("A", maxEConfigJSONSize+1)) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		req, err := buildAdminRequest(queryVal, http.MethodPut, "/config", | 
					
						
							|  |  |  | 			int64(len(invalidCfg)), bytes.NewReader(invalidCfg)) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Failed to construct set-config object request - %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		respBody := string(rec.Body.Bytes()) | 
					
						
							|  |  |  | 		if rec.Code != http.StatusBadRequest || | 
					
						
							|  |  |  | 			!strings.Contains(respBody, "Configuration data provided exceeds the allowed maximum of") { | 
					
						
							|  |  |  | 			t.Errorf("Got unexpected response code or body %d - %s", rec.Code, respBody) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check that a config with duplicate keys in an object return
 | 
					
						
							|  |  |  | 	// error.
 | 
					
						
							|  |  |  | 	{ | 
					
						
							| 
									
										
										
										
											2018-08-15 12:41:47 +08:00
										 |  |  | 		invalidCfg := append(econfigJSON[:len(econfigJSON)-1], []byte(`, "version": "15"}`)...) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		req, err := buildAdminRequest(queryVal, http.MethodPut, "/config", | 
					
						
							|  |  |  | 			int64(len(invalidCfg)), bytes.NewReader(invalidCfg)) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Failed to construct set-config object request - %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 		respBody := string(rec.Body.Bytes()) | 
					
						
							|  |  |  | 		if rec.Code != http.StatusBadRequest || | 
					
						
							| 
									
										
										
										
											2018-10-10 05:00:01 +08:00
										 |  |  | 			!strings.Contains(respBody, "JSON configuration provided is of incorrect format") { | 
					
						
							| 
									
										
										
										
											2018-01-11 15:06:36 +08:00
										 |  |  | 			t.Errorf("Got unexpected response code or body %d - %s", rec.Code, respBody) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | func TestAdminServerInfo(t *testing.T) { | 
					
						
							|  |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize admin peers to make admin RPC calls.
 | 
					
						
							| 
									
										
										
										
											2017-04-12 06:44:27 +08:00
										 |  |  | 	globalMinioAddr = "127.0.0.1:9000" | 
					
						
							|  |  |  | 	initGlobalAdminPeers(mustGetNewEndpointList("http://127.0.0.1:9000/d1")) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Prepare query params for set-config mgmt REST API.
 | 
					
						
							|  |  |  | 	queryVal := url.Values{} | 
					
						
							|  |  |  | 	queryVal.Set("info", "") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	req, err := buildAdminRequest(queryVal, http.MethodGet, "/info", 0, nil) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to construct get-config object request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 	adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	if rec.Code != http.StatusOK { | 
					
						
							|  |  |  | 		t.Errorf("Expected to succeed but failed with %d", rec.Code) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 	results := []ServerInfo{} | 
					
						
							|  |  |  | 	err = json.NewDecoder(rec.Body).Decode(&results) | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to decode set config result json %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 	if len(results) == 0 { | 
					
						
							|  |  |  | 		t.Error("Expected at least one server info result") | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for _, serverInfo := range results { | 
					
						
							|  |  |  | 		if len(serverInfo.Addr) == 0 { | 
					
						
							|  |  |  | 			t.Error("Expected server address to be non empty") | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-05-15 22:28:47 +08:00
										 |  |  | 		if serverInfo.Error != "" { | 
					
						
							| 
									
										
										
										
											2017-04-21 22:15:53 +08:00
										 |  |  | 			t.Errorf("Unexpected error = %v\n", serverInfo.Error) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if serverInfo.Data.Properties.Region != globalMinioDefaultRegion { | 
					
						
							|  |  |  | 			t.Errorf("Expected %s, got %s", globalMinioDefaultRegion, serverInfo.Data.Properties.Region) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-07 14:08:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | // TestToAdminAPIErr - test for toAdminAPIErr helper function.
 | 
					
						
							|  |  |  | func TestToAdminAPIErr(t *testing.T) { | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		err            error | 
					
						
							|  |  |  | 		expectedAPIErr APIErrorCode | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		// 1. Server not in quorum.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			err:            errXLWriteQuorum, | 
					
						
							|  |  |  | 			expectedAPIErr: ErrAdminConfigNoQuorum, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// 2. No error.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			err:            nil, | 
					
						
							|  |  |  | 			expectedAPIErr: ErrNone, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// 3. Non-admin API specific error.
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			err:            errDiskNotFound, | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 			expectedAPIErr: toAPIErrorCode(context.Background(), errDiskNotFound), | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for i, test := range testCases { | 
					
						
							| 
									
										
										
										
											2018-11-13 03:07:43 +08:00
										 |  |  | 		actualErr := toAdminAPIErrCode(context.Background(), test.err) | 
					
						
							| 
									
										
										
										
											2017-02-28 03:40:27 +08:00
										 |  |  | 		if actualErr != test.expectedAPIErr { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: Expected %v but received %v", | 
					
						
							|  |  |  | 				i+1, test.expectedAPIErr, actualErr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | func mkHealStartReq(t *testing.T, bucket, prefix string, | 
					
						
							|  |  |  | 	opts madmin.HealOpts) *http.Request { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	body, err := json.Marshal(opts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Unable marshal heal opts") | 
					
						
							| 
									
										
										
										
											2017-04-15 01:28:35 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 	path := fmt.Sprintf("/minio/admin/v1/heal/%s", bucket) | 
					
						
							|  |  |  | 	if bucket != "" && prefix != "" { | 
					
						
							|  |  |  | 		path += "/" + prefix | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	req, err := newTestRequest("POST", path, | 
					
						
							|  |  |  | 		int64(len(body)), bytes.NewReader(body)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to construct request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cred := globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 	err = signRequestV4(req, cred.AccessKey, cred.SecretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to sign request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func mkHealStatusReq(t *testing.T, bucket, prefix, | 
					
						
							|  |  |  | 	clientToken string) *http.Request { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	path := fmt.Sprintf("/minio/admin/v1/heal/%s", bucket) | 
					
						
							|  |  |  | 	if bucket != "" && prefix != "" { | 
					
						
							|  |  |  | 		path += "/" + prefix | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	path += fmt.Sprintf("?clientToken=%s", clientToken) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	req, err := newTestRequest("POST", path, 0, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to construct request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cred := globalServerConfig.GetCredential() | 
					
						
							|  |  |  | 	err = signRequestV4(req, cred.AccessKey, cred.SecretKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to sign request - %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return req | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func collectHealResults(t *testing.T, adminTestBed *adminXLTestBed, bucket, | 
					
						
							|  |  |  | 	prefix, clientToken string, timeLimitSecs int) madmin.HealTaskStatus { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var res, cur madmin.HealTaskStatus | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// loop and fetch heal status. have a time-limit to loop over
 | 
					
						
							|  |  |  | 	// all statuses.
 | 
					
						
							|  |  |  | 	timeLimit := UTCNow().Add(time.Second * time.Duration(timeLimitSecs)) | 
					
						
							|  |  |  | 	for cur.Summary != healStoppedStatus && cur.Summary != healFinishedStatus { | 
					
						
							|  |  |  | 		if UTCNow().After(timeLimit) { | 
					
						
							|  |  |  | 			t.Fatalf("heal-status loop took too long - clientToken: %s", clientToken) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		req := mkHealStatusReq(t, bucket, prefix, clientToken) | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if http.StatusOK != rec.Code { | 
					
						
							|  |  |  | 			t.Errorf("Unexpected status code - got %d but expected %d", | 
					
						
							|  |  |  | 				rec.Code, http.StatusOK) | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		err := json.NewDecoder(rec.Body).Decode(&cur) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("unable to unmarshal resp: %v", err) | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// all results are accumulated into a slice
 | 
					
						
							|  |  |  | 		// and returned to caller in the end
 | 
					
						
							|  |  |  | 		allItems := append(res.Items, cur.Items...) | 
					
						
							|  |  |  | 		res = cur | 
					
						
							|  |  |  | 		res.Items = allItems | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		time.Sleep(time.Millisecond * 200) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestHealStartNStatusHandler(t *testing.T) { | 
					
						
							|  |  |  | 	adminTestBed, err := prepareAdminXLTestBed() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal("Failed to initialize a single node XL backend for admin handler tests.") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer adminTestBed.TearDown() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// gen. test data
 | 
					
						
							|  |  |  | 	adminTestBed.GenerateHealTestData(t) | 
					
						
							|  |  |  | 	defer adminTestBed.CleanupHealTestData(t) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Prepare heal-start request to send to the server.
 | 
					
						
							|  |  |  | 	healOpts := madmin.HealOpts{ | 
					
						
							|  |  |  | 		Recursive: true, | 
					
						
							|  |  |  | 		DryRun:    false, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bucketName, objName := "mybucket", "myobject-0" | 
					
						
							|  |  |  | 	var hss madmin.HealStartSuccess | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		req := mkHealStartReq(t, bucketName, objName, healOpts) | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if http.StatusOK != rec.Code { | 
					
						
							|  |  |  | 			t.Errorf("Unexpected status code - got %d but expected %d", | 
					
						
							|  |  |  | 				rec.Code, http.StatusOK) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = json.Unmarshal(rec.Body.Bytes(), &hss) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatal("unable to unmarshal response") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if hss.ClientToken == "" { | 
					
						
							|  |  |  | 			t.Errorf("unexpected result") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// test with an invalid client token
 | 
					
						
							|  |  |  | 		req := mkHealStatusReq(t, bucketName, objName, hss.ClientToken+hss.ClientToken) | 
					
						
							|  |  |  | 		rec := httptest.NewRecorder() | 
					
						
							| 
									
										
										
										
											2018-04-22 10:23:54 +08:00
										 |  |  | 		adminTestBed.router.ServeHTTP(rec, req) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if rec.Code != http.StatusBadRequest { | 
					
						
							|  |  |  | 			t.Errorf("Unexpected status code") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		// fetch heal status
 | 
					
						
							|  |  |  | 		results := collectHealResults(t, adminTestBed, bucketName, | 
					
						
							|  |  |  | 			objName, hss.ClientToken, 5) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// check if we got back an expected record
 | 
					
						
							|  |  |  | 		foundIt := false | 
					
						
							|  |  |  | 		for _, item := range results.Items { | 
					
						
							|  |  |  | 			if item.Type == madmin.HealItemObject && | 
					
						
							|  |  |  | 				item.Bucket == bucketName && item.Object == objName { | 
					
						
							|  |  |  | 				foundIt = true | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !foundIt { | 
					
						
							|  |  |  | 			t.Error("did not find expected heal record in heal results") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// check that the heal settings in the results is the
 | 
					
						
							|  |  |  | 		// same as what we started the heal seq. with.
 | 
					
						
							|  |  |  | 		if results.HealSettings != healOpts { | 
					
						
							|  |  |  | 			t.Errorf("unexpected heal settings: %v", | 
					
						
							|  |  |  | 				results.HealSettings) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if results.Summary == healStoppedStatus { | 
					
						
							|  |  |  | 			t.Errorf("heal sequence stopped unexpectedly") | 
					
						
							| 
									
										
										
										
											2017-04-15 01:28:35 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |