|
|
|
/*
|
|
|
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
|
|
|
*
|
|
|
|
* 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 (
|
|
|
|
"encoding/json"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
minioAdminOpHeader = "X-Minio-Operation"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ServiceStatusHandler - GET /?service
|
|
|
|
// HTTP header x-minio-operation: status
|
|
|
|
// ----------
|
|
|
|
// Fetches server status information like total disk space available
|
|
|
|
// to use, online disks, offline disks and quorum threshold.
|
|
|
|
func (adminAPI adminAPIHandlers) ServiceStatusHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
adminAPIErr := checkRequestAuthType(r, "", "", "")
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
storageInfo := newObjectLayerFn().StorageInfo()
|
|
|
|
jsonBytes, err := json.Marshal(storageInfo)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
errorIf(err, "Failed to marshal storage info into json.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Reply with storage information (across nodes in a
|
|
|
|
// distributed setup) as json.
|
|
|
|
writeSuccessResponseJSON(w, jsonBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceStopHandler - POST /?service
|
|
|
|
// HTTP header x-minio-operation: stop
|
|
|
|
// ----------
|
|
|
|
// Stops minio server gracefully. In a distributed setup, stops all the
|
|
|
|
// servers in the cluster.
|
|
|
|
func (adminAPI adminAPIHandlers) ServiceStopHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
adminAPIErr := checkRequestAuthType(r, "", "", "")
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reply to the client before stopping minio server.
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
|
|
sendServiceCmd(globalAdminPeers, serviceStop)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ServiceRestartHandler - POST /?service
|
|
|
|
// HTTP header x-minio-operation: restart
|
|
|
|
// ----------
|
|
|
|
// Restarts minio server gracefully. In a distributed setup, restarts
|
|
|
|
// all the servers in the cluster.
|
|
|
|
func (adminAPI adminAPIHandlers) ServiceRestartHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
adminAPIErr := checkRequestAuthType(r, "", "", "")
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reply to the client before restarting minio server.
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
|
|
|
sendServiceCmd(globalAdminPeers, serviceRestart)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Type-safe lock query params.
|
|
|
|
type lockQueryKey string
|
|
|
|
|
|
|
|
// Only valid query params for list/clear locks management APIs.
|
|
|
|
const (
|
|
|
|
lockBucket lockQueryKey = "bucket"
|
|
|
|
lockPrefix lockQueryKey = "prefix"
|
|
|
|
lockOlderThan lockQueryKey = "older-than"
|
|
|
|
)
|
|
|
|
|
|
|
|
// validateLockQueryParams - Validates query params for list/clear locks management APIs.
|
|
|
|
func validateLockQueryParams(vars url.Values) (string, string, time.Duration, APIErrorCode) {
|
|
|
|
bucket := vars.Get(string(lockBucket))
|
|
|
|
prefix := vars.Get(string(lockPrefix))
|
|
|
|
relTimeStr := vars.Get(string(lockOlderThan))
|
|
|
|
|
|
|
|
// N B empty bucket name is invalid
|
|
|
|
if !IsValidBucketName(bucket) {
|
|
|
|
return "", "", time.Duration(0), ErrInvalidBucketName
|
|
|
|
}
|
|
|
|
|
|
|
|
// empty prefix is valid.
|
|
|
|
if !IsValidObjectPrefix(prefix) {
|
|
|
|
return "", "", time.Duration(0), ErrInvalidObjectName
|
|
|
|
}
|
|
|
|
|
|
|
|
// If older-than parameter was empty then set it to 0s to list
|
|
|
|
// all locks older than now.
|
|
|
|
if relTimeStr == "" {
|
|
|
|
relTimeStr = "0s"
|
|
|
|
}
|
|
|
|
relTime, err := time.ParseDuration(relTimeStr)
|
|
|
|
if err != nil {
|
|
|
|
errorIf(err, "Failed to parse duration passed as query value.")
|
|
|
|
return "", "", time.Duration(0), ErrInvalidDuration
|
|
|
|
}
|
|
|
|
|
|
|
|
return bucket, prefix, relTime, ErrNone
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListLocksHandler - GET /?lock&bucket=mybucket&prefix=myprefix&older-than=rel_time
|
|
|
|
// - bucket is a mandatory query parameter
|
|
|
|
// - prefix and older-than are optional query parameters
|
|
|
|
// HTTP header x-minio-operation: list
|
|
|
|
// ---------
|
|
|
|
// Lists locks held on a given bucket, prefix and relative time.
|
|
|
|
func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
adminAPIErr := checkRequestAuthType(r, "", "", "")
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := r.URL.Query()
|
|
|
|
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch lock information of locks matching bucket/prefix that
|
|
|
|
// are available since relTime.
|
|
|
|
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
errorIf(err, "Failed to fetch lock information from remote nodes.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal list of locks as json.
|
|
|
|
jsonBytes, err := json.Marshal(volLocks)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
errorIf(err, "Failed to marshal lock information into json.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reply with list of locks held on bucket, matching prefix
|
|
|
|
// older than relTime supplied, as json.
|
|
|
|
writeSuccessResponseJSON(w, jsonBytes)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ClearLocksHandler - POST /?lock&bucket=mybucket&prefix=myprefix&older-than=relTime
|
|
|
|
// - bucket is a mandatory query parameter
|
|
|
|
// - prefix and older-than are optional query parameters
|
|
|
|
// HTTP header x-minio-operation: clear
|
|
|
|
// ---------
|
|
|
|
// Clear locks held on a given bucket, prefix and relative time.
|
|
|
|
func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
|
adminAPIErr := checkRequestAuthType(r, "", "", "")
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
vars := r.URL.Query()
|
|
|
|
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
|
|
|
|
if adminAPIErr != ErrNone {
|
|
|
|
writeErrorResponse(w, adminAPIErr, r.URL)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fetch lock information of locks matching bucket/prefix that
|
|
|
|
// are available since relTime.
|
|
|
|
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, relTime)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
errorIf(err, "Failed to fetch lock information from remote nodes.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Marshal list of locks as json.
|
|
|
|
jsonBytes, err := json.Marshal(volLocks)
|
|
|
|
if err != nil {
|
|
|
|
writeErrorResponse(w, ErrInternalError, r.URL)
|
|
|
|
errorIf(err, "Failed to marshal lock information into json.")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove lock matching bucket/prefix older than relTime.
|
|
|
|
for _, volLock := range volLocks {
|
|
|
|
globalNSMutex.ForceUnlock(volLock.Bucket, volLock.Object)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reply with list of locks cleared, as json.
|
|
|
|
writeSuccessResponseJSON(w, jsonBytes)
|
|
|
|
}
|