Commit 330c10af authored by Justin Santa Barbara's avatar Justin Santa Barbara

Update AWS SDK to 1.12.7

Fix #54032
parent 57f6c9dd
......@@ -162,158 +162,158 @@
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/endpointcreds",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/stscreds",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/endpoints",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/signer/v4",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/internal/shareddefaults",
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ecr",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/elb",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/kms",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/sts",
"Comment": "v1.6.10",
"Rev": "63ce630574a5ec05ecd8e8de5cea16332a5a684d"
"Comment": "v1.12.7",
"Rev": "760741802ad40f49ae9fc4a69ef6706d2527d62e"
},
{
"ImportPath": "github.com/beorn7/perks/quantile",
......@@ -1157,8 +1157,8 @@
},
{
"ImportPath": "github.com/go-ini/ini",
"Comment": "v0-54-g2e44421",
"Rev": "2e44421e256d82ebbf3d4d4fcabe8930b905eff3"
"Comment": "v1.25.4",
"Rev": "300e940a926eb277d3901b20bdfcc54928ad3642"
},
{
"ImportPath": "github.com/go-openapi/analysis",
......@@ -1854,8 +1854,8 @@
},
{
"ImportPath": "github.com/jmespath/go-jmespath",
"Comment": "0.2.2",
"Rev": "3433f3ea46d9f8019119e7dd41274e112a2359a9"
"Comment": "0.2.2-12-g0b12d6b",
"Rev": "0b12d6b521d83fc7f755e7cfc1b1fbdd35a01a74"
},
{
"ImportPath": "github.com/jonboulle/clockwork",
......
......@@ -5173,7 +5173,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol licensed under: =
= vendor/github.com/aws/aws-sdk-go/internal/shareddefaults licensed under: =
Apache License
......@@ -5383,7 +5383,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/ec2query licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol licensed under: =
Apache License
......@@ -5593,7 +5593,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/ec2query licensed under: =
Apache License
......@@ -5803,7 +5803,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/json/jsonutil licensed under: =
Apache License
......@@ -6013,7 +6013,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/query licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/jsonrpc licensed under: =
Apache License
......@@ -6223,7 +6223,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/query licensed under: =
Apache License
......@@ -6433,7 +6433,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/rest licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/query/queryutil licensed under: =
Apache License
......@@ -6643,7 +6643,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/rest licensed under: =
Apache License
......@@ -6853,7 +6853,7 @@ SOFTWARE.
================================================================================
= vendor/github.com/aws/aws-sdk-go/private/waiter licensed under: =
= vendor/github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil licensed under: =
Apache License
......@@ -33,14 +33,16 @@ filegroup(
"//vendor/github.com/armon/circbuf:all-srcs",
"//vendor/github.com/asaskevich/govalidator:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/aws:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/internal/shareddefaults:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/private/protocol:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/private/waiter:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/autoscaling:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/ec2:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/ecr:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/elb:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/kms:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/service/sts:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/vendor/github.com/go-ini/ini:all-srcs",
"//vendor/github.com/aws/aws-sdk-go/vendor/github.com/jmespath/go-jmespath:all-srcs",
"//vendor/github.com/beorn7/perks/quantile:all-srcs",
"//vendor/github.com/blang/semver:all-srcs",
"//vendor/github.com/boltdb/bolt:all-srcs",
......
......@@ -4,10 +4,17 @@ go_library(
name = "go_default_library",
srcs = [
"config.go",
"context.go",
"context_1_6.go",
"context_1_7.go",
"convert_types.go",
"doc.go",
"errors.go",
"jsonvalue.go",
"logger.go",
"types.go",
"url.go",
"url_1_7.go",
"version.go",
],
importpath = "github.com/aws/aws-sdk-go/aws",
......
......@@ -5,6 +5,7 @@ go_library(
srcs = [
"client.go",
"default_retryer.go",
"logger.go",
],
importpath = "github.com/aws/aws-sdk-go/aws/client",
visibility = ["//visibility:public"],
......
......@@ -2,7 +2,6 @@ package client
import (
"fmt"
"net/http/httputil"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client/metadata"
......@@ -24,6 +23,13 @@ type ConfigProvider interface {
ClientConfig(serviceName string, cfgs ...*aws.Config) Config
}
// ConfigNoResolveEndpointProvider same as ConfigProvider except it will not
// resolve the endpoint automatically. The service client's endpoint must be
// provided via the aws.Config.Endpoint field.
type ConfigNoResolveEndpointProvider interface {
ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) Config
}
// A Client implements the base client request and response handling
// used by all service clients.
type Client struct {
......@@ -39,7 +45,7 @@ func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, op
svc := &Client{
Config: cfg,
ClientInfo: info,
Handlers: handlers,
Handlers: handlers.Copy(),
}
switch retryer, ok := cfg.Retryer.(request.Retryer); {
......@@ -79,61 +85,6 @@ func (c *Client) AddDebugHandlers() {
return
}
c.Handlers.Send.PushFront(logRequest)
c.Handlers.Send.PushBack(logResponse)
}
const logReqMsg = `DEBUG: Request %s/%s Details:
---[ REQUEST POST-SIGN ]-----------------------------
%s
-----------------------------------------------------`
const logReqErrMsg = `DEBUG ERROR: Request %s/%s:
---[ REQUEST DUMP ERROR ]-----------------------------
%s
-----------------------------------------------------`
func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
if logBody {
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
// Body as a NoOpCloser and will not be reset after read by the HTTP
// client reader.
r.ResetBody()
}
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
---[ RESPONSE ]--------------------------------------
%s
-----------------------------------------------------`
const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
---[ RESPONSE DUMP ERROR ]-----------------------------
%s
-----------------------------------------------------`
func logResponse(r *request.Request) {
var msg = "no response data"
if r.HTTPResponse != nil {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, err := httputil.DumpResponse(r.HTTPResponse, logBody)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
msg = string(dumpedBody)
} else if r.Error != nil {
msg = r.Error.Error()
}
r.Config.Logger.Log(fmt.Sprintf(logRespMsg, r.ClientInfo.ServiceName, r.Operation.Name, msg))
c.Handlers.Send.PushFrontNamed(request.NamedHandler{Name: "awssdk.client.LogRequest", Fn: logRequest})
c.Handlers.Send.PushBackNamed(request.NamedHandler{Name: "awssdk.client.LogResponse", Fn: logResponse})
}
......@@ -15,11 +15,11 @@ import (
// the MaxRetries method:
//
// type retryer struct {
// service.DefaultRetryer
// client.DefaultRetryer
// }
//
// // This implementation always has 100 max retries
// func (d retryer) MaxRetries() uint { return 100 }
// func (d retryer) MaxRetries() int { return 100 }
type DefaultRetryer struct {
NumMaxRetries int
}
......@@ -54,6 +54,12 @@ func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
// ShouldRetry returns true if the request should be retried.
func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
// If one of the other handlers already set the retry state
// we don't want to override it based on the service's state
if r.Retryable != nil {
return *r.Retryable
}
if r.HTTPResponse.StatusCode >= 500 {
return true
}
......
package client
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"net/http/httputil"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
)
const logReqMsg = `DEBUG: Request %s/%s Details:
---[ REQUEST POST-SIGN ]-----------------------------
%s
-----------------------------------------------------`
const logReqErrMsg = `DEBUG ERROR: Request %s/%s:
---[ REQUEST DUMP ERROR ]-----------------------------
%s
------------------------------------------------------`
type logWriter struct {
// Logger is what we will use to log the payload of a response.
Logger aws.Logger
// buf stores the contents of what has been read
buf *bytes.Buffer
}
func (logger *logWriter) Write(b []byte) (int, error) {
return logger.buf.Write(b)
}
type teeReaderCloser struct {
// io.Reader will be a tee reader that is used during logging.
// This structure will read from a body and write the contents to a logger.
io.Reader
// Source is used just to close when we are done reading.
Source io.ReadCloser
}
func (reader *teeReaderCloser) Close() error {
return reader.Source.Close()
}
func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
return
}
if logBody {
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
// Body as a NoOpCloser and will not be reset after read by the HTTP
// client reader.
r.ResetBody()
}
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
---[ RESPONSE ]--------------------------------------
%s
-----------------------------------------------------`
const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
---[ RESPONSE DUMP ERROR ]-----------------------------
%s
-----------------------------------------------------`
func logResponse(r *request.Request) {
lw := &logWriter{r.Config.Logger, bytes.NewBuffer(nil)}
r.HTTPResponse.Body = &teeReaderCloser{
Reader: io.TeeReader(r.HTTPResponse.Body, lw),
Source: r.HTTPResponse.Body,
}
handlerFn := func(req *request.Request) {
body, err := httputil.DumpResponse(req.HTTPResponse, false)
if err != nil {
lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err))
return
}
b, err := ioutil.ReadAll(lw.buf)
if err != nil {
lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err))
return
}
lw.Logger.Log(fmt.Sprintf(logRespMsg, req.ClientInfo.ServiceName, req.Operation.Name, string(body)))
if req.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) {
lw.Logger.Log(string(b))
}
}
const handlerName = "awsdk.client.LogResponse.ResponseBody"
r.Handlers.Unmarshal.SetBackNamed(request.NamedHandler{
Name: handlerName, Fn: handlerFn,
})
r.Handlers.UnmarshalError.SetBackNamed(request.NamedHandler{
Name: handlerName, Fn: handlerFn,
})
}
......@@ -22,9 +22,9 @@ type RequestRetryer interface{}
//
// // Create Session with MaxRetry configuration to be shared by multiple
// // service clients.
// sess, err := session.NewSession(&aws.Config{
// sess := session.Must(session.NewSession(&aws.Config{
// MaxRetries: aws.Int(3),
// })
// }))
//
// // Create S3 service client with a specific Region.
// svc := s3.New(sess, &aws.Config{
......@@ -53,6 +53,13 @@ type Config struct {
// to use based on region.
EndpointResolver endpoints.Resolver
// EnforceShouldRetryCheck is used in the AfterRetryHandler to always call
// ShouldRetry regardless of whether or not if request.Retryable is set.
// This will utilize ShouldRetry method of custom retryers. If EnforceShouldRetryCheck
// is not set, then ShouldRetry will only be called if request.Retryable is nil.
// Proper handling of the request.Retryable field is important when setting this field.
EnforceShouldRetryCheck *bool
// The region to send requests to. This parameter is required and must
// be configured globally or on a per-client basis unless otherwise
// noted. A full list of regions is found in the "Regions and Endpoints"
......@@ -88,7 +95,7 @@ type Config struct {
// recoverable failures.
//
// When nil or the value does not implement the request.Retryer interface,
// the request.DefaultRetryer will be used.
// the client.DefaultRetryer will be used.
//
// When both Retryer and MaxRetries are non-nil, the former is used and
// the latter ignored.
......@@ -154,7 +161,8 @@ type Config struct {
// the EC2Metadata overriding the timeout for default credentials chain.
//
// Example:
// sess, err := session.NewSession(aws.NewConfig().WithEC2MetadataDiableTimeoutOverride(true))
// sess := session.Must(session.NewSession(aws.NewConfig()
// .WithEC2MetadataDiableTimeoutOverride(true)))
//
// svc := s3.New(sess)
//
......@@ -174,7 +182,7 @@ type Config struct {
//
// Only supported with.
//
// sess, err := session.NewSession()
// sess := session.Must(session.NewSession())
//
// svc := s3.New(sess, &aws.Config{
// UseDualStack: aws.Bool(true),
......@@ -186,13 +194,19 @@ type Config struct {
// request delays. This value should only be used for testing. To adjust
// the delay of a request see the aws/client.DefaultRetryer and
// aws/request.Retryer.
//
// SleepDelay will prevent any Context from being used for canceling retry
// delay of an API operation. It is recommended to not use SleepDelay at all
// and specify a Retryer instead.
SleepDelay func(time.Duration)
// DisableRestProtocolURICleaning will not clean the URL path when making rest protocol requests.
// Will default to false. This would only be used for empty directory names in s3 requests.
//
// Example:
// sess, err := session.NewSession(&aws.Config{DisableRestProtocolURICleaning: aws.Bool(true))
// sess := session.Must(session.NewSession(&aws.Config{
// DisableRestProtocolURICleaning: aws.Bool(true),
// }))
//
// svc := s3.New(sess)
// out, err := svc.GetObject(&s3.GetObjectInput {
......@@ -207,9 +221,9 @@ type Config struct {
//
// // Create Session with MaxRetry configuration to be shared by multiple
// // service clients.
// sess, err := session.NewSession(aws.NewConfig().
// sess := session.Must(session.NewSession(aws.NewConfig().
// WithMaxRetries(3),
// )
// ))
//
// // Create S3 service client with a specific Region.
// svc := s3.New(sess, aws.NewConfig().
......@@ -436,6 +450,10 @@ func mergeInConfig(dst *Config, other *Config) {
if other.DisableRestProtocolURICleaning != nil {
dst.DisableRestProtocolURICleaning = other.DisableRestProtocolURICleaning
}
if other.EnforceShouldRetryCheck != nil {
dst.EnforceShouldRetryCheck = other.EnforceShouldRetryCheck
}
}
// Copy will return a shallow copy of the Config object. If any additional
......
package aws
import (
"time"
)
// Context is an copy of the Go v1.7 stdlib's context.Context interface.
// It is represented as a SDK interface to enable you to use the "WithContext"
// API methods with Go v1.6 and a Context type such as golang.org/x/net/context.
//
// See https://golang.org/pkg/context on how to use contexts.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
Done() <-chan struct{}
// Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
Value(key interface{}) interface{}
}
// BackgroundContext returns a context that will never be canceled, has no
// values, and no deadline. This context is used by the SDK to provide
// backwards compatibility with non-context API operations and functionality.
//
// Go 1.6 and before:
// This context function is equivalent to context.Background in the Go stdlib.
//
// Go 1.7 and later:
// The context returned will be the value returned by context.Background()
//
// See https://golang.org/pkg/context for more information on Contexts.
func BackgroundContext() Context {
return backgroundCtx
}
// SleepWithContext will wait for the timer duration to expire, or the context
// is canceled. Which ever happens first. If the context is canceled the Context's
// error will be returned.
//
// Expects Context to always return a non-nil error if the Done channel is closed.
func SleepWithContext(ctx Context, dur time.Duration) error {
t := time.NewTimer(dur)
defer t.Stop()
select {
case <-t.C:
break
case <-ctx.Done():
return ctx.Err()
}
return nil
}
// +build !go1.7
package aws
import "time"
// An emptyCtx is a copy of the Go 1.7 context.emptyCtx type. This is copied to
// provide a 1.6 and 1.5 safe version of context that is compatible with Go
// 1.7's Context.
//
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case backgroundCtx:
return "aws.BackgroundContext"
}
return "unknown empty Context"
}
var (
backgroundCtx = new(emptyCtx)
)
// +build go1.7
package aws
import "context"
var (
backgroundCtx = context.Background()
)
......@@ -311,6 +311,24 @@ func TimeValue(v *time.Time) time.Time {
return time.Time{}
}
// SecondsTimeValue converts an int64 pointer to a time.Time value
// representing seconds since Epoch or time.Time{} if the pointer is nil.
func SecondsTimeValue(v *int64) time.Time {
if v != nil {
return time.Unix((*v / 1000), 0)
}
return time.Time{}
}
// MillisecondsTimeValue converts an int64 pointer to a time.Time value
// representing milliseconds sinch Epoch or time.Time{} if the pointer is nil.
func MillisecondsTimeValue(v *int64) time.Time {
if v != nil {
return time.Unix(0, (*v * 1000000))
}
return time.Time{}
}
// TimeUnixMilli returns a Unix timestamp in milliseconds from "January 1, 1970 UTC".
// The result is undefined if the Unix time cannot be represented by an int64.
// Which includes calling TimeUnixMilli on a zero Time is undefined.
......
......@@ -27,7 +27,7 @@ type lener interface {
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
// to determine request body length and no "Content-Length" was specified it will panic.
//
// The Content-Length will only be aded to the request if the length of the body
// The Content-Length will only be added to the request if the length of the body
// is greater than 0. If the body is empty or the current `Content-Length`
// header is <= 0, the header will also be stripped.
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
......@@ -71,8 +71,8 @@ var reStatusCode = regexp.MustCompile(`^(\d{3})`)
// ValidateReqSigHandler is a request handler to ensure that the request's
// signature doesn't expire before it is sent. This can happen when a request
// is built and signed signficantly before it is sent. Or significant delays
// occur whne retrying requests that would cause the signature to expire.
// is built and signed significantly before it is sent. Or significant delays
// occur when retrying requests that would cause the signature to expire.
var ValidateReqSigHandler = request.NamedHandler{
Name: "core.ValidateReqSigHandler",
Fn: func(r *request.Request) {
......@@ -98,44 +98,95 @@ var ValidateReqSigHandler = request.NamedHandler{
}
// SendHandler is a request handler to send service request using HTTP client.
var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *request.Request) {
var err error
r.HTTPResponse, err = r.Config.HTTPClient.Do(r.HTTPRequest)
if err != nil {
// Prevent leaking if an HTTPResponse was returned. Clean up
// the body.
if r.HTTPResponse != nil {
r.HTTPResponse.Body.Close()
var SendHandler = request.NamedHandler{
Name: "core.SendHandler",
Fn: func(r *request.Request) {
sender := sendFollowRedirects
if r.DisableFollowRedirects {
sender = sendWithoutFollowRedirects
}
// Capture the case where url.Error is returned for error processing
// response. e.g. 301 without location header comes back as string
// error and r.HTTPResponse is nil. Other url redirect errors will
// comeback in a similar method.
if e, ok := err.(*url.Error); ok && e.Err != nil {
if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
code, _ := strconv.ParseInt(s[1], 10, 64)
r.HTTPResponse = &http.Response{
StatusCode: int(code),
Status: http.StatusText(int(code)),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
return
}
if request.NoBody == r.HTTPRequest.Body {
// Strip off the request body if the NoBody reader was used as a
// place holder for a request body. This prevents the SDK from
// making requests with a request body when it would be invalid
// to do so.
//
// Use a shallow copy of the http.Request to ensure the race condition
// of transport on Body will not trigger
reqOrig, reqCopy := r.HTTPRequest, *r.HTTPRequest
reqCopy.Body = nil
r.HTTPRequest = &reqCopy
defer func() {
r.HTTPRequest = reqOrig
}()
}
if r.HTTPResponse == nil {
// Add a dummy request response object to ensure the HTTPResponse
// value is consistent.
var err error
r.HTTPResponse, err = sender(r)
if err != nil {
handleSendError(r, err)
}
},
}
func sendFollowRedirects(r *request.Request) (*http.Response, error) {
return r.Config.HTTPClient.Do(r.HTTPRequest)
}
func sendWithoutFollowRedirects(r *request.Request) (*http.Response, error) {
transport := r.Config.HTTPClient.Transport
if transport == nil {
transport = http.DefaultTransport
}
return transport.RoundTrip(r.HTTPRequest)
}
func handleSendError(r *request.Request, err error) {
// Prevent leaking if an HTTPResponse was returned. Clean up
// the body.
if r.HTTPResponse != nil {
r.HTTPResponse.Body.Close()
}
// Capture the case where url.Error is returned for error processing
// response. e.g. 301 without location header comes back as string
// error and r.HTTPResponse is nil. Other URL redirect errors will
// comeback in a similar method.
if e, ok := err.(*url.Error); ok && e.Err != nil {
if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
code, _ := strconv.ParseInt(s[1], 10, 64)
r.HTTPResponse = &http.Response{
StatusCode: int(0),
Status: http.StatusText(int(0)),
StatusCode: int(code),
Status: http.StatusText(int(code)),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
return
}
// Catch all other request errors.
r.Error = awserr.New("RequestError", "send request failed", err)
r.Retryable = aws.Bool(true) // network errors are retryable
}
}}
if r.HTTPResponse == nil {
// Add a dummy request response object to ensure the HTTPResponse
// value is consistent.
r.HTTPResponse = &http.Response{
StatusCode: int(0),
Status: http.StatusText(int(0)),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
}
// Catch all other request errors.
r.Error = awserr.New("RequestError", "send request failed", err)
r.Retryable = aws.Bool(true) // network errors are retryable
// Override the error with a context canceled error, if that was canceled.
ctx := r.Context()
select {
case <-ctx.Done():
r.Error = awserr.New(request.CanceledErrorCode,
"request context canceled", ctx.Err())
r.Retryable = aws.Bool(false)
default:
}
}
// ValidateResponseHandler is a request handler to validate service response.
var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
......@@ -150,13 +201,22 @@ var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseH
var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
// If one of the other handlers already set the retry state
// we don't want to override it based on the service's state
if r.Retryable == nil {
if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) {
r.Retryable = aws.Bool(r.ShouldRetry(r))
}
if r.WillRetry() {
r.RetryDelay = r.RetryRules(r)
r.Config.SleepDelay(r.RetryDelay)
if sleepFn := r.Config.SleepDelay; sleepFn != nil {
// Support SleepDelay for backwards compatibility and testing
sleepFn(r.RetryDelay)
} else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
r.Error = awserr.New(request.CanceledErrorCode,
"request context canceled", err)
r.Retryable = aws.Bool(false)
return
}
// when the expired token exception occurs the credentials
// need to be expired locally so that the next request to
......
......@@ -13,6 +13,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/internal/shareddefaults:go_default_library",
"//vendor/github.com/go-ini/ini:go_default_library",
],
)
......
......@@ -13,7 +13,7 @@ var (
//
// @readonly
ErrNoValidProvidersFoundInChain = awserr.New("NoCredentialProviders",
`no valid providers in chain. Deprecated.
`no valid providers in chain. Deprecated.
For verbose messaging see aws.Config.CredentialsChainVerboseErrors`,
nil)
)
......@@ -39,16 +39,18 @@ var (
// does not return any credentials ChainProvider will return the error
// ErrNoValidProvidersFoundInChain
//
// creds := NewChainCredentials(
// []Provider{
// &EnvProvider{},
// &EC2RoleProvider{
// creds := credentials.NewChainCredentials(
// []credentials.Provider{
// &credentials.EnvProvider{},
// &ec2rolecreds.EC2RoleProvider{
// Client: ec2metadata.New(sess),
// },
// })
//
// // Usage of ChainCredentials with aws.Config
// svc := ec2.New(&aws.Config{Credentials: creds})
// svc := ec2.New(session.Must(session.NewSession(&aws.Config{
// Credentials: creds,
// })))
//
type ChainProvider struct {
Providers []Provider
......
......@@ -14,7 +14,7 @@
//
// Example of using the environment variable credentials.
//
// creds := NewEnvCredentials()
// creds := credentials.NewEnvCredentials()
//
// // Retrieve the credentials value
// credValue, err := creds.Get()
......@@ -26,7 +26,7 @@
// This may be helpful to proactively expire credentials and refresh them sooner
// than they would naturally expire on their own.
//
// creds := NewCredentials(&EC2RoleProvider{})
// creds := credentials.NewCredentials(&ec2rolecreds.EC2RoleProvider{})
// creds.Expire()
// credsValue, err := creds.Get()
// // New credentials will be retrieved instead of from cache.
......@@ -43,7 +43,7 @@
// func (m *MyProvider) Retrieve() (Value, error) {...}
// func (m *MyProvider) IsExpired() bool {...}
//
// creds := NewCredentials(&MyProvider{})
// creds := credentials.NewCredentials(&MyProvider{})
// credValue, err := creds.Get()
//
package credentials
......@@ -60,7 +60,9 @@ import (
// when making service API calls. For example, when accessing public
// s3 buckets.
//
// svc := s3.New(&aws.Config{Credentials: AnonymousCredentials})
// svc := s3.New(session.Must(session.NewSession(&aws.Config{
// Credentials: credentials.AnonymousCredentials,
// })))
// // Access public S3 buckets.
//
// @readonly
......@@ -88,7 +90,7 @@ type Value struct {
// The Provider should not need to implement its own mutexes, because
// that will be managed by Credentials.
type Provider interface {
// Refresh returns nil if it successfully retrieved the value.
// Retrieve returns nil if it successfully retrieved the value.
// Error is returned if the value were not obtainable, or empty.
Retrieve() (Value, error)
......@@ -97,6 +99,27 @@ type Provider interface {
IsExpired() bool
}
// An ErrorProvider is a stub credentials provider that always returns an error
// this is used by the SDK when construction a known provider is not possible
// due to an error.
type ErrorProvider struct {
// The error to be returned from Retrieve
Err error
// The provider name to set on the Retrieved returned Value
ProviderName string
}
// Retrieve will always return the error that the ErrorProvider was created with.
func (p ErrorProvider) Retrieve() (Value, error) {
return Value{ProviderName: p.ProviderName}, p.Err
}
// IsExpired will always return not expired.
func (p ErrorProvider) IsExpired() bool {
return false
}
// A Expiry provides shared expiration logic to be used by credentials
// providers to implement expiry functionality.
//
......
......@@ -29,6 +29,7 @@ var (
// Environment variables used:
//
// * Access Key ID: AWS_ACCESS_KEY_ID or AWS_ACCESS_KEY
//
// * Secret Access Key: AWS_SECRET_ACCESS_KEY or AWS_SECRET_KEY
type EnvProvider struct {
retrieved bool
......
......@@ -3,11 +3,11 @@ package credentials
import (
"fmt"
"os"
"path/filepath"
"github.com/go-ini/ini"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/internal/shareddefaults"
)
// SharedCredsProviderName provides a name of SharedCreds provider
......@@ -15,8 +15,6 @@ const SharedCredsProviderName = "SharedCredentialsProvider"
var (
// ErrSharedCredentialsHomeNotFound is emitted when the user directory cannot be found.
//
// @readonly
ErrSharedCredentialsHomeNotFound = awserr.New("UserHomeNotFound", "user home directory not found.", nil)
)
......@@ -117,22 +115,23 @@ func loadProfile(filename, profile string) (Value, error) {
//
// Will return an error if the user's home directory path cannot be found.
func (p *SharedCredentialsProvider) filename() (string, error) {
if p.Filename == "" {
if p.Filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); p.Filename != "" {
return p.Filename, nil
}
homeDir := os.Getenv("HOME") // *nix
if homeDir == "" { // Windows
homeDir = os.Getenv("USERPROFILE")
}
if homeDir == "" {
return "", ErrSharedCredentialsHomeNotFound
}
p.Filename = filepath.Join(homeDir, ".aws", "credentials")
if len(p.Filename) != 0 {
return p.Filename, nil
}
if p.Filename = os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); len(p.Filename) != 0 {
return p.Filename, nil
}
if home := shareddefaults.UserHomeDir(); len(home) == 0 {
// Backwards compatibility of home directly not found error being returned.
// This error is too verbose, failure when opening the file would of been
// a better error to return.
return "", ErrSharedCredentialsHomeNotFound
}
p.Filename = shareddefaults.SharedCredentialsFilename()
return p.Filename, nil
}
......
......@@ -7,6 +7,7 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/client:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/credentials:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/service/sts:go_default_library",
......
// Package stscreds are credential Providers to retrieve STS AWS credentials.
//
// STS provides multiple ways to retrieve credentials which can be used when making
// future AWS service API operation calls.
/*
Package stscreds are credential Providers to retrieve STS AWS credentials.
STS provides multiple ways to retrieve credentials which can be used when making
future AWS service API operation calls.
The SDK will ensure that per instance of credentials.Credentials all requests
to refresh the credentials will be synchronized. But, the SDK is unable to
ensure synchronous usage of the AssumeRoleProvider if the value is shared
between multiple Credentials, Sessions or service clients.
Assume Role
To assume an IAM role using STS with the SDK you can create a new Credentials
with the SDKs's stscreds package.
// Initial credentials loaded from SDK's default credential chain. Such as
// the environment, shared credentials (~/.aws/credentials), or EC2 Instance
// Role. These credentials will be used to to make the STS Assume Role API.
sess := session.Must(session.NewSession())
// Create the credentials from AssumeRoleProvider to assume the role
// referenced by the "myRoleARN" ARN.
creds := stscreds.NewCredentials(sess, "myRoleArn")
// Create service client value configured for credentials
// from assumed role.
svc := s3.New(sess, &aws.Config{Credentials: creds})
Assume Role with static MFA Token
To assume an IAM role with a MFA token you can either specify a MFA token code
directly or provide a function to prompt the user each time the credentials
need to refresh the role's credentials. Specifying the TokenCode should be used
for short lived operations that will not need to be refreshed, and when you do
not want to have direct control over the user provides their MFA token.
With TokenCode the AssumeRoleProvider will be not be able to refresh the role's
credentials.
// Create the credentials from AssumeRoleProvider to assume the role
// referenced by the "myRoleARN" ARN using the MFA token code provided.
creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
p.SerialNumber = aws.String("myTokenSerialNumber")
p.TokenCode = aws.String("00000000")
})
// Create service client value configured for credentials
// from assumed role.
svc := s3.New(sess, &aws.Config{Credentials: creds})
Assume Role with MFA Token Provider
To assume an IAM role with MFA for longer running tasks where the credentials
may need to be refreshed setting the TokenProvider field of AssumeRoleProvider
will allow the credential provider to prompt for new MFA token code when the
role's credentials need to be refreshed.
The StdinTokenProvider function is available to prompt on stdin to retrieve
the MFA token code from the user. You can also implement custom prompts by
satisfing the TokenProvider function signature.
Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
have undesirable results as the StdinTokenProvider will not be synchronized. A
single Credentials with an AssumeRoleProvider can be shared safely.
// Create the credentials from AssumeRoleProvider to assume the role
// referenced by the "myRoleARN" ARN. Prompting for MFA token from stdin.
creds := stscreds.NewCredentials(sess, "myRoleArn", func(p *stscreds.AssumeRoleProvider) {
p.SerialNumber = aws.String("myTokenSerialNumber")
p.TokenProvider = stscreds.StdinTokenProvider
})
// Create service client value configured for credentials
// from assumed role.
svc := s3.New(sess, &aws.Config{Credentials: creds})
*/
package stscreds
import (
......@@ -9,11 +83,31 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/sts"
)
// StdinTokenProvider will prompt on stdout and read from stdin for a string value.
// An error is returned if reading from stdin fails.
//
// Use this function go read MFA tokens from stdin. The function makes no attempt
// to make atomic prompts from stdin across multiple gorouties.
//
// Using StdinTokenProvider with multiple AssumeRoleProviders, or Credentials will
// have undesirable results as the StdinTokenProvider will not be synchronized. A
// single Credentials with an AssumeRoleProvider can be shared safely
//
// Will wait forever until something is provided on the stdin.
func StdinTokenProvider() (string, error) {
var v string
fmt.Printf("Assume Role MFA token code: ")
_, err := fmt.Scanln(&v)
return v, err
}
// ProviderName provides a name of AssumeRole provider
const ProviderName = "AssumeRoleProvider"
......@@ -27,8 +121,15 @@ type AssumeRoler interface {
var DefaultDuration = time.Duration(15) * time.Minute
// AssumeRoleProvider retrieves temporary credentials from the STS service, and
// keeps track of their expiration time. This provider must be used explicitly,
// as it is not included in the credentials chain.
// keeps track of their expiration time.
//
// This credential provider will be used by the SDKs default credential change
// when shared configuration is enabled, and the shared config or shared credentials
// file configure assume role. See Session docs for how to do this.
//
// AssumeRoleProvider does not provide any synchronization and it is not safe
// to share this value across multiple Credentials, Sessions, or service clients
// without also sharing the same Credentials instance.
type AssumeRoleProvider struct {
credentials.Expiry
......@@ -65,8 +166,23 @@ type AssumeRoleProvider struct {
// assumed requires MFA (that is, if the policy includes a condition that tests
// for MFA). If the role being assumed requires MFA and if the TokenCode value
// is missing or expired, the AssumeRole call returns an "access denied" error.
//
// If SerialNumber is set and neither TokenCode nor TokenProvider are also
// set an error will be returned.
TokenCode *string
// Async method of providing MFA token code for assuming an IAM role with MFA.
// The value returned by the function will be used as the TokenCode in the Retrieve
// call. See StdinTokenProvider for a provider that prompts and reads from stdin.
//
// This token provider will be called when ever the assumed role's
// credentials need to be refreshed when SerialNumber is also set and
// TokenCode is not set.
//
// If both TokenCode and TokenProvider is set, TokenProvider will be used and
// TokenCode is ignored.
TokenProvider func() (string, error)
// ExpiryWindow will allow the credentials to trigger refreshing prior to
// the credentials actually expiring. This is beneficial so race conditions
// with expiring credentials do not cause request to fail unexpectedly
......@@ -85,6 +201,10 @@ type AssumeRoleProvider struct {
//
// Takes a Config provider to create the STS client. The ConfigProvider is
// satisfied by the session.Session type.
//
// It is safe to share the returned Credentials with multiple Sessions and
// service clients. All access to the credentials and refreshing them
// will be synchronized.
func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
p := &AssumeRoleProvider{
Client: sts.New(c),
......@@ -103,7 +223,11 @@ func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*As
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
// role will be named after a nanosecond timestamp of this operation.
//
// Takes an AssumeRoler which can be satisfiede by the STS client.
// Takes an AssumeRoler which can be satisfied by the STS client.
//
// It is safe to share the returned Credentials with multiple Sessions and
// service clients. All access to the credentials and refreshing them
// will be synchronized.
func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
p := &AssumeRoleProvider{
Client: svc,
......@@ -139,12 +263,25 @@ func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
if p.Policy != nil {
input.Policy = p.Policy
}
if p.SerialNumber != nil && p.TokenCode != nil {
input.SerialNumber = p.SerialNumber
input.TokenCode = p.TokenCode
if p.SerialNumber != nil {
if p.TokenCode != nil {
input.SerialNumber = p.SerialNumber
input.TokenCode = p.TokenCode
} else if p.TokenProvider != nil {
input.SerialNumber = p.SerialNumber
code, err := p.TokenProvider()
if err != nil {
return credentials.Value{ProviderName: ProviderName}, err
}
input.TokenCode = aws.String(code)
} else {
return credentials.Value{ProviderName: ProviderName},
awserr.New("AssumeRoleTokenNotAvailable",
"assume role with MFA enabled, but neither TokenCode nor TokenProvider are set", nil)
}
}
roleOutput, err := p.Client.AssumeRole(input)
roleOutput, err := p.Client.AssumeRole(input)
if err != nil {
return credentials.Value{ProviderName: ProviderName}, err
}
......
......@@ -2,11 +2,15 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["defaults.go"],
srcs = [
"defaults.go",
"shared_config.go",
],
importpath = "github.com/aws/aws-sdk-go/aws/defaults",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/aws/aws-sdk-go/aws:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/awserr:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/corehandlers:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/credentials:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds:go_default_library",
......@@ -14,6 +18,7 @@ go_library(
"//vendor/github.com/aws/aws-sdk-go/aws/ec2metadata:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/endpoints:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/aws/request:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/internal/shareddefaults:go_default_library",
],
)
......
......@@ -10,10 +10,12 @@ package defaults
import (
"fmt"
"net/http"
"net/url"
"os"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
......@@ -56,7 +58,6 @@ func Config() *aws.Config {
WithMaxRetries(aws.UseServiceDefaultRetries).
WithLogger(aws.NewDefaultLogger()).
WithLogLevel(aws.LogOff).
WithSleepDelay(time.Sleep).
WithEndpointResolver(endpoints.DefaultResolver())
}
......@@ -97,23 +98,51 @@ func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credenti
})
}
// RemoteCredProvider returns a credenitials provider for the default remote
const (
httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
ecsCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
)
// RemoteCredProvider returns a credentials provider for the default remote
// endpoints such as EC2 or ECS Roles.
func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.Provider {
ecsCredURI := os.Getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")
if u := os.Getenv(httpProviderEnvVar); len(u) > 0 {
return localHTTPCredProvider(cfg, handlers, u)
}
if len(ecsCredURI) > 0 {
return ecsCredProvider(cfg, handlers, ecsCredURI)
if uri := os.Getenv(ecsCredsProviderEnvVar); len(uri) > 0 {
u := fmt.Sprintf("http://169.254.170.2%s", uri)
return httpCredProvider(cfg, handlers, u)
}
return ec2RoleProvider(cfg, handlers)
}
func ecsCredProvider(cfg aws.Config, handlers request.Handlers, uri string) credentials.Provider {
const host = `169.254.170.2`
func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
var errMsg string
parsed, err := url.Parse(u)
if err != nil {
errMsg = fmt.Sprintf("invalid URL, %v", err)
} else if host := aws.URLHostname(parsed); !(host == "localhost" || host == "127.0.0.1") {
errMsg = fmt.Sprintf("invalid host address, %q, only localhost and 127.0.0.1 are valid.", host)
}
if len(errMsg) > 0 {
if cfg.Logger != nil {
cfg.Logger.Log("Ignoring, HTTP credential provider", errMsg, err)
}
return credentials.ErrorProvider{
Err: awserr.New("CredentialsEndpointError", errMsg, err),
ProviderName: endpointcreds.ProviderName,
}
}
return httpCredProvider(cfg, handlers, u)
}
return endpointcreds.NewProviderClient(cfg, handlers,
fmt.Sprintf("http://%s%s", host, uri),
func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
return endpointcreds.NewProviderClient(cfg, handlers, u,
func(p *endpointcreds.Provider) {
p.ExpiryWindow = 5 * time.Minute
},
......
package defaults
import (
"github.com/aws/aws-sdk-go/internal/shareddefaults"
)
// SharedCredentialsFilename returns the SDK's default file path
// for the shared credentials file.
//
// Builds the shared config file path based on the OS's platform.
//
// - Linux/Unix: $HOME/.aws/credentials
// - Windows: %USERPROFILE%\.aws\credentials
func SharedCredentialsFilename() string {
return shareddefaults.SharedCredentialsFilename()
}
// SharedConfigFilename returns the SDK's default file path for
// the shared config file.
//
// Builds the shared config file path based on the OS's platform.
//
// - Linux/Unix: $HOME/.aws/config
// - Windows: %USERPROFILE%\.aws\config
func SharedConfigFilename() string {
return shareddefaults.SharedConfigFilename()
}
// Package aws provides the core SDK's utilities and shared types. Use this package's
// utilities to simplify setting and reading API operations parameters.
//
// Value and Pointer Conversion Utilities
//
// This package includes a helper conversion utility for each scalar type the SDK's
// API use. These utilities make getting a pointer of the scalar, and dereferencing
// a pointer easier.
//
// Each conversion utility comes in two forms. Value to Pointer and Pointer to Value.
// The Pointer to value will safely dereference the pointer and return its value.
// If the pointer was nil, the scalar's zero value will be returned.
//
// The value to pointer functions will be named after the scalar type. So get a
// *string from a string value use the "String" function. This makes it easy to
// to get pointer of a literal string value, because getting the address of a
// literal requires assigning the value to a variable first.
//
// var strPtr *string
//
// // Without the SDK's conversion functions
// str := "my string"
// strPtr = &str
//
// // With the SDK's conversion functions
// strPtr = aws.String("my string")
//
// // Convert *string to string value
// str = aws.StringValue(strPtr)
//
// In addition to scalars the aws package also includes conversion utilities for
// map and slice for commonly types used in API parameters. The map and slice
// conversion functions use similar naming pattern as the scalar conversion
// functions.
//
// var strPtrs []*string
// var strs []string = []string{"Go", "Gophers", "Go"}
//
// // Convert []string to []*string
// strPtrs = aws.StringSlice(strs)
//
// // Convert []*string to []string
// strs = aws.StringValueSlice(strPtrs)
//
// SDK Default HTTP Client
//
// The SDK will use the http.DefaultClient if a HTTP client is not provided to
// the SDK's Session, or service client constructor. This means that if the
// http.DefaultClient is modified by other components of your application the
// modifications will be picked up by the SDK as well.
//
// In some cases this might be intended, but it is a better practice to create
// a custom HTTP Client to share explicitly through your application. You can
// configure the SDK to use the custom HTTP Client by setting the HTTPClient
// value of the SDK's Config type when creating a Session or service client.
package aws
......@@ -21,12 +21,12 @@
// partitions := resolver.(endpoints.EnumPartitions).Partitions()
//
// for _, p := range partitions {
// fmt.Println("Regions for", p.Name)
// fmt.Println("Regions for", p.ID())
// for id, _ := range p.Regions() {
// fmt.Println("*", id)
// }
//
// fmt.Println("Services for", p.Name)
// fmt.Println("Services for", p.ID())
// for id, _ := range p.Services() {
// fmt.Println("*", id)
// }
......
......@@ -79,7 +79,9 @@ func (p partition) EndpointFor(service, region string, opts ...func(*Options)) (
opt.Set(opts...)
s, hasService := p.Services[service]
if !hasService {
if !(hasService || opt.ResolveUnknownService) {
// Only return error if the resolver will not fallback to creating
// endpoint based on service endpoint ID passed in.
return resolved, NewUnknownServiceError(p.ID, service, serviceList(p.Services))
}
......
......@@ -158,7 +158,7 @@ var funcMap = template.FuncMap{
const v3Tmpl = `
{{ define "defaults" -}}
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
// Code generated by aws/endpoints/v3model_codegen.go. DO NOT EDIT.
package endpoints
......@@ -209,17 +209,20 @@ import (
// DefaultResolver returns an Endpoint resolver that will be able
// to resolve endpoints for: {{ ListPartitionNames . }}.
//
// Casting the return value of this func to a EnumPartitions will
// allow you to get a list of the partitions in the order the endpoints
// will be resolved in.
// Use DefaultPartitions() to get the list of the default partitions.
func DefaultResolver() Resolver {
return defaultPartitions
}
// DefaultPartitions returns a list of the partitions the SDK is bundled
// with. The available partitions are: {{ ListPartitionNames . }}.
//
// resolver := endpoints.DefaultResolver()
// partitions := resolver.(endpoints.EnumPartitions).Partitions()
// partitions := endpoints.DefaultPartitions
// for _, p := range partitions {
// // ... inspect partitions
// }
func DefaultResolver() Resolver {
return defaultPartitions
func DefaultPartitions() []Partition {
return defaultPartitions.Partitions()
}
var defaultPartitions = partitions{
......
package aws
// JSONValue is a representation of a grab bag type that will be marshaled
// into a json string. This type can be used just like any other map.
//
// Example:
//
// values := aws.JSONValue{
// "Foo": "Bar",
// }
// values["Baz"] = "Qux"
type JSONValue map[string]interface{}
......@@ -26,14 +26,14 @@ func (l *LogLevelType) Value() LogLevelType {
// Matches returns true if the v LogLevel is enabled by this LogLevel. Should be
// used with logging sub levels. Is safe to use on nil value LogLevelTypes. If
// LogLevel is nill, will default to LogOff comparison.
// LogLevel is nil, will default to LogOff comparison.
func (l *LogLevelType) Matches(v LogLevelType) bool {
c := l.Value()
return c&v == v
}
// AtLeast returns true if this LogLevel is at least high enough to satisfies v.
// Is safe to use on nil value LogLevelTypes. If LogLevel is nill, will default
// Is safe to use on nil value LogLevelTypes. If LogLevel is nil, will default
// to LogOff comparison.
func (l *LogLevelType) AtLeast(v LogLevelType) bool {
c := l.Value()
......
......@@ -3,15 +3,20 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"connection_reset_error.go",
"handlers.go",
"http_request.go",
"offset_reader.go",
"request.go",
"request_1_7.go",
"request_1_8.go",
"request_context.go",
"request_context_1_6.go",
"request_pagination.go",
"retryer.go",
"timeout_read_closer.go",
"validation.go",
"waiter.go",
],
importpath = "github.com/aws/aws-sdk-go/aws/request",
visibility = ["//visibility:public"],
......
// +build !appengine,!plan9
package request
import (
"net"
"os"
"syscall"
)
func isErrConnectionReset(err error) bool {
if opErr, ok := err.(*net.OpError); ok {
if sysErr, ok := opErr.Err.(*os.SyscallError); ok {
return sysErr.Err == syscall.ECONNRESET
}
}
return false
}
// +build appengine plan9
package request
import (
"strings"
)
func isErrConnectionReset(err error) bool {
return strings.Contains(err.Error(), "connection reset")
}
......@@ -18,6 +18,7 @@ type Handlers struct {
UnmarshalError HandlerList
Retry HandlerList
AfterRetry HandlerList
Complete HandlerList
}
// Copy returns of this handler's lists.
......@@ -33,6 +34,7 @@ func (h *Handlers) Copy() Handlers {
UnmarshalMeta: h.UnmarshalMeta.copy(),
Retry: h.Retry.copy(),
AfterRetry: h.AfterRetry.copy(),
Complete: h.Complete.copy(),
}
}
......@@ -48,6 +50,7 @@ func (h *Handlers) Clear() {
h.ValidateResponse.Clear()
h.Retry.Clear()
h.AfterRetry.Clear()
h.Complete.Clear()
}
// A HandlerListRunItem represents an entry in the HandlerList which
......@@ -85,13 +88,17 @@ func (l *HandlerList) copy() HandlerList {
n := HandlerList{
AfterEachFn: l.AfterEachFn,
}
n.list = append([]NamedHandler{}, l.list...)
if len(l.list) == 0 {
return n
}
n.list = append(make([]NamedHandler, 0, len(l.list)), l.list...)
return n
}
// Clear clears the handler list.
func (l *HandlerList) Clear() {
l.list = []NamedHandler{}
l.list = l.list[0:0]
}
// Len returns the number of handlers in the list.
......@@ -101,33 +108,85 @@ func (l *HandlerList) Len() int {
// PushBack pushes handler f to the back of the handler list.
func (l *HandlerList) PushBack(f func(*Request)) {
l.list = append(l.list, NamedHandler{"__anonymous", f})
}
// PushFront pushes handler f to the front of the handler list.
func (l *HandlerList) PushFront(f func(*Request)) {
l.list = append([]NamedHandler{{"__anonymous", f}}, l.list...)
l.PushBackNamed(NamedHandler{"__anonymous", f})
}
// PushBackNamed pushes named handler f to the back of the handler list.
func (l *HandlerList) PushBackNamed(n NamedHandler) {
if cap(l.list) == 0 {
l.list = make([]NamedHandler, 0, 5)
}
l.list = append(l.list, n)
}
// PushFront pushes handler f to the front of the handler list.
func (l *HandlerList) PushFront(f func(*Request)) {
l.PushFrontNamed(NamedHandler{"__anonymous", f})
}
// PushFrontNamed pushes named handler f to the front of the handler list.
func (l *HandlerList) PushFrontNamed(n NamedHandler) {
l.list = append([]NamedHandler{n}, l.list...)
if cap(l.list) == len(l.list) {
// Allocating new list required
l.list = append([]NamedHandler{n}, l.list...)
} else {
// Enough room to prepend into list.
l.list = append(l.list, NamedHandler{})
copy(l.list[1:], l.list)
l.list[0] = n
}
}
// Remove removes a NamedHandler n
func (l *HandlerList) Remove(n NamedHandler) {
newlist := []NamedHandler{}
for _, m := range l.list {
if m.Name != n.Name {
newlist = append(newlist, m)
l.RemoveByName(n.Name)
}
// RemoveByName removes a NamedHandler by name.
func (l *HandlerList) RemoveByName(name string) {
for i := 0; i < len(l.list); i++ {
m := l.list[i]
if m.Name == name {
// Shift array preventing creating new arrays
copy(l.list[i:], l.list[i+1:])
l.list[len(l.list)-1] = NamedHandler{}
l.list = l.list[:len(l.list)-1]
// decrement list so next check to length is correct
i--
}
}
l.list = newlist
}
// SwapNamed will swap out any existing handlers with the same name as the
// passed in NamedHandler returning true if handlers were swapped. False is
// returned otherwise.
func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) {
for i := 0; i < len(l.list); i++ {
if l.list[i].Name == n.Name {
l.list[i].Fn = n.Fn
swapped = true
}
}
return swapped
}
// SetBackNamed will replace the named handler if it exists in the handler list.
// If the handler does not exist the handler will be added to the end of the list.
func (l *HandlerList) SetBackNamed(n NamedHandler) {
if !l.SwapNamed(n) {
l.PushBackNamed(n)
}
}
// SetFrontNamed will replace the named handler if it exists in the handler list.
// If the handler does not exist the handler will be added to the beginning of
// the list.
func (l *HandlerList) SetFrontNamed(n NamedHandler) {
if !l.SwapNamed(n) {
l.PushFrontNamed(n)
}
}
// Run executes all handlers in the list with a given request object.
......@@ -163,6 +222,16 @@ func HandlerListStopOnError(item HandlerListRunItem) bool {
return item.Request.Error == nil
}
// WithAppendUserAgent will add a string to the user agent prefixed with a
// single white space.
func WithAppendUserAgent(s string) Option {
return func(r *Request) {
r.Handlers.Build.PushBack(func(r2 *Request) {
AddToUserAgent(r, s)
})
}
}
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
// header. If the extra parameters are provided they will be added as metadata to the
// name/version pair resulting in the following format.
......
......@@ -16,6 +16,24 @@ func (noBody) Read([]byte) (int, error) { return 0, io.EOF }
func (noBody) Close() error { return nil }
func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil }
// Is an empty reader that will trigger the Go HTTP client to not include
// NoBody is an empty reader that will trigger the Go HTTP client to not include
// and body in the HTTP request.
var noBodyReader = noBody{}
var NoBody = noBody{}
// ResetBody rewinds the request body back to its starting position, and
// set's the HTTP Request body reference. When the body is read prior
// to being sent in the HTTP request it will need to be rewound.
//
// ResetBody will automatically be called by the SDK's build handler, but if
// the request is being used directly ResetBody must be called before the request
// is Sent. SetStringBody, SetBufferBody, and SetReaderBody will automatically
// call ResetBody.
func (r *Request) ResetBody() {
body, err := r.getNextRequestBody()
if err != nil {
r.Error = err
return
}
r.HTTPRequest.Body = body
}
......@@ -2,8 +2,32 @@
package request
import "net/http"
import (
"net/http"
)
// Is a http.NoBody reader instructing Go HTTP client to not include
// NoBody is a http.NoBody reader instructing Go HTTP client to not include
// and body in the HTTP request.
var noBodyReader = http.NoBody
var NoBody = http.NoBody
// ResetBody rewinds the request body back to its starting position, and
// set's the HTTP Request body reference. When the body is read prior
// to being sent in the HTTP request it will need to be rewound.
//
// ResetBody will automatically be called by the SDK's build handler, but if
// the request is being used directly ResetBody must be called before the request
// is Sent. SetStringBody, SetBufferBody, and SetReaderBody will automatically
// call ResetBody.
//
// Will also set the Go 1.8's http.Request.GetBody member to allow retrying
// PUT/POST redirects.
func (r *Request) ResetBody() {
body, err := r.getNextRequestBody()
if err != nil {
r.Error = err
return
}
r.HTTPRequest.Body = body
r.HTTPRequest.GetBody = r.getNextRequestBody
}
// +build go1.7
package request
import "github.com/aws/aws-sdk-go/aws"
// setContext updates the Request to use the passed in context for cancellation.
// Context will also be used for request retry delay.
//
// Creates shallow copy of the http.Request with the WithContext method.
func setRequestContext(r *Request, ctx aws.Context) {
r.context = ctx
r.HTTPRequest = r.HTTPRequest.WithContext(ctx)
}
// +build !go1.7
package request
import "github.com/aws/aws-sdk-go/aws"
// setContext updates the Request to use the passed in context for cancellation.
// Context will also be used for request retry delay.
//
// Creates shallow copy of the http.Request with the WithContext method.
func setRequestContext(r *Request, ctx aws.Context) {
r.context = ctx
r.HTTPRequest.Cancel = ctx.Done()
}
......@@ -2,29 +2,125 @@ package request
import (
"reflect"
"sync/atomic"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
)
//type Paginater interface {
// HasNextPage() bool
// NextPage() *Request
// EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error
//}
// A Pagination provides paginating of SDK API operations which are paginatable.
// Generally you should not use this type directly, but use the "Pages" API
// operations method to automatically perform pagination for you. Such as,
// "S3.ListObjectsPages", and "S3.ListObjectsPagesWithContext" methods.
//
// Pagination differs from a Paginator type in that pagination is the type that
// does the pagination between API operations, and Paginator defines the
// configuration that will be used per page request.
//
// cont := true
// for p.Next() && cont {
// data := p.Page().(*s3.ListObjectsOutput)
// // process the page's data
// }
// return p.Err()
//
// See service client API operation Pages methods for examples how the SDK will
// use the Pagination type.
type Pagination struct {
// Function to return a Request value for each pagination request.
// Any configuration or handlers that need to be applied to the request
// prior to getting the next page should be done here before the request
// returned.
//
// NewRequest should always be built from the same API operations. It is
// undefined if different API operations are returned on subsequent calls.
NewRequest func() (*Request, error)
// HasNextPage returns true if this request has more pages of data available.
func (r *Request) HasNextPage() bool {
return len(r.nextPageTokens()) > 0
started bool
nextTokens []interface{}
err error
curPage interface{}
}
// nextPageTokens returns the tokens to use when asking for the next page of
// data.
// HasNextPage will return true if Pagination is able to determine that the API
// operation has additional pages. False will be returned if there are no more
// pages remaining.
//
// Will always return true if Next has not been called yet.
func (p *Pagination) HasNextPage() bool {
return !(p.started && len(p.nextTokens) == 0)
}
// Err returns the error Pagination encountered when retrieving the next page.
func (p *Pagination) Err() error {
return p.err
}
// Page returns the current page. Page should only be called after a successful
// call to Next. It is undefined what Page will return if Page is called after
// Next returns false.
func (p *Pagination) Page() interface{} {
return p.curPage
}
// Next will attempt to retrieve the next page for the API operation. When a page
// is retrieved true will be returned. If the page cannot be retrieved, or there
// are no more pages false will be returned.
//
// Use the Page method to retrieve the current page data. The data will need
// to be cast to the API operation's output type.
//
// Use the Err method to determine if an error occurred if Page returns false.
func (p *Pagination) Next() bool {
if !p.HasNextPage() {
return false
}
req, err := p.NewRequest()
if err != nil {
p.err = err
return false
}
if p.started {
for i, intok := range req.Operation.InputTokens {
awsutil.SetValueAtPath(req.Params, intok, p.nextTokens[i])
}
}
p.started = true
err = req.Send()
if err != nil {
p.err = err
return false
}
p.nextTokens = req.nextPageTokens()
p.curPage = req.Data
return true
}
// A Paginator is the configuration data that defines how an API operation
// should be paginated. This type is used by the API service models to define
// the generated pagination config for service APIs.
//
// The Pagination type is what provides iterating between pages of an API. It
// is only used to store the token metadata the SDK should use for performing
// pagination.
type Paginator struct {
InputTokens []string
OutputTokens []string
LimitToken string
TruncationToken string
}
// nextPageTokens returns the tokens to use when asking for the next page of data.
func (r *Request) nextPageTokens() []interface{} {
if r.Operation.Paginator == nil {
return nil
}
if r.Operation.TruncationToken != "" {
tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken)
if len(tr) == 0 {
......@@ -61,9 +157,40 @@ func (r *Request) nextPageTokens() []interface{} {
return tokens
}
// Ensure a deprecated item is only logged once instead of each time its used.
func logDeprecatedf(logger aws.Logger, flag *int32, msg string) {
if logger == nil {
return
}
if atomic.CompareAndSwapInt32(flag, 0, 1) {
logger.Log(msg)
}
}
var (
logDeprecatedHasNextPage int32
logDeprecatedNextPage int32
logDeprecatedEachPage int32
)
// HasNextPage returns true if this request has more pages of data available.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) HasNextPage() bool {
logDeprecatedf(r.Config.Logger, &logDeprecatedHasNextPage,
"Request.HasNextPage deprecated. Use Pagination type for configurable pagination of API operations")
return len(r.nextPageTokens()) > 0
}
// NextPage returns a new Request that can be executed to return the next
// page of result data. Call .Send() on this request to execute it.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) NextPage() *Request {
logDeprecatedf(r.Config.Logger, &logDeprecatedNextPage,
"Request.NextPage deprecated. Use Pagination type for configurable pagination of API operations")
tokens := r.nextPageTokens()
if len(tokens) == 0 {
return nil
......@@ -90,7 +217,12 @@ func (r *Request) NextPage() *Request {
// as the structure "T". The lastPage value represents whether the page is
// the last page of data or not. The return value of this function should
// return true to keep iterating or false to stop.
//
// Deprecated Use Pagination type for configurable pagination of API operations
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
logDeprecatedf(r.Config.Logger, &logDeprecatedEachPage,
"Request.EachPage deprecated. Use Pagination type for configurable pagination of API operations")
for page := r; page != nil; page = page.NextPage() {
if err := page.Send(); err != nil {
return err
......
......@@ -8,7 +8,7 @@ import (
)
// Retryer is an interface to control retry logic for a given service.
// The default implementation used by most services is the service.DefaultRetryer
// The default implementation used by most services is the client.DefaultRetryer
// structure, which contains basic retry logic using exponential backoff.
type Retryer interface {
RetryRules(*Request) time.Duration
......@@ -26,8 +26,10 @@ func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
// retryableCodes is a collection of service response codes which are retry-able
// without any further action.
var retryableCodes = map[string]struct{}{
"RequestError": {},
"RequestTimeout": {},
"RequestError": {},
"RequestTimeout": {},
ErrCodeResponseTimeout: {},
"RequestTimeoutException": {}, // Glacier's flavor of RequestTimeout
}
var throttleCodes = map[string]struct{}{
......@@ -36,7 +38,6 @@ var throttleCodes = map[string]struct{}{
"ThrottlingException": {},
"RequestLimitExceeded": {},
"RequestThrottled": {},
"LimitExceededException": {}, // Deleting 10+ DynamoDb tables at once
"TooManyRequestsException": {}, // Lambda functions
"PriorRequestNotComplete": {}, // Route53
}
......@@ -68,35 +69,93 @@ func isCodeExpiredCreds(code string) bool {
return ok
}
var validParentCodes = map[string]struct{}{
ErrCodeSerialization: {},
ErrCodeRead: {},
}
type temporaryError interface {
Temporary() bool
}
func isNestedErrorRetryable(parentErr awserr.Error) bool {
if parentErr == nil {
return false
}
if _, ok := validParentCodes[parentErr.Code()]; !ok {
return false
}
err := parentErr.OrigErr()
if err == nil {
return false
}
if aerr, ok := err.(awserr.Error); ok {
return isCodeRetryable(aerr.Code())
}
if t, ok := err.(temporaryError); ok {
return t.Temporary()
}
return isErrConnectionReset(err)
}
// IsErrorRetryable returns whether the error is retryable, based on its Code.
// Returns false if the request has no Error set.
func (r *Request) IsErrorRetryable() bool {
if r.Error != nil {
if err, ok := r.Error.(awserr.Error); ok {
return isCodeRetryable(err.Code())
// Returns false if error is nil.
func IsErrorRetryable(err error) bool {
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
return isCodeRetryable(aerr.Code()) || isNestedErrorRetryable(aerr)
}
}
return false
}
// IsErrorThrottle returns whether the error is to be throttled based on its code.
// Returns false if the request has no Error set
func (r *Request) IsErrorThrottle() bool {
if r.Error != nil {
if err, ok := r.Error.(awserr.Error); ok {
return isCodeThrottle(err.Code())
// Returns false if error is nil.
func IsErrorThrottle(err error) bool {
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
return isCodeThrottle(aerr.Code())
}
}
return false
}
// IsErrorExpired returns whether the error code is a credential expiry error.
// Returns false if the request has no Error set.
func (r *Request) IsErrorExpired() bool {
if r.Error != nil {
if err, ok := r.Error.(awserr.Error); ok {
return isCodeExpiredCreds(err.Code())
// IsErrorExpiredCreds returns whether the error code is a credential expiry error.
// Returns false if error is nil.
func IsErrorExpiredCreds(err error) bool {
if err != nil {
if aerr, ok := err.(awserr.Error); ok {
return isCodeExpiredCreds(aerr.Code())
}
}
return false
}
// IsErrorRetryable returns whether the error is retryable, based on its Code.
// Returns false if the request has no Error set.
//
// Alias for the utility function IsErrorRetryable
func (r *Request) IsErrorRetryable() bool {
return IsErrorRetryable(r.Error)
}
// IsErrorThrottle returns whether the error is to be throttled based on its code.
// Returns false if the request has no Error set
//
// Alias for the utility function IsErrorThrottle
func (r *Request) IsErrorThrottle() bool {
return IsErrorThrottle(r.Error)
}
// IsErrorExpired returns whether the error code is a credential expiry error.
// Returns false if the request has no Error set.
//
// Alias for the utility function IsErrorExpiredCreds
func (r *Request) IsErrorExpired() bool {
return IsErrorExpiredCreds(r.Error)
}
package request
import (
"io"
"time"
"github.com/aws/aws-sdk-go/aws/awserr"
)
var timeoutErr = awserr.New(
ErrCodeResponseTimeout,
"read on body has reached the timeout limit",
nil,
)
type readResult struct {
n int
err error
}
// timeoutReadCloser will handle body reads that take too long.
// We will return a ErrReadTimeout error if a timeout occurs.
type timeoutReadCloser struct {
reader io.ReadCloser
duration time.Duration
}
// Read will spin off a goroutine to call the reader's Read method. We will
// select on the timer's channel or the read's channel. Whoever completes first
// will be returned.
func (r *timeoutReadCloser) Read(b []byte) (int, error) {
timer := time.NewTimer(r.duration)
c := make(chan readResult, 1)
go func() {
n, err := r.reader.Read(b)
timer.Stop()
c <- readResult{n: n, err: err}
}()
select {
case data := <-c:
return data.n, data.err
case <-timer.C:
return 0, timeoutErr
}
}
func (r *timeoutReadCloser) Close() error {
return r.reader.Close()
}
const (
// HandlerResponseTimeout is what we use to signify the name of the
// response timeout handler.
HandlerResponseTimeout = "ResponseTimeoutHandler"
)
// adaptToResponseTimeoutError is a handler that will replace any top level error
// to a ErrCodeResponseTimeout, if its child is that.
func adaptToResponseTimeoutError(req *Request) {
if err, ok := req.Error.(awserr.Error); ok {
aerr, ok := err.OrigErr().(awserr.Error)
if ok && aerr.Code() == ErrCodeResponseTimeout {
req.Error = aerr
}
}
}
// WithResponseReadTimeout is a request option that will wrap the body in a timeout read closer.
// This will allow for per read timeouts. If a timeout occurred, we will return the
// ErrCodeResponseTimeout.
//
// svc.PutObjectWithContext(ctx, params, request.WithTimeoutReadCloser(30 * time.Second)
func WithResponseReadTimeout(duration time.Duration) Option {
return func(r *Request) {
var timeoutHandler = NamedHandler{
HandlerResponseTimeout,
func(req *Request) {
req.HTTPResponse.Body = &timeoutReadCloser{
reader: req.HTTPResponse.Body,
duration: duration,
}
}}
// remove the handler so we are not stomping over any new durations.
r.Handlers.Send.RemoveByName(HandlerResponseTimeout)
r.Handlers.Send.PushBackNamed(timeoutHandler)
r.Handlers.Unmarshal.PushBack(adaptToResponseTimeoutError)
r.Handlers.UnmarshalError.PushBack(adaptToResponseTimeoutError)
}
}
......@@ -220,7 +220,7 @@ type ErrParamMinLen struct {
func NewErrParamMinLen(field string, min int) *ErrParamMinLen {
return &ErrParamMinLen{
errInvalidParam: errInvalidParam{
code: ParamMinValueErrCode,
code: ParamMinLenErrCode,
field: field,
msg: fmt.Sprintf("minimum field size of %v", min),
},
......
package request
import (
"fmt"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
)
// WaiterResourceNotReadyErrorCode is the error code returned by a waiter when
// the waiter's max attempts have been exhausted.
const WaiterResourceNotReadyErrorCode = "ResourceNotReady"
// A WaiterOption is a function that will update the Waiter value's fields to
// configure the waiter.
type WaiterOption func(*Waiter)
// WithWaiterMaxAttempts returns the maximum number of times the waiter should
// attempt to check the resource for the target state.
func WithWaiterMaxAttempts(max int) WaiterOption {
return func(w *Waiter) {
w.MaxAttempts = max
}
}
// WaiterDelay will return a delay the waiter should pause between attempts to
// check the resource state. The passed in attempt is the number of times the
// Waiter has checked the resource state.
//
// Attempt is the number of attempts the Waiter has made checking the resource
// state.
type WaiterDelay func(attempt int) time.Duration
// ConstantWaiterDelay returns a WaiterDelay that will always return a constant
// delay the waiter should use between attempts. It ignores the number of
// attempts made.
func ConstantWaiterDelay(delay time.Duration) WaiterDelay {
return func(attempt int) time.Duration {
return delay
}
}
// WithWaiterDelay will set the Waiter to use the WaiterDelay passed in.
func WithWaiterDelay(delayer WaiterDelay) WaiterOption {
return func(w *Waiter) {
w.Delay = delayer
}
}
// WithWaiterLogger returns a waiter option to set the logger a waiter
// should use to log warnings and errors to.
func WithWaiterLogger(logger aws.Logger) WaiterOption {
return func(w *Waiter) {
w.Logger = logger
}
}
// WithWaiterRequestOptions returns a waiter option setting the request
// options for each request the waiter makes. Appends to waiter's request
// options already set.
func WithWaiterRequestOptions(opts ...Option) WaiterOption {
return func(w *Waiter) {
w.RequestOptions = append(w.RequestOptions, opts...)
}
}
// A Waiter provides the functionality to perform a blocking call which will
// wait for a resource state to be satisfied by a service.
//
// This type should not be used directly. The API operations provided in the
// service packages prefixed with "WaitUntil" should be used instead.
type Waiter struct {
Name string
Acceptors []WaiterAcceptor
Logger aws.Logger
MaxAttempts int
Delay WaiterDelay
RequestOptions []Option
NewRequest func([]Option) (*Request, error)
SleepWithContext func(aws.Context, time.Duration) error
}
// ApplyOptions updates the waiter with the list of waiter options provided.
func (w *Waiter) ApplyOptions(opts ...WaiterOption) {
for _, fn := range opts {
fn(w)
}
}
// WaiterState are states the waiter uses based on WaiterAcceptor definitions
// to identify if the resource state the waiter is waiting on has occurred.
type WaiterState int
// String returns the string representation of the waiter state.
func (s WaiterState) String() string {
switch s {
case SuccessWaiterState:
return "success"
case FailureWaiterState:
return "failure"
case RetryWaiterState:
return "retry"
default:
return "unknown waiter state"
}
}
// States the waiter acceptors will use to identify target resource states.
const (
SuccessWaiterState WaiterState = iota // waiter successful
FailureWaiterState // waiter failed
RetryWaiterState // waiter needs to be retried
)
// WaiterMatchMode is the mode that the waiter will use to match the WaiterAcceptor
// definition's Expected attribute.
type WaiterMatchMode int
// Modes the waiter will use when inspecting API response to identify target
// resource states.
const (
PathAllWaiterMatch WaiterMatchMode = iota // match on all paths
PathWaiterMatch // match on specific path
PathAnyWaiterMatch // match on any path
PathListWaiterMatch // match on list of paths
StatusWaiterMatch // match on status code
ErrorWaiterMatch // match on error
)
// String returns the string representation of the waiter match mode.
func (m WaiterMatchMode) String() string {
switch m {
case PathAllWaiterMatch:
return "pathAll"
case PathWaiterMatch:
return "path"
case PathAnyWaiterMatch:
return "pathAny"
case PathListWaiterMatch:
return "pathList"
case StatusWaiterMatch:
return "status"
case ErrorWaiterMatch:
return "error"
default:
return "unknown waiter match mode"
}
}
// WaitWithContext will make requests for the API operation using NewRequest to
// build API requests. The request's response will be compared against the
// Waiter's Acceptors to determine the successful state of the resource the
// waiter is inspecting.
//
// The passed in context must not be nil. If it is nil a panic will occur. The
// Context will be used to cancel the waiter's pending requests and retry delays.
// Use aws.BackgroundContext if no context is available.
//
// The waiter will continue until the target state defined by the Acceptors,
// or the max attempts expires.
//
// Will return the WaiterResourceNotReadyErrorCode error code if the waiter's
// retryer ShouldRetry returns false. This normally will happen when the max
// wait attempts expires.
func (w Waiter) WaitWithContext(ctx aws.Context) error {
for attempt := 1; ; attempt++ {
req, err := w.NewRequest(w.RequestOptions)
if err != nil {
waiterLogf(w.Logger, "unable to create request %v", err)
return err
}
req.Handlers.Build.PushBack(MakeAddToUserAgentFreeFormHandler("Waiter"))
err = req.Send()
// See if any of the acceptors match the request's response, or error
for _, a := range w.Acceptors {
if matched, matchErr := a.match(w.Name, w.Logger, req, err); matched {
return matchErr
}
}
// The Waiter should only check the resource state MaxAttempts times
// This is here instead of in the for loop above to prevent delaying
// unnecessary when the waiter will not retry.
if attempt == w.MaxAttempts {
break
}
// Delay to wait before inspecting the resource again
delay := w.Delay(attempt)
if sleepFn := req.Config.SleepDelay; sleepFn != nil {
// Support SleepDelay for backwards compatibility and testing
sleepFn(delay)
} else {
sleepCtxFn := w.SleepWithContext
if sleepCtxFn == nil {
sleepCtxFn = aws.SleepWithContext
}
if err := sleepCtxFn(ctx, delay); err != nil {
return awserr.New(CanceledErrorCode, "waiter context canceled", err)
}
}
}
return awserr.New(WaiterResourceNotReadyErrorCode, "exceeded wait attempts", nil)
}
// A WaiterAcceptor provides the information needed to wait for an API operation
// to complete.
type WaiterAcceptor struct {
State WaiterState
Matcher WaiterMatchMode
Argument string
Expected interface{}
}
// match returns if the acceptor found a match with the passed in request
// or error. True is returned if the acceptor made a match, error is returned
// if there was an error attempting to perform the match.
func (a *WaiterAcceptor) match(name string, l aws.Logger, req *Request, err error) (bool, error) {
result := false
var vals []interface{}
switch a.Matcher {
case PathAllWaiterMatch, PathWaiterMatch:
// Require all matches to be equal for result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
if len(vals) == 0 {
break
}
result = true
for _, val := range vals {
if !awsutil.DeepEqual(val, a.Expected) {
result = false
break
}
}
case PathAnyWaiterMatch:
// Only a single match needs to equal for the result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
case PathListWaiterMatch:
// ignored matcher
case StatusWaiterMatch:
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case ErrorWaiterMatch:
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
}
default:
waiterLogf(l, "WARNING: Waiter %s encountered unexpected matcher: %s",
name, a.Matcher)
}
if !result {
// If there was no matching result found there is nothing more to do
// for this response, retry the request.
return false, nil
}
switch a.State {
case SuccessWaiterState:
// waiter completed
return true, nil
case FailureWaiterState:
// Waiter failure state triggered
return true, awserr.New(WaiterResourceNotReadyErrorCode,
"failed waiting for successful resource state", err)
case RetryWaiterState:
// clear the error and retry the operation
return false, nil
default:
waiterLogf(l, "WARNING: Waiter %s encountered unexpected state: %s",
name, a.State)
return false, nil
}
}
func waiterLogf(logger aws.Logger, msg string, args ...interface{}) {
if logger != nil {
logger.Log(fmt.Sprintf(msg, args...))
}
}
......@@ -23,7 +23,7 @@ additional config if the AWS_SDK_LOAD_CONFIG environment variable is set.
Alternatively you can explicitly create a Session with shared config enabled.
To do this you can use NewSessionWithOptions to configure how the Session will
be created. Using the NewSessionWithOptions with SharedConfigState set to
SharedConfigEnabled will create the session as if the AWS_SDK_LOAD_CONFIG
SharedConfigEnable will create the session as if the AWS_SDK_LOAD_CONFIG
environment variable was set.
Creating Sessions
......@@ -45,16 +45,16 @@ region, and profile loaded from the environment and shared config automatically.
Requires the AWS_PROFILE to be set, or "default" is used.
// Create Session
sess, err := session.NewSession()
sess := session.Must(session.NewSession())
// Create a Session with a custom region
sess, err := session.NewSession(&aws.Config{Region: aws.String("us-east-1")})
sess := session.Must(session.NewSession(&aws.Config{
Region: aws.String("us-east-1"),
}))
// Create a S3 client instance from a session
sess, err := session.NewSession()
if err != nil {
// Handle Session creation error
}
sess := session.Must(session.NewSession())
svc := s3.New(sess)
Create Session With Option Overrides
......@@ -67,23 +67,25 @@ Use NewSessionWithOptions when you want to provide the config profile, or
override the shared config state (AWS_SDK_LOAD_CONFIG).
// Equivalent to session.NewSession()
sess, err := session.NewSessionWithOptions(session.Options{})
sess := session.Must(session.NewSessionWithOptions(session.Options{
// Options
}))
// Specify profile to load for the session's config
sess, err := session.NewSessionWithOptions(session.Options{
sess := session.Must(session.NewSessionWithOptions(session.Options{
Profile: "profile_name",
})
}))
// Specify profile for config and region for requests
sess, err := session.NewSessionWithOptions(session.Options{
sess := session.Must(session.NewSessionWithOptions(session.Options{
Config: aws.Config{Region: aws.String("us-east-1")},
Profile: "profile_name",
})
}))
// Force enable Shared Config support
sess, err := session.NewSessionWithOptions(session.Options{
SharedConfigState: SharedConfigEnable,
})
sess := session.Must(session.NewSessionWithOptions(session.Options{
SharedConfigState: session.SharedConfigEnable,
}))
Adding Handlers
......@@ -93,7 +95,8 @@ handler logs every request and its payload made by a service client:
// Create a session, and add additional handlers for all service
// clients created with the Session to inherit. Adds logging handler.
sess, err := session.NewSession()
sess := session.Must(session.NewSession())
sess.Handlers.Send.PushFront(func(r *request.Request) {
// Log every request made and its payload
logger.Println("Request: %s/%s, Payload: %s",
......@@ -121,9 +124,8 @@ file (~/.aws/config) and shared credentials file (~/.aws/credentials). Both
files have the same format.
If both config files are present the configuration from both files will be
read. The Session will be created from configuration values from the shared
credentials file (~/.aws/credentials) over those in the shared credentials
file (~/.aws/config).
read. The Session will be created from configuration values from the shared
credentials file (~/.aws/credentials) over those in the shared config file (~/.aws/config).
Credentials are the values the SDK should use for authenticating requests with
AWS Services. They arfrom a configuration file will need to include both
......@@ -138,15 +140,14 @@ the other two fields are also provided.
Assume Role values allow you to configure the SDK to assume an IAM role using
a set of credentials provided in a config file via the source_profile field.
Both "role_arn" and "source_profile" are required. The SDK does not support
assuming a role with MFA token Via the Session's constructor. You can use the
stscreds.AssumeRoleProvider credentials provider to specify custom
configuration and support for MFA.
Both "role_arn" and "source_profile" are required. The SDK supports assuming
a role with MFA token if the session option AssumeRoleTokenProvider
is set.
role_arn = arn:aws:iam::<account_number>:role/<role_name>
source_profile = profile_with_creds
external_id = 1234
mfa_serial = not supported!
mfa_serial = <serial or mfa arn>
role_session_name = session_name
Region is the region the SDK should use for looking up AWS service endpoints
......@@ -154,6 +155,37 @@ and signing requests.
region = us-east-1
Assume Role with MFA token
To create a session with support for assuming an IAM role with MFA set the
session option AssumeRoleTokenProvider to a function that will prompt for the
MFA token code when the SDK assumes the role and refreshes the role's credentials.
This allows you to configure the SDK via the shared config to assumea role
with MFA tokens.
In order for the SDK to assume a role with MFA the SharedConfigState
session option must be set to SharedConfigEnable, or AWS_SDK_LOAD_CONFIG
environment variable set.
The shared configuration instructs the SDK to assume an IAM role with MFA
when the mfa_serial configuration field is set in the shared config
(~/.aws/config) or shared credentials (~/.aws/credentials) file.
If mfa_serial is set in the configuration, the SDK will assume the role, and
the AssumeRoleTokenProvider session option is not set an an error will
be returned when creating the session.
sess := session.Must(session.NewSessionWithOptions(session.Options{
AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
}))
// Create service client value configured for credentials
// from assumed role.
svc := s3.New(sess)
To setup assume role outside of a session see the stscrds.AssumeRoleProvider
documentation.
Environment Variables
When a Session is created several environment variables can be set to adjust
......@@ -218,6 +250,24 @@ $HOME/.aws/config on Linux/Unix based systems, and
AWS_CONFIG_FILE=$HOME/my_shared_config
Path to a custom Credentials Authority (CA) bundle PEM file that the SDK
will use instead of the default system's root CA bundle. Use this only
if you want to replace the CA bundle the SDK uses for TLS requests.
AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
Enabling this option will attempt to merge the Transport into the SDK's HTTP
client. If the client's Transport is not a http.Transport an error will be
returned. If the Transport's TLS config is set this option will cause the SDK
to overwrite the Transport's TLS config's RootCAs value. If the CA bundle file
contains multiple certificates all of them will be loaded.
The Session option CustomCABundle is also available when creating sessions
to also enable this feature. CustomCABundle session option field has priority
over the AWS_CA_BUNDLE environment variable, and will be used if both are set.
Setting a custom HTTPClient in the aws.Config options will override this setting.
To use this option and custom HTTP client, the HTTP client needs to be provided
when creating the session. Not the service client.
*/
package session
......@@ -2,12 +2,14 @@ package session
import (
"os"
"path/filepath"
"strconv"
"github.com/aws/aws-sdk-go/aws/credentials"
)
// EnvProviderName provides a name of the provider when config is loaded from environment.
const EnvProviderName = "EnvConfigCredentials"
// envConfig is a collection of environment values the SDK will read
// setup config from. All environment values are optional. But some values
// such as credentials require multiple values to be complete or the values
......@@ -75,6 +77,24 @@ type envConfig struct {
//
// AWS_CONFIG_FILE=$HOME/my_shared_config
SharedConfigFile string
// Sets the path to a custom Credentials Authroity (CA) Bundle PEM file
// that the SDK will use instead of the system's root CA bundle.
// Only use this if you want to configure the SDK to use a custom set
// of CAs.
//
// Enabling this option will attempt to merge the Transport
// into the SDK's HTTP client. If the client's Transport is
// not a http.Transport an error will be returned. If the
// Transport's TLS config is set this option will cause the
// SDK to overwrite the Transport's TLS config's RootCAs value.
//
// Setting a custom HTTPClient in the aws.Config options will override this setting.
// To use this option and custom HTTP client, the HTTP client needs to be provided
// when creating the session. Not the service client.
//
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
CustomCABundle string
}
var (
......@@ -98,6 +118,12 @@ var (
"AWS_PROFILE",
"AWS_DEFAULT_PROFILE", // Only read if AWS_SDK_LOAD_CONFIG is also set
}
sharedCredsFileEnvKey = []string{
"AWS_SHARED_CREDENTIALS_FILE",
}
sharedConfigFileEnvKey = []string{
"AWS_CONFIG_FILE",
}
)
// loadEnvConfig retrieves the SDK's environment configuration.
......@@ -134,7 +160,7 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 {
cfg.Creds = credentials.Value{}
} else {
cfg.Creds.ProviderName = "EnvConfigCredentials"
cfg.Creds.ProviderName = EnvProviderName
}
regionKeys := regionEnvKeys
......@@ -147,8 +173,10 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
setFromEnvVal(&cfg.Region, regionKeys)
setFromEnvVal(&cfg.Profile, profileKeys)
cfg.SharedCredentialsFile = sharedCredentialsFilename()
cfg.SharedConfigFile = sharedConfigFilename()
setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey)
setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey)
cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
return cfg
}
......@@ -161,28 +189,3 @@ func setFromEnvVal(dst *string, keys []string) {
}
}
}
func sharedCredentialsFilename() string {
if name := os.Getenv("AWS_SHARED_CREDENTIALS_FILE"); len(name) > 0 {
return name
}
return filepath.Join(userHomeDir(), ".aws", "credentials")
}
func sharedConfigFilename() string {
if name := os.Getenv("AWS_CONFIG_FILE"); len(name) > 0 {
return name
}
return filepath.Join(userHomeDir(), ".aws", "config")
}
func userHomeDir() string {
homeDir := os.Getenv("HOME") // *nix
if len(homeDir) == 0 { // windows
homeDir = os.Getenv("USERPROFILE")
}
return homeDir
}
......@@ -113,7 +113,7 @@ func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
f, err := ini.Load(b)
if err != nil {
return nil, SharedConfigLoadError{Filename: filename}
return nil, SharedConfigLoadError{Filename: filename, Err: err}
}
files = append(files, sharedConfigFile{
......
......@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"header_rules.go",
"options.go",
"uri_path.go",
"v4.go",
],
......
package v4
// WithUnsignedPayload will enable and set the UnsignedPayload field to
// true of the signer.
func WithUnsignedPayload(v4 *Signer) {
v4.UnsignedPayload = true
}
......@@ -45,7 +45,7 @@
// If signing a request intended for HTTP2 server, and you're using Go 1.6.2
// through 1.7.4 you should use the URL.RawPath as the pre-escaped form of the
// request URL. https://github.com/golang/go/issues/16847 points to a bug in
// Go pre 1.8 that failes to make HTTP2 requests using absolute URL in the HTTP
// Go pre 1.8 that fails to make HTTP2 requests using absolute URL in the HTTP
// message. URL.Opaque generally will force Go to make requests with absolute URL.
// URL.RawPath does not do this, but RawPath must be a valid escaping of Path
// or url.EscapedPath will ignore the RawPath escaping.
......@@ -55,7 +55,6 @@
package v4
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
......@@ -194,6 +193,10 @@ type Signer struct {
// This value should only be used for testing. If it is nil the default
// time.Now will be used.
currentTimeFn func() time.Time
// UnsignedPayload will prevent signing of the payload. This will only
// work for services that have support for this.
UnsignedPayload bool
}
// NewSigner returns a Signer pointer configured with the credentials and optional
......@@ -227,6 +230,7 @@ type signingCtx struct {
isPresign bool
formattedTime string
formattedShortTime string
unsignedPayload bool
bodyDigest string
signedHeaders string
......@@ -317,6 +321,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
ServiceName: service,
Region: region,
DisableURIPathEscaping: v4.DisableURIPathEscaping,
unsignedPayload: v4.UnsignedPayload,
}
for key := range ctx.Query {
......@@ -396,7 +401,7 @@ var SignRequestHandler = request.NamedHandler{
}
// SignSDKRequest signs an AWS request with the V4 signature. This
// request handler is bested used only with the SDK's built in service client's
// request handler should only be used with the SDK's built in service client's
// API operation requests.
//
// This function should not be used on its on its own, but in conjunction with
......@@ -409,7 +414,18 @@ var SignRequestHandler = request.NamedHandler{
func SignSDKRequest(req *request.Request) {
signSDKRequestWithCurrTime(req, time.Now)
}
func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time) {
// BuildNamedHandler will build a generic handler for signing.
func BuildNamedHandler(name string, opts ...func(*Signer)) request.NamedHandler {
return request.NamedHandler{
Name: name,
Fn: func(req *request.Request) {
signSDKRequestWithCurrTime(req, time.Now, opts...)
},
}
}
func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time, opts ...func(*Signer)) {
// If the request does not need to be signed ignore the signing of the
// request if the AnonymousCredentials object is used.
if req.Config.Credentials == credentials.AnonymousCredentials {
......@@ -441,6 +457,10 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
v4.DisableRequestBodyOverwrite = true
})
for _, opt := range opts {
opt(v4)
}
signingTime := req.Time
if !req.LastSignedAt.IsZero() {
signingTime = req.LastSignedAt
......@@ -482,6 +502,8 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
ctx.buildTime() // no depends
ctx.buildCredentialString() // no depends
ctx.buildBodyDigest()
unsignedHeaders := ctx.Request.Header
if ctx.isPresign {
if !disableHeaderHoisting {
......@@ -493,7 +515,6 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
}
}
ctx.buildBodyDigest()
ctx.buildCanonicalHeaders(ignoredHeaders, unsignedHeaders)
ctx.buildCanonicalString() // depends on canon headers / signed headers
ctx.buildStringToSign() // depends on canon string
......@@ -583,14 +604,18 @@ func (ctx *signingCtx) buildCanonicalHeaders(r rule, header http.Header) {
headerValues := make([]string, len(headers))
for i, k := range headers {
if k == "host" {
headerValues[i] = "host:" + ctx.Request.URL.Host
if ctx.Request.Host != "" {
headerValues[i] = "host:" + ctx.Request.Host
} else {
headerValues[i] = "host:" + ctx.Request.URL.Host
}
} else {
headerValues[i] = k + ":" +
strings.Join(ctx.SignedHeaderVals[k], ",")
}
}
ctx.canonicalHeaders = strings.Join(stripExcessSpaces(headerValues), "\n")
stripExcessSpaces(headerValues)
ctx.canonicalHeaders = strings.Join(headerValues, "\n")
}
func (ctx *signingCtx) buildCanonicalString() {
......@@ -634,14 +659,14 @@ func (ctx *signingCtx) buildSignature() {
func (ctx *signingCtx) buildBodyDigest() {
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
if hash == "" {
if ctx.isPresign && ctx.ServiceName == "s3" {
if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
hash = "UNSIGNED-PAYLOAD"
} else if ctx.Body == nil {
hash = emptyStringSHA256
} else {
hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
}
if ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
}
}
......@@ -692,49 +717,46 @@ func makeSha256Reader(reader io.ReadSeeker) []byte {
return hash.Sum(nil)
}
const doubleSpaces = " "
var doubleSpaceBytes = []byte(doubleSpaces)
const doubleSpace = " "
func stripExcessSpaces(headerVals []string) []string {
vals := make([]string, len(headerVals))
for i, str := range headerVals {
// Trim leading and trailing spaces
trimmed := strings.TrimSpace(str)
// stripExcessSpaces will rewrite the passed in slice's string values to not
// contain muliple side-by-side spaces.
func stripExcessSpaces(vals []string) {
var j, k, l, m, spaces int
for i, str := range vals {
// Trim trailing spaces
for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
}
idx := strings.Index(trimmed, doubleSpaces)
var buf []byte
for idx > -1 {
// Multiple adjacent spaces found
if buf == nil {
// first time create the buffer
buf = []byte(trimmed)
}
// Trim leading spaces
for k = 0; k < j && str[k] == ' '; k++ {
}
str = str[k : j+1]
stripToIdx := -1
for j := idx + 1; j < len(buf); j++ {
if buf[j] != ' ' {
buf = append(buf[:idx+1], buf[j:]...)
stripToIdx = j
break
}
}
// Strip multiple spaces.
j = strings.Index(str, doubleSpace)
if j < 0 {
vals[i] = str
continue
}
if stripToIdx >= 0 {
idx = bytes.Index(buf[stripToIdx:], doubleSpaceBytes)
if idx >= 0 {
idx += stripToIdx
buf := []byte(str)
for k, m, l = j, j, len(buf); k < l; k++ {
if buf[k] == ' ' {
if spaces == 0 {
// First space.
buf[m] = buf[k]
m++
}
spaces++
} else {
idx = -1
// End of multiple spaces.
spaces = 0
buf[m] = buf[k]
m++
}
}
if buf != nil {
vals[i] = string(buf)
} else {
vals[i] = trimmed
}
vals[i] = string(buf[:m])
}
return vals
}
......@@ -114,5 +114,5 @@ func (b *WriteAtBuffer) WriteAt(p []byte, pos int64) (n int, err error) {
func (b *WriteAtBuffer) Bytes() []byte {
b.m.Lock()
defer b.m.Unlock()
return b.buf[:len(b.buf):len(b.buf)]
return b.buf
}
// +build go1.8
package aws
import "net/url"
// URLHostname will extract the Hostname without port from the URL value.
//
// Wrapper of net/url#URL.Hostname for backwards Go version compatibility.
func URLHostname(url *url.URL) string {
return url.Hostname()
}
// +build !go1.8
package aws
import (
"net/url"
"strings"
)
// URLHostname will extract the Hostname without port from the URL value.
//
// Copy of Go 1.8's net/url#URL.Hostname functionality.
func URLHostname(url *url.URL) string {
return stripPort(url.Host)
}
// stripPort is copy of Go 1.8 url#URL.Hostname functionality.
// https://golang.org/src/net/url/url.go
func stripPort(hostport string) string {
colon := strings.IndexByte(hostport, ':')
if colon == -1 {
return hostport
}
if i := strings.IndexByte(hostport, ']'); i != -1 {
return strings.TrimPrefix(hostport[:i], "[")
}
return hostport[:colon]
}
......@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
const SDKVersion = "1.6.10"
const SDKVersion = "1.12.7"
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["shared_config.go"],
importpath = "github.com/aws/aws-sdk-go/internal/shareddefaults",
visibility = ["//vendor/github.com/aws/aws-sdk-go:__subpackages__"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
package shareddefaults
import (
"os"
"path/filepath"
"runtime"
)
// SharedCredentialsFilename returns the SDK's default file path
// for the shared credentials file.
//
// Builds the shared config file path based on the OS's platform.
//
// - Linux/Unix: $HOME/.aws/credentials
// - Windows: %USERPROFILE%\.aws\credentials
func SharedCredentialsFilename() string {
return filepath.Join(UserHomeDir(), ".aws", "credentials")
}
// SharedConfigFilename returns the SDK's default file path for
// the shared config file.
//
// Builds the shared config file path based on the OS's platform.
//
// - Linux/Unix: $HOME/.aws/config
// - Windows: %USERPROFILE%\.aws\config
func SharedConfigFilename() string {
return filepath.Join(UserHomeDir(), ".aws", "config")
}
// UserHomeDir returns the home directory for the user the process is
// running under.
func UserHomeDir() string {
if runtime.GOOS == "windows" { // Windows
return os.Getenv("USERPROFILE")
}
// *nix
return os.Getenv("HOME")
}
......@@ -4,7 +4,9 @@ package jsonutil
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"math"
"reflect"
"sort"
"strconv"
......@@ -25,6 +27,7 @@ func BuildJSON(v interface{}) ([]byte, error) {
}
func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
origVal := value
value = reflect.Indirect(value)
if !value.IsValid() {
return nil
......@@ -61,7 +64,7 @@ func buildAny(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) err
case "map":
return buildMap(value, buf, tag)
default:
return buildScalar(value, buf, tag)
return buildScalar(origVal, buf, tag)
}
}
......@@ -87,6 +90,10 @@ func buildStruct(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag)
first := true
for i := 0; i < t.NumField(); i++ {
member := value.Field(i)
// This allocates the most memory.
// Additionally, we cannot skip nil fields due to
// idempotency auto filling.
field := t.Field(i)
if field.PkgPath != "" {
......@@ -182,21 +189,32 @@ func buildMap(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) err
return nil
}
func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
switch value.Kind() {
func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) error {
// prevents allocation on the heap.
scratch := [64]byte{}
switch value := reflect.Indirect(v); value.Kind() {
case reflect.String:
writeString(value.String(), buf)
case reflect.Bool:
buf.WriteString(strconv.FormatBool(value.Bool()))
if value.Bool() {
buf.WriteString("true")
} else {
buf.WriteString("false")
}
case reflect.Int64:
buf.WriteString(strconv.FormatInt(value.Int(), 10))
buf.Write(strconv.AppendInt(scratch[:0], value.Int(), 10))
case reflect.Float64:
buf.WriteString(strconv.FormatFloat(value.Float(), 'f', -1, 64))
f := value.Float()
if math.IsInf(f, 0) || math.IsNaN(f) {
return &json.UnsupportedValueError{Value: v, Str: strconv.FormatFloat(f, 'f', -1, 64)}
}
buf.Write(strconv.AppendFloat(scratch[:0], f, 'f', -1, 64))
default:
switch value.Type() {
case timeType:
converted := value.Interface().(time.Time)
buf.WriteString(strconv.FormatInt(converted.UTC().Unix(), 10))
converted := v.Interface().(*time.Time)
buf.Write(strconv.AppendInt(scratch[:0], converted.UTC().Unix(), 10))
case byteSliceType:
if !value.IsNil() {
converted := value.Interface().([]byte)
......@@ -222,27 +240,31 @@ func buildScalar(value reflect.Value, buf *bytes.Buffer, tag reflect.StructTag)
return nil
}
var hex = "0123456789abcdef"
func writeString(s string, buf *bytes.Buffer) {
buf.WriteByte('"')
for _, r := range s {
if r == '"' {
for i := 0; i < len(s); i++ {
if s[i] == '"' {
buf.WriteString(`\"`)
} else if r == '\\' {
} else if s[i] == '\\' {
buf.WriteString(`\\`)
} else if r == '\b' {
} else if s[i] == '\b' {
buf.WriteString(`\b`)
} else if r == '\f' {
} else if s[i] == '\f' {
buf.WriteString(`\f`)
} else if r == '\r' {
} else if s[i] == '\r' {
buf.WriteString(`\r`)
} else if r == '\t' {
} else if s[i] == '\t' {
buf.WriteString(`\t`)
} else if r == '\n' {
} else if s[i] == '\n' {
buf.WriteString(`\n`)
} else if r < 32 {
fmt.Fprintf(buf, "\\u%0.4x", r)
} else if s[i] < 32 {
buf.WriteString("\\u00")
buf.WriteByte(hex[s[i]>>4])
buf.WriteByte(hex[s[i]&0xF])
} else {
buf.WriteRune(r)
buf.WriteByte(s[i])
}
}
buf.WriteByte('"')
......
......@@ -80,7 +80,6 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri
continue
}
if protocol.CanSetIdempotencyToken(value.Field(i), field) {
token := protocol.GetIdempotencyToken()
elemValue = reflect.ValueOf(token)
......@@ -122,9 +121,17 @@ func (q *queryParser) parseList(v url.Values, value reflect.Value, prefix string
return nil
}
if _, ok := value.Interface().([]byte); ok {
return q.parseScalar(v, value, prefix, tag)
}
// check for unflattened list member
if !q.isEC2 && tag.Get("flattened") == "" {
prefix += ".member"
if listName := tag.Get("locationNameList"); listName == "" {
prefix += ".member"
} else {
prefix += "." + listName
}
}
for i := 0; i < value.Len(); i++ {
......
......@@ -4,6 +4,7 @@ package rest
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
......@@ -82,8 +83,12 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
if name == "" {
name = field.Name
}
if m.Kind() == reflect.Ptr {
if kind := m.Kind(); kind == reflect.Ptr {
m = m.Elem()
} else if kind == reflect.Interface {
if !m.Elem().IsValid() {
continue
}
}
if !m.IsValid() {
continue
......@@ -95,16 +100,16 @@ func buildLocationElements(r *request.Request, v reflect.Value, buildGETQuery bo
var err error
switch field.Tag.Get("location") {
case "headers": // header maps
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag.Get("locationName"))
err = buildHeaderMap(&r.HTTPRequest.Header, m, field.Tag)
case "header":
err = buildHeader(&r.HTTPRequest.Header, m, name)
err = buildHeader(&r.HTTPRequest.Header, m, name, field.Tag)
case "uri":
err = buildURI(r.HTTPRequest.URL, m, name)
err = buildURI(r.HTTPRequest.URL, m, name, field.Tag)
case "querystring":
err = buildQueryString(query, m, name)
err = buildQueryString(query, m, name, field.Tag)
default:
if buildGETQuery {
err = buildQueryString(query, m, name)
err = buildQueryString(query, m, name, field.Tag)
}
}
r.Error = err
......@@ -145,8 +150,8 @@ func buildBody(r *request.Request, v reflect.Value) {
}
}
func buildHeader(header *http.Header, v reflect.Value, name string) error {
str, err := convertType(v)
func buildHeader(header *http.Header, v reflect.Value, name string, tag reflect.StructTag) error {
str, err := convertType(v, tag)
if err == errValueNotSet {
return nil
} else if err != nil {
......@@ -158,9 +163,10 @@ func buildHeader(header *http.Header, v reflect.Value, name string) error {
return nil
}
func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error {
func buildHeaderMap(header *http.Header, v reflect.Value, tag reflect.StructTag) error {
prefix := tag.Get("locationName")
for _, key := range v.MapKeys() {
str, err := convertType(v.MapIndex(key))
str, err := convertType(v.MapIndex(key), tag)
if err == errValueNotSet {
continue
} else if err != nil {
......@@ -173,8 +179,8 @@ func buildHeaderMap(header *http.Header, v reflect.Value, prefix string) error {
return nil
}
func buildURI(u *url.URL, v reflect.Value, name string) error {
value, err := convertType(v)
func buildURI(u *url.URL, v reflect.Value, name string, tag reflect.StructTag) error {
value, err := convertType(v, tag)
if err == errValueNotSet {
return nil
} else if err != nil {
......@@ -190,7 +196,7 @@ func buildURI(u *url.URL, v reflect.Value, name string) error {
return nil
}
func buildQueryString(query url.Values, v reflect.Value, name string) error {
func buildQueryString(query url.Values, v reflect.Value, name string, tag reflect.StructTag) error {
switch value := v.Interface().(type) {
case []*string:
for _, item := range value {
......@@ -207,7 +213,7 @@ func buildQueryString(query url.Values, v reflect.Value, name string) error {
}
}
default:
str, err := convertType(v)
str, err := convertType(v, tag)
if err == errValueNotSet {
return nil
} else if err != nil {
......@@ -246,7 +252,7 @@ func EscapePath(path string, encodeSep bool) string {
return buf.String()
}
func convertType(v reflect.Value) (string, error) {
func convertType(v reflect.Value, tag reflect.StructTag) (string, error) {
v = reflect.Indirect(v)
if !v.IsValid() {
return "", errValueNotSet
......@@ -266,6 +272,16 @@ func convertType(v reflect.Value) (string, error) {
str = strconv.FormatFloat(value, 'f', -1, 64)
case time.Time:
str = value.UTC().Format(RFC822)
case aws.JSONValue:
b, err := json.Marshal(value)
if err != nil {
return "", err
}
if tag.Get("location") == "header" {
str = base64.StdEncoding.EncodeToString(b)
} else {
str = string(b)
}
default:
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
return "", err
......
......@@ -3,6 +3,7 @@ package rest
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
......@@ -12,6 +13,7 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
)
......@@ -111,7 +113,7 @@ func unmarshalLocationElements(r *request.Request, v reflect.Value) {
case "statusCode":
unmarshalStatusCode(m, r.HTTPResponse.StatusCode)
case "header":
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name))
err := unmarshalHeader(m, r.HTTPResponse.Header.Get(name), field.Tag)
if err != nil {
r.Error = awserr.New("SerializationError", "failed to decode REST response", err)
break
......@@ -158,8 +160,13 @@ func unmarshalHeaderMap(r reflect.Value, headers http.Header, prefix string) err
return nil
}
func unmarshalHeader(v reflect.Value, header string) error {
if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) error {
isJSONValue := tag.Get("type") == "jsonvalue"
if isJSONValue {
if len(header) == 0 {
return nil
}
} else if !v.IsValid() || (header == "" && v.Elem().Kind() != reflect.String) {
return nil
}
......@@ -196,6 +203,22 @@ func unmarshalHeader(v reflect.Value, header string) error {
return err
}
v.Set(reflect.ValueOf(&t))
case aws.JSONValue:
b := []byte(header)
var err error
if tag.Get("location") == "header" {
b, err = base64.StdEncoding.DecodeString(header)
if err != nil {
return err
}
}
m := aws.JSONValue{}
err = json.Unmarshal(b, &m)
if err != nil {
return err
}
v.Set(reflect.ValueOf(m))
default:
err := fmt.Errorf("Unsupported value for param %v (%s)", v.Interface(), v.Type())
return err
......
......@@ -131,7 +131,6 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
continue
}
mTag := field.Tag
if mTag.Get("location") != "" { // skip non-body members
continue
......
......@@ -15,7 +15,10 @@ import (
// needs to match the shape of the XML expected to be decoded.
// If the shape doesn't match unmarshaling will fail.
func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
n, _ := XMLToStruct(d, nil)
n, err := XMLToStruct(d, nil)
if err != nil {
return err
}
if n.Children != nil {
for _, root := range n.Children {
for _, c := range root {
......@@ -23,7 +26,7 @@ func UnmarshalXML(v interface{}, d *xml.Decoder, wrapper string) error {
c = wrappedChild[0] // pull out wrapped element
}
err := parse(reflect.ValueOf(v), c, "")
err = parse(reflect.ValueOf(v), c, "")
if err != nil {
if err == io.EOF {
return nil
......
......@@ -40,11 +40,16 @@ func XMLToStruct(d *xml.Decoder, s *xml.StartElement) (*XMLNode, error) {
out := &XMLNode{}
for {
tok, err := d.Token()
if tok == nil || err == io.EOF {
break
}
if err != nil {
return out, err
if err == io.EOF {
break
} else {
return out, err
}
}
if tok == nil {
break
}
switch typed := tok.(type) {
......
package waiter
import (
"fmt"
"reflect"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request"
)
// A Config provides a collection of configuration values to setup a generated
// waiter code with.
type Config struct {
Name string
Delay int
MaxAttempts int
Operation string
Acceptors []WaitAcceptor
}
// A WaitAcceptor provides the information needed to wait for an API operation
// to complete.
type WaitAcceptor struct {
Expected interface{}
Matcher string
State string
Argument string
}
// A Waiter provides waiting for an operation to complete.
type Waiter struct {
Config
Client interface{}
Input interface{}
}
// Wait waits for an operation to complete, expire max attempts, or fail. Error
// is returned if the operation fails.
func (w *Waiter) Wait() error {
client := reflect.ValueOf(w.Client)
in := reflect.ValueOf(w.Input)
method := client.MethodByName(w.Config.Operation + "Request")
for i := 0; i < w.MaxAttempts; i++ {
res := method.Call([]reflect.Value{in})
req := res[0].Interface().(*request.Request)
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Waiter"))
err := req.Send()
for _, a := range w.Acceptors {
result := false
var vals []interface{}
switch a.Matcher {
case "pathAll", "path":
// Require all matches to be equal for result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
if len(vals) == 0 {
break
}
result = true
for _, val := range vals {
if !awsutil.DeepEqual(val, a.Expected) {
result = false
break
}
}
case "pathAny":
// Only a single match needs to equal for the result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
case "status":
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case "error":
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
}
case "pathList":
// ignored matcher
default:
logf(client, "WARNING: Waiter for %s encountered unexpected matcher: %s",
w.Config.Operation, a.Matcher)
}
if !result {
// If there was no matching result found there is nothing more to do
// for this response, retry the request.
continue
}
switch a.State {
case "success":
// waiter completed
return nil
case "failure":
// Waiter failure state triggered
return awserr.New("ResourceNotReady",
fmt.Sprintf("failed waiting for successful resource state"), err)
case "retry":
// clear the error and retry the operation
err = nil
default:
logf(client, "WARNING: Waiter for %s encountered unexpected state: %s",
w.Config.Operation, a.State)
}
}
if err != nil {
return err
}
time.Sleep(time.Second * time.Duration(w.Delay))
}
return awserr.New("ResourceNotReady",
fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
}
func logf(client reflect.Value, msg string, args ...interface{}) {
cfgVal := client.FieldByName("Config")
if !cfgVal.IsValid() {
return
}
if cfg, ok := cfgVal.Interface().(*aws.Config); ok && cfg.Logger != nil {
cfg.Logger.Log(fmt.Sprintf(msg, args...))
}
}
......@@ -4,6 +4,8 @@ go_library(
name = "go_default_library",
srcs = [
"api.go",
"doc.go",
"errors.go",
"service.go",
"waiters.go",
],
......@@ -18,7 +20,6 @@ go_library(
"//vendor/github.com/aws/aws-sdk-go/aws/signer/v4:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/protocol:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/protocol/query:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/waiter:go_default_library",
],
)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package autoscaling provides the client and types for making API
// requests to Auto Scaling.
//
// Auto Scaling is designed to automatically launch or terminate EC2 instances
// based on user-defined policies, schedules, and health checks. Use this service
// in conjunction with the Amazon CloudWatch and Elastic Load Balancing services.
//
// See https://docs.aws.amazon.com/goto/WebAPI/autoscaling-2011-01-01 for more information on this service.
//
// See autoscaling package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/autoscaling/
//
// Using the Client
//
// To Auto Scaling with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the Auto Scaling client AutoScaling for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/autoscaling/#New
package autoscaling
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package autoscaling
const (
// ErrCodeAlreadyExistsFault for service response error code
// "AlreadyExists".
//
// You already have an Auto Scaling group or launch configuration with this
// name.
ErrCodeAlreadyExistsFault = "AlreadyExists"
// ErrCodeInvalidNextToken for service response error code
// "InvalidNextToken".
//
// The NextToken value is not valid.
ErrCodeInvalidNextToken = "InvalidNextToken"
// ErrCodeLimitExceededFault for service response error code
// "LimitExceeded".
//
// You have already reached a limit for your Auto Scaling resources (for example,
// groups, launch configurations, or lifecycle hooks). For more information,
// see DescribeAccountLimits.
ErrCodeLimitExceededFault = "LimitExceeded"
// ErrCodeResourceContentionFault for service response error code
// "ResourceContention".
//
// You already have a pending update to an Auto Scaling resource (for example,
// a group, instance, or load balancer).
ErrCodeResourceContentionFault = "ResourceContention"
// ErrCodeResourceInUseFault for service response error code
// "ResourceInUse".
//
// The operation can't be performed because the resource is in use.
ErrCodeResourceInUseFault = "ResourceInUse"
// ErrCodeScalingActivityInProgressFault for service response error code
// "ScalingActivityInProgress".
//
// The operation can't be performed because there are scaling activities in
// progress.
ErrCodeScalingActivityInProgressFault = "ScalingActivityInProgress"
)
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package autoscaling
......@@ -11,12 +11,12 @@ import (
"github.com/aws/aws-sdk-go/private/protocol/query"
)
// Auto Scaling is designed to automatically launch or terminate EC2 instances
// based on user-defined policies, schedules, and health checks. Use this service
// in conjunction with the Amazon CloudWatch and Elastic Load Balancing services.
// The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
// Please also see https://docs.aws.amazon.com/goto/WebAPI/autoscaling-2011-01-01
// AutoScaling provides the API operation methods for making requests to
// Auto Scaling. See this package's package overview docs
// for details on the service.
//
// AutoScaling methods are safe to use concurrently. It is not safe to
// modify mutate any of the struct's properties though.
type AutoScaling struct {
*client.Client
}
......
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
package autoscaling
import (
"github.com/aws/aws-sdk-go/private/waiter"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
)
// WaitUntilGroupExists uses the Auto Scaling API operation
// DescribeAutoScalingGroups to wait for a condition to be met before returning.
// If the condition is not meet within the max attempt window an error will
// If the condition is not met within the max attempt window, an error will
// be returned.
func (c *AutoScaling) WaitUntilGroupExists(input *DescribeAutoScalingGroupsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeAutoScalingGroups",
Delay: 5,
return c.WaitUntilGroupExistsWithContext(aws.BackgroundContext(), input)
}
// WaitUntilGroupExistsWithContext is an extended version of WaitUntilGroupExists.
// With the support for passing in a context and options to configure the
// Waiter and the underlying request options.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AutoScaling) WaitUntilGroupExistsWithContext(ctx aws.Context, input *DescribeAutoScalingGroupsInput, opts ...request.WaiterOption) error {
w := request.Waiter{
Name: "WaitUntilGroupExists",
MaxAttempts: 10,
Acceptors: []waiter.WaitAcceptor{
Delay: request.ConstantWaiterDelay(5 * time.Second),
Acceptors: []request.WaiterAcceptor{
{
State: "success",
Matcher: "path",
Argument: "length(AutoScalingGroups) > `0`",
State: request.SuccessWaiterState,
Matcher: request.PathWaiterMatch, Argument: "length(AutoScalingGroups) > `0`",
Expected: true,
},
{
State: "retry",
Matcher: "path",
Argument: "length(AutoScalingGroups) > `0`",
State: request.RetryWaiterState,
Matcher: request.PathWaiterMatch, Argument: "length(AutoScalingGroups) > `0`",
Expected: false,
},
},
Logger: c.Config.Logger,
NewRequest: func(opts []request.Option) (*request.Request, error) {
var inCpy *DescribeAutoScalingGroupsInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := c.DescribeAutoScalingGroupsRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
w.ApplyOptions(opts...)
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
return w.WaitWithContext(ctx)
}
// WaitUntilGroupInService uses the Auto Scaling API operation
// DescribeAutoScalingGroups to wait for a condition to be met before returning.
// If the condition is not meet within the max attempt window an error will
// If the condition is not met within the max attempt window, an error will
// be returned.
func (c *AutoScaling) WaitUntilGroupInService(input *DescribeAutoScalingGroupsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeAutoScalingGroups",
Delay: 15,
return c.WaitUntilGroupInServiceWithContext(aws.BackgroundContext(), input)
}
// WaitUntilGroupInServiceWithContext is an extended version of WaitUntilGroupInService.
// With the support for passing in a context and options to configure the
// Waiter and the underlying request options.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AutoScaling) WaitUntilGroupInServiceWithContext(ctx aws.Context, input *DescribeAutoScalingGroupsInput, opts ...request.WaiterOption) error {
w := request.Waiter{
Name: "WaitUntilGroupInService",
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
Delay: request.ConstantWaiterDelay(15 * time.Second),
Acceptors: []request.WaiterAcceptor{
{
State: "success",
Matcher: "path",
Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
State: request.SuccessWaiterState,
Matcher: request.PathWaiterMatch, Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
Expected: false,
},
{
State: "retry",
Matcher: "path",
Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
State: request.RetryWaiterState,
Matcher: request.PathWaiterMatch, Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)",
Expected: true,
},
},
Logger: c.Config.Logger,
NewRequest: func(opts []request.Option) (*request.Request, error) {
var inCpy *DescribeAutoScalingGroupsInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := c.DescribeAutoScalingGroupsRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
w.ApplyOptions(opts...)
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
return w.WaitWithContext(ctx)
}
// WaitUntilGroupNotExists uses the Auto Scaling API operation
// DescribeAutoScalingGroups to wait for a condition to be met before returning.
// If the condition is not meet within the max attempt window an error will
// If the condition is not met within the max attempt window, an error will
// be returned.
func (c *AutoScaling) WaitUntilGroupNotExists(input *DescribeAutoScalingGroupsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeAutoScalingGroups",
Delay: 15,
return c.WaitUntilGroupNotExistsWithContext(aws.BackgroundContext(), input)
}
// WaitUntilGroupNotExistsWithContext is an extended version of WaitUntilGroupNotExists.
// With the support for passing in a context and options to configure the
// Waiter and the underlying request options.
//
// The context must be non-nil and will be used for request cancellation. If
// the context is nil a panic will occur. In the future the SDK may create
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
// for more information on using Contexts.
func (c *AutoScaling) WaitUntilGroupNotExistsWithContext(ctx aws.Context, input *DescribeAutoScalingGroupsInput, opts ...request.WaiterOption) error {
w := request.Waiter{
Name: "WaitUntilGroupNotExists",
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
Delay: request.ConstantWaiterDelay(15 * time.Second),
Acceptors: []request.WaiterAcceptor{
{
State: "success",
Matcher: "path",
Argument: "length(AutoScalingGroups) > `0`",
State: request.SuccessWaiterState,
Matcher: request.PathWaiterMatch, Argument: "length(AutoScalingGroups) > `0`",
Expected: false,
},
{
State: "retry",
Matcher: "path",
Argument: "length(AutoScalingGroups) > `0`",
State: request.RetryWaiterState,
Matcher: request.PathWaiterMatch, Argument: "length(AutoScalingGroups) > `0`",
Expected: true,
},
},
Logger: c.Config.Logger,
NewRequest: func(opts []request.Option) (*request.Request, error) {
var inCpy *DescribeAutoScalingGroupsInput
if input != nil {
tmp := *input
inCpy = &tmp
}
req, _ := c.DescribeAutoScalingGroupsRequest(inCpy)
req.SetContext(ctx)
req.ApplyOptions(opts...)
return req, nil
},
}
w.ApplyOptions(opts...)
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
return w.WaitWithContext(ctx)
}
......@@ -5,6 +5,8 @@ go_library(
srcs = [
"api.go",
"customizations.go",
"doc.go",
"errors.go",
"service.go",
"waiters.go",
],
......@@ -20,7 +22,6 @@ go_library(
"//vendor/github.com/aws/aws-sdk-go/aws/signer/v4:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/protocol:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/protocol/ec2query:go_default_library",
"//vendor/github.com/aws/aws-sdk-go/private/waiter:go_default_library",
],
)
......
This source diff could not be displayed because it is too large. You can view the blob instead.
// Code generated by private/model/cli/gen-api/main.go. DO NOT EDIT.
// Package ec2 provides the client and types for making API
// requests to Amazon Elastic Compute Cloud.
//
// Amazon Elastic Compute Cloud (Amazon EC2) provides resizable computing capacity
// in the Amazon Web Services (AWS) cloud. Using Amazon EC2 eliminates your
// need to invest in hardware up front, so you can develop and deploy applications
// faster.
//
// See https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15 for more information on this service.
//
// See ec2 package documentation for more information.
// https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/
//
// Using the Client
//
// To Amazon Elastic Compute Cloud with the SDK use the New function to create
// a new service client. With that client you can make API requests to the service.
// These clients are safe to use concurrently.
//
// See the SDK's documentation for more information on how to use the SDK.
// https://docs.aws.amazon.com/sdk-for-go/api/
//
// See aws.Config documentation for more information on configuring SDK clients.
// https://docs.aws.amazon.com/sdk-for-go/api/aws/#Config
//
// See the Amazon Elastic Compute Cloud client EC2 for more
// information on creating client for this service.
// https://docs.aws.amazon.com/sdk-for-go/api/service/ec2/#New
package ec2
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment