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 }