summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/minio/minio-go/api.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/minio/minio-go/api.go')
-rw-r--r--vendor/github.com/minio/minio-go/api.go220
1 files changed, 127 insertions, 93 deletions
diff --git a/vendor/github.com/minio/minio-go/api.go b/vendor/github.com/minio/minio-go/api.go
index a21c40e80..c04034a86 100644
--- a/vendor/github.com/minio/minio-go/api.go
+++ b/vendor/github.com/minio/minio-go/api.go
@@ -1,5 +1,6 @@
/*
- * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015, 2016 Minio, Inc.
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 2015, 2016, 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.
@@ -20,6 +21,7 @@ import (
"bytes"
"encoding/base64"
"encoding/hex"
+ "errors"
"fmt"
"io"
"io/ioutil"
@@ -34,6 +36,7 @@ import (
"sync"
"time"
+ "github.com/minio/minio-go/pkg/credentials"
"github.com/minio/minio-go/pkg/s3signer"
"github.com/minio/minio-go/pkg/s3utils"
)
@@ -45,14 +48,11 @@ type Client struct {
// Parsed endpoint url provided by the user.
endpointURL url.URL
- // AccessKeyID required for authorized requests.
- accessKeyID string
- // SecretAccessKey required for authorized requests.
- secretAccessKey string
- // Choose a signature type if necessary.
- signature SignatureType
- // Set to 'true' if Client has no access and secret keys.
- anonymous bool
+ // Holds various credential providers.
+ credsProvider *credentials.Credentials
+
+ // Custom signerType value overrides all credentials.
+ overrideSignerType credentials.SignatureType
// User supplied.
appInfo struct {
@@ -74,6 +74,9 @@ type Client struct {
// S3 specific accelerated endpoint.
s3AccelerateEndpoint string
+ // Region endpoint
+ region string
+
// Random seed.
random *rand.Rand
}
@@ -81,7 +84,7 @@ type Client struct {
// Global constants.
const (
libraryName = "minio-go"
- libraryVersion = "2.0.4"
+ libraryVersion = "2.1.0"
)
// User Agent should always following the below style.
@@ -96,53 +99,67 @@ const (
// NewV2 - instantiate minio client with Amazon S3 signature version
// '2' compatibility.
func NewV2(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
- clnt, err := privateNew(endpoint, accessKeyID, secretAccessKey, secure)
+ creds := credentials.NewStaticV2(accessKeyID, secretAccessKey, "")
+ clnt, err := privateNew(endpoint, creds, secure, "")
if err != nil {
return nil, err
}
- // Set to use signature version '2'.
- clnt.signature = SignatureV2
+ clnt.overrideSignerType = credentials.SignatureV2
return clnt, nil
}
// NewV4 - instantiate minio client with Amazon S3 signature version
// '4' compatibility.
func NewV4(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
- clnt, err := privateNew(endpoint, accessKeyID, secretAccessKey, secure)
+ creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
+ clnt, err := privateNew(endpoint, creds, secure, "")
if err != nil {
return nil, err
}
- // Set to use signature version '4'.
- clnt.signature = SignatureV4
+ clnt.overrideSignerType = credentials.SignatureV4
return clnt, nil
}
-// New - instantiate minio client Client, adds automatic verification
-// of signature.
-func New(endpoint string, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
- clnt, err := privateNew(endpoint, accessKeyID, secretAccessKey, secure)
+// New - instantiate minio client, adds automatic verification of signature.
+func New(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
+ creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
+ clnt, err := privateNew(endpoint, creds, secure, "")
if err != nil {
return nil, err
}
// Google cloud storage should be set to signature V2, force it if not.
if s3utils.IsGoogleEndpoint(clnt.endpointURL) {
- clnt.signature = SignatureV2
+ clnt.overrideSignerType = credentials.SignatureV2
}
- // If Amazon S3 set to signature v2.n
+ // If Amazon S3 set to signature v4.
if s3utils.IsAmazonEndpoint(clnt.endpointURL) {
- clnt.signature = SignatureV4
+ clnt.overrideSignerType = credentials.SignatureV4
}
return clnt, nil
}
+// NewWithCredentials - instantiate minio client with credentials provider
+// for retrieving credentials from various credentials provider such as
+// IAM, File, Env etc.
+func NewWithCredentials(endpoint string, creds *credentials.Credentials, secure bool, region string) (*Client, error) {
+ return privateNew(endpoint, creds, secure, region)
+}
+
+// NewWithRegion - instantiate minio client, with region configured. Unlike New(),
+// NewWithRegion avoids bucket-location lookup operations and it is slightly faster.
+// Use this function when if your application deals with single region.
+func NewWithRegion(endpoint, accessKeyID, secretAccessKey string, secure bool, region string) (*Client, error) {
+ creds := credentials.NewStaticV4(accessKeyID, secretAccessKey, "")
+ return privateNew(endpoint, creds, secure, region)
+}
+
// lockedRandSource provides protected rand source, implements rand.Source interface.
type lockedRandSource struct {
lk sync.Mutex
src rand.Source
}
-// Int63 returns a non-negative pseudo-random 63-bit integer as an
-// int64.
+// Int63 returns a non-negative pseudo-random 63-bit integer as an int64.
func (r *lockedRandSource) Int63() (n int64) {
r.lk.Lock()
n = r.src.Int63()
@@ -170,7 +187,7 @@ func redirectHeaders(req *http.Request, via []*http.Request) error {
return nil
}
-func privateNew(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Client, error) {
+func privateNew(endpoint string, creds *credentials.Credentials, secure bool, region string) (*Client, error) {
// construct endpoint.
endpointURL, err := getEndpointURL(endpoint, secure)
if err != nil {
@@ -179,11 +196,9 @@ func privateNew(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Cl
// instantiate new Client.
clnt := new(Client)
- clnt.accessKeyID = accessKeyID
- clnt.secretAccessKey = secretAccessKey
- if clnt.accessKeyID == "" || clnt.secretAccessKey == "" {
- clnt.anonymous = true
- }
+
+ // Save the credentials.
+ clnt.credsProvider = creds
// Remember whether we are using https or not
clnt.secure = secure
@@ -197,7 +212,10 @@ func privateNew(endpoint, accessKeyID, secretAccessKey string, secure bool) (*Cl
CheckRedirect: redirectHeaders,
}
- // Instantiae bucket location cache.
+ // Sets custom region, if region is empty bucket location cache is used automatically.
+ clnt.region = region
+
+ // Instantiate bucket location cache.
clnt.bucketLocCache = newBucketLocationCache()
// Introduce a new locked random seed.
@@ -299,12 +317,12 @@ var regSign = regexp.MustCompile("Signature=([[0-9a-f]+)")
// Filter out signature value from Authorization header.
func (c Client) filterSignature(req *http.Request) {
- // For anonymous requests, no need to filter.
- if c.anonymous {
+ origAuth := req.Header.Get("Authorization")
+ if origAuth != "" {
return
}
- // Handle if Signature V2.
- if c.signature.isV2() {
+
+ if !strings.HasPrefix(origAuth, signV4Algorithm) {
// Set a temporary redacted auth
req.Header.Set("Authorization", "AWS **REDACTED**:**REDACTED**")
return
@@ -312,8 +330,6 @@ func (c Client) filterSignature(req *http.Request) {
/// Signature V4 authorization header.
- // Save the original auth.
- origAuth := req.Header.Get("Authorization")
// Strip out accessKeyID from:
// Credential=<access-key-id>/<date>/<aws-region>/<aws-service>/aws4_request
newAuth := regCred.ReplaceAllString(origAuth, "Credential=**REDACTED**/")
@@ -323,6 +339,7 @@ func (c Client) filterSignature(req *http.Request) {
// Set a temporary redacted auth
req.Header.Set("Authorization", newAuth)
+
return
}
@@ -411,7 +428,7 @@ func (c Client) do(req *http.Request) (*http.Response, error) {
return nil, &url.Error{
Op: urlErr.Op,
URL: urlErr.URL,
- Err: fmt.Errorf("Connection closed by foreign host %s. Retry again.", urlErr.URL),
+ Err: errors.New("Connection closed by foreign host " + urlErr.URL + ". Retry again."),
}
}
return nil, err
@@ -460,9 +477,13 @@ func (c Client) executeMethod(method string, metadata requestMetadata) (res *htt
if metadata.contentBody != nil {
// Check if body is seekable then it is retryable.
bodySeeker, isRetryable = metadata.contentBody.(io.Seeker)
+ switch bodySeeker {
+ case os.Stdin, os.Stdout, os.Stderr:
+ isRetryable = false
+ }
}
- // Create a done channel to control 'ListObjects' go routine.
+ // Create a done channel to control 'newRetryTimer' go routine.
doneCh := make(chan struct{}, 1)
// Indicate to our routine to exit cleanly upon return.
@@ -471,7 +492,7 @@ func (c Client) executeMethod(method string, metadata requestMetadata) (res *htt
// Blank indentifier is kept here on purpose since 'range' without
// blank identifiers is only supported since go1.4
// https://golang.org/doc/go1.4#forrange.
- for _ = range c.newRetryTimer(MaxRetry, time.Second, time.Second*30, MaxJitter, doneCh) {
+ for _ = range c.newRetryTimer(MaxRetry, DefaultRetryUnit, DefaultRetryCap, MaxJitter, doneCh) {
// Retry executes the following function body if request has an
// error until maxRetries have been exhausted, retry attempts are
// performed after waiting for a given period of time in a
@@ -520,15 +541,22 @@ func (c Client) executeMethod(method string, metadata requestMetadata) (res *htt
if err != nil {
return nil, err
}
+
// Save the body.
errBodySeeker := bytes.NewReader(errBodyBytes)
res.Body = ioutil.NopCloser(errBodySeeker)
// For errors verify if its retryable otherwise fail quickly.
errResponse := ToErrorResponse(httpRespToErrorResponse(res, metadata.bucketName, metadata.objectName))
- // Bucket region if set in error response, we can retry the
- // request with the new region.
- if errResponse.Region != "" {
+
+ // Save the body back again.
+ errBodySeeker.Seek(0, 0) // Seek back to starting point.
+ res.Body = ioutil.NopCloser(errBodySeeker)
+
+ // Bucket region if set in error response and the error
+ // code dictates invalid region, we can retry the request
+ // with the new region.
+ if res.StatusCode == http.StatusBadRequest && errResponse.Region != "" {
c.bucketLocCache.Set(metadata.bucketName, errResponse.Region)
continue // Retry.
}
@@ -543,10 +571,6 @@ func (c Client) executeMethod(method string, metadata requestMetadata) (res *htt
continue // Retry.
}
- // Save the body back again.
- errBodySeeker.Seek(0, 0) // Seek back to starting point.
- res.Body = ioutil.NopCloser(errBodySeeker)
-
// For all other cases break out of the retry loop.
break
}
@@ -588,39 +612,50 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R
}
// Initialize a new HTTP request for the method.
- req, err = http.NewRequest(method, targetURL.String(), nil)
+ req, err = http.NewRequest(method, targetURL.String(), metadata.contentBody)
if err != nil {
return nil, err
}
+ // Get credentials from the configured credentials provider.
+ value, err := c.credsProvider.Get()
+ if err != nil {
+ return nil, err
+ }
+
+ var (
+ signerType = value.SignerType
+ accessKeyID = value.AccessKeyID
+ secretAccessKey = value.SecretAccessKey
+ sessionToken = value.SessionToken
+ )
+
+ // Custom signer set then override the behavior.
+ if c.overrideSignerType != credentials.SignatureDefault {
+ signerType = c.overrideSignerType
+ }
+
+ // If signerType returned by credentials helper is anonymous,
+ // then do not sign regardless of signerType override.
+ if value.SignerType == credentials.SignatureAnonymous {
+ signerType = credentials.SignatureAnonymous
+ }
+
// Generate presign url if needed, return right here.
if metadata.expires != 0 && metadata.presignURL {
- if c.anonymous {
- return nil, ErrInvalidArgument("Requests cannot be presigned with anonymous credentials.")
+ if signerType.IsAnonymous() {
+ return nil, ErrInvalidArgument("Presigned URLs cannot be generated with anonymous credentials.")
}
- if c.signature.isV2() {
+ if signerType.IsV2() {
// Presign URL with signature v2.
- req = s3signer.PreSignV2(*req, c.accessKeyID, c.secretAccessKey, metadata.expires)
- } else {
+ req = s3signer.PreSignV2(*req, accessKeyID, secretAccessKey, metadata.expires)
+ } else if signerType.IsV4() {
// Presign URL with signature v4.
- req = s3signer.PreSignV4(*req, c.accessKeyID, c.secretAccessKey, location, metadata.expires)
+ req = s3signer.PreSignV4(*req, accessKeyID, secretAccessKey, sessionToken, location, metadata.expires)
}
return req, nil
}
- // Set content body if available.
- if metadata.contentBody != nil {
- req.Body = ioutil.NopCloser(metadata.contentBody)
- }
-
- // FIXME: Enable this when Google Cloud Storage properly supports 100-continue.
- // Skip setting 'expect' header for Google Cloud Storage, there
- // are some known issues - https://github.com/restic/restic/issues/520
- if !s3utils.IsGoogleEndpoint(c.endpointURL) && c.s3AccelerateEndpoint == "" {
- // Set 'Expect' header for the request.
- req.Header.Set("Expect", "100-continue")
- }
-
// Set 'User-Agent' header for the request.
c.setUserAgent(req)
@@ -634,37 +669,33 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R
req.ContentLength = metadata.contentLength
}
- // Set sha256 sum only for non anonymous credentials.
- if !c.anonymous {
- // set sha256 sum for signature calculation only with
- // signature version '4'.
- if c.signature.isV4() {
- shaHeader := unsignedPayload
- if !c.secure {
- if metadata.contentSHA256Bytes == nil {
- shaHeader = hex.EncodeToString(sum256([]byte{}))
- } else {
- shaHeader = hex.EncodeToString(metadata.contentSHA256Bytes)
- }
- }
- req.Header.Set("X-Amz-Content-Sha256", shaHeader)
- }
- }
-
// set md5Sum for content protection.
if metadata.contentMD5Bytes != nil {
req.Header.Set("Content-Md5", base64.StdEncoding.EncodeToString(metadata.contentMD5Bytes))
}
- // Sign the request for all authenticated requests.
- if !c.anonymous {
- if c.signature.isV2() {
- // Add signature version '2' authorization header.
- req = s3signer.SignV2(*req, c.accessKeyID, c.secretAccessKey)
- } else if c.signature.isV4() {
- // Add signature version '4' authorization header.
- req = s3signer.SignV4(*req, c.accessKeyID, c.secretAccessKey, location)
+ // For anonymous requests just return.
+ if signerType.IsAnonymous() {
+ return req, nil
+ }
+
+ switch {
+ case signerType.IsV2():
+ // Add signature version '2' authorization header.
+ req = s3signer.SignV2(*req, accessKeyID, secretAccessKey)
+ case signerType.IsStreamingV4() && method == "PUT":
+ req = s3signer.StreamingSignV4(req, accessKeyID,
+ secretAccessKey, sessionToken, location, metadata.contentLength, time.Now().UTC())
+ default:
+ // Set sha256 sum for signature calculation only with signature version '4'.
+ shaHeader := unsignedPayload
+ if len(metadata.contentSHA256Bytes) > 0 {
+ shaHeader = hex.EncodeToString(metadata.contentSHA256Bytes)
}
+ req.Header.Set("X-Amz-Content-Sha256", shaHeader)
+
+ // Add signature version '4' authorization header.
+ req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, location)
}
// Return request.
@@ -726,13 +757,16 @@ func (c Client) makeTargetURL(bucketName, objectName, bucketLocation string, que
}
}
}
+
// If there are any query values, add them to the end.
if len(queryValues) > 0 {
urlStr = urlStr + "?" + s3utils.QueryEncode(queryValues)
}
+
u, err := url.Parse(urlStr)
if err != nil {
return nil, err
}
+
return u, nil
}