2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2019-04-18 02:16:27 -04:00
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
2020-02-21 00:59:57 -05:00
|
|
|
"bytes"
|
2020-06-17 17:49:26 -04:00
|
|
|
"context"
|
2019-04-18 02:16:27 -04:00
|
|
|
"io"
|
|
|
|
"net/url"
|
2020-10-24 16:23:16 -04:00
|
|
|
"strconv"
|
2019-04-18 02:16:27 -04:00
|
|
|
|
|
|
|
"github.com/minio/minio/cmd/http"
|
2020-06-17 17:49:26 -04:00
|
|
|
xhttp "github.com/minio/minio/cmd/http"
|
2019-04-18 02:16:27 -04:00
|
|
|
"github.com/minio/minio/cmd/rest"
|
2019-11-13 15:17:45 -05:00
|
|
|
"github.com/minio/minio/pkg/dsync"
|
2019-04-18 02:16:27 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// lockRESTClient is authenticable lock REST client
|
|
|
|
type lockRESTClient struct {
|
|
|
|
restClient *rest.Client
|
2020-12-10 10:28:37 -05:00
|
|
|
u *url.URL
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
2019-08-28 19:12:57 -04:00
|
|
|
func toLockError(err error) error {
|
|
|
|
if err == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
switch err.Error() {
|
|
|
|
case errLockConflict.Error():
|
|
|
|
return errLockConflict
|
2021-03-03 21:36:43 -05:00
|
|
|
case errLockNotFound.Error():
|
|
|
|
return errLockNotFound
|
2019-08-28 19:12:57 -04:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-11-13 15:17:45 -05:00
|
|
|
// String stringer *dsync.NetLocker* interface compatible method.
|
|
|
|
func (client *lockRESTClient) String() string {
|
2020-12-10 10:28:37 -05:00
|
|
|
return client.u.String()
|
2019-04-18 02:16:27 -04: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 02:15:34 -04: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 02:16:27 -04:00
|
|
|
if values == nil {
|
|
|
|
values = make(url.Values)
|
|
|
|
}
|
|
|
|
|
2020-09-04 12:45:06 -04:00
|
|
|
respBody, err = client.restClient.Call(ctx, method, values, body, length)
|
2019-04-18 02:16:27 -04:00
|
|
|
if err == nil {
|
|
|
|
return respBody, nil
|
|
|
|
}
|
|
|
|
|
2019-08-28 19:12:57 -04:00
|
|
|
return nil, toLockError(err)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// IsOnline - returns whether REST client failed to connect or not.
|
|
|
|
func (client *lockRESTClient) IsOnline() bool {
|
2020-06-16 21:59:32 -04:00
|
|
|
return client.restClient.IsOnline()
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
2020-10-08 15:32:32 -04:00
|
|
|
// Not a local locker
|
|
|
|
func (client *lockRESTClient) IsLocal() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-04-18 02:16:27 -04: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 02:15:34 -04:00
|
|
|
func (client *lockRESTClient) restCall(ctx context.Context, call string, args dsync.LockArgs) (reply bool, err error) {
|
2019-08-05 14:45:30 -04:00
|
|
|
values := url.Values{}
|
|
|
|
values.Set(lockRESTUID, args.UID)
|
2020-09-25 22:21:52 -04:00
|
|
|
values.Set(lockRESTOwner, args.Owner)
|
2019-08-05 14:45:30 -04:00
|
|
|
values.Set(lockRESTSource, args.Source)
|
2020-10-24 16:23:16 -04:00
|
|
|
values.Set(lockRESTQuorum, strconv.Itoa(args.Quorum))
|
2020-02-21 00:59:57 -05:00
|
|
|
var buffer bytes.Buffer
|
|
|
|
for _, resource := range args.Resources {
|
|
|
|
buffer.WriteString(resource)
|
|
|
|
buffer.WriteString("\n")
|
|
|
|
}
|
2020-07-30 02:15:34 -04:00
|
|
|
respBody, err := client.callWithContext(ctx, call, values, &buffer, -1)
|
2019-04-18 02:16:27 -04:00
|
|
|
defer http.DrainBody(respBody)
|
2019-08-28 19:12:57 -04:00
|
|
|
switch err {
|
|
|
|
case nil:
|
|
|
|
return true, nil
|
2021-03-03 21:36:43 -05:00
|
|
|
case errLockConflict, errLockNotFound:
|
2019-08-28 19:12:57 -04:00
|
|
|
return false, nil
|
|
|
|
default:
|
|
|
|
return false, err
|
|
|
|
}
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RLock calls read lock REST API.
|
2020-07-30 02:15:34 -04:00
|
|
|
func (client *lockRESTClient) RLock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) {
|
|
|
|
return client.restCall(ctx, lockRESTMethodRLock, args)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Lock calls lock REST API.
|
2020-07-30 02:15:34 -04:00
|
|
|
func (client *lockRESTClient) Lock(ctx context.Context, args dsync.LockArgs) (reply bool, err error) {
|
|
|
|
return client.restCall(ctx, lockRESTMethodLock, args)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// RUnlock calls read unlock REST API.
|
|
|
|
func (client *lockRESTClient) RUnlock(args dsync.LockArgs) (reply bool, err error) {
|
2020-07-30 02:15:34 -04:00
|
|
|
return client.restCall(context.Background(), lockRESTMethodRUnlock, args)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
2021-03-03 21:36:43 -05:00
|
|
|
// RUnlock calls read unlock REST API.
|
|
|
|
func (client *lockRESTClient) Refresh(ctx context.Context, args dsync.LockArgs) (reply bool, err error) {
|
|
|
|
return client.restCall(ctx, lockRESTMethodRefresh, args)
|
|
|
|
}
|
|
|
|
|
2019-04-18 02:16:27 -04:00
|
|
|
// Unlock calls write unlock RPC.
|
|
|
|
func (client *lockRESTClient) Unlock(args dsync.LockArgs) (reply bool, err error) {
|
2020-07-30 02:15:34 -04:00
|
|
|
return client.restCall(context.Background(), lockRESTMethodUnlock, args)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
2021-01-25 13:01:27 -05: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-13 15:17:45 -05:00
|
|
|
func newLockAPI(endpoint Endpoint) dsync.NetLocker {
|
|
|
|
if endpoint.IsLocal {
|
2020-12-10 10:28:37 -05:00
|
|
|
return globalLockServer
|
2019-11-13 15:17:45 -05:00
|
|
|
}
|
|
|
|
return newlockRESTClient(endpoint)
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a lock rest client.
|
2019-11-13 15:17:45 -05:00
|
|
|
func newlockRESTClient(endpoint Endpoint) *lockRESTClient {
|
2019-04-18 02:16:27 -04:00
|
|
|
serverURL := &url.URL{
|
2019-11-13 15:17:45 -05:00
|
|
|
Scheme: endpoint.Scheme,
|
|
|
|
Host: endpoint.Host,
|
2020-12-10 10:28:37 -05:00
|
|
|
Path: pathJoin(lockRESTPrefix, lockRESTVersion),
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|
|
|
|
|
2020-11-02 10:43:11 -05:00
|
|
|
restClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken)
|
2020-10-29 12:52:11 -04:00
|
|
|
restClient.ExpectTimeouts = true
|
2020-11-10 12:28:23 -05:00
|
|
|
// Use a separate client to avoid recursive calls.
|
|
|
|
healthClient := rest.NewClient(serverURL, globalInternodeTransport, newAuthToken)
|
|
|
|
healthClient.ExpectTimeouts = true
|
2020-06-17 17:49:26 -04:00
|
|
|
restClient.HealthCheckFn = func() bool {
|
2021-01-28 16:38:12 -05:00
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), restClient.HealthCheckTimeout)
|
2020-11-10 12:28:23 -05:00
|
|
|
defer cancel()
|
|
|
|
respBody, err := healthClient.Call(ctx, lockRESTMethodHealth, nil, nil, -1)
|
2020-06-17 17:49:26 -04:00
|
|
|
xhttp.DrainBody(respBody)
|
2020-11-19 16:53:49 -05:00
|
|
|
return !isNetworkError(err)
|
2020-06-17 17:49:26 -04:00
|
|
|
}
|
2019-04-18 02:16:27 -04:00
|
|
|
|
2020-12-10 10:28:37 -05:00
|
|
|
return &lockRESTClient{u: &url.URL{
|
|
|
|
Scheme: endpoint.Scheme,
|
|
|
|
Host: endpoint.Host,
|
|
|
|
}, restClient: restClient}
|
2019-04-18 02:16:27 -04:00
|
|
|
}
|