storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/lock-rest-client.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 "bytes" 21 "context" 22 "io" 23 "net/url" 24 "strconv" 25 26 "storj.io/minio/cmd/http" 27 xhttp "storj.io/minio/cmd/http" 28 "storj.io/minio/cmd/rest" 29 "storj.io/minio/pkg/dsync" 30 ) 31 32 // lockRESTClient is authenticable lock REST client 33 type lockRESTClient struct { 34 restClient *rest.Client 35 u *url.URL 36 } 37 38 func toLockError(err error) error { 39 if err == nil { 40 return nil 41 } 42 43 switch err.Error() { 44 case errLockConflict.Error(): 45 return errLockConflict 46 case errLockNotFound.Error(): 47 return errLockNotFound 48 } 49 return err 50 } 51 52 // String stringer *dsync.NetLocker* interface compatible method. 53 func (client *lockRESTClient) String() string { 54 return client.u.String() 55 } 56 57 // Wrapper to restClient.Call to handle network errors, in case of network error the connection is marked disconnected 58 // permanently. The only way to restore the connection is at the xl-sets layer by xlsets.monitorAndConnectEndpoints() 59 // after verifying format.json 60 func (client *lockRESTClient) callWithContext(ctx context.Context, method string, values url.Values, body io.Reader, length int64) (respBody io.ReadCloser, err error) { 61 if values == nil { 62 values = make(url.Values) 63 } 64 65 respBody, err = client.restClient.Call(ctx, method, values, body, length) 66 if err == nil { 67 return respBody, nil 68 } 69 70 return nil, toLockError(err) 71 } 72 73 // IsOnline - returns whether REST client failed to connect or not. 74 func (client *lockRESTClient) IsOnline() bool { 75 return client.restClient.IsOnline() 76 } 77 78 // Not a local locker 79 func (client *lockRESTClient) IsLocal() bool { 80 return false 81 } 82 83 // Close - marks the client as closed. 84 func (client *lockRESTClient) Close() error { 85 client.restClient.Close() 86 return nil 87 } 88 89 // restCall makes a call to the lock REST server. 90 func (client *lockRESTClient) restCall(ctx context.Context, call string, args dsync.LockArgs) (reply bool, err error) { 91 values := url.Values{} 92 values.Set(lockRESTUID, args.UID) 93 values.Set(lockRESTOwner, args.Owner) 94 values.Set(lockRESTSource, args.Source) 95 values.Set(lockRESTQuorum, strconv.Itoa(args.Quorum)) 96 var buffer bytes.Buffer 97 for _, resource := range args.Resources { 98 buffer.WriteString(resource) 99 buffer.WriteString("\n") 100 } 101 respBody, err := client.callWithContext(ctx, call, values, &buffer, -1) 102 defer http.DrainBody(respBody) 103 switch err { 104 case nil: 105 return true, nil 106 case errLockConflict, errLockNotFound: 107 return false, nil 108 default: 109 return false, err 110 } 111 } 112 113 // RLock calls read lock REST API. 114 func (client *lockRESTClient) RLock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { 115 return client.restCall(ctx, lockRESTMethodRLock, args) 116 } 117 118 // Lock calls lock REST API. 119 func (client *lockRESTClient) Lock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { 120 return client.restCall(ctx, lockRESTMethodLock, args) 121 } 122 123 // RUnlock calls read unlock REST API. 124 func (client *lockRESTClient) RUnlock(args dsync.LockArgs) (reply bool, err error) { 125 return client.restCall(context.Background(), lockRESTMethodRUnlock, args) 126 } 127 128 // RUnlock calls read unlock REST API. 129 func (client *lockRESTClient) Refresh(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { 130 return client.restCall(ctx, lockRESTMethodRefresh, args) 131 } 132 133 // Unlock calls write unlock RPC. 134 func (client *lockRESTClient) Unlock(args dsync.LockArgs) (reply bool, err error) { 135 return client.restCall(context.Background(), lockRESTMethodUnlock, args) 136 } 137 138 // ForceUnlock calls force unlock handler to forcibly unlock an active lock. 139 func (client *lockRESTClient) ForceUnlock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { 140 return client.restCall(ctx, lockRESTMethodForceUnlock, args) 141 } 142 143 func newLockAPI(endpoint Endpoint) dsync.NetLocker { 144 if endpoint.IsLocal { 145 return globalLockServer 146 } 147 return newlockRESTClient(endpoint) 148 } 149 150 // Returns a lock rest client. 151 func newlockRESTClient(endpoint Endpoint) *lockRESTClient { 152 serverURL := &url.URL{ 153 Scheme: endpoint.Scheme, 154 Host: endpoint.Host, 155 Path: pathJoin(lockRESTPrefix, lockRESTVersion), 156 } 157 158 restClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken) 159 restClient.ExpectTimeouts = true 160 // Use a separate client to avoid recursive calls. 161 healthClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken) 162 healthClient.ExpectTimeouts = true 163 restClient.HealthCheckFn = func() bool { 164 ctx, cancel := context.WithTimeout(context.Background(), restClient.HealthCheckTimeout) 165 defer cancel() 166 respBody, err := healthClient.Call(ctx, lockRESTMethodHealth, nil, nil, -1) 167 xhttp.DrainBody(respBody) 168 return !isNetworkError(err) 169 } 170 171 return &lockRESTClient{u: &url.URL{ 172 Scheme: endpoint.Scheme, 173 Host: endpoint.Host, 174 }, restClient: restClient} 175 }