storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/lock-rest-server.go (about)

     1  /*
     2   * Minio Cloud Storage, (C) 2019 Minio, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"bufio"
    21  	"context"
    22  	"errors"
    23  	"net/http"
    24  	"sort"
    25  	"strconv"
    26  	"time"
    27  
    28  	"github.com/gorilla/mux"
    29  
    30  	"storj.io/minio/pkg/dsync"
    31  )
    32  
    33  const (
    34  	// Lock maintenance interval.
    35  	lockMaintenanceInterval = 1 * time.Minute
    36  
    37  	// Lock validity duration
    38  	lockValidityDuration = 20 * time.Second
    39  )
    40  
    41  // To abstract a node over network.
    42  type lockRESTServer struct {
    43  	ll *localLocker
    44  }
    45  
    46  func (l *lockRESTServer) WriteErrorResponse(w http.ResponseWriter, err error) {
    47  	w.WriteHeader(http.StatusForbidden)
    48  	w.Write([]byte(err.Error()))
    49  }
    50  
    51  // IsValid - To authenticate and verify the time difference.
    52  func (l *lockRESTServer) IsValid(w http.ResponseWriter, r *http.Request) bool {
    53  	if l.ll == nil {
    54  		l.WriteErrorResponse(w, errLockNotInitialized)
    55  		return false
    56  	}
    57  
    58  	if err := storageServerRequestValidate(r); err != nil {
    59  		l.WriteErrorResponse(w, err)
    60  		return false
    61  	}
    62  	return true
    63  }
    64  
    65  func getLockArgs(r *http.Request) (args dsync.LockArgs, err error) {
    66  	quorum, err := strconv.Atoi(r.URL.Query().Get(lockRESTQuorum))
    67  	if err != nil {
    68  		return args, err
    69  	}
    70  
    71  	args = dsync.LockArgs{
    72  		Owner:  r.URL.Query().Get(lockRESTOwner),
    73  		UID:    r.URL.Query().Get(lockRESTUID),
    74  		Source: r.URL.Query().Get(lockRESTSource),
    75  		Quorum: quorum,
    76  	}
    77  
    78  	var resources []string
    79  	bio := bufio.NewScanner(r.Body)
    80  	for bio.Scan() {
    81  		resources = append(resources, bio.Text())
    82  	}
    83  
    84  	if err := bio.Err(); err != nil {
    85  		return args, err
    86  	}
    87  
    88  	sort.Strings(resources)
    89  	args.Resources = resources
    90  	return args, nil
    91  }
    92  
    93  // HealthHandler returns success if request is authenticated.
    94  func (l *lockRESTServer) HealthHandler(w http.ResponseWriter, r *http.Request) {
    95  	l.IsValid(w, r)
    96  }
    97  
    98  // RefreshHandler - refresh the current lock
    99  func (l *lockRESTServer) RefreshHandler(w http.ResponseWriter, r *http.Request) {
   100  	if !l.IsValid(w, r) {
   101  		l.WriteErrorResponse(w, errors.New("invalid request"))
   102  		return
   103  	}
   104  
   105  	args, err := getLockArgs(r)
   106  	if err != nil {
   107  		l.WriteErrorResponse(w, err)
   108  		return
   109  	}
   110  
   111  	refreshed, err := l.ll.Refresh(r.Context(), args)
   112  	if err != nil {
   113  		l.WriteErrorResponse(w, err)
   114  		return
   115  	}
   116  
   117  	if !refreshed {
   118  		l.WriteErrorResponse(w, errLockNotFound)
   119  		return
   120  	}
   121  }
   122  
   123  // LockHandler - Acquires a lock.
   124  func (l *lockRESTServer) LockHandler(w http.ResponseWriter, r *http.Request) {
   125  	if !l.IsValid(w, r) {
   126  		l.WriteErrorResponse(w, errors.New("invalid request"))
   127  		return
   128  	}
   129  
   130  	args, err := getLockArgs(r)
   131  	if err != nil {
   132  		l.WriteErrorResponse(w, err)
   133  		return
   134  	}
   135  
   136  	success, err := l.ll.Lock(r.Context(), args)
   137  	if err == nil && !success {
   138  		err = errLockConflict
   139  	}
   140  	if err != nil {
   141  		l.WriteErrorResponse(w, err)
   142  		return
   143  	}
   144  }
   145  
   146  // UnlockHandler - releases the acquired lock.
   147  func (l *lockRESTServer) UnlockHandler(w http.ResponseWriter, r *http.Request) {
   148  	if !l.IsValid(w, r) {
   149  		l.WriteErrorResponse(w, errors.New("invalid request"))
   150  		return
   151  	}
   152  
   153  	args, err := getLockArgs(r)
   154  	if err != nil {
   155  		l.WriteErrorResponse(w, err)
   156  		return
   157  	}
   158  
   159  	_, err = l.ll.Unlock(args)
   160  	// Ignore the Unlock() "reply" return value because if err == nil, "reply" is always true
   161  	// Consequently, if err != nil, reply is always false
   162  	if err != nil {
   163  		l.WriteErrorResponse(w, err)
   164  		return
   165  	}
   166  }
   167  
   168  // LockHandler - Acquires an RLock.
   169  func (l *lockRESTServer) RLockHandler(w http.ResponseWriter, r *http.Request) {
   170  	if !l.IsValid(w, r) {
   171  		l.WriteErrorResponse(w, errors.New("invalid request"))
   172  		return
   173  	}
   174  
   175  	args, err := getLockArgs(r)
   176  	if err != nil {
   177  		l.WriteErrorResponse(w, err)
   178  		return
   179  	}
   180  
   181  	success, err := l.ll.RLock(r.Context(), args)
   182  	if err == nil && !success {
   183  		err = errLockConflict
   184  	}
   185  	if err != nil {
   186  		l.WriteErrorResponse(w, err)
   187  		return
   188  	}
   189  }
   190  
   191  // RUnlockHandler - releases the acquired read lock.
   192  func (l *lockRESTServer) RUnlockHandler(w http.ResponseWriter, r *http.Request) {
   193  	if !l.IsValid(w, r) {
   194  		l.WriteErrorResponse(w, errors.New("invalid request"))
   195  		return
   196  	}
   197  
   198  	args, err := getLockArgs(r)
   199  	if err != nil {
   200  		l.WriteErrorResponse(w, err)
   201  		return
   202  	}
   203  
   204  	// Ignore the RUnlock() "reply" return value because if err == nil, "reply" is always true.
   205  	// Consequently, if err != nil, reply is always false
   206  	if _, err = l.ll.RUnlock(args); err != nil {
   207  		l.WriteErrorResponse(w, err)
   208  		return
   209  	}
   210  }
   211  
   212  // ForceUnlockHandler - query expired lock status.
   213  func (l *lockRESTServer) ForceUnlockHandler(w http.ResponseWriter, r *http.Request) {
   214  	if !l.IsValid(w, r) {
   215  		l.WriteErrorResponse(w, errors.New("invalid request"))
   216  		return
   217  	}
   218  
   219  	args, err := getLockArgs(r)
   220  	if err != nil {
   221  		l.WriteErrorResponse(w, err)
   222  		return
   223  	}
   224  
   225  	if _, err = l.ll.ForceUnlock(r.Context(), args); err != nil {
   226  		l.WriteErrorResponse(w, err)
   227  		return
   228  	}
   229  }
   230  
   231  // lockMaintenance loops over all locks and discards locks
   232  // that have not been refreshed for some time.
   233  func lockMaintenance(ctx context.Context) {
   234  	// Wait until the object API is ready
   235  	// no need to start the lock maintenance
   236  	// if ObjectAPI is not initialized.
   237  
   238  	var objAPI ObjectLayer
   239  
   240  	for {
   241  		objAPI = newObjectLayerFn()
   242  		if objAPI == nil {
   243  			time.Sleep(time.Second)
   244  			continue
   245  		}
   246  		break
   247  	}
   248  
   249  	if _, ok := objAPI.(*erasureServerPools); !ok {
   250  		return
   251  	}
   252  
   253  	// Initialize a new ticker with 1 minute between each ticks.
   254  	lkTimer := time.NewTimer(lockMaintenanceInterval)
   255  	// Stop the timer upon returning.
   256  	defer lkTimer.Stop()
   257  
   258  	for {
   259  		// Verifies every minute for locks held more than 2 minutes.
   260  		select {
   261  		case <-ctx.Done():
   262  			return
   263  		case <-lkTimer.C:
   264  			// Reset the timer for next cycle.
   265  			lkTimer.Reset(lockMaintenanceInterval)
   266  
   267  			globalLockServer.expireOldLocks(lockValidityDuration)
   268  		}
   269  	}
   270  }
   271  
   272  // registerLockRESTHandlers - register lock rest router.
   273  func registerLockRESTHandlers(router *mux.Router) {
   274  	lockServer := &lockRESTServer{
   275  		ll: newLocker(),
   276  	}
   277  
   278  	subrouter := router.PathPrefix(lockRESTPrefix).Subrouter()
   279  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodHealth).HandlerFunc(HTTPTraceHdrs(lockServer.HealthHandler))
   280  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRefresh).HandlerFunc(HTTPTraceHdrs(lockServer.RefreshHandler))
   281  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodLock).HandlerFunc(HTTPTraceHdrs(lockServer.LockHandler))
   282  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRLock).HandlerFunc(HTTPTraceHdrs(lockServer.RLockHandler))
   283  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodUnlock).HandlerFunc(HTTPTraceHdrs(lockServer.UnlockHandler))
   284  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodRUnlock).HandlerFunc(HTTPTraceHdrs(lockServer.RUnlockHandler))
   285  	subrouter.Methods(http.MethodPost).Path(lockRESTVersionPrefix + lockRESTMethodForceUnlock).HandlerFunc(HTTPTraceAll(lockServer.ForceUnlockHandler))
   286  
   287  	globalLockServer = lockServer.ll
   288  
   289  	go lockMaintenance(GlobalContext)
   290  }