| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | /* | 
					
						
							|  |  |  |  * MinIO Cloud Storage, (C) 2019 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 ( | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 	xhttp "github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/rest" | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/dsync" | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // lockRESTClient is authenticable lock REST client
 | 
					
						
							|  |  |  | type lockRESTClient struct { | 
					
						
							|  |  |  | 	restClient *rest.Client | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	u          *url.URL | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | func toLockError(err error) error { | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch err.Error() { | 
					
						
							|  |  |  | 	case errLockConflict.Error(): | 
					
						
							|  |  |  | 		return errLockConflict | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	case errLockNotExpired.Error(): | 
					
						
							|  |  |  | 		return errLockNotExpired | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | // String stringer *dsync.NetLocker* interface compatible method.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) String() string { | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	return client.u.String() | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wrapper to restClient.Call to handle network errors, in case of network error the connection is marked disconnected
 | 
					
						
							|  |  |  | // permanently. The only way to restore the connection is at the xl-sets layer by xlsets.monitorAndConnectEndpoints()
 | 
					
						
							|  |  |  | // after verifying format.json
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (client *lockRESTClient) callWithContext(ctx context.Context, method string, values url.Values, body io.Reader, length int64) (respBody io.ReadCloser, err error) { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	if values == nil { | 
					
						
							|  |  |  | 		values = make(url.Values) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 	respBody, err = client.restClient.Call(ctx, method, values, body, length) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return respBody, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	return nil, toLockError(err) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsOnline - returns whether REST client failed to connect or not.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) IsOnline() bool { | 
					
						
							| 
									
										
										
										
											2020-06-17 09:59:32 +08:00
										 |  |  | 	return client.restClient.IsOnline() | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-09 03:32:32 +08:00
										 |  |  | // Not a local locker
 | 
					
						
							|  |  |  | func (client *lockRESTClient) IsLocal() bool { | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | // Close - marks the client as closed.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) Close() error { | 
					
						
							|  |  |  | 	client.restClient.Close() | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // restCall makes a call to the lock REST server.
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (client *lockRESTClient) restCall(ctx context.Context, call string, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2019-08-06 02:45:30 +08:00
										 |  |  | 	values := url.Values{} | 
					
						
							|  |  |  | 	values.Set(lockRESTUID, args.UID) | 
					
						
							| 
									
										
										
										
											2020-09-26 10:21:52 +08:00
										 |  |  | 	values.Set(lockRESTOwner, args.Owner) | 
					
						
							| 
									
										
										
										
											2019-08-06 02:45:30 +08:00
										 |  |  | 	values.Set(lockRESTSource, args.Source) | 
					
						
							| 
									
										
										
										
											2020-10-25 04:23:16 +08:00
										 |  |  | 	values.Set(lockRESTQuorum, strconv.Itoa(args.Quorum)) | 
					
						
							| 
									
										
										
										
											2020-02-21 13:59:57 +08:00
										 |  |  | 	var buffer bytes.Buffer | 
					
						
							|  |  |  | 	for _, resource := range args.Resources { | 
					
						
							|  |  |  | 		buffer.WriteString(resource) | 
					
						
							|  |  |  | 		buffer.WriteString("\n") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	respBody, err := client.callWithContext(ctx, call, values, &buffer, -1) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 	switch err { | 
					
						
							|  |  |  | 	case nil: | 
					
						
							|  |  |  | 		return true, nil | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | 	case errLockConflict, errLockNotExpired: | 
					
						
							| 
									
										
										
										
											2019-08-29 07:12:57 +08:00
										 |  |  | 		return false, nil | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RLock calls read lock REST API.
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (client *lockRESTClient) RLock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							|  |  |  | 	return client.restCall(ctx, lockRESTMethodRLock, args) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Lock calls lock REST API.
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (client *lockRESTClient) Lock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							|  |  |  | 	return client.restCall(ctx, lockRESTMethodLock, args) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RUnlock calls read unlock REST API.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) RUnlock(args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	return client.restCall(context.Background(), lockRESTMethodRUnlock, args) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Unlock calls write unlock RPC.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) Unlock(args dsync.LockArgs) (reply bool, err error) { | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | 	return client.restCall(context.Background(), lockRESTMethodUnlock, args) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | // Expired calls expired handler to check if lock args have expired.
 | 
					
						
							| 
									
										
										
										
											2020-07-30 14:15:34 +08:00
										 |  |  | func (client *lockRESTClient) Expired(ctx context.Context, args dsync.LockArgs) (expired bool, err error) { | 
					
						
							|  |  |  | 	return client.restCall(ctx, lockRESTMethodExpired, args) | 
					
						
							| 
									
										
										
										
											2019-11-26 08:39:43 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-26 02:01:27 +08:00
										 |  |  | // ForceUnlock calls force unlock handler to forcibly unlock an active lock.
 | 
					
						
							|  |  |  | func (client *lockRESTClient) ForceUnlock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) { | 
					
						
							|  |  |  | 	return client.restCall(ctx, lockRESTMethodForceUnlock, args) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | func newLockAPI(endpoint Endpoint) dsync.NetLocker { | 
					
						
							|  |  |  | 	if endpoint.IsLocal { | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 		return globalLockServer | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return newlockRESTClient(endpoint) | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns a lock rest client.
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | func newlockRESTClient(endpoint Endpoint) *lockRESTClient { | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	serverURL := &url.URL{ | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		Scheme: endpoint.Scheme, | 
					
						
							|  |  |  | 		Host:   endpoint.Host, | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 		Path:   pathJoin(lockRESTPrefix, lockRESTVersion), | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-02 23:43:11 +08:00
										 |  |  | 	restClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken) | 
					
						
							| 
									
										
										
										
											2020-10-30 00:52:11 +08:00
										 |  |  | 	restClient.ExpectTimeouts = true | 
					
						
							| 
									
										
										
										
											2020-11-11 01:28:23 +08:00
										 |  |  | 	// Use a separate client to avoid recursive calls.
 | 
					
						
							|  |  |  | 	healthClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken) | 
					
						
							|  |  |  | 	healthClient.ExpectTimeouts = true | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 	restClient.HealthCheckFn = func() bool { | 
					
						
							| 
									
										
										
										
											2021-01-29 05:38:12 +08:00
										 |  |  | 		ctx, cancel := context.WithTimeout(context.Background(), restClient.HealthCheckTimeout) | 
					
						
							| 
									
										
										
										
											2020-11-11 01:28:23 +08:00
										 |  |  | 		defer cancel() | 
					
						
							|  |  |  | 		respBody, err := healthClient.Call(ctx, lockRESTMethodHealth, nil, nil, -1) | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 		xhttp.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2020-11-20 05:53:49 +08:00
										 |  |  | 		return !isNetworkError(err) | 
					
						
							| 
									
										
										
										
											2020-06-18 05:49:26 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-10 23:28:37 +08:00
										 |  |  | 	return &lockRESTClient{u: &url.URL{ | 
					
						
							|  |  |  | 		Scheme: endpoint.Scheme, | 
					
						
							|  |  |  | 		Host:   endpoint.Host, | 
					
						
							|  |  |  | 	}, restClient: restClient} | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | } |