Change query param name to duration in list/clear locks API (#3664)

Following is a sample list lock API request schematic,

  /?lock&bucket=mybucket&prefix=myprefix&duration=holdDuration
  x-minio-operation: list

The response would contain the list of locks held on mybucket matching
myprefix for a duration longer than holdDuration.
This commit is contained in:
Krishnan Parthasarathi 2017-02-02 00:47:30 +05:30 committed by Harshavardhana
parent 6a6c930f5b
commit 0472e5c1e1
11 changed files with 76 additions and 75 deletions

View File

@ -39,7 +39,7 @@ const (
mgmtBucket mgmtQueryKey = "bucket"
mgmtObject mgmtQueryKey = "object"
mgmtPrefix mgmtQueryKey = "prefix"
mgmtOlderThan mgmtQueryKey = "older-than"
mgmtLockDuration mgmtQueryKey = "duration"
mgmtDelimiter mgmtQueryKey = "delimiter"
mgmtMarker mgmtQueryKey = "marker"
mgmtMaxKey mgmtQueryKey = "max-key"
@ -185,7 +185,7 @@ func (adminAPI adminAPIHandlers) ServiceCredentialsHandler(w http.ResponseWriter
func validateLockQueryParams(vars url.Values) (string, string, time.Duration, APIErrorCode) {
bucket := vars.Get(string(mgmtBucket))
prefix := vars.Get(string(mgmtPrefix))
relTimeStr := vars.Get(string(mgmtOlderThan))
durationStr := vars.Get(string(mgmtLockDuration))
// N B empty bucket name is invalid
if !IsValidBucketName(bucket) {
@ -198,24 +198,24 @@ func validateLockQueryParams(vars url.Values) (string, string, time.Duration, AP
// If older-than parameter was empty then set it to 0s to list
// all locks older than now.
if relTimeStr == "" {
relTimeStr = "0s"
if durationStr == "" {
durationStr = "0s"
}
relTime, err := time.ParseDuration(relTimeStr)
duration, err := time.ParseDuration(durationStr)
if err != nil {
errorIf(err, "Failed to parse duration passed as query value.")
return "", "", time.Duration(0), ErrInvalidDuration
}
return bucket, prefix, relTime, ErrNone
return bucket, prefix, duration, ErrNone
}
// ListLocksHandler - GET /?lock&bucket=mybucket&prefix=myprefix&older-than=rel_time
// ListLocksHandler - GET /?lock&bucket=mybucket&prefix=myprefix&duration=duration
// - 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.
// Lists locks held on a given bucket, prefix and duration it was held for.
func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone {
@ -224,15 +224,15 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http
}
vars := r.URL.Query()
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
bucket, prefix, duration, 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)
// are available for longer than duration.
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, duration)
if err != nil {
writeErrorResponse(w, ErrInternalError, r.URL)
errorIf(err, "Failed to fetch lock information from remote nodes.")
@ -248,16 +248,16 @@ func (adminAPI adminAPIHandlers) ListLocksHandler(w http.ResponseWriter, r *http
}
// Reply with list of locks held on bucket, matching prefix
// older than relTime supplied, as json.
// held longer than duration supplied, as json.
writeSuccessResponseJSON(w, jsonBytes)
}
// ClearLocksHandler - POST /?lock&bucket=mybucket&prefix=myprefix&older-than=relTime
// ClearLocksHandler - POST /?lock&bucket=mybucket&prefix=myprefix&duration=duration
// - 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.
// Clear locks held on a given bucket, prefix and duration it was held for.
func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *http.Request) {
adminAPIErr := checkRequestAuthType(r, "", "", "")
if adminAPIErr != ErrNone {
@ -266,15 +266,15 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt
}
vars := r.URL.Query()
bucket, prefix, relTime, adminAPIErr := validateLockQueryParams(vars)
bucket, prefix, duration, 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)
// are held for longer than duration.
volLocks, err := listPeerLocksInfo(globalAdminPeers, bucket, prefix, duration)
if err != nil {
writeErrorResponse(w, ErrInternalError, r.URL)
errorIf(err, "Failed to fetch lock information from remote nodes.")
@ -289,7 +289,7 @@ func (adminAPI adminAPIHandlers) ClearLocksHandler(w http.ResponseWriter, r *htt
return
}
// Remove lock matching bucket/prefix older than relTime.
// Remove lock matching bucket/prefix held longer than duration.
for _, volLock := range volLocks {
globalNSMutex.ForceUnlock(volLock.Bucket, volLock.Object)
}

View File

@ -334,12 +334,12 @@ func TestServiceSetCreds(t *testing.T) {
}
// mkLockQueryVal - helper function to build lock query param.
func mkLockQueryVal(bucket, prefix, relTimeStr string) url.Values {
func mkLockQueryVal(bucket, prefix, durationStr string) url.Values {
qVal := url.Values{}
qVal.Set("lock", "")
qVal.Set(string(mgmtBucket), bucket)
qVal.Set(string(mgmtPrefix), prefix)
qVal.Set(string(mgmtOlderThan), relTimeStr)
qVal.Set(string(mgmtLockDuration), durationStr)
return qVal
}
@ -364,41 +364,41 @@ func TestListLocksHandler(t *testing.T) {
testCases := []struct {
bucket string
prefix string
relTime string
duration string
expectedStatus int
}{
// Test 1 - valid testcase
{
bucket: "mybucket",
prefix: "myobject",
relTime: "1s",
duration: "1s",
expectedStatus: http.StatusOK,
},
// Test 2 - invalid duration
{
bucket: "mybucket",
prefix: "myprefix",
relTime: "invalidDuration",
duration: "invalidDuration",
expectedStatus: http.StatusBadRequest,
},
// Test 3 - invalid bucket name
{
bucket: `invalid\\Bucket`,
prefix: "myprefix",
relTime: "1h",
duration: "1h",
expectedStatus: http.StatusBadRequest,
},
// Test 4 - invalid prefix
{
bucket: "mybucket",
prefix: `invalid\\Prefix`,
relTime: "1h",
duration: "1h",
expectedStatus: http.StatusBadRequest,
},
}
for i, test := range testCases {
queryVal := mkLockQueryVal(test.bucket, test.prefix, test.relTime)
queryVal := mkLockQueryVal(test.bucket, test.prefix, test.duration)
req, err := newTestRequest("GET", "/?"+queryVal.Encode(), 0, nil)
if err != nil {
t.Fatalf("Test %d - Failed to construct list locks request - %v", i+1, err)
@ -436,41 +436,41 @@ func TestClearLocksHandler(t *testing.T) {
testCases := []struct {
bucket string
prefix string
relTime string
duration string
expectedStatus int
}{
// Test 1 - valid testcase
{
bucket: "mybucket",
prefix: "myobject",
relTime: "1s",
duration: "1s",
expectedStatus: http.StatusOK,
},
// Test 2 - invalid duration
{
bucket: "mybucket",
prefix: "myprefix",
relTime: "invalidDuration",
duration: "invalidDuration",
expectedStatus: http.StatusBadRequest,
},
// Test 3 - invalid bucket name
{
bucket: `invalid\\Bucket`,
prefix: "myprefix",
relTime: "1h",
duration: "1h",
expectedStatus: http.StatusBadRequest,
},
// Test 4 - invalid prefix
{
bucket: "mybucket",
prefix: `invalid\\Prefix`,
relTime: "1h",
duration: "1h",
expectedStatus: http.StatusBadRequest,
},
}
for i, test := range testCases {
queryVal := mkLockQueryVal(test.bucket, test.prefix, test.relTime)
queryVal := mkLockQueryVal(test.bucket, test.prefix, test.duration)
req, err := newTestRequest("POST", "/?"+queryVal.Encode(), 0, nil)
if err != nil {
t.Fatalf("Test %d - Failed to construct clear locks request - %v", i+1, err)

View File

@ -37,7 +37,7 @@ type remoteAdminClient struct {
// commands like service stop and service restart.
type adminCmdRunner interface {
Restart() error
ListLocks(bucket, prefix string, relTime time.Duration) ([]VolumeLockInfo, error)
ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error)
ReInitDisks() error
}
@ -49,8 +49,8 @@ func (lc localAdminClient) Restart() error {
}
// ListLocks - Fetches lock information from local lock instrumentation.
func (lc localAdminClient) ListLocks(bucket, prefix string, relTime time.Duration) ([]VolumeLockInfo, error) {
return listLocksInfo(bucket, prefix, relTime), nil
func (lc localAdminClient) ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error) {
return listLocksInfo(bucket, prefix, duration), nil
}
// Restart - Sends restart command to remote server via RPC.
@ -61,11 +61,11 @@ func (rc remoteAdminClient) Restart() error {
}
// ListLocks - Sends list locks command to remote server via RPC.
func (rc remoteAdminClient) ListLocks(bucket, prefix string, relTime time.Duration) ([]VolumeLockInfo, error) {
func (rc remoteAdminClient) ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error) {
listArgs := ListLocksQuery{
bucket: bucket,
prefix: prefix,
relTime: relTime,
duration: duration,
}
var reply ListLocksReply
if err := rc.Call("Admin.ListLocks", &listArgs, &reply); err != nil {
@ -175,8 +175,8 @@ func sendServiceCmd(cps adminPeers, cmd serviceSignal) {
}
// listPeerLocksInfo - fetch list of locks held on the given bucket,
// matching prefix older than relTime from all peer servers.
func listPeerLocksInfo(peers adminPeers, bucket, prefix string, relTime time.Duration) ([]VolumeLockInfo, error) {
// matching prefix held longer than duration from all peer servers.
func listPeerLocksInfo(peers adminPeers, bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error) {
// Used to aggregate volume lock information from all nodes.
allLocks := make([][]VolumeLockInfo, len(peers))
errs := make([]error, len(peers))
@ -188,11 +188,11 @@ func listPeerLocksInfo(peers adminPeers, bucket, prefix string, relTime time.Dur
go func(idx int, remotePeer adminPeer) {
defer wg.Done()
// `remotePeers` is right-shifted by one position relative to `peers`
allLocks[idx], errs[idx] = remotePeer.cmdRunner.ListLocks(bucket, prefix, relTime)
allLocks[idx], errs[idx] = remotePeer.cmdRunner.ListLocks(bucket, prefix, duration)
}(i+1, remotePeer)
}
wg.Wait()
allLocks[0], errs[0] = localPeer.cmdRunner.ListLocks(bucket, prefix, relTime)
allLocks[0], errs[0] = localPeer.cmdRunner.ListLocks(bucket, prefix, duration)
// Summarizing errors received for ListLocks RPC across all
// nodes. N B the possible unavailability of quorum in errors

View File

@ -39,7 +39,7 @@ type ListLocksQuery struct {
AuthRPCArgs
bucket string
prefix string
relTime time.Duration
duration time.Duration
}
// ListLocksReply - wraps ListLocks response over RPC.
@ -63,7 +63,7 @@ func (s *adminCmd) ListLocks(query *ListLocksQuery, reply *ListLocksReply) error
if err := query.IsAuthenticated(); err != nil {
return err
}
volLocks := listLocksInfo(query.bucket, query.prefix, query.relTime)
volLocks := listLocksInfo(query.bucket, query.prefix, query.duration)
*reply = ListLocksReply{volLocks: volLocks}
return nil
}

View File

@ -483,7 +483,7 @@ var errorCodeResponse = map[APIErrorCode]APIError{
},
ErrInvalidDuration: {
Code: "InvalidDuration",
Description: "Relative duration provided in the request is invalid.",
Description: "Duration provided in the request is invalid.",
HTTPStatusCode: http.StatusBadRequest,
},

View File

@ -68,8 +68,8 @@ type OpsLockState struct {
Duration time.Duration `json:"duration"` // Duration since the lock was held.
}
// listLocksInfo - Fetches locks held on bucket, matching prefix older than relTime.
func listLocksInfo(bucket, prefix string, relTime time.Duration) []VolumeLockInfo {
// listLocksInfo - Fetches locks held on bucket, matching prefix held for longer than duration.
func listLocksInfo(bucket, prefix string, duration time.Duration) []VolumeLockInfo {
globalNSMutex.lockMapMutex.Lock()
defer globalNSMutex.lockMapMutex.Unlock()
@ -95,11 +95,12 @@ func listLocksInfo(bucket, prefix string, relTime time.Duration) []VolumeLockInf
}
// Filter locks that are held on bucket, prefix.
for opsID, lockInfo := range debugLock.lockInfo {
// filter locks that were held for longer than duration.
elapsed := timeNow.Sub(lockInfo.since)
if elapsed < relTime {
if elapsed < duration {
continue
}
// Add locks that are older than relTime.
// Add locks that are held for longer than duration.
volLockInfo.LockDetailsOnObject = append(volLockInfo.LockDetailsOnObject,
OpsLockState{
OperationID: opsID,

View File

@ -45,34 +45,34 @@ func TestListLocksInfo(t *testing.T) {
testCases := []struct {
bucket string
prefix string
relTime time.Duration
duration time.Duration
numLocks int
}{
// Test 1 - Matches all the locks acquired above.
{
bucket: "bucket1",
prefix: "prefix1",
relTime: time.Duration(0 * time.Second),
duration: time.Duration(0 * time.Second),
numLocks: 20,
},
// Test 2 - Bucket doesn't match.
{
bucket: "bucket",
prefix: "prefix1",
relTime: time.Duration(0 * time.Second),
duration: time.Duration(0 * time.Second),
numLocks: 0,
},
// Test 3 - Prefix doesn't match.
{
bucket: "bucket1",
prefix: "prefix11",
relTime: time.Duration(0 * time.Second),
duration: time.Duration(0 * time.Second),
numLocks: 0,
},
}
for i, test := range testCases {
actual := listLocksInfo(test.bucket, test.prefix, test.relTime)
actual := listLocksInfo(test.bucket, test.prefix, test.duration)
if len(actual) != test.numLocks {
t.Errorf("Test %d - Expected %d locks but observed %d locks",
i+1, test.numLocks, len(actual))

View File

@ -66,9 +66,9 @@
### Lock Management APIs
* ListLocks
- GET /?lock&bucket=mybucket&prefix=myprefix&older-than=rel_time
- GET /?lock&bucket=mybucket&prefix=myprefix&duration=duration
- x-minio-operation: list
- Response: On success 200, json encoded response containing all locks held, older than rel_time. e.g, older than 3 hours.
- Response: On success 200, json encoded response containing all locks held, for longer than duration.
- Possible error responses
- ErrInvalidBucketName
<Error>
@ -95,7 +95,7 @@
- ErrInvalidDuration
<Error>
<Code>InvalidDuration</Code>
<Message>Relative duration provided in the request is invalid.</Message>
<Message>Duration provided in the request is invalid.</Message>
<Key></Key>
<BucketName></BucketName>
<Resource>/</Resource>
@ -105,9 +105,9 @@
* ClearLocks
- POST /?lock&bucket=mybucket&prefix=myprefix&older-than=rel_time
- POST /?lock&bucket=mybucket&prefix=myprefix&duration=duration
- x-minio-operation: clear
- Response: On success 200, json encoded response containing all locks cleared, older than rel_time. e.g, older than 3 hours.
- Response: On success 200, json encoded response containing all locks cleared, for longer than duration.
- Possible error responses, similar to errors listed in ListLocks.
- ErrInvalidBucketName
- ErrInvalidObjectName

View File

@ -120,8 +120,8 @@ If successful restarts the running minio service, for distributed setup restarts
```
<a name="ListLocks"></a>
### ListLocks(bucket, prefix string, olderThan time.Duration) ([]VolumeLockInfo, error)
If successful returns information on the list of locks held on ``bucket`` matching ``prefix`` older than ``olderThan`` seconds.
### ListLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error)
If successful returns information on the list of locks held on ``bucket`` matching ``prefix`` for longer than ``duration`` seconds.
__Example__
@ -135,8 +135,8 @@ __Example__
```
<a name="ClearLocks"></a>
### ClearLocks(bucket, prefix string, olderThan time.Duration) ([]VolumeLockInfo, error)
If successful returns information on the list of locks cleared on ``bucket`` matching ``prefix`` older than ``olderThan`` seconds.
### ClearLocks(bucket, prefix string, duration time.Duration) ([]VolumeLockInfo, error)
If successful returns information on the list of locks cleared on ``bucket`` matching ``prefix`` for longer than ``duration`` seconds.
__Example__

View File

@ -37,7 +37,7 @@ func main() {
log.Fatalln(err)
}
// Clear locks held on mybucket/myprefix older than olderThan seconds.
// Clear locks held on mybucket/myprefix for longer than 30s.
olderThan := time.Duration(30 * time.Second)
locksCleared, err := madmClnt.ClearLocks("mybucket", "myprefix", olderThan)
if err != nil {

View File

@ -37,7 +37,7 @@ func main() {
log.Fatalln(err)
}
// List locks held on mybucket/myprefix older than 30s.
// List locks held on mybucket/myprefix for longer than 30s.
locksHeld, err := madmClnt.ListLocks("mybucket", "myprefix", time.Duration(30*time.Second))
if err != nil {
log.Fatalln(err)