| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2018-2019 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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 ( | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	"bufio" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"crypto/tls" | 
					
						
							| 
									
										
										
										
											2019-09-26 14:08:24 +08:00
										 |  |  | 	"encoding/gob" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"io/ioutil" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"path" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 	"sync/atomic" | 
					
						
							| 
									
										
										
										
											2019-01-31 02:53:57 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/http" | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/logger" | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	"github.com/minio/minio/cmd/rest" | 
					
						
							|  |  |  | 	xnet "github.com/minio/minio/pkg/net" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | func isNetworkError(err error) bool { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return false | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-07-30 05:48:18 +08:00
										 |  |  | 	if nerr, ok := err.(*rest.NetworkError); ok { | 
					
						
							| 
									
										
										
										
											2019-09-13 07:44:51 +08:00
										 |  |  | 		return xnet.IsNetworkOrHostDown(nerr.Err) | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-30 01:21:47 +08:00
										 |  |  | 	return false | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | // Converts network error to storageErr. This function is
 | 
					
						
							|  |  |  | // written so that the storageAPI errors are consistent
 | 
					
						
							|  |  |  | // across network disks.
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | func toStorageErr(err error) error { | 
					
						
							|  |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	if isNetworkError(err) { | 
					
						
							| 
									
										
										
										
											2019-05-30 01:21:47 +08:00
										 |  |  | 		return errDiskNotFound | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch err.Error() { | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	case errFaultyDisk.Error(): | 
					
						
							|  |  |  | 		return errFaultyDisk | 
					
						
							|  |  |  | 	case errFileCorrupt.Error(): | 
					
						
							|  |  |  | 		return errFileCorrupt | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	case errUnexpected.Error(): | 
					
						
							|  |  |  | 		return errUnexpected | 
					
						
							|  |  |  | 	case errDiskFull.Error(): | 
					
						
							|  |  |  | 		return errDiskFull | 
					
						
							|  |  |  | 	case errVolumeNotFound.Error(): | 
					
						
							|  |  |  | 		return errVolumeNotFound | 
					
						
							|  |  |  | 	case errVolumeExists.Error(): | 
					
						
							|  |  |  | 		return errVolumeExists | 
					
						
							|  |  |  | 	case errFileNotFound.Error(): | 
					
						
							|  |  |  | 		return errFileNotFound | 
					
						
							|  |  |  | 	case errFileNameTooLong.Error(): | 
					
						
							|  |  |  | 		return errFileNameTooLong | 
					
						
							|  |  |  | 	case errFileAccessDenied.Error(): | 
					
						
							|  |  |  | 		return errFileAccessDenied | 
					
						
							|  |  |  | 	case errIsNotRegular.Error(): | 
					
						
							|  |  |  | 		return errIsNotRegular | 
					
						
							|  |  |  | 	case errVolumeNotEmpty.Error(): | 
					
						
							|  |  |  | 		return errVolumeNotEmpty | 
					
						
							|  |  |  | 	case errVolumeAccessDenied.Error(): | 
					
						
							|  |  |  | 		return errVolumeAccessDenied | 
					
						
							|  |  |  | 	case errCorruptedFormat.Error(): | 
					
						
							|  |  |  | 		return errCorruptedFormat | 
					
						
							|  |  |  | 	case errUnformattedDisk.Error(): | 
					
						
							|  |  |  | 		return errUnformattedDisk | 
					
						
							|  |  |  | 	case errInvalidAccessKeyID.Error(): | 
					
						
							|  |  |  | 		return errInvalidAccessKeyID | 
					
						
							|  |  |  | 	case errAuthentication.Error(): | 
					
						
							|  |  |  | 		return errAuthentication | 
					
						
							|  |  |  | 	case errRPCAPIVersionUnsupported.Error(): | 
					
						
							|  |  |  | 		return errRPCAPIVersionUnsupported | 
					
						
							|  |  |  | 	case errServerTimeMismatch.Error(): | 
					
						
							|  |  |  | 		return errServerTimeMismatch | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	case io.EOF.Error(): | 
					
						
							|  |  |  | 		return io.EOF | 
					
						
							|  |  |  | 	case io.ErrUnexpectedEOF.Error(): | 
					
						
							|  |  |  | 		return io.ErrUnexpectedEOF | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	case errDiskStale.Error(): | 
					
						
							|  |  |  | 		return errDiskNotFound | 
					
						
							| 
									
										
										
										
											2019-01-31 02:53:57 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Abstracts a remote disk.
 | 
					
						
							|  |  |  | type storageRESTClient struct { | 
					
						
							|  |  |  | 	endpoint   Endpoint | 
					
						
							|  |  |  | 	restClient *rest.Client | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 	connected  int32 | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	diskID     string | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wrapper to restClient.Call to handle network errors, in case of network error the connection is makred disconnected
 | 
					
						
							|  |  |  | // permanently. The only way to restore the storage connection is at the xl-sets layer by xlsets.monitorAndConnectEndpoints()
 | 
					
						
							|  |  |  | // after verifying format.json
 | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | func (client *storageRESTClient) call(method string, values url.Values, body io.Reader, length int64) (io.ReadCloser, error) { | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 	if !client.IsOnline() { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		return nil, errDiskNotFound | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-14 07:29:46 +08:00
										 |  |  | 	if values == nil { | 
					
						
							|  |  |  | 		values = make(url.Values) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | 	values.Set(storageRESTDiskID, client.diskID) | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | 	respBody, err := client.restClient.Call(method, values, body, length) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return respBody, nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	err = toStorageErr(err) | 
					
						
							|  |  |  | 	if err == errDiskNotFound { | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 		atomic.StoreInt32(&client.connected, 0) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-15 10:45:17 +08:00
										 |  |  | 	return nil, err | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Stringer provides a canonicalized representation of network device.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) String() string { | 
					
						
							|  |  |  | 	return client.endpoint.String() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // IsOnline - returns whether RPC client failed to connect or not.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) IsOnline() bool { | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 	return atomic.LoadInt32(&client.connected) == 1 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 05:09:10 +08:00
										 |  |  | func (client *storageRESTClient) Hostname() string { | 
					
						
							|  |  |  | 	return client.endpoint.Host | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 22:02:37 +08:00
										 |  |  | func (client *storageRESTClient) CrawlAndGetDataUsage(endCh <-chan struct{}) (DataUsageInfo, error) { | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodCrawlAndGetDataUsage, nil, nil, -1) | 
					
						
							|  |  |  | 	defer http.DrainBody(respBody) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return DataUsageInfo{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reader := bufio.NewReader(respBody) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		b, err := reader.ReadByte() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return DataUsageInfo{}, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if b != ' ' { | 
					
						
							|  |  |  | 			reader.UnreadByte() | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	var usageInfo DataUsageInfo | 
					
						
							|  |  |  | 	err = gob.NewDecoder(reader).Decode(&usageInfo) | 
					
						
							|  |  |  | 	return usageInfo, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-26 01:37:53 +08:00
										 |  |  | func (client *storageRESTClient) SetDiskID(id string) { | 
					
						
							|  |  |  | 	client.diskID = id | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // DiskInfo - fetch disk information for a remote disk.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) DiskInfo() (info DiskInfo, err error) { | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodDiskInfo, nil, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	err = gob.NewDecoder(respBody).Decode(&info) | 
					
						
							|  |  |  | 	return info, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-24 08:31:03 +08:00
										 |  |  | // MakeVolBulk - create multiple volumes in a bulk operation.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) MakeVolBulk(volumes ...string) (err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolumes, strings.Join(volumes, ",")) | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodMakeVolBulk, values, nil, -1) | 
					
						
							|  |  |  | 	defer http.DrainBody(respBody) | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // MakeVol - create a volume on a remote disk.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) MakeVol(volume string) (err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodMakeVol, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ListVols - List all volumes on a remote disk.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) ListVols() (volinfo []VolInfo, err error) { | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodListVols, nil, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	err = gob.NewDecoder(respBody).Decode(&volinfo) | 
					
						
							|  |  |  | 	return volinfo, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // StatVol - get volume info over the network.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) StatVol(volume string) (volInfo VolInfo, err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodStatVol, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	err = gob.NewDecoder(respBody).Decode(&volInfo) | 
					
						
							|  |  |  | 	return volInfo, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteVol - Deletes a volume over the network.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) DeleteVol(volume string) (err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodDeleteVol, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // AppendFile - append to a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) AppendFile(volume, path string, buffer []byte) error { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	reader := bytes.NewBuffer(buffer) | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodAppendFile, values, reader, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | func (client *storageRESTClient) CreateFile(volume, path string, length int64, r io.Reader) error { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	values.Set(storageRESTLength, strconv.Itoa(int(length))) | 
					
						
							| 
									
										
										
										
											2019-05-02 22:09:57 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodCreateFile, values, ioutil.NopCloser(r), length) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | // WriteAll - write all data to a file.
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:47:15 +08:00
										 |  |  | func (client *storageRESTClient) WriteAll(volume, path string, reader io.Reader) error { | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodWriteAll, values, reader, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-11-14 22:18:35 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // StatFile - stat a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) StatFile(volume, path string) (info FileInfo, err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodStatFile, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return info, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	err = gob.NewDecoder(respBody).Decode(&info) | 
					
						
							|  |  |  | 	return info, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReadAll - reads all contents of a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) ReadAll(volume, path string) ([]byte, error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodReadAll, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return ioutil.ReadAll(respBody) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | // ReadFileStream - returns a reader for the requested file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) ReadFileStream(volume, path string, offset, length int64) (io.ReadCloser, error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							|  |  |  | 	values.Set(storageRESTOffset, strconv.Itoa(int(offset))) | 
					
						
							|  |  |  | 	values.Set(storageRESTLength, strconv.Itoa(int(length))) | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodReadFileStream, values, nil, -1) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return respBody, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ReadFile - reads section of a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) ReadFile(volume, path string, offset int64, buffer []byte, verifier *BitrotVerifier) (int64, error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							|  |  |  | 	values.Set(storageRESTOffset, strconv.Itoa(int(offset))) | 
					
						
							|  |  |  | 	values.Set(storageRESTLength, strconv.Itoa(len(buffer))) | 
					
						
							|  |  |  | 	if verifier != nil { | 
					
						
							|  |  |  | 		values.Set(storageRESTBitrotAlgo, verifier.algorithm.String()) | 
					
						
							|  |  |  | 		values.Set(storageRESTBitrotHash, hex.EncodeToString(verifier.sum)) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		values.Set(storageRESTBitrotAlgo, "") | 
					
						
							|  |  |  | 		values.Set(storageRESTBitrotHash, "") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodReadFile, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	n, err := io.ReadFull(respBody, buffer) | 
					
						
							|  |  |  | 	return int64(n), err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-15 04:49:10 +08:00
										 |  |  | func (client *storageRESTClient) Walk(volume, dirPath, marker string, recursive bool, leafFile string, | 
					
						
							|  |  |  | 	readMetadataFn readMetadataFunc, endWalkCh chan struct{}) (chan FileInfo, error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTDirPath, dirPath) | 
					
						
							|  |  |  | 	values.Set(storageRESTMarkerPath, marker) | 
					
						
							|  |  |  | 	values.Set(storageRESTRecursive, strconv.FormatBool(recursive)) | 
					
						
							|  |  |  | 	values.Set(storageRESTLeafFile, leafFile) | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodWalk, values, nil, -1) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ch := make(chan FileInfo) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		defer close(ch) | 
					
						
							|  |  |  | 		defer http.DrainBody(respBody) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		decoder := gob.NewDecoder(respBody) | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			var fi FileInfo | 
					
						
							|  |  |  | 			if gerr := decoder.Decode(&fi); gerr != nil { | 
					
						
							|  |  |  | 				// Upon error return
 | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case ch <- fi: | 
					
						
							|  |  |  | 			case <-endWalkCh: | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ch, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // ListDir - lists a directory.
 | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | func (client *storageRESTClient) ListDir(volume, dirPath string, count int, leafFile string) (entries []string, err error) { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTDirPath, dirPath) | 
					
						
							|  |  |  | 	values.Set(storageRESTCount, strconv.Itoa(count)) | 
					
						
							| 
									
										
										
										
											2019-04-24 05:54:28 +08:00
										 |  |  | 	values.Set(storageRESTLeafFile, leafFile) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodListDir, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	err = gob.NewDecoder(respBody).Decode(&entries) | 
					
						
							|  |  |  | 	return entries, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteFile - deletes a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) DeleteFile(volume, path string) error { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodDeleteFile, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | // DeleteFileBulk - deletes files in bulk.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) DeleteFileBulk(volume string, paths []string) (errs []error, err error) { | 
					
						
							| 
									
										
										
										
											2019-08-16 04:15:49 +08:00
										 |  |  | 	if len(paths) == 0 { | 
					
						
							|  |  |  | 		return errs, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	for _, path := range paths { | 
					
						
							|  |  |  | 		values.Add(storageRESTFilePath, path) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	respBody, err := client.call(storageRESTMethodDeleteFileBulk, values, nil, -1) | 
					
						
							|  |  |  | 	defer http.DrainBody(respBody) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 	dErrResp := &DeleteFileBulkErrsResp{} | 
					
						
							|  |  |  | 	if err = gob.NewDecoder(respBody).Decode(dErrResp); err != nil { | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-01 10:01:28 +08:00
										 |  |  | 	for _, dErr := range dErrResp.Errs { | 
					
						
							|  |  |  | 		errs = append(errs, toStorageErr(dErr)) | 
					
						
							| 
									
										
										
										
											2019-05-14 03:25:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return errs, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // RenameFile - renames a file.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) RenameFile(srcVolume, srcPath, dstVolume, dstPath string) (err error) { | 
					
						
							|  |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTSrcVolume, srcVolume) | 
					
						
							|  |  |  | 	values.Set(storageRESTSrcPath, srcPath) | 
					
						
							|  |  |  | 	values.Set(storageRESTDstVolume, dstVolume) | 
					
						
							|  |  |  | 	values.Set(storageRESTDstPath, dstPath) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodRenameFile, values, nil, -1) | 
					
						
							| 
									
										
										
										
											2019-02-07 04:07:03 +08:00
										 |  |  | 	defer http.DrainBody(respBody) | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 04:49:53 +08:00
										 |  |  | func (client *storageRESTClient) VerifyFile(volume, path string, size int64, algo BitrotAlgorithm, sum []byte, shardSize int64) error { | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	values := make(url.Values) | 
					
						
							|  |  |  | 	values.Set(storageRESTVolume, volume) | 
					
						
							|  |  |  | 	values.Set(storageRESTFilePath, path) | 
					
						
							|  |  |  | 	values.Set(storageRESTBitrotAlgo, algo.String()) | 
					
						
							| 
									
										
										
										
											2019-09-12 04:49:53 +08:00
										 |  |  | 	values.Set(storageRESTLength, strconv.FormatInt(size, 10)) | 
					
						
							|  |  |  | 	values.Set(storageRESTShardSize, strconv.Itoa(int(shardSize))) | 
					
						
							| 
									
										
										
										
											2019-09-13 04:08:02 +08:00
										 |  |  | 	values.Set(storageRESTBitrotHash, hex.EncodeToString(sum)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 	respBody, err := client.call(storageRESTMethodVerifyFile, values, nil, -1) | 
					
						
							|  |  |  | 	defer http.DrainBody(respBody) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reader := bufio.NewReader(respBody) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		b, err := reader.ReadByte() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if b != ' ' { | 
					
						
							|  |  |  | 			reader.UnreadByte() | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	verifyResp := &VerifyFileResp{} | 
					
						
							| 
									
										
										
										
											2019-10-02 04:12:15 +08:00
										 |  |  | 	if err = gob.NewDecoder(reader).Decode(verifyResp); err != nil { | 
					
						
							| 
									
										
										
										
											2019-07-09 04:51:18 +08:00
										 |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return toStorageErr(verifyResp.Err) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | // Close - marks the client as closed.
 | 
					
						
							|  |  |  | func (client *storageRESTClient) Close() error { | 
					
						
							| 
									
										
										
										
											2019-10-09 14:11:15 +08:00
										 |  |  | 	atomic.StoreInt32(&client.connected, 0) | 
					
						
							| 
									
										
										
										
											2018-11-21 03:07:19 +08:00
										 |  |  | 	client.restClient.Close() | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Returns a storage rest client.
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | func newStorageRESTClient(endpoint Endpoint) *storageRESTClient { | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	serverURL := &url.URL{ | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		Scheme: endpoint.Scheme, | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		Host:   endpoint.Host, | 
					
						
							| 
									
										
										
										
											2019-11-05 01:30:59 +08:00
										 |  |  | 		Path:   path.Join(storageRESTPrefix, endpoint.Path, storageRESTVersion), | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var tlsConfig *tls.Config | 
					
						
							|  |  |  | 	if globalIsSSL { | 
					
						
							|  |  |  | 		tlsConfig = &tls.Config{ | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 			ServerName: endpoint.Hostname(), | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 			RootCAs:    globalRootCAs, | 
					
						
							| 
									
										
										
										
											2019-04-18 14:16:27 +08:00
										 |  |  | 			NextProtos: []string{"http/1.1"}, // Force http1.1
 | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	trFn := newCustomHTTPTransport(tlsConfig, rest.DefaultRESTTimeout, rest.DefaultRESTTimeout) | 
					
						
							|  |  |  | 	restClient, err := rest.NewClient(serverURL, trFn, newAuthToken) | 
					
						
							| 
									
										
										
										
											2019-02-15 09:53:46 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 		logger.LogIf(context.Background(), err) | 
					
						
							|  |  |  | 		return &storageRESTClient{endpoint: endpoint, restClient: restClient, connected: 0} | 
					
						
							| 
									
										
										
										
											2019-02-15 09:53:46 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-14 04:17:45 +08:00
										 |  |  | 	return &storageRESTClient{endpoint: endpoint, restClient: restClient, connected: 1} | 
					
						
							| 
									
										
										
										
											2018-10-05 08:44:06 +08:00
										 |  |  | } |