mirror of
https://github.com/minio/minio.git
synced 2025-11-07 04:42:56 -05:00
Unify gateway and object layer. (#5487)
* Unify gateway and object layer. Bring bucket policies into object layer.
This commit is contained in:
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 azure
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/storage"
|
||||
"github.com/minio/minio/pkg/errors"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
// Copied from github.com/Azure/azure-sdk-for-go/storage/container.go
|
||||
func azureListBlobsGetParameters(p storage.ListBlobsParameters) url.Values {
|
||||
out := url.Values{}
|
||||
|
||||
if p.Prefix != "" {
|
||||
out.Set("prefix", p.Prefix)
|
||||
}
|
||||
if p.Delimiter != "" {
|
||||
out.Set("delimiter", p.Delimiter)
|
||||
}
|
||||
if p.Marker != "" {
|
||||
out.Set("marker", p.Marker)
|
||||
}
|
||||
if p.Include != nil {
|
||||
addString := func(datasets []string, include bool, text string) []string {
|
||||
if include {
|
||||
datasets = append(datasets, text)
|
||||
}
|
||||
return datasets
|
||||
}
|
||||
|
||||
include := []string{}
|
||||
include = addString(include, p.Include.Snapshots, "snapshots")
|
||||
include = addString(include, p.Include.Metadata, "metadata")
|
||||
include = addString(include, p.Include.UncommittedBlobs, "uncommittedblobs")
|
||||
include = addString(include, p.Include.Copy, "copy")
|
||||
fullInclude := strings.Join(include, ",")
|
||||
out.Set("include", fullInclude)
|
||||
}
|
||||
if p.MaxResults != 0 {
|
||||
out.Set("maxresults", fmt.Sprintf("%v", p.MaxResults))
|
||||
}
|
||||
if p.Timeout != 0 {
|
||||
out.Set("timeout", fmt.Sprintf("%v", p.Timeout))
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
// Make anonymous HTTP request to azure endpoint.
|
||||
func azureAnonRequest(verb, urlStr string, header http.Header) (*http.Response, error) {
|
||||
req, err := http.NewRequest(verb, urlStr, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if header != nil {
|
||||
req.Header = header
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 4XX and 5XX are error HTTP codes.
|
||||
if resp.StatusCode >= 400 && resp.StatusCode <= 511 {
|
||||
defer resp.Body.Close()
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(respBody) == 0 {
|
||||
// no error in response body, might happen in HEAD requests
|
||||
return nil, storage.AzureStorageServiceError{
|
||||
StatusCode: resp.StatusCode,
|
||||
Code: resp.Status,
|
||||
Message: "no response body was available for error status code",
|
||||
}
|
||||
}
|
||||
// Response contains Azure storage service error object.
|
||||
var storageErr storage.AzureStorageServiceError
|
||||
if err := xml.Unmarshal(respBody, &storageErr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
storageErr.StatusCode = resp.StatusCode
|
||||
return nil, storageErr
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// AnonGetBucketInfo - Get bucket metadata from azure anonymously.
|
||||
func (a *azureObjects) AnonGetBucketInfo(bucket string) (bucketInfo minio.BucketInfo, err error) {
|
||||
blobURL := a.client.GetContainerReference(bucket).GetBlobReference("").GetURL()
|
||||
url, err := url.Parse(blobURL)
|
||||
if err != nil {
|
||||
return bucketInfo, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
url.RawQuery = "restype=container"
|
||||
resp, err := azureAnonRequest(http.MethodHead, url.String(), nil)
|
||||
if err != nil {
|
||||
return bucketInfo, azureToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return bucketInfo, azureToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket)), bucket)
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
|
||||
if err != nil {
|
||||
return bucketInfo, errors.Trace(err)
|
||||
}
|
||||
|
||||
return minio.BucketInfo{
|
||||
Name: bucket,
|
||||
Created: t,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AnonGetObject - SendGET request without authentication.
|
||||
// This is needed when clients send GET requests on objects that can be downloaded without auth.
|
||||
func (a *azureObjects) AnonGetObject(bucket, object string, startOffset int64, length int64, writer io.Writer, etag string) (err error) {
|
||||
h := make(http.Header)
|
||||
if length > 0 && startOffset > 0 {
|
||||
h.Add("Range", fmt.Sprintf("bytes=%d-%d", startOffset, startOffset+length-1))
|
||||
} else if startOffset > 0 {
|
||||
h.Add("Range", fmt.Sprintf("bytes=%d-", startOffset))
|
||||
}
|
||||
|
||||
blobURL := a.client.GetContainerReference(bucket).GetBlobReference(object).GetURL()
|
||||
resp, err := azureAnonRequest(http.MethodGet, blobURL, h)
|
||||
if err != nil {
|
||||
return azureToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusPartialContent && resp.StatusCode != http.StatusOK {
|
||||
return azureToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket, object)), bucket, object)
|
||||
}
|
||||
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
// AnonGetObjectInfo - Send HEAD request without authentication and convert the
|
||||
// result to ObjectInfo.
|
||||
func (a *azureObjects) AnonGetObjectInfo(bucket, object string) (objInfo minio.ObjectInfo, err error) {
|
||||
blobURL := a.client.GetContainerReference(bucket).GetBlobReference(object).GetURL()
|
||||
resp, err := azureAnonRequest(http.MethodHead, blobURL, nil)
|
||||
if err != nil {
|
||||
return objInfo, azureToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return objInfo, azureToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket, object)), bucket, object)
|
||||
}
|
||||
|
||||
var contentLength int64
|
||||
contentLengthStr := resp.Header.Get("Content-Length")
|
||||
if contentLengthStr != "" {
|
||||
contentLength, err = strconv.ParseInt(contentLengthStr, 0, 64)
|
||||
if err != nil {
|
||||
return objInfo, azureToObjectError(errors.Trace(fmt.Errorf("Unexpected error")), bucket, object)
|
||||
}
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
|
||||
if err != nil {
|
||||
return objInfo, errors.Trace(err)
|
||||
}
|
||||
|
||||
objInfo.ModTime = t
|
||||
objInfo.Bucket = bucket
|
||||
objInfo.UserDefined = make(map[string]string)
|
||||
if resp.Header.Get("Content-Encoding") != "" {
|
||||
objInfo.UserDefined["Content-Encoding"] = resp.Header.Get("Content-Encoding")
|
||||
}
|
||||
objInfo.UserDefined["Content-Type"] = resp.Header.Get("Content-Type")
|
||||
objInfo.ETag = resp.Header.Get("Etag")
|
||||
objInfo.ModTime = t
|
||||
objInfo.Name = object
|
||||
objInfo.Size = contentLength
|
||||
return
|
||||
}
|
||||
|
||||
// AnonListObjects - Use Azure equivalent ListBlobs.
|
||||
func (a *azureObjects) AnonListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (result minio.ListObjectsInfo, err error) {
|
||||
params := storage.ListBlobsParameters{
|
||||
Prefix: prefix,
|
||||
Marker: marker,
|
||||
Delimiter: delimiter,
|
||||
MaxResults: uint(maxKeys),
|
||||
}
|
||||
|
||||
q := azureListBlobsGetParameters(params)
|
||||
q.Set("restype", "container")
|
||||
q.Set("comp", "list")
|
||||
|
||||
blobURL := a.client.GetContainerReference(bucket).GetBlobReference("").GetURL()
|
||||
url, err := url.Parse(blobURL)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
resp, err := azureAnonRequest(http.MethodGet, url.String(), nil)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var listResp storage.BlobListResponse
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
err = xml.Unmarshal(data, &listResp)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
|
||||
result.IsTruncated = listResp.NextMarker != ""
|
||||
result.NextMarker = listResp.NextMarker
|
||||
for _, object := range listResp.Blobs {
|
||||
result.Objects = append(result.Objects, minio.ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: object.Name,
|
||||
ModTime: time.Time(object.Properties.LastModified),
|
||||
Size: object.Properties.ContentLength,
|
||||
ETag: object.Properties.Etag,
|
||||
ContentType: object.Properties.ContentType,
|
||||
ContentEncoding: object.Properties.ContentEncoding,
|
||||
})
|
||||
}
|
||||
result.Prefixes = listResp.BlobPrefixes
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// AnonListObjectsV2 - List objects in V2 mode, anonymously
|
||||
func (a *azureObjects) AnonListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (result minio.ListObjectsV2Info, err error) {
|
||||
params := storage.ListBlobsParameters{
|
||||
Prefix: prefix,
|
||||
Marker: continuationToken,
|
||||
Delimiter: delimiter,
|
||||
MaxResults: uint(maxKeys),
|
||||
}
|
||||
|
||||
q := azureListBlobsGetParameters(params)
|
||||
q.Set("restype", "container")
|
||||
q.Set("comp", "list")
|
||||
|
||||
blobURL := a.client.GetContainerReference(bucket).GetBlobReference("").GetURL()
|
||||
url, err := url.Parse(blobURL)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
url.RawQuery = q.Encode()
|
||||
|
||||
resp, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var listResp storage.BlobListResponse
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
err = xml.Unmarshal(data, &listResp)
|
||||
if err != nil {
|
||||
return result, azureToObjectError(errors.Trace(err))
|
||||
}
|
||||
|
||||
// If NextMarker is not empty, this means response is truncated and NextContinuationToken should be set
|
||||
if listResp.NextMarker != "" {
|
||||
result.IsTruncated = true
|
||||
result.NextContinuationToken = listResp.NextMarker
|
||||
}
|
||||
for _, object := range listResp.Blobs {
|
||||
result.Objects = append(result.Objects, minio.ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: object.Name,
|
||||
ModTime: time.Time(object.Properties.LastModified),
|
||||
Size: object.Properties.ContentLength,
|
||||
ETag: minio.CanonicalizeETag(object.Properties.Etag),
|
||||
ContentType: object.Properties.ContentType,
|
||||
ContentEncoding: object.Properties.ContentEncoding,
|
||||
})
|
||||
}
|
||||
result.Prefixes = listResp.BlobPrefixes
|
||||
return result, nil
|
||||
}
|
||||
@@ -117,7 +117,7 @@ func (g *Azure) Name() string {
|
||||
}
|
||||
|
||||
// NewGatewayLayer initializes azure blob storage client and returns AzureObjects.
|
||||
func (g *Azure) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
func (g *Azure) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
var err error
|
||||
var endpoint = storage.DefaultBaseURL
|
||||
var secure = true
|
||||
@@ -959,13 +959,13 @@ func (a *azureObjects) CompleteMultipartUpload(bucket, object, uploadID string,
|
||||
return a.GetObjectInfo(bucket, object)
|
||||
}
|
||||
|
||||
// SetBucketPolicies - Azure supports three types of container policies:
|
||||
// SetBucketPolicy - Azure supports three types of container policies:
|
||||
// storage.ContainerAccessTypeContainer - readonly in minio terminology
|
||||
// storage.ContainerAccessTypeBlob - readonly without listing in minio terminology
|
||||
// storage.ContainerAccessTypePrivate - none in minio terminology
|
||||
// As the common denominator for minio and azure is readonly and none, we support
|
||||
// these two policies at the bucket level.
|
||||
func (a *azureObjects) SetBucketPolicies(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
func (a *azureObjects) SetBucketPolicy(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
var policies []minio.BucketAccessPolicy
|
||||
|
||||
for prefix, policy := range policy.GetPolicies(policyInfo.Statements, bucket) {
|
||||
@@ -993,8 +993,8 @@ func (a *azureObjects) SetBucketPolicies(bucket string, policyInfo policy.Bucket
|
||||
return azureToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
|
||||
// GetBucketPolicies - Get the container ACL and convert it to canonical []bucketAccessPolicy
|
||||
func (a *azureObjects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
// GetBucketPolicy - Get the container ACL and convert it to canonical []bucketAccessPolicy
|
||||
func (a *azureObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
policyInfo := policy.BucketAccessPolicy{Version: "2012-10-17"}
|
||||
container := a.client.GetContainerReference(bucket)
|
||||
perm, err := container.GetPermissions(nil)
|
||||
@@ -1012,8 +1012,8 @@ func (a *azureObjects) GetBucketPolicies(bucket string) (policy.BucketAccessPoli
|
||||
return policyInfo, nil
|
||||
}
|
||||
|
||||
// DeleteBucketPolicies - Set the container ACL to "private"
|
||||
func (a *azureObjects) DeleteBucketPolicies(bucket string) error {
|
||||
// DeleteBucketPolicy - Set the container ACL to "private"
|
||||
func (a *azureObjects) DeleteBucketPolicy(bucket string) error {
|
||||
perm := storage.ContainerPermissions{
|
||||
AccessType: storage.ContainerAccessTypePrivate,
|
||||
AccessPolicies: nil,
|
||||
|
||||
@@ -19,7 +19,6 @@ package azure
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -262,56 +261,6 @@ func TestAzureParseBlockID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test azureListBlobsGetParameters()
|
||||
func TestAzureListBlobsGetParameters(t *testing.T) {
|
||||
|
||||
// Test values set 1
|
||||
expectedURLValues := url.Values{}
|
||||
expectedURLValues.Set("prefix", "test")
|
||||
expectedURLValues.Set("delimiter", "_")
|
||||
expectedURLValues.Set("marker", "marker")
|
||||
expectedURLValues.Set("include", "metadata")
|
||||
expectedURLValues.Set("maxresults", "20")
|
||||
expectedURLValues.Set("timeout", "10")
|
||||
|
||||
setBlobParameters := storage.ListBlobsParameters{
|
||||
Prefix: "test",
|
||||
Delimiter: "_",
|
||||
Marker: "marker",
|
||||
Include: &storage.IncludeBlobDataset{Metadata: true},
|
||||
MaxResults: 20,
|
||||
Timeout: 10,
|
||||
}
|
||||
|
||||
// Test values set 2
|
||||
expectedURLValues1 := url.Values{}
|
||||
|
||||
setBlobParameters1 := storage.ListBlobsParameters{
|
||||
Prefix: "",
|
||||
Delimiter: "",
|
||||
Marker: "",
|
||||
Include: nil,
|
||||
MaxResults: 0,
|
||||
Timeout: 0,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
args storage.ListBlobsParameters
|
||||
want url.Values
|
||||
}{
|
||||
{"TestIfValuesSet", setBlobParameters, expectedURLValues},
|
||||
{"TestIfValuesNotSet", setBlobParameters1, expectedURLValues1},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := azureListBlobsGetParameters(test.args); !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("azureListBlobsGetParameters() = %v, want %v", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnonErrToObjectErr(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 b2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/pkg/errors"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
// mkRange converts offset, size into Range header equivalent.
|
||||
func mkRange(offset, size int64) string {
|
||||
if offset == 0 && size == 0 {
|
||||
return ""
|
||||
}
|
||||
if size == 0 {
|
||||
return fmt.Sprintf("bytes=%d-", offset)
|
||||
}
|
||||
return fmt.Sprintf("bytes=%d-%d", offset, offset+size-1)
|
||||
}
|
||||
|
||||
// AnonGetObject - performs a plain http GET request on a public resource,
|
||||
// fails if the resource is not public.
|
||||
func (l *b2Objects) AnonGetObject(bucket string, object string, startOffset int64, length int64, writer io.Writer, etag string) error {
|
||||
uri := fmt.Sprintf("%s/file/%s/%s", l.b2Client.DownloadURI, bucket, object)
|
||||
req, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
rng := mkRange(startOffset, length)
|
||||
if rng != "" {
|
||||
req.Header.Set("Range", rng)
|
||||
}
|
||||
resp, err := l.anonClient.Do(req)
|
||||
if err != nil {
|
||||
return b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return b2ToObjectError(errors.Trace(fmt.Errorf(resp.Status)), bucket, object)
|
||||
}
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
return b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
// Converts http Header into ObjectInfo. This function looks for all the
|
||||
// standard Backblaze B2 headers to convert into ObjectInfo.
|
||||
//
|
||||
// Content-Length is converted to Size.
|
||||
// X-Bz-Upload-Timestamp is converted to ModTime.
|
||||
// X-Bz-Info-<header>:<value> is converted to <header>:<value>
|
||||
// Content-Type is converted to ContentType.
|
||||
// X-Bz-Content-Sha1 is converted to ETag.
|
||||
func headerToObjectInfo(bucket, object string, header http.Header) (objInfo minio.ObjectInfo, err error) {
|
||||
clen, err := strconv.ParseInt(header.Get("Content-Length"), 10, 64)
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
// Converting upload timestamp in milliseconds to a time.Time value for ObjectInfo.ModTime.
|
||||
timeStamp, err := strconv.ParseInt(header.Get("X-Bz-Upload-Timestamp"), 10, 64)
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
// Populate user metadata by looking for all the X-Bz-Info-<name>
|
||||
// HTTP headers, ignore other headers since they have their own
|
||||
// designated meaning, for more details refer B2 API documentation.
|
||||
userMetadata := make(map[string]string)
|
||||
for key := range header {
|
||||
if strings.HasPrefix(key, "X-Bz-Info-") {
|
||||
var name string
|
||||
name, err = url.QueryUnescape(strings.TrimPrefix(key, "X-Bz-Info-"))
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
var val string
|
||||
val, err = url.QueryUnescape(header.Get(key))
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
userMetadata[name] = val
|
||||
}
|
||||
}
|
||||
|
||||
return minio.ObjectInfo{
|
||||
Bucket: bucket,
|
||||
Name: object,
|
||||
ContentType: header.Get("Content-Type"),
|
||||
ModTime: time.Unix(0, 0).Add(time.Duration(timeStamp) * time.Millisecond),
|
||||
Size: clen,
|
||||
ETag: header.Get("X-Bz-File-Id"),
|
||||
UserDefined: userMetadata,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AnonGetObjectInfo - performs a plain http HEAD request on a public resource,
|
||||
// fails if the resource is not public.
|
||||
func (l *b2Objects) AnonGetObjectInfo(bucket string, object string) (objInfo minio.ObjectInfo, err error) {
|
||||
uri := fmt.Sprintf("%s/file/%s/%s", l.b2Client.DownloadURI, bucket, object)
|
||||
req, err := http.NewRequest("HEAD", uri, nil)
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
resp, err := l.anonClient.Do(req)
|
||||
if err != nil {
|
||||
return objInfo, b2ToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return objInfo, b2ToObjectError(errors.Trace(fmt.Errorf(resp.Status)), bucket, object)
|
||||
}
|
||||
return headerToObjectInfo(bucket, object, resp.Header)
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -96,9 +95,9 @@ func (g *B2) Name() string {
|
||||
return b2Backend
|
||||
}
|
||||
|
||||
// NewGatewayLayer returns b2 gateway layer, implements GatewayLayer interface to
|
||||
// NewGatewayLayer returns b2 gateway layer, implements ObjectLayer interface to
|
||||
// talk to B2 remote backend.
|
||||
func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
ctx := context.Background()
|
||||
client, err := b2.AuthorizeAccount(ctx, creds.AccessKey, creds.SecretKey, b2.Transport(minio.NewCustomHTTPTransport()))
|
||||
if err != nil {
|
||||
@@ -108,10 +107,7 @@ func (g *B2) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error)
|
||||
return &b2Objects{
|
||||
creds: creds,
|
||||
b2Client: client,
|
||||
anonClient: &http.Client{
|
||||
Transport: minio.NewCustomHTTPTransport(),
|
||||
},
|
||||
ctx: ctx,
|
||||
ctx: ctx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -124,11 +120,10 @@ func (g *B2) Production() bool {
|
||||
// b2Object implements gateway for Minio and BackBlaze B2 compatible object storage servers.
|
||||
type b2Objects struct {
|
||||
minio.GatewayUnsupported
|
||||
mu sync.Mutex
|
||||
creds auth.Credentials
|
||||
b2Client *b2.B2
|
||||
anonClient *http.Client
|
||||
ctx context.Context
|
||||
mu sync.Mutex
|
||||
creds auth.Credentials
|
||||
b2Client *b2.B2
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Convert B2 errors to minio object layer errors.
|
||||
@@ -694,11 +689,11 @@ func (l *b2Objects) CompleteMultipartUpload(bucket string, object string, upload
|
||||
return l.GetObjectInfo(bucket, object)
|
||||
}
|
||||
|
||||
// SetBucketPolicies - B2 supports 2 types of bucket policies:
|
||||
// SetBucketPolicy - B2 supports 2 types of bucket policies:
|
||||
// bucketType.AllPublic - bucketTypeReadOnly means that anybody can download the files is the bucket;
|
||||
// bucketType.AllPrivate - bucketTypePrivate means that you need an authorization token to download them.
|
||||
// Default is AllPrivate for all buckets.
|
||||
func (l *b2Objects) SetBucketPolicies(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
func (l *b2Objects) SetBucketPolicy(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
var policies []minio.BucketAccessPolicy
|
||||
|
||||
for prefix, policy := range policy.GetPolicies(policyInfo.Statements, bucket) {
|
||||
@@ -726,9 +721,9 @@ func (l *b2Objects) SetBucketPolicies(bucket string, policyInfo policy.BucketAcc
|
||||
return b2ToObjectError(errors.Trace(err))
|
||||
}
|
||||
|
||||
// GetBucketPolicies, returns the current bucketType from B2 backend and convert
|
||||
// GetBucketPolicy, returns the current bucketType from B2 backend and convert
|
||||
// it into S3 compatible bucket policy info.
|
||||
func (l *b2Objects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
func (l *b2Objects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
policyInfo := policy.BucketAccessPolicy{Version: "2012-10-17"}
|
||||
bkt, err := l.Bucket(bucket)
|
||||
if err != nil {
|
||||
@@ -744,8 +739,8 @@ func (l *b2Objects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy,
|
||||
return policy.BucketAccessPolicy{}, errors.Trace(minio.PolicyNotFound{Bucket: bucket})
|
||||
}
|
||||
|
||||
// DeleteBucketPolicies - resets the bucketType of bucket on B2 to 'allPrivate'.
|
||||
func (l *b2Objects) DeleteBucketPolicies(bucket string) error {
|
||||
// DeleteBucketPolicy - resets the bucketType of bucket on B2 to 'allPrivate'.
|
||||
func (l *b2Objects) DeleteBucketPolicy(bucket string) error {
|
||||
bkt, err := l.Bucket(bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -18,7 +18,6 @@ package b2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
b2 "github.com/minio/blazer/base"
|
||||
@@ -27,88 +26,6 @@ import (
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
// Tests headerToObjectInfo
|
||||
func TestHeaderToObjectInfo(t *testing.T) {
|
||||
testCases := []struct {
|
||||
bucket, object string
|
||||
header http.Header
|
||||
objInfo minio.ObjectInfo
|
||||
}{
|
||||
{
|
||||
bucket: "bucket",
|
||||
object: "object",
|
||||
header: http.Header{
|
||||
"Content-Length": []string{"10"},
|
||||
"Content-Type": []string{"application/javascript"},
|
||||
"X-Bz-Upload-Timestamp": []string{"1000"},
|
||||
"X-Bz-Info-X-Amz-Meta-1": []string{"test1"},
|
||||
"X-Bz-File-Id": []string{"xxxxx"},
|
||||
},
|
||||
objInfo: minio.ObjectInfo{
|
||||
Bucket: "bucket",
|
||||
Name: "object",
|
||||
ContentType: "application/javascript",
|
||||
Size: 10,
|
||||
UserDefined: map[string]string{
|
||||
"X-Amz-Meta-1": "test1",
|
||||
},
|
||||
ETag: "xxxxx",
|
||||
},
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
gotObjInfo, err := headerToObjectInfo(testCase.bucket, testCase.object, testCase.header)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: %s", i+1, err)
|
||||
}
|
||||
if gotObjInfo.Bucket != testCase.objInfo.Bucket {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.objInfo.Bucket, gotObjInfo.Bucket)
|
||||
}
|
||||
if gotObjInfo.Name != testCase.objInfo.Name {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.objInfo.Name, gotObjInfo.Name)
|
||||
}
|
||||
if gotObjInfo.ContentType != testCase.objInfo.ContentType {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.objInfo.ContentType, gotObjInfo.ContentType)
|
||||
}
|
||||
if gotObjInfo.ETag != testCase.objInfo.ETag {
|
||||
t.Errorf("Test %d: Expected %s, got %s", i+1, testCase.objInfo.ETag, gotObjInfo.ETag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests mkRange test.
|
||||
func TestMkRange(t *testing.T) {
|
||||
testCases := []struct {
|
||||
offset, size int64
|
||||
expectedRng string
|
||||
}{
|
||||
// No offset set, size not set.
|
||||
{
|
||||
offset: 0,
|
||||
size: 0,
|
||||
expectedRng: "",
|
||||
},
|
||||
// Offset set, size not set.
|
||||
{
|
||||
offset: 10,
|
||||
size: 0,
|
||||
expectedRng: "bytes=10-",
|
||||
},
|
||||
// Offset set, size set.
|
||||
{
|
||||
offset: 10,
|
||||
size: 11,
|
||||
expectedRng: "bytes=10-20",
|
||||
},
|
||||
}
|
||||
for i, testCase := range testCases {
|
||||
gotRng := mkRange(testCase.offset, testCase.size)
|
||||
if gotRng != testCase.expectedRng {
|
||||
t.Errorf("Test %d: expected %s, got %s", i+1, testCase.expectedRng, gotRng)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test b2 object error.
|
||||
func TestB2ObjectError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 gcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/pkg/errors"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
func toGCSPublicURL(bucket, object string) string {
|
||||
return fmt.Sprintf("https://storage.googleapis.com/%s/%s", bucket, object)
|
||||
}
|
||||
|
||||
// AnonGetObject - Get object anonymously
|
||||
func (l *gcsGateway) AnonGetObject(bucket string, object string, startOffset int64, length int64, writer io.Writer, etag string) error {
|
||||
req, err := http.NewRequest("GET", toGCSPublicURL(bucket, object), nil)
|
||||
if err != nil {
|
||||
return gcsToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
if length > 0 && startOffset > 0 {
|
||||
req.Header.Add("Range", fmt.Sprintf("bytes=%d-%d", startOffset, startOffset+length-1))
|
||||
} else if startOffset > 0 {
|
||||
req.Header.Add("Range", fmt.Sprintf("bytes=%d-", startOffset))
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return gcsToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusPartialContent && resp.StatusCode != http.StatusOK {
|
||||
return gcsToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket, object)), bucket, object)
|
||||
}
|
||||
|
||||
_, err = io.Copy(writer, resp.Body)
|
||||
return gcsToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
// AnonGetObjectInfo - Get object info anonymously
|
||||
func (l *gcsGateway) AnonGetObjectInfo(bucket string, object string) (objInfo minio.ObjectInfo, err error) {
|
||||
resp, err := http.Head(toGCSPublicURL(bucket, object))
|
||||
if err != nil {
|
||||
return objInfo, gcsToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return objInfo, gcsToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket, object)), bucket, object)
|
||||
}
|
||||
|
||||
var contentLength int64
|
||||
contentLengthStr := resp.Header.Get("Content-Length")
|
||||
if contentLengthStr != "" {
|
||||
contentLength, err = strconv.ParseInt(contentLengthStr, 0, 64)
|
||||
if err != nil {
|
||||
return objInfo, gcsToObjectError(errors.Trace(fmt.Errorf("Unexpected error")), bucket, object)
|
||||
}
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
|
||||
if err != nil {
|
||||
return objInfo, errors.Trace(err)
|
||||
}
|
||||
|
||||
objInfo.ModTime = t
|
||||
objInfo.Bucket = bucket
|
||||
objInfo.UserDefined = make(map[string]string)
|
||||
if resp.Header.Get("Content-Encoding") != "" {
|
||||
objInfo.UserDefined["Content-Encoding"] = resp.Header.Get("Content-Encoding")
|
||||
}
|
||||
objInfo.UserDefined["Content-Type"] = resp.Header.Get("Content-Type")
|
||||
objInfo.ETag = resp.Header.Get("Etag")
|
||||
objInfo.ModTime = t
|
||||
objInfo.Name = object
|
||||
objInfo.Size = contentLength
|
||||
return
|
||||
}
|
||||
|
||||
// AnonListObjects - List objects anonymously
|
||||
func (l *gcsGateway) AnonListObjects(bucket string, prefix string, marker string, delimiter string, maxKeys int) (minio.ListObjectsInfo, error) {
|
||||
result, err := l.anonClient.ListObjects(bucket, prefix, marker, delimiter, maxKeys)
|
||||
if err != nil {
|
||||
return minio.ListObjectsInfo{}, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
|
||||
return minio.FromMinioClientListBucketResult(bucket, result), nil
|
||||
}
|
||||
|
||||
// AnonListObjectsV2 - List objects in V2 mode, anonymously
|
||||
func (l *gcsGateway) AnonListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (minio.ListObjectsV2Info, error) {
|
||||
// Request V1 List Object to the backend
|
||||
result, err := l.anonClient.ListObjects(bucket, prefix, continuationToken, delimiter, maxKeys)
|
||||
if err != nil {
|
||||
return minio.ListObjectsV2Info{}, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
// translate V1 Result to V2Info
|
||||
return minio.FromMinioClientListBucketResultToV2Info(bucket, result), nil
|
||||
}
|
||||
|
||||
// AnonGetBucketInfo - Get bucket metadata anonymously.
|
||||
func (l *gcsGateway) AnonGetBucketInfo(bucket string) (bucketInfo minio.BucketInfo, err error) {
|
||||
resp, err := http.Head(toGCSPublicURL(bucket, ""))
|
||||
if err != nil {
|
||||
return bucketInfo, gcsToObjectError(errors.Trace(err))
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return bucketInfo, gcsToObjectError(errors.Trace(minio.AnonErrToObjectErr(resp.StatusCode, bucket)), bucket)
|
||||
}
|
||||
|
||||
t, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
|
||||
if err != nil {
|
||||
return bucketInfo, errors.Trace(err)
|
||||
}
|
||||
|
||||
// Last-Modified date being returned by GCS
|
||||
return minio.BucketInfo{
|
||||
Name: bucket,
|
||||
Created: t,
|
||||
}, nil
|
||||
}
|
||||
@@ -42,7 +42,6 @@ import (
|
||||
"google.golang.org/api/iterator"
|
||||
"google.golang.org/api/option"
|
||||
|
||||
miniogo "github.com/minio/minio-go"
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
@@ -155,13 +154,13 @@ type GCS struct {
|
||||
projectID string
|
||||
}
|
||||
|
||||
// Name returns the name of gcs gatewaylayer.
|
||||
// Name returns the name of gcs ObjectLayer.
|
||||
func (g *GCS) Name() string {
|
||||
return gcsBackend
|
||||
}
|
||||
|
||||
// NewGatewayLayer returns gcs gatewaylayer.
|
||||
func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
// NewGatewayLayer returns gcs ObjectLayer.
|
||||
func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
var err error
|
||||
@@ -182,18 +181,10 @@ func (g *GCS) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize a anonymous client with minio core APIs.
|
||||
anonClient, err := miniogo.NewCore(googleStorageEndpoint, "", "", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
anonClient.SetCustomTransport(minio.NewCustomHTTPTransport())
|
||||
|
||||
gcs := &gcsGateway{
|
||||
client: client,
|
||||
projectID: g.projectID,
|
||||
ctx: ctx,
|
||||
anonClient: anonClient,
|
||||
client: client,
|
||||
projectID: g.projectID,
|
||||
ctx: ctx,
|
||||
}
|
||||
|
||||
// Start background process to cleanup old files in minio.sys.tmp
|
||||
@@ -349,10 +340,9 @@ func isValidGCSProjectIDFormat(projectID string) bool {
|
||||
// gcsGateway - Implements gateway for Minio and GCS compatible object storage servers.
|
||||
type gcsGateway struct {
|
||||
minio.GatewayUnsupported
|
||||
client *storage.Client
|
||||
anonClient *miniogo.Core
|
||||
projectID string
|
||||
ctx context.Context
|
||||
client *storage.Client
|
||||
projectID string
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
const googleStorageEndpoint = "storage.googleapis.com"
|
||||
@@ -1057,8 +1047,8 @@ func (l *gcsGateway) CompleteMultipartUpload(bucket string, key string, uploadID
|
||||
return fromGCSAttrsToObjectInfo(attrs), nil
|
||||
}
|
||||
|
||||
// SetBucketPolicies - Set policy on bucket
|
||||
func (l *gcsGateway) SetBucketPolicies(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
// SetBucketPolicy - Set policy on bucket
|
||||
func (l *gcsGateway) SetBucketPolicy(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
var policies []minio.BucketAccessPolicy
|
||||
|
||||
for prefix, policy := range policy.GetPolicies(policyInfo.Statements, bucket) {
|
||||
@@ -1102,8 +1092,8 @@ func (l *gcsGateway) SetBucketPolicies(bucket string, policyInfo policy.BucketAc
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBucketPolicies - Get policy on bucket
|
||||
func (l *gcsGateway) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
// GetBucketPolicy - Get policy on bucket
|
||||
func (l *gcsGateway) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
rules, err := l.client.Bucket(bucket).ACL().List(l.ctx)
|
||||
if err != nil {
|
||||
return policy.BucketAccessPolicy{}, gcsToObjectError(errors.Trace(err), bucket)
|
||||
@@ -1127,8 +1117,8 @@ func (l *gcsGateway) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy
|
||||
return policyInfo, nil
|
||||
}
|
||||
|
||||
// DeleteBucketPolicies - Delete all policies on bucket
|
||||
func (l *gcsGateway) DeleteBucketPolicies(bucket string) error {
|
||||
// DeleteBucketPolicy - Delete all policies on bucket
|
||||
func (l *gcsGateway) DeleteBucketPolicy(bucket string) error {
|
||||
// This only removes the storage.AllUsers policies
|
||||
if err := l.client.Bucket(bucket).ACL().Delete(l.ctx, storage.AllUsers); err != nil {
|
||||
return gcsToObjectError(errors.Trace(err), bucket)
|
||||
|
||||
@@ -223,13 +223,6 @@ func TestGCSParseProjectID(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCSPublicURL(t *testing.T) {
|
||||
gcsURL := toGCSPublicURL("bucket", "testing")
|
||||
if gcsURL != "https://storage.googleapis.com/bucket/testing" {
|
||||
t.Errorf(`Expected "https://storage.googleapis.com/bucket/testing", got %s"`, gcsURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGCSToObjectError(t *testing.T) {
|
||||
testCases := []struct {
|
||||
params []string
|
||||
|
||||
@@ -118,9 +118,9 @@ func (g *Manta) Name() string {
|
||||
return mantaBackend
|
||||
}
|
||||
|
||||
// NewGatewayLayer returns manta gateway layer, implements GatewayLayer interface to
|
||||
// NewGatewayLayer returns manta gateway layer, implements ObjectLayer interface to
|
||||
// talk to manta remote backend.
|
||||
func (g *Manta) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
func (g *Manta) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
var err error
|
||||
var signer authentication.Signer
|
||||
var endpoint = defaultMantaURL
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 oss
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
)
|
||||
|
||||
// AnonPutObject creates a new object anonymously with the incoming data,
|
||||
func (l *ossObjects) AnonPutObject(bucket, object string, data *hash.Reader, metadata map[string]string) (objInfo minio.ObjectInfo, err error) {
|
||||
return ossPutObject(l.anonClient, bucket, object, data, metadata)
|
||||
}
|
||||
|
||||
// AnonGetObject - Get object anonymously
|
||||
func (l *ossObjects) AnonGetObject(bucket, key string, startOffset, length int64, writer io.Writer, etag string) error {
|
||||
return ossGetObject(l.anonClient, bucket, key, startOffset, length, writer, etag)
|
||||
}
|
||||
|
||||
// AnonGetObjectInfo - Get object info anonymously
|
||||
func (l *ossObjects) AnonGetObjectInfo(bucket, object string) (objInfo minio.ObjectInfo, err error) {
|
||||
return ossGetObjectInfo(l.anonClient, bucket, object)
|
||||
}
|
||||
|
||||
// AnonListObjects lists objects anonymously.
|
||||
func (l *ossObjects) AnonListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (loi minio.ListObjectsInfo, err error) {
|
||||
return ossListObjects(l.anonClient, bucket, prefix, marker, delimiter, maxKeys)
|
||||
}
|
||||
|
||||
// AnonListObjectsV2 lists objects in V2 mode, anonymously.
|
||||
func (l *ossObjects) AnonListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (loi minio.ListObjectsV2Info, err error) {
|
||||
return ossListObjectsV2(l.anonClient, bucket, prefix, continuationToken, delimiter, maxKeys, fetchOwner, startAfter)
|
||||
}
|
||||
|
||||
// AnonGetBucketInfo gets bucket metadata anonymously.
|
||||
func (l *ossObjects) AnonGetBucketInfo(bucket string) (bi minio.BucketInfo, err error) {
|
||||
return ossGeBucketInfo(l.anonClient, bucket)
|
||||
}
|
||||
@@ -109,8 +109,8 @@ func (g *OSS) Name() string {
|
||||
return ossBackend
|
||||
}
|
||||
|
||||
// NewGatewayLayer implements Gateway interface and returns OSS GatewayLayer.
|
||||
func (g *OSS) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
// NewGatewayLayer implements Gateway interface and returns OSS ObjectLayer.
|
||||
func (g *OSS) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
var err error
|
||||
|
||||
// Regions and endpoints
|
||||
@@ -125,14 +125,8 @@ func (g *OSS) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
anonClient, err := oss.New(g.host, "", "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ossObjects{
|
||||
Client: client,
|
||||
anonClient: anonClient,
|
||||
Client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -324,8 +318,7 @@ func ossToObjectError(err error, params ...string) error {
|
||||
// ossObjects implements gateway for Aliyun Object Storage Service.
|
||||
type ossObjects struct {
|
||||
minio.GatewayUnsupported
|
||||
Client *oss.Client
|
||||
anonClient *oss.Client
|
||||
Client *oss.Client
|
||||
}
|
||||
|
||||
// Shutdown saves any gateway metadata to disk
|
||||
@@ -920,12 +913,12 @@ func (l *ossObjects) CompleteMultipartUpload(bucket, object, uploadID string, up
|
||||
return l.GetObjectInfo(bucket, object)
|
||||
}
|
||||
|
||||
// SetBucketPolicies sets policy on bucket.
|
||||
// SetBucketPolicy sets policy on bucket.
|
||||
// OSS supports three types of bucket policies:
|
||||
// oss.ACLPublicReadWrite: readwrite in minio terminology
|
||||
// oss.ACLPublicRead: readonly in minio terminology
|
||||
// oss.ACLPrivate: none in minio terminology
|
||||
func (l *ossObjects) SetBucketPolicies(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
func (l *ossObjects) SetBucketPolicy(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
bucketPolicies := policy.GetPolicies(policyInfo.Statements, bucket)
|
||||
if len(bucketPolicies) != 1 {
|
||||
return errors.Trace(minio.NotImplemented{})
|
||||
@@ -958,8 +951,8 @@ func (l *ossObjects) SetBucketPolicies(bucket string, policyInfo policy.BucketAc
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBucketPolicies will get policy on bucket.
|
||||
func (l *ossObjects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
// GetBucketPolicy will get policy on bucket.
|
||||
func (l *ossObjects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
result, err := l.Client.GetBucketACL(bucket)
|
||||
if err != nil {
|
||||
return policy.BucketAccessPolicy{}, ossToObjectError(errors.Trace(err))
|
||||
@@ -981,8 +974,8 @@ func (l *ossObjects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy
|
||||
return policyInfo, nil
|
||||
}
|
||||
|
||||
// DeleteBucketPolicies deletes all policies on bucket.
|
||||
func (l *ossObjects) DeleteBucketPolicies(bucket string) error {
|
||||
// DeleteBucketPolicy deletes all policies on bucket.
|
||||
func (l *ossObjects) DeleteBucketPolicy(bucket string) error {
|
||||
err := l.Client.SetBucketACL(bucket, oss.ACLPrivate)
|
||||
if err != nil {
|
||||
return ossToObjectError(errors.Trace(err), bucket)
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 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 s3
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
miniogo "github.com/minio/minio-go"
|
||||
"github.com/minio/minio/pkg/errors"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
|
||||
minio "github.com/minio/minio/cmd"
|
||||
)
|
||||
|
||||
// AnonPutObject creates a new object anonymously with the incoming data,
|
||||
func (l *s3Objects) AnonPutObject(bucket string, object string, data *hash.Reader, metadata map[string]string) (objInfo minio.ObjectInfo, e error) {
|
||||
oi, err := l.anonClient.PutObject(bucket, object, data, data.Size(), data.MD5Base64String(), data.SHA256HexString(), minio.ToMinioClientMetadata(metadata))
|
||||
if err != nil {
|
||||
return objInfo, minio.ErrorRespToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
return minio.FromMinioClientObjectInfo(bucket, oi), nil
|
||||
}
|
||||
|
||||
// AnonGetObject - Get object anonymously
|
||||
func (l *s3Objects) AnonGetObject(bucket string, key string, startOffset int64, length int64, writer io.Writer, etag string) error {
|
||||
opts := miniogo.GetObjectOptions{}
|
||||
if err := opts.SetRange(startOffset, startOffset+length-1); err != nil {
|
||||
return minio.ErrorRespToObjectError(errors.Trace(err), bucket, key)
|
||||
}
|
||||
object, _, err := l.anonClient.GetObject(bucket, key, opts)
|
||||
if err != nil {
|
||||
return minio.ErrorRespToObjectError(errors.Trace(err), bucket, key)
|
||||
}
|
||||
|
||||
defer object.Close()
|
||||
|
||||
if _, err := io.CopyN(writer, object, length); err != nil {
|
||||
return minio.ErrorRespToObjectError(errors.Trace(err), bucket, key)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AnonGetObjectInfo - Get object info anonymously
|
||||
func (l *s3Objects) AnonGetObjectInfo(bucket string, object string) (objInfo minio.ObjectInfo, e error) {
|
||||
oi, err := l.anonClient.StatObject(bucket, object, miniogo.StatObjectOptions{})
|
||||
if err != nil {
|
||||
return objInfo, minio.ErrorRespToObjectError(errors.Trace(err), bucket, object)
|
||||
}
|
||||
|
||||
return minio.FromMinioClientObjectInfo(bucket, oi), nil
|
||||
}
|
||||
|
||||
// AnonListObjects - List objects anonymously
|
||||
func (l *s3Objects) AnonListObjects(bucket string, prefix string, marker string, delimiter string, maxKeys int) (loi minio.ListObjectsInfo, e error) {
|
||||
result, err := l.anonClient.ListObjects(bucket, prefix, marker, delimiter, maxKeys)
|
||||
if err != nil {
|
||||
return loi, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
|
||||
return minio.FromMinioClientListBucketResult(bucket, result), nil
|
||||
}
|
||||
|
||||
// AnonListObjectsV2 - List objects in V2 mode, anonymously
|
||||
func (l *s3Objects) AnonListObjectsV2(bucket, prefix, continuationToken, delimiter string, maxKeys int, fetchOwner bool, startAfter string) (loi minio.ListObjectsV2Info, e error) {
|
||||
result, err := l.anonClient.ListObjectsV2(bucket, prefix, continuationToken, fetchOwner, delimiter, maxKeys)
|
||||
if err != nil {
|
||||
return loi, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
|
||||
return minio.FromMinioClientListBucketV2Result(bucket, result), nil
|
||||
}
|
||||
|
||||
// AnonGetBucketInfo - Get bucket metadata anonymously.
|
||||
func (l *s3Objects) AnonGetBucketInfo(bucket string) (bi minio.BucketInfo, e error) {
|
||||
if exists, err := l.anonClient.BucketExists(bucket); err != nil {
|
||||
return bi, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
} else if !exists {
|
||||
return bi, errors.Trace(minio.BucketNotFound{Bucket: bucket})
|
||||
}
|
||||
|
||||
buckets, err := l.anonClient.ListBuckets()
|
||||
if err != nil {
|
||||
return bi, minio.ErrorRespToObjectError(errors.Trace(err), bucket)
|
||||
}
|
||||
|
||||
for _, bi := range buckets {
|
||||
if bi.Name != bucket {
|
||||
continue
|
||||
}
|
||||
|
||||
return minio.BucketInfo{
|
||||
Name: bi.Name,
|
||||
Created: bi.CreationDate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return bi, errors.Trace(minio.BucketNotFound{Bucket: bucket})
|
||||
}
|
||||
@@ -99,8 +99,8 @@ func (g *S3) Name() string {
|
||||
return s3Backend
|
||||
}
|
||||
|
||||
// NewGatewayLayer returns s3 gatewaylayer.
|
||||
func (g *S3) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
// NewGatewayLayer returns s3 ObjectLayer.
|
||||
func (g *S3) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
var err error
|
||||
var endpoint string
|
||||
var secure = true
|
||||
@@ -125,15 +125,8 @@ func (g *S3) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
anonClient, err := miniogo.NewCore(endpoint, "", "", secure)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
anonClient.SetCustomTransport(minio.NewCustomHTTPTransport())
|
||||
|
||||
return &s3Objects{
|
||||
Client: client,
|
||||
anonClient: anonClient,
|
||||
Client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -145,8 +138,7 @@ func (g *S3) Production() bool {
|
||||
// s3Objects implements gateway for Minio and S3 compatible object storage servers.
|
||||
type s3Objects struct {
|
||||
minio.GatewayUnsupported
|
||||
Client *miniogo.Core
|
||||
anonClient *miniogo.Core
|
||||
Client *miniogo.Core
|
||||
}
|
||||
|
||||
// Shutdown saves any gateway metadata to disk
|
||||
@@ -392,8 +384,8 @@ func (l *s3Objects) CompleteMultipartUpload(bucket string, object string, upload
|
||||
return l.GetObjectInfo(bucket, object)
|
||||
}
|
||||
|
||||
// SetBucketPolicies sets policy on bucket
|
||||
func (l *s3Objects) SetBucketPolicies(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
// SetBucketPolicy sets policy on bucket
|
||||
func (l *s3Objects) SetBucketPolicy(bucket string, policyInfo policy.BucketAccessPolicy) error {
|
||||
if err := l.Client.PutBucketPolicy(bucket, policyInfo); err != nil {
|
||||
return minio.ErrorRespToObjectError(errors.Trace(err), bucket, "")
|
||||
}
|
||||
@@ -401,8 +393,8 @@ func (l *s3Objects) SetBucketPolicies(bucket string, policyInfo policy.BucketAcc
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBucketPolicies will get policy on bucket
|
||||
func (l *s3Objects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
// GetBucketPolicy will get policy on bucket
|
||||
func (l *s3Objects) GetBucketPolicy(bucket string) (policy.BucketAccessPolicy, error) {
|
||||
policyInfo, err := l.Client.GetBucketPolicy(bucket)
|
||||
if err != nil {
|
||||
return policy.BucketAccessPolicy{}, minio.ErrorRespToObjectError(errors.Trace(err), bucket, "")
|
||||
@@ -410,8 +402,8 @@ func (l *s3Objects) GetBucketPolicies(bucket string) (policy.BucketAccessPolicy,
|
||||
return policyInfo, nil
|
||||
}
|
||||
|
||||
// DeleteBucketPolicies deletes all policies on bucket
|
||||
func (l *s3Objects) DeleteBucketPolicies(bucket string) error {
|
||||
// DeleteBucketPolicy deletes all policies on bucket
|
||||
func (l *s3Objects) DeleteBucketPolicy(bucket string) error {
|
||||
if err := l.Client.PutBucketPolicy(bucket, policy.BucketAccessPolicy{}); err != nil {
|
||||
return minio.ErrorRespToObjectError(errors.Trace(err), bucket, "")
|
||||
}
|
||||
|
||||
@@ -113,9 +113,9 @@ func (g *Sia) Name() string {
|
||||
return siaBackend
|
||||
}
|
||||
|
||||
// NewGatewayLayer returns Sia gateway layer, implements GatewayLayer interface to
|
||||
// NewGatewayLayer returns Sia gateway layer, implements ObjectLayer interface to
|
||||
// talk to Sia backend.
|
||||
func (g *Sia) NewGatewayLayer(creds auth.Credentials) (minio.GatewayLayer, error) {
|
||||
func (g *Sia) NewGatewayLayer(creds auth.Credentials) (minio.ObjectLayer, error) {
|
||||
sia := &siaObjects{
|
||||
Address: g.host,
|
||||
// RootDir uses access key directly, provides partitioning for
|
||||
|
||||
Reference in New Issue
Block a user