Merge pull request #6849 from rickard-von-essen/fix-6717
WIP: amazon: Update aws-sdk-go to v1.15.54
This commit is contained in:
commit
6a28da4fb3
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
## Code of Conduct
|
||||
This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
|
||||
For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
|
||||
opensource-codeofconduct@amazon.com with any additional questions or comments.
|
|
@ -89,7 +89,7 @@ go test -tags codegen ./private/...
|
|||
See the `Makefile` for additional testing tags that can be used in testing.
|
||||
|
||||
To test on multiple platform the SDK includes several DockerFiles under the
|
||||
`awstesting/sandbox` folder, and associated make recipes to to execute
|
||||
`awstesting/sandbox` folder, and associated make recipes to execute
|
||||
unit testing within environments configured for specific Go versions.
|
||||
|
||||
```
|
||||
|
|
|
@ -4,6 +4,7 @@ LINTIGNORECONST='service/[^/]+/(api|service|waiters)\.go:.+(type|struct field|co
|
|||
LINTIGNORESTUTTER='service/[^/]+/(api|service)\.go:.+(and that stutters)'
|
||||
LINTIGNOREINFLECT='service/[^/]+/(api|errors|service)\.go:.+(method|const) .+ should be '
|
||||
LINTIGNOREINFLECTS3UPLOAD='service/s3/s3manager/upload\.go:.+struct field SSEKMSKeyId should be '
|
||||
LINTIGNOREENDPOINTS='aws/endpoints/defaults.go:.+(method|const) .+ should be '
|
||||
LINTIGNOREDEPS='vendor/.+\.go'
|
||||
LINTIGNOREPKGCOMMENT='service/[^/]+/doc_custom.go:.+package comment should be of the form'
|
||||
UNIT_TEST_TAGS="example codegen awsinclude"
|
||||
|
@ -13,6 +14,7 @@ SDK_ONLY_PKGS=$(shell go list ./... | grep -v "/vendor/")
|
|||
SDK_UNIT_TEST_ONLY_PKGS=$(shell go list -tags ${UNIT_TEST_TAGS} ./... | grep -v "/vendor/")
|
||||
SDK_GO_1_4=$(shell go version | grep "go1.4")
|
||||
SDK_GO_1_5=$(shell go version | grep "go1.5")
|
||||
SDK_GO_1_6=$(shell go version | grep "go1.6")
|
||||
SDK_GO_VERSION=$(shell go version | awk '''{print $$3}''' | tr -d '''\n''')
|
||||
|
||||
all: get-deps generate unit
|
||||
|
@ -35,9 +37,12 @@ help:
|
|||
@echo " get-deps-tests to get the SDK's test dependencies"
|
||||
@echo " get-deps-verify to get the SDK's verification dependencies"
|
||||
|
||||
generate: gen-test gen-endpoints gen-services
|
||||
generate: cleanup-models gen-test gen-endpoints gen-services
|
||||
|
||||
gen-test: gen-protocol-test
|
||||
gen-test: gen-protocol-test gen-codegen-test
|
||||
|
||||
gen-codegen-test:
|
||||
go generate ./private/model/api/codegentest/service
|
||||
|
||||
gen-services:
|
||||
go generate ./service
|
||||
|
@ -48,6 +53,10 @@ gen-protocol-test:
|
|||
gen-endpoints:
|
||||
go generate ./models/endpoints/
|
||||
|
||||
cleanup-models:
|
||||
@echo "Cleaning up stale model versions"
|
||||
@./cleanup_models.sh
|
||||
|
||||
build:
|
||||
@echo "go build SDK and vendor packages"
|
||||
@go build ${SDK_ONLY_PKGS}
|
||||
|
@ -60,10 +69,23 @@ unit-with-race-cover: get-deps-tests build verify
|
|||
@echo "go test SDK and vendor packages"
|
||||
@go test -tags ${UNIT_TEST_TAGS} -race -cpu=1,2,4 $(SDK_UNIT_TEST_ONLY_PKGS)
|
||||
|
||||
ci-test: ci-test-generate unit-with-race-cover ci-test-generate-validate
|
||||
|
||||
ci-test-generate: get-deps
|
||||
@echo "CI test generated code"
|
||||
@if [ \( -z "${SDK_GO_1_6}" \) -a \( -z "${SDK_GO_1_5}" \) ]; then make generate; else echo "skipping generate"; fi
|
||||
|
||||
ci-test-generate-validate:
|
||||
@echo "CI test validate no generated code changes"
|
||||
@git add . -A
|
||||
@gitstatus=`if [ \( -z "${SDK_GO_1_6}" \) -a \( -z "${SDK_GO_1_5}" \) ]; then git diff --cached --ignore-space-change; else echo "skipping validation"; fi`; \
|
||||
echo "$$gitstatus"; \
|
||||
if [ "$$gitstatus" != "" ] && [ "$$gitstatus" != "skipping validation" ]; then echo "$$gitstatus"; exit 1; fi
|
||||
|
||||
integration: get-deps-tests integ-custom smoke-tests performance
|
||||
|
||||
integ-custom:
|
||||
go test -tags "integration" ./awstesting/integration/customizations/...
|
||||
go test -tags "integration" -v ./awstesting/integration/customizations/...
|
||||
|
||||
cleanup-integ:
|
||||
go run -tags "integration" ./awstesting/cmd/bucket_cleanup/main.go "aws-sdk-go-integration"
|
||||
|
@ -112,12 +134,26 @@ sandbox-test-go18: sandbox-build-go18
|
|||
docker run -t aws-sdk-go-1.8
|
||||
|
||||
sandbox-build-go19:
|
||||
docker build -f ./awstesting/sandbox/Dockerfile.test.go1.8 -t "aws-sdk-go-1.9" .
|
||||
docker build -f ./awstesting/sandbox/Dockerfile.test.go1.9 -t "aws-sdk-go-1.9" .
|
||||
sandbox-go19: sandbox-build-go19
|
||||
docker run -i -t aws-sdk-go-1.9 bash
|
||||
sandbox-test-go19: sandbox-build-go19
|
||||
docker run -t aws-sdk-go-1.9
|
||||
|
||||
sandbox-build-go110:
|
||||
docker build -f ./awstesting/sandbox/Dockerfile.test.go1.10 -t "aws-sdk-go-1.10" .
|
||||
sandbox-go110: sandbox-build-go110
|
||||
docker run -i -t aws-sdk-go-1.10 bash
|
||||
sandbox-test-go110: sandbox-build-go110
|
||||
docker run -t aws-sdk-go-1.10
|
||||
|
||||
sandbox-build-go111:
|
||||
docker build -f ./awstesting/sandbox/Dockerfile.test.go1.11 -t "aws-sdk-go-1.11" .
|
||||
sandbox-go111: sandbox-build-go111
|
||||
docker run -i -t aws-sdk-go-1.11 bash
|
||||
sandbox-test-go111: sandbox-build-go111
|
||||
docker run -t aws-sdk-go-1.11
|
||||
|
||||
sandbox-build-gotip:
|
||||
@echo "Run make update-aws-golang-tip, if this test fails because missing aws-golang:tip container"
|
||||
docker build -f ./awstesting/sandbox/Dockerfile.test.gotip -t "aws-sdk-go-tip" .
|
||||
|
@ -134,7 +170,7 @@ verify: get-deps-verify lint vet
|
|||
lint:
|
||||
@echo "go lint SDK and vendor packages"
|
||||
@lint=`if [ \( -z "${SDK_GO_1_4}" \) -a \( -z "${SDK_GO_1_5}" \) ]; then golint ./...; else echo "skipping golint"; fi`; \
|
||||
lint=`echo "$$lint" | grep -E -v -e ${LINTIGNOREDOT} -e ${LINTIGNOREDOC} -e ${LINTIGNORECONST} -e ${LINTIGNORESTUTTER} -e ${LINTIGNOREINFLECT} -e ${LINTIGNOREDEPS} -e ${LINTIGNOREINFLECTS3UPLOAD} -e ${LINTIGNOREPKGCOMMENT}`; \
|
||||
lint=`echo "$$lint" | grep -E -v -e ${LINTIGNOREDOT} -e ${LINTIGNOREDOC} -e ${LINTIGNORECONST} -e ${LINTIGNORESTUTTER} -e ${LINTIGNOREINFLECT} -e ${LINTIGNOREDEPS} -e ${LINTIGNOREINFLECTS3UPLOAD} -e ${LINTIGNOREPKGCOMMENT} -e ${LINTIGNOREENDPOINTS}`; \
|
||||
echo "$$lint"; \
|
||||
if [ "$$lint" != "" ] && [ "$$lint" != "skipping golint" ]; then exit 1; fi
|
||||
|
||||
|
@ -164,7 +200,7 @@ get-deps-tests:
|
|||
|
||||
get-deps-verify:
|
||||
@echo "go get SDK verification utilities"
|
||||
@if [ \( -z "${SDK_GO_1_4}" \) -a \( -z "${SDK_GO_1_5}" \) ]; then go get github.com/golang/lint/golint; else echo "skipped getting golint"; fi
|
||||
@if [ \( -z "${SDK_GO_1_4}" \) -a \( -z "${SDK_GO_1_5}" \) ]; then go get golang.org/x/lint/golint; else echo "skipped getting golint"; fi
|
||||
|
||||
bench:
|
||||
@echo "go bench SDK packages"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[![API Reference](http://img.shields.io/badge/api-reference-blue.svg)](http://docs.aws.amazon.com/sdk-for-go/api) [![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go) [![Apache V2 License](http://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt)
|
||||
[![API Reference](https://img.shields.io/badge/api-reference-blue.svg)](https://docs.aws.amazon.com/sdk-for-go/api) [![Join the chat at https://gitter.im/aws/aws-sdk-go](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aws/aws-sdk-go?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://img.shields.io/travis/aws/aws-sdk-go.svg)](https://travis-ci.org/aws/aws-sdk-go) [![Apache V2 License](https://img.shields.io/badge/license-Apache%20V2-blue.svg)](https://github.com/aws/aws-sdk-go/blob/master/LICENSE.txt)
|
||||
|
||||
# AWS SDK for Go
|
||||
|
||||
|
@ -6,7 +6,7 @@ aws-sdk-go is the official AWS SDK for the Go programming language.
|
|||
|
||||
Checkout our [release notes](https://github.com/aws/aws-sdk-go/releases) for information about the latest bug fixes, updates, and features added to the SDK.
|
||||
|
||||
We [announced](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-2-0-developer-preview/) the Developer Preview for the [v2 AWS SDK for Go](). The v2 SDK is available at https://github.com/aws/aws-sdk-go-v2, and `go get github.com/aws/aws-sdk-go-v2` via `go get`. Check out the v2 SDK's [changes and updates](https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md), and let us know what you think. We want your feedback.
|
||||
We [announced](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-2-0-developer-preview/) the Developer Preview for the [v2 AWS SDK for Go](https://github.com/aws/aws-sdk-go-v2). The v2 SDK is available at https://github.com/aws/aws-sdk-go-v2, and `go get github.com/aws/aws-sdk-go-v2` via `go get`. Check out the v2 SDK's [changes and updates](https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md), and let us know what you think. We want your feedback.
|
||||
|
||||
## Installing
|
||||
|
||||
|
@ -334,7 +334,7 @@ take a callback function that will be called for each page of the API's response
|
|||
```
|
||||
|
||||
Waiter helper methods provide the functionality to wait for an AWS resource
|
||||
state. These methods abstract the logic needed to to check the state of an
|
||||
state. These methods abstract the logic needed to check the state of an
|
||||
AWS resource, and wait until that resource is in a desired state. The waiter
|
||||
will block until the resource is in the state that is desired, an error occurs,
|
||||
or the waiter times out. If a resource times out the error code returned will
|
||||
|
|
|
@ -15,6 +15,12 @@ type Config struct {
|
|||
Endpoint string
|
||||
SigningRegion string
|
||||
SigningName string
|
||||
|
||||
// States that the signing name did not come from a modeled source but
|
||||
// was derived based on other data. Used by service client constructors
|
||||
// to determine if the signin name can be overriden based on metadata the
|
||||
// service has.
|
||||
SigningNameDerived bool
|
||||
}
|
||||
|
||||
// ConfigProvider provides a generic way for a service client to receive
|
||||
|
@ -85,6 +91,6 @@ func (c *Client) AddDebugHandlers() {
|
|||
return
|
||||
}
|
||||
|
||||
c.Handlers.Send.PushFrontNamed(request.NamedHandler{Name: "awssdk.client.LogRequest", Fn: logRequest})
|
||||
c.Handlers.Send.PushBackNamed(request.NamedHandler{Name: "awssdk.client.LogResponse", Fn: logResponse})
|
||||
c.Handlers.Send.PushFrontNamed(LogHTTPRequestHandler)
|
||||
c.Handlers.Send.PushBackNamed(LogHTTPResponseHandler)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||
)
|
||||
|
||||
// DefaultRetryer implements basic retry logic using exponential backoff for
|
||||
|
@ -31,8 +30,6 @@ func (d DefaultRetryer) MaxRetries() int {
|
|||
return d.NumMaxRetries
|
||||
}
|
||||
|
||||
var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
|
||||
|
||||
// RetryRules returns the delay duration before retrying this request again
|
||||
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
||||
// Set the upper limit of delay in retrying at ~five minutes
|
||||
|
@ -53,7 +50,7 @@ func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
|
|||
retryCount = 13
|
||||
}
|
||||
|
||||
delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime)
|
||||
delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime)
|
||||
return time.Duration(delay) * time.Millisecond
|
||||
}
|
||||
|
||||
|
@ -65,7 +62,7 @@ func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
|
|||
return *r.Retryable
|
||||
}
|
||||
|
||||
if r.HTTPResponse.StatusCode >= 500 {
|
||||
if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 {
|
||||
return true
|
||||
}
|
||||
return r.IsErrorRetryable() || d.shouldThrottle(r)
|
||||
|
@ -117,22 +114,3 @@ func canUseRetryAfterHeader(r *request.Request) bool {
|
|||
|
||||
return true
|
||||
}
|
||||
|
||||
// lockedSource is a thread-safe implementation of rand.Source
|
||||
type lockedSource struct {
|
||||
lk sync.Mutex
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
func (r *lockedSource) Int63() (n int64) {
|
||||
r.lk.Lock()
|
||||
n = r.src.Int63()
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *lockedSource) Seed(seed int64) {
|
||||
r.lk.Lock()
|
||||
r.src.Seed(seed)
|
||||
r.lk.Unlock()
|
||||
}
|
||||
|
|
|
@ -44,22 +44,57 @@ func (reader *teeReaderCloser) Close() error {
|
|||
return reader.Source.Close()
|
||||
}
|
||||
|
||||
// LogHTTPRequestHandler is a SDK request handler to log the HTTP request sent
|
||||
// to a service. Will include the HTTP request body if the LogLevel of the
|
||||
// request matches LogDebugWithHTTPBody.
|
||||
var LogHTTPRequestHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogRequest",
|
||||
Fn: logRequest,
|
||||
}
|
||||
|
||||
func logRequest(r *request.Request) {
|
||||
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||
bodySeekable := aws.IsReaderSeekable(r.Body)
|
||||
|
||||
b, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
if logBody {
|
||||
if !bodySeekable {
|
||||
r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body))
|
||||
}
|
||||
// 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)))
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
|
||||
// LogHTTPRequestHeaderHandler is a SDK request handler to log the HTTP request sent
|
||||
// to a service. Will only log the HTTP request's headers. The request payload
|
||||
// will not be read.
|
||||
var LogHTTPRequestHeaderHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogRequestHeader",
|
||||
Fn: logRequestHeader,
|
||||
}
|
||||
|
||||
func logRequestHeader(r *request.Request) {
|
||||
b, err := httputil.DumpRequestOut(r.HTTPRequest, false)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
r.Config.Logger.Log(fmt.Sprintf(logReqMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
|
||||
const logRespMsg = `DEBUG: Response %s/%s Details:
|
||||
|
@ -72,27 +107,44 @@ const logRespErrMsg = `DEBUG ERROR: Response %s/%s:
|
|||
%s
|
||||
-----------------------------------------------------`
|
||||
|
||||
// LogHTTPResponseHandler is a SDK request handler to log the HTTP response
|
||||
// received from a service. Will include the HTTP response body if the LogLevel
|
||||
// of the request matches LogDebugWithHTTPBody.
|
||||
var LogHTTPResponseHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogResponse",
|
||||
Fn: logResponse,
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
|
||||
if logBody {
|
||||
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)
|
||||
b, err := httputil.DumpResponse(req.HTTPResponse, false)
|
||||
if err != nil {
|
||||
lw.Logger.Log(fmt.Sprintf(logRespErrMsg, req.ClientInfo.ServiceName, req.Operation.Name, err))
|
||||
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(fmt.Sprintf(logRespMsg,
|
||||
req.ClientInfo.ServiceName, req.Operation.Name, string(b)))
|
||||
|
||||
if logBody {
|
||||
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(string(b))
|
||||
}
|
||||
}
|
||||
|
@ -106,3 +158,27 @@ func logResponse(r *request.Request) {
|
|||
Name: handlerName, Fn: handlerFn,
|
||||
})
|
||||
}
|
||||
|
||||
// LogHTTPResponseHeaderHandler is a SDK request handler to log the HTTP
|
||||
// response received from a service. Will only log the HTTP response's headers.
|
||||
// The response payload will not be read.
|
||||
var LogHTTPResponseHeaderHandler = request.NamedHandler{
|
||||
Name: "awssdk.client.LogResponseHeader",
|
||||
Fn: logResponseHeader,
|
||||
}
|
||||
|
||||
func logResponseHeader(r *request.Request) {
|
||||
if r.Config.Logger == nil {
|
||||
return
|
||||
}
|
||||
|
||||
b, err := httputil.DumpResponse(r.HTTPResponse, false)
|
||||
if err != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(logRespErrMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, err))
|
||||
return
|
||||
}
|
||||
|
||||
r.Config.Logger.Log(fmt.Sprintf(logRespMsg,
|
||||
r.ClientInfo.ServiceName, r.Operation.Name, string(b)))
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package metadata
|
|||
// ClientInfo wraps immutable data from the client.Client structure.
|
||||
type ClientInfo struct {
|
||||
ServiceName string
|
||||
ServiceID string
|
||||
APIVersion string
|
||||
Endpoint string
|
||||
SigningName string
|
||||
|
|
|
@ -18,7 +18,7 @@ const UseServiceDefaultRetries = -1
|
|||
type RequestRetryer interface{}
|
||||
|
||||
// A Config provides service configuration for service clients. By default,
|
||||
// all clients will use the defaults.DefaultConfig tructure.
|
||||
// all clients will use the defaults.DefaultConfig structure.
|
||||
//
|
||||
// // Create Session with MaxRetry configuration to be shared by multiple
|
||||
// // service clients.
|
||||
|
@ -151,6 +151,15 @@ type Config struct {
|
|||
// with accelerate.
|
||||
S3UseAccelerate *bool
|
||||
|
||||
// S3DisableContentMD5Validation config option is temporarily disabled,
|
||||
// For S3 GetObject API calls, #1837.
|
||||
//
|
||||
// Set this to `true` to disable the S3 service client from automatically
|
||||
// adding the ContentMD5 to S3 Object Put and Upload API calls. This option
|
||||
// will also disable the SDK from performing object ContentMD5 validation
|
||||
// on GetObject API calls.
|
||||
S3DisableContentMD5Validation *bool
|
||||
|
||||
// Set this to `true` to disable the EC2Metadata client from overriding the
|
||||
// default http.Client's Timeout. This is helpful if you do not want the
|
||||
// EC2Metadata client to create a new http.Client. This options is only
|
||||
|
@ -336,6 +345,15 @@ func (c *Config) WithS3Disable100Continue(disable bool) *Config {
|
|||
func (c *Config) WithS3UseAccelerate(enable bool) *Config {
|
||||
c.S3UseAccelerate = &enable
|
||||
return c
|
||||
|
||||
}
|
||||
|
||||
// WithS3DisableContentMD5Validation sets a config
|
||||
// S3DisableContentMD5Validation value returning a Config pointer for chaining.
|
||||
func (c *Config) WithS3DisableContentMD5Validation(enable bool) *Config {
|
||||
c.S3DisableContentMD5Validation = &enable
|
||||
return c
|
||||
|
||||
}
|
||||
|
||||
// WithUseDualStack sets a config UseDualStack value returning a Config
|
||||
|
@ -435,6 +453,10 @@ func mergeInConfig(dst *Config, other *Config) {
|
|||
dst.S3UseAccelerate = other.S3UseAccelerate
|
||||
}
|
||||
|
||||
if other.S3DisableContentMD5Validation != nil {
|
||||
dst.S3DisableContentMD5Validation = other.S3DisableContentMD5Validation
|
||||
}
|
||||
|
||||
if other.UseDualStack != nil {
|
||||
dst.UseDualStack = other.UseDualStack
|
||||
}
|
||||
|
|
|
@ -3,12 +3,10 @@ package corehandlers
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -36,18 +34,13 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen
|
|||
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
|
||||
length, _ = strconv.ParseInt(slength, 10, 64)
|
||||
} else {
|
||||
switch body := r.Body.(type) {
|
||||
case nil:
|
||||
length = 0
|
||||
case lener:
|
||||
length = int64(body.Len())
|
||||
case io.Seeker:
|
||||
r.BodyStart, _ = body.Seek(0, 1)
|
||||
end, _ := body.Seek(0, 2)
|
||||
body.Seek(r.BodyStart, 0) // make sure to seek back to original location
|
||||
length = end - r.BodyStart
|
||||
default:
|
||||
panic("Cannot get length of body, must provide `ContentLength`")
|
||||
if r.Body != nil {
|
||||
var err error
|
||||
length, err = aws.SeekerLen(r.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,13 +53,6 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen
|
|||
}
|
||||
}}
|
||||
|
||||
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
|
||||
var SDKVersionUserAgentHandler = request.NamedHandler{
|
||||
Name: "core.SDKVersionUserAgentHandler",
|
||||
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
|
||||
runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
|
||||
var reStatusCode = regexp.MustCompile(`^(\d{3})`)
|
||||
|
||||
// ValidateReqSigHandler is a request handler to ensure that the request's
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package corehandlers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version
|
||||
// to the user agent.
|
||||
var SDKVersionUserAgentHandler = request.NamedHandler{
|
||||
Name: "core.SDKVersionUserAgentHandler",
|
||||
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
|
||||
runtime.Version(), runtime.GOOS, runtime.GOARCH),
|
||||
}
|
||||
|
||||
const execEnvVar = `AWS_EXECUTION_ENV`
|
||||
const execEnvUAKey = `exec_env`
|
||||
|
||||
// AddHostExecEnvUserAgentHander is a request handler appending the SDK's
|
||||
// execution environment to the user agent.
|
||||
//
|
||||
// If the environment variable AWS_EXECUTION_ENV is set, its value will be
|
||||
// appended to the user agent string.
|
||||
var AddHostExecEnvUserAgentHander = request.NamedHandler{
|
||||
Name: "core.AddHostExecEnvUserAgentHander",
|
||||
Fn: func(r *request.Request) {
|
||||
v := os.Getenv(execEnvVar)
|
||||
if len(v) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
request.AddToUserAgent(r, execEnvUAKey+"/"+v)
|
||||
},
|
||||
}
|
|
@ -158,13 +158,14 @@ func (e *Expiry) SetExpiration(expiration time.Time, window time.Duration) {
|
|||
|
||||
// IsExpired returns if the credentials are expired.
|
||||
func (e *Expiry) IsExpired() bool {
|
||||
if e.CurrentTime == nil {
|
||||
e.CurrentTime = time.Now
|
||||
curTime := e.CurrentTime
|
||||
if curTime == nil {
|
||||
curTime = time.Now
|
||||
}
|
||||
return e.expiration.Before(e.CurrentTime())
|
||||
return e.expiration.Before(curTime())
|
||||
}
|
||||
|
||||
// A Credentials provides synchronous safe retrieval of AWS credentials Value.
|
||||
// A Credentials provides concurrency safe retrieval of AWS credentials Value.
|
||||
// Credentials will cache the credentials value until they expire. Once the value
|
||||
// expires the next Get will attempt to retrieve valid credentials.
|
||||
//
|
||||
|
@ -178,7 +179,8 @@ func (e *Expiry) IsExpired() bool {
|
|||
type Credentials struct {
|
||||
creds Value
|
||||
forceRefresh bool
|
||||
m sync.Mutex
|
||||
|
||||
m sync.RWMutex
|
||||
|
||||
provider Provider
|
||||
}
|
||||
|
@ -201,6 +203,17 @@ func NewCredentials(provider Provider) *Credentials {
|
|||
// If Credentials.Expire() was called the credentials Value will be force
|
||||
// expired, and the next call to Get() will cause them to be refreshed.
|
||||
func (c *Credentials) Get() (Value, error) {
|
||||
// Check the cached credentials first with just the read lock.
|
||||
c.m.RLock()
|
||||
if !c.isExpired() {
|
||||
creds := c.creds
|
||||
c.m.RUnlock()
|
||||
return creds, nil
|
||||
}
|
||||
c.m.RUnlock()
|
||||
|
||||
// Credentials are expired need to retrieve the credentials taking the full
|
||||
// lock.
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
|
||||
|
@ -234,8 +247,8 @@ func (c *Credentials) Expire() {
|
|||
// If the Credentials were forced to be expired with Expire() this will
|
||||
// reflect that override.
|
||||
func (c *Credentials) IsExpired() bool {
|
||||
c.m.Lock()
|
||||
defer c.m.Unlock()
|
||||
c.m.RLock()
|
||||
defer c.m.RUnlock()
|
||||
|
||||
return c.isExpired()
|
||||
}
|
||||
|
|
6
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
6
vendor/github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds/ec2_role_provider.go
generated
vendored
|
@ -4,7 +4,6 @@ import (
|
|||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -12,6 +11,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||
)
|
||||
|
||||
// ProviderName provides a name of EC2Role provider
|
||||
|
@ -125,7 +125,7 @@ type ec2RoleCredRespBody struct {
|
|||
Message string
|
||||
}
|
||||
|
||||
const iamSecurityCredsPath = "/iam/security-credentials"
|
||||
const iamSecurityCredsPath = "iam/security-credentials/"
|
||||
|
||||
// requestCredList requests a list of credentials from the EC2 service.
|
||||
// If there are no credentials, or there is an error making or receiving the request
|
||||
|
@ -153,7 +153,7 @@ func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
|
|||
// If the credentials cannot be found, or there is an error reading the response
|
||||
// and error will be returned.
|
||||
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
|
||||
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
|
||||
resp, err := client.GetMetadata(sdkuri.PathJoin(iamSecurityCredsPath, credsName))
|
||||
if err != nil {
|
||||
return ec2RoleCredRespBody{},
|
||||
awserr.New("EC2RoleRequestError",
|
||||
|
|
|
@ -65,6 +65,10 @@ type Provider struct {
|
|||
//
|
||||
// If ExpiryWindow is 0 or less it will be ignored.
|
||||
ExpiryWindow time.Duration
|
||||
|
||||
// Optional authorization token value if set will be used as the value of
|
||||
// the Authorization header of the endpoint credential request.
|
||||
AuthorizationToken string
|
||||
}
|
||||
|
||||
// NewProviderClient returns a credentials Provider for retrieving AWS credentials
|
||||
|
@ -152,6 +156,9 @@ func (p *Provider) getCredentials() (*getCredentialsOutput, error) {
|
|||
out := &getCredentialsOutput{}
|
||||
req := p.Client.NewRequest(op, nil, out)
|
||||
req.HTTPRequest.Header.Set("Accept", "application/json")
|
||||
if authToken := p.AuthorizationToken; len(authToken) != 0 {
|
||||
req.HTTPRequest.Header.Set("Authorization", authToken)
|
||||
}
|
||||
|
||||
return out, req.Send()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// Package csm provides Client Side Monitoring (CSM) which enables sending metrics
|
||||
// via UDP connection. Using the Start function will enable the reporting of
|
||||
// metrics on a given port. If Start is called, with different parameters, again,
|
||||
// a panic will occur.
|
||||
//
|
||||
// Pause can be called to pause any metrics publishing on a given port. Sessions
|
||||
// that have had their handlers modified via InjectHandlers may still be used.
|
||||
// However, the handlers will act as a no-op meaning no metrics will be published.
|
||||
//
|
||||
// Example:
|
||||
// r, err := csm.Start("clientID", ":31000")
|
||||
// if err != nil {
|
||||
// panic(fmt.Errorf("failed starting CSM: %v", err))
|
||||
// }
|
||||
//
|
||||
// sess, err := session.NewSession(&aws.Config{})
|
||||
// if err != nil {
|
||||
// panic(fmt.Errorf("failed loading session: %v", err))
|
||||
// }
|
||||
//
|
||||
// r.InjectHandlers(&sess.Handlers)
|
||||
//
|
||||
// client := s3.New(sess)
|
||||
// resp, err := client.GetObject(&s3.GetObjectInput{
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("key"),
|
||||
// })
|
||||
//
|
||||
// // Will pause monitoring
|
||||
// r.Pause()
|
||||
// resp, err = client.GetObject(&s3.GetObjectInput{
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("key"),
|
||||
// })
|
||||
//
|
||||
// // Resume monitoring
|
||||
// r.Continue()
|
||||
//
|
||||
// Start returns a Reporter that is used to enable or disable monitoring. If
|
||||
// access to the Reporter is required later, calling Get will return the Reporter
|
||||
// singleton.
|
||||
//
|
||||
// Example:
|
||||
// r := csm.Get()
|
||||
// r.Continue()
|
||||
package csm
|
|
@ -0,0 +1,67 @@
|
|||
package csm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
lock sync.Mutex
|
||||
)
|
||||
|
||||
// Client side metric handler names
|
||||
const (
|
||||
APICallMetricHandlerName = "awscsm.SendAPICallMetric"
|
||||
APICallAttemptMetricHandlerName = "awscsm.SendAPICallAttemptMetric"
|
||||
)
|
||||
|
||||
// Start will start the a long running go routine to capture
|
||||
// client side metrics. Calling start multiple time will only
|
||||
// start the metric listener once and will panic if a different
|
||||
// client ID or port is passed in.
|
||||
//
|
||||
// Example:
|
||||
// r, err := csm.Start("clientID", "127.0.0.1:8094")
|
||||
// if err != nil {
|
||||
// panic(fmt.Errorf("expected no error, but received %v", err))
|
||||
// }
|
||||
// sess := session.NewSession()
|
||||
// r.InjectHandlers(sess.Handlers)
|
||||
//
|
||||
// svc := s3.New(sess)
|
||||
// out, err := svc.GetObject(&s3.GetObjectInput{
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("key"),
|
||||
// })
|
||||
func Start(clientID string, url string) (*Reporter, error) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if sender == nil {
|
||||
sender = newReporter(clientID, url)
|
||||
} else {
|
||||
if sender.clientID != clientID {
|
||||
panic(fmt.Errorf("inconsistent client IDs. %q was expected, but received %q", sender.clientID, clientID))
|
||||
}
|
||||
|
||||
if sender.url != url {
|
||||
panic(fmt.Errorf("inconsistent URLs. %q was expected, but received %q", sender.url, url))
|
||||
}
|
||||
}
|
||||
|
||||
if err := connect(url); err != nil {
|
||||
sender = nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sender, nil
|
||||
}
|
||||
|
||||
// Get will return a reporter if one exists, if one does not exist, nil will
|
||||
// be returned.
|
||||
func Get() *Reporter {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
return sender
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package csm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type metricTime time.Time
|
||||
|
||||
func (t metricTime) MarshalJSON() ([]byte, error) {
|
||||
ns := time.Duration(time.Time(t).UnixNano())
|
||||
return []byte(strconv.FormatInt(int64(ns/time.Millisecond), 10)), nil
|
||||
}
|
||||
|
||||
type metric struct {
|
||||
ClientID *string `json:"ClientId,omitempty"`
|
||||
API *string `json:"Api,omitempty"`
|
||||
Service *string `json:"Service,omitempty"`
|
||||
Timestamp *metricTime `json:"Timestamp,omitempty"`
|
||||
Type *string `json:"Type,omitempty"`
|
||||
Version *int `json:"Version,omitempty"`
|
||||
|
||||
AttemptCount *int `json:"AttemptCount,omitempty"`
|
||||
Latency *int `json:"Latency,omitempty"`
|
||||
|
||||
Fqdn *string `json:"Fqdn,omitempty"`
|
||||
UserAgent *string `json:"UserAgent,omitempty"`
|
||||
AttemptLatency *int `json:"AttemptLatency,omitempty"`
|
||||
|
||||
SessionToken *string `json:"SessionToken,omitempty"`
|
||||
Region *string `json:"Region,omitempty"`
|
||||
AccessKey *string `json:"AccessKey,omitempty"`
|
||||
HTTPStatusCode *int `json:"HttpStatusCode,omitempty"`
|
||||
XAmzID2 *string `json:"XAmzId2,omitempty"`
|
||||
XAmzRequestID *string `json:"XAmznRequestId,omitempty"`
|
||||
|
||||
AWSException *string `json:"AwsException,omitempty"`
|
||||
AWSExceptionMessage *string `json:"AwsExceptionMessage,omitempty"`
|
||||
SDKException *string `json:"SdkException,omitempty"`
|
||||
SDKExceptionMessage *string `json:"SdkExceptionMessage,omitempty"`
|
||||
|
||||
DestinationIP *string `json:"DestinationIp,omitempty"`
|
||||
ConnectionReused *int `json:"ConnectionReused,omitempty"`
|
||||
|
||||
AcquireConnectionLatency *int `json:"AcquireConnectionLatency,omitempty"`
|
||||
ConnectLatency *int `json:"ConnectLatency,omitempty"`
|
||||
RequestLatency *int `json:"RequestLatency,omitempty"`
|
||||
DNSLatency *int `json:"DnsLatency,omitempty"`
|
||||
TCPLatency *int `json:"TcpLatency,omitempty"`
|
||||
SSLLatency *int `json:"SslLatency,omitempty"`
|
||||
|
||||
MaxRetriesExceeded *int `json:"MaxRetriesExceeded,omitempty"`
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package csm
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
runningEnum = iota
|
||||
pausedEnum
|
||||
)
|
||||
|
||||
var (
|
||||
// MetricsChannelSize of metrics to hold in the channel
|
||||
MetricsChannelSize = 100
|
||||
)
|
||||
|
||||
type metricChan struct {
|
||||
ch chan metric
|
||||
paused int64
|
||||
}
|
||||
|
||||
func newMetricChan(size int) metricChan {
|
||||
return metricChan{
|
||||
ch: make(chan metric, size),
|
||||
}
|
||||
}
|
||||
|
||||
func (ch *metricChan) Pause() {
|
||||
atomic.StoreInt64(&ch.paused, pausedEnum)
|
||||
}
|
||||
|
||||
func (ch *metricChan) Continue() {
|
||||
atomic.StoreInt64(&ch.paused, runningEnum)
|
||||
}
|
||||
|
||||
func (ch *metricChan) IsPaused() bool {
|
||||
v := atomic.LoadInt64(&ch.paused)
|
||||
return v == pausedEnum
|
||||
}
|
||||
|
||||
// Push will push metrics to the metric channel if the channel
|
||||
// is not paused
|
||||
func (ch *metricChan) Push(m metric) bool {
|
||||
if ch.IsPaused() {
|
||||
return false
|
||||
}
|
||||
|
||||
select {
|
||||
case ch.ch <- m:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
package csm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultPort is used when no port is specified
|
||||
DefaultPort = "31000"
|
||||
)
|
||||
|
||||
// Reporter will gather metrics of API requests made and
|
||||
// send those metrics to the CSM endpoint.
|
||||
type Reporter struct {
|
||||
clientID string
|
||||
url string
|
||||
conn net.Conn
|
||||
metricsCh metricChan
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
var (
|
||||
sender *Reporter
|
||||
)
|
||||
|
||||
func connect(url string) error {
|
||||
const network = "udp"
|
||||
if err := sender.connect(network, url); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sender.done == nil {
|
||||
sender.done = make(chan struct{})
|
||||
go sender.start()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newReporter(clientID, url string) *Reporter {
|
||||
return &Reporter{
|
||||
clientID: clientID,
|
||||
url: url,
|
||||
metricsCh: newMetricChan(MetricsChannelSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (rep *Reporter) sendAPICallAttemptMetric(r *request.Request) {
|
||||
if rep == nil {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
creds, _ := r.Config.Credentials.Get()
|
||||
|
||||
m := metric{
|
||||
ClientID: aws.String(rep.clientID),
|
||||
API: aws.String(r.Operation.Name),
|
||||
Service: aws.String(r.ClientInfo.ServiceID),
|
||||
Timestamp: (*metricTime)(&now),
|
||||
UserAgent: aws.String(r.HTTPRequest.Header.Get("User-Agent")),
|
||||
Region: r.Config.Region,
|
||||
Type: aws.String("ApiCallAttempt"),
|
||||
Version: aws.Int(1),
|
||||
|
||||
XAmzRequestID: aws.String(r.RequestID),
|
||||
|
||||
AttemptCount: aws.Int(r.RetryCount + 1),
|
||||
AttemptLatency: aws.Int(int(now.Sub(r.AttemptTime).Nanoseconds() / int64(time.Millisecond))),
|
||||
AccessKey: aws.String(creds.AccessKeyID),
|
||||
}
|
||||
|
||||
if r.HTTPResponse != nil {
|
||||
m.HTTPStatusCode = aws.Int(r.HTTPResponse.StatusCode)
|
||||
}
|
||||
|
||||
if r.Error != nil {
|
||||
if awserr, ok := r.Error.(awserr.Error); ok {
|
||||
setError(&m, awserr)
|
||||
}
|
||||
}
|
||||
|
||||
rep.metricsCh.Push(m)
|
||||
}
|
||||
|
||||
func setError(m *metric, err awserr.Error) {
|
||||
msg := err.Error()
|
||||
code := err.Code()
|
||||
|
||||
switch code {
|
||||
case "RequestError",
|
||||
"SerializationError",
|
||||
request.CanceledErrorCode:
|
||||
m.SDKException = &code
|
||||
m.SDKExceptionMessage = &msg
|
||||
default:
|
||||
m.AWSException = &code
|
||||
m.AWSExceptionMessage = &msg
|
||||
}
|
||||
}
|
||||
|
||||
func (rep *Reporter) sendAPICallMetric(r *request.Request) {
|
||||
if rep == nil {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
m := metric{
|
||||
ClientID: aws.String(rep.clientID),
|
||||
API: aws.String(r.Operation.Name),
|
||||
Service: aws.String(r.ClientInfo.ServiceID),
|
||||
Timestamp: (*metricTime)(&now),
|
||||
Type: aws.String("ApiCall"),
|
||||
AttemptCount: aws.Int(r.RetryCount + 1),
|
||||
Region: r.Config.Region,
|
||||
Latency: aws.Int(int(time.Now().Sub(r.Time) / time.Millisecond)),
|
||||
XAmzRequestID: aws.String(r.RequestID),
|
||||
MaxRetriesExceeded: aws.Int(boolIntValue(r.RetryCount >= r.MaxRetries())),
|
||||
}
|
||||
|
||||
// TODO: Probably want to figure something out for logging dropped
|
||||
// metrics
|
||||
rep.metricsCh.Push(m)
|
||||
}
|
||||
|
||||
func (rep *Reporter) connect(network, url string) error {
|
||||
if rep.conn != nil {
|
||||
rep.conn.Close()
|
||||
}
|
||||
|
||||
conn, err := net.Dial(network, url)
|
||||
if err != nil {
|
||||
return awserr.New("UDPError", "Could not connect", err)
|
||||
}
|
||||
|
||||
rep.conn = conn
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rep *Reporter) close() {
|
||||
if rep.done != nil {
|
||||
close(rep.done)
|
||||
}
|
||||
|
||||
rep.metricsCh.Pause()
|
||||
}
|
||||
|
||||
func (rep *Reporter) start() {
|
||||
defer func() {
|
||||
rep.metricsCh.Pause()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-rep.done:
|
||||
rep.done = nil
|
||||
return
|
||||
case m := <-rep.metricsCh.ch:
|
||||
// TODO: What to do with this error? Probably should just log
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
rep.conn.Write(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pause will pause the metric channel preventing any new metrics from
|
||||
// being added.
|
||||
func (rep *Reporter) Pause() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
if rep == nil {
|
||||
return
|
||||
}
|
||||
|
||||
rep.close()
|
||||
}
|
||||
|
||||
// Continue will reopen the metric channel and allow for monitoring
|
||||
// to be resumed.
|
||||
func (rep *Reporter) Continue() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
if rep == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !rep.metricsCh.IsPaused() {
|
||||
return
|
||||
}
|
||||
|
||||
rep.metricsCh.Continue()
|
||||
}
|
||||
|
||||
// InjectHandlers will will enable client side metrics and inject the proper
|
||||
// handlers to handle how metrics are sent.
|
||||
//
|
||||
// Example:
|
||||
// // Start must be called in order to inject the correct handlers
|
||||
// r, err := csm.Start("clientID", "127.0.0.1:8094")
|
||||
// if err != nil {
|
||||
// panic(fmt.Errorf("expected no error, but received %v", err))
|
||||
// }
|
||||
//
|
||||
// sess := session.NewSession()
|
||||
// r.InjectHandlers(&sess.Handlers)
|
||||
//
|
||||
// // create a new service client with our client side metric session
|
||||
// svc := s3.New(sess)
|
||||
func (rep *Reporter) InjectHandlers(handlers *request.Handlers) {
|
||||
if rep == nil {
|
||||
return
|
||||
}
|
||||
|
||||
apiCallHandler := request.NamedHandler{Name: APICallMetricHandlerName, Fn: rep.sendAPICallMetric}
|
||||
apiCallAttemptHandler := request.NamedHandler{Name: APICallAttemptMetricHandlerName, Fn: rep.sendAPICallAttemptMetric}
|
||||
|
||||
handlers.Complete.PushFrontNamed(apiCallHandler)
|
||||
handlers.Complete.PushFrontNamed(apiCallAttemptHandler)
|
||||
|
||||
handlers.AfterRetry.PushFrontNamed(apiCallAttemptHandler)
|
||||
}
|
||||
|
||||
// boolIntValue return 1 for true and 0 for false.
|
||||
func boolIntValue(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/ec2metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
||||
)
|
||||
|
||||
// A Defaults provides a collection of default values for SDK clients.
|
||||
|
@ -73,6 +74,7 @@ func Handlers() request.Handlers {
|
|||
handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
|
||||
handlers.Validate.AfterEachFn = request.HandlerListStopOnError
|
||||
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
|
||||
handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander)
|
||||
handlers.Build.AfterEachFn = request.HandlerListStopOnError
|
||||
handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
|
||||
handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler)
|
||||
|
@ -91,17 +93,28 @@ func Handlers() request.Handlers {
|
|||
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
|
||||
return credentials.NewCredentials(&credentials.ChainProvider{
|
||||
VerboseErrors: aws.BoolValue(cfg.CredentialsChainVerboseErrors),
|
||||
Providers: []credentials.Provider{
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
|
||||
RemoteCredProvider(*cfg, handlers),
|
||||
},
|
||||
Providers: CredProviders(cfg, handlers),
|
||||
})
|
||||
}
|
||||
|
||||
// CredProviders returns the slice of providers used in
|
||||
// the default credential chain.
|
||||
//
|
||||
// For applications that need to use some other provider (for example use
|
||||
// different environment variables for legacy reasons) but still fall back
|
||||
// on the default chain of providers. This allows that default chaint to be
|
||||
// automatically updated
|
||||
func CredProviders(cfg *aws.Config, handlers request.Handlers) []credentials.Provider {
|
||||
return []credentials.Provider{
|
||||
&credentials.EnvProvider{},
|
||||
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
|
||||
RemoteCredProvider(*cfg, handlers),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||
ecsCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
httpProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
||||
httpProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||
)
|
||||
|
||||
// RemoteCredProvider returns a credentials provider for the default remote
|
||||
|
@ -111,8 +124,8 @@ func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.P
|
|||
return localHTTPCredProvider(cfg, handlers, u)
|
||||
}
|
||||
|
||||
if uri := os.Getenv(ecsCredsProviderEnvVar); len(uri) > 0 {
|
||||
u := fmt.Sprintf("http://169.254.170.2%s", uri)
|
||||
if uri := os.Getenv(shareddefaults.ECSCredsProviderEnvVar); len(uri) > 0 {
|
||||
u := fmt.Sprintf("%s%s", shareddefaults.ECSContainerCredentialsURI, uri)
|
||||
return httpCredProvider(cfg, handlers, u)
|
||||
}
|
||||
|
||||
|
@ -175,6 +188,7 @@ func httpCredProvider(cfg aws.Config, handlers request.Handlers, u string) crede
|
|||
return endpointcreds.NewProviderClient(cfg, handlers, u,
|
||||
func(p *endpointcreds.Provider) {
|
||||
p.ExpiryWindow = 5 * time.Minute
|
||||
p.AuthorizationToken = os.Getenv(httpProviderAuthorizationEnvVar)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkuri"
|
||||
)
|
||||
|
||||
// GetMetadata uses the path provided to request information from the EC2
|
||||
|
@ -19,7 +19,7 @@ func (c *EC2Metadata) GetMetadata(p string) (string, error) {
|
|||
op := &request.Operation{
|
||||
Name: "GetMetadata",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "meta-data", p),
|
||||
HTTPPath: sdkuri.PathJoin("/meta-data", p),
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
|
@ -35,7 +35,7 @@ func (c *EC2Metadata) GetUserData() (string, error) {
|
|||
op := &request.Operation{
|
||||
Name: "GetUserData",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "user-data"),
|
||||
HTTPPath: "/user-data",
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
|
@ -56,7 +56,7 @@ func (c *EC2Metadata) GetDynamicData(p string) (string, error) {
|
|||
op := &request.Operation{
|
||||
Name: "GetDynamicData",
|
||||
HTTPMethod: "GET",
|
||||
HTTPPath: path.Join("/", "dynamic", p),
|
||||
HTTPPath: sdkuri.PathJoin("/dynamic", p),
|
||||
}
|
||||
|
||||
output := &metadataOutput{}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
// Package ec2metadata provides the client for making API calls to the
|
||||
// EC2 Metadata service.
|
||||
//
|
||||
// This package's client can be disabled completely by setting the environment
|
||||
// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
|
||||
// true instructs the SDK to disable the EC2 Metadata client. The client cannot
|
||||
// be used while the environemnt variable is set to true, (case insensitive).
|
||||
package ec2metadata
|
||||
|
||||
import (
|
||||
|
@ -7,17 +12,21 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"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/client/metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// ServiceName is the name of the service.
|
||||
const ServiceName = "ec2metadata"
|
||||
const disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED"
|
||||
|
||||
// A EC2Metadata is an EC2 Metadata service Client.
|
||||
type EC2Metadata struct {
|
||||
|
@ -63,6 +72,7 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
ServiceID: ServiceName,
|
||||
Endpoint: endpoint,
|
||||
APIVersion: "latest",
|
||||
},
|
||||
|
@ -75,6 +85,21 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
svc.Handlers.Validate.Clear()
|
||||
svc.Handlers.Validate.PushBack(validateEndpointHandler)
|
||||
|
||||
// Disable the EC2 Metadata service if the environment variable is set.
|
||||
// This shortcirctes the service's functionality to always fail to send
|
||||
// requests.
|
||||
if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" {
|
||||
svc.Handlers.Send.SwapNamed(request.NamedHandler{
|
||||
Name: corehandlers.SendHandler.Name,
|
||||
Fn: func(r *request.Request) {
|
||||
r.Error = awserr.New(
|
||||
request.CanceledErrorCode,
|
||||
"EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
|
||||
nil)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Add additional options to the service config
|
||||
for _, option := range opts {
|
||||
option(svc.Client)
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
)
|
||||
|
@ -85,34 +84,12 @@ func decodeV3Endpoints(modelDef modelDefinition, opts DecodeModelOptions) (Resol
|
|||
custAddEC2Metadata(p)
|
||||
custAddS3DualStack(p)
|
||||
custRmIotDataService(p)
|
||||
|
||||
custFixCloudHSMv2SigningName(p)
|
||||
custFixAppAutoscalingChina(p)
|
||||
}
|
||||
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func custFixCloudHSMv2SigningName(p *partition) {
|
||||
// Workaround for aws/aws-sdk-go#1745 until the endpoint model can be
|
||||
// fixed upstream. TODO remove this once the endpoints model is updated.
|
||||
|
||||
s, ok := p.Services["cloudhsmv2"]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if len(s.Defaults.CredentialScope.Service) != 0 {
|
||||
fmt.Fprintf(os.Stderr, "cloudhsmv2 signing name already set, ignoring override.\n")
|
||||
// If the value is already set don't override
|
||||
return
|
||||
}
|
||||
|
||||
s.Defaults.CredentialScope.Service = "cloudhsm"
|
||||
fmt.Fprintf(os.Stderr, "cloudhsmv2 signing name not set, overriding.\n")
|
||||
|
||||
p.Services["cloudhsmv2"] = s
|
||||
}
|
||||
|
||||
func custAddS3DualStack(p *partition) {
|
||||
if p.ID != "aws" {
|
||||
return
|
||||
|
@ -146,6 +123,27 @@ func custRmIotDataService(p *partition) {
|
|||
delete(p.Services, "data.iot")
|
||||
}
|
||||
|
||||
func custFixAppAutoscalingChina(p *partition) {
|
||||
if p.ID != "aws-cn" {
|
||||
return
|
||||
}
|
||||
|
||||
const serviceName = "application-autoscaling"
|
||||
s, ok := p.Services[serviceName]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
const expectHostname = `autoscaling.{region}.amazonaws.com`
|
||||
if e, a := s.Defaults.Hostname, expectHostname; e != a {
|
||||
fmt.Printf("custFixAppAutoscalingChina: ignoring customization, expected %s, got %s\n", e, a)
|
||||
return
|
||||
}
|
||||
|
||||
s.Defaults.Hostname = expectHostname + ".cn"
|
||||
p.Services[serviceName] = s
|
||||
}
|
||||
|
||||
type decodeModelError struct {
|
||||
awsError
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -206,10 +206,11 @@ func (p Partition) EndpointFor(service, region string, opts ...func(*Options)) (
|
|||
// enumerating over the regions in a partition.
|
||||
func (p Partition) Regions() map[string]Region {
|
||||
rs := map[string]Region{}
|
||||
for id := range p.p.Regions {
|
||||
for id, r := range p.p.Regions {
|
||||
rs[id] = Region{
|
||||
id: id,
|
||||
p: p.p,
|
||||
id: id,
|
||||
desc: r.Description,
|
||||
p: p.p,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,6 +241,10 @@ type Region struct {
|
|||
// ID returns the region's identifier.
|
||||
func (r Region) ID() string { return r.id }
|
||||
|
||||
// Description returns the region's description. The region description
|
||||
// is free text, it can be empty, and it may change between SDK releases.
|
||||
func (r Region) Description() string { return r.desc }
|
||||
|
||||
// ResolveEndpoint resolves an endpoint from the context of the region given
|
||||
// a service. See Partition.EndpointFor for usage and errors that can be returned.
|
||||
func (r Region) ResolveEndpoint(service string, opts ...func(*Options)) (ResolvedEndpoint, error) {
|
||||
|
@ -284,10 +289,11 @@ func (s Service) ResolveEndpoint(region string, opts ...func(*Options)) (Resolve
|
|||
func (s Service) Regions() map[string]Region {
|
||||
rs := map[string]Region{}
|
||||
for id := range s.p.Services[s.id].Endpoints {
|
||||
if _, ok := s.p.Regions[id]; ok {
|
||||
if r, ok := s.p.Regions[id]; ok {
|
||||
rs[id] = Region{
|
||||
id: id,
|
||||
p: s.p,
|
||||
id: id,
|
||||
desc: r.Description,
|
||||
p: s.p,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -347,6 +353,10 @@ type ResolvedEndpoint struct {
|
|||
// The service name that should be used for signing requests.
|
||||
SigningName string
|
||||
|
||||
// States that the signing name for this endpoint was derived from metadata
|
||||
// passed in, but was not explicitly modeled.
|
||||
SigningNameDerived bool
|
||||
|
||||
// The signing method that should be used for signing requests.
|
||||
SigningMethod string
|
||||
}
|
||||
|
|
|
@ -226,16 +226,20 @@ func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, op
|
|||
if len(signingRegion) == 0 {
|
||||
signingRegion = region
|
||||
}
|
||||
|
||||
signingName := e.CredentialScope.Service
|
||||
var signingNameDerived bool
|
||||
if len(signingName) == 0 {
|
||||
signingName = service
|
||||
signingNameDerived = true
|
||||
}
|
||||
|
||||
return ResolvedEndpoint{
|
||||
URL: u,
|
||||
SigningRegion: signingRegion,
|
||||
SigningName: signingName,
|
||||
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
|
||||
URL: u,
|
||||
SigningRegion: signingRegion,
|
||||
SigningName: signingName,
|
||||
SigningNameDerived: signingNameDerived,
|
||||
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,12 @@ const (
|
|||
// LogDebugWithRequestErrors states the SDK should log when service requests fail
|
||||
// to build, send, validate, or unmarshal.
|
||||
LogDebugWithRequestErrors
|
||||
|
||||
// LogDebugWithEventStreamBody states the SDK should log EventStream
|
||||
// request and response bodys. This should be used to log the EventStream
|
||||
// wire unmarshaled message content of requests and responses made while
|
||||
// using the SDK Will also enable LogDebug.
|
||||
LogDebugWithEventStreamBody
|
||||
)
|
||||
|
||||
// A Logger is a minimalistic interface for the SDK to log messages to. Should
|
||||
|
|
|
@ -14,6 +14,7 @@ type Handlers struct {
|
|||
Send HandlerList
|
||||
ValidateResponse HandlerList
|
||||
Unmarshal HandlerList
|
||||
UnmarshalStream HandlerList
|
||||
UnmarshalMeta HandlerList
|
||||
UnmarshalError HandlerList
|
||||
Retry HandlerList
|
||||
|
@ -30,6 +31,7 @@ func (h *Handlers) Copy() Handlers {
|
|||
Send: h.Send.copy(),
|
||||
ValidateResponse: h.ValidateResponse.copy(),
|
||||
Unmarshal: h.Unmarshal.copy(),
|
||||
UnmarshalStream: h.UnmarshalStream.copy(),
|
||||
UnmarshalError: h.UnmarshalError.copy(),
|
||||
UnmarshalMeta: h.UnmarshalMeta.copy(),
|
||||
Retry: h.Retry.copy(),
|
||||
|
@ -45,6 +47,7 @@ func (h *Handlers) Clear() {
|
|||
h.Send.Clear()
|
||||
h.Sign.Clear()
|
||||
h.Unmarshal.Clear()
|
||||
h.UnmarshalStream.Clear()
|
||||
h.UnmarshalMeta.Clear()
|
||||
h.UnmarshalError.Clear()
|
||||
h.ValidateResponse.Clear()
|
||||
|
@ -172,6 +175,21 @@ func (l *HandlerList) SwapNamed(n NamedHandler) (swapped bool) {
|
|||
return swapped
|
||||
}
|
||||
|
||||
// Swap will swap out all handlers matching the name passed in. The matched
|
||||
// handlers will be swapped in. True is returned if the handlers were swapped.
|
||||
func (l *HandlerList) Swap(name string, replace NamedHandler) bool {
|
||||
var swapped bool
|
||||
|
||||
for i := 0; i < len(l.list); i++ {
|
||||
if l.list[i].Name == name {
|
||||
l.list[i] = replace
|
||||
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) {
|
||||
|
|
|
@ -3,6 +3,8 @@ package request
|
|||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/aws/aws-sdk-go/internal/sdkio"
|
||||
)
|
||||
|
||||
// offsetReader is a thread-safe io.ReadCloser to prevent racing
|
||||
|
@ -15,7 +17,7 @@ type offsetReader struct {
|
|||
|
||||
func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader {
|
||||
reader := &offsetReader{}
|
||||
buf.Seek(offset, 0)
|
||||
buf.Seek(offset, sdkio.SeekStart)
|
||||
|
||||
reader.buf = buf
|
||||
return reader
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkio"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -45,6 +46,7 @@ type Request struct {
|
|||
Handlers Handlers
|
||||
|
||||
Retryer
|
||||
AttemptTime time.Time
|
||||
Time time.Time
|
||||
Operation *Operation
|
||||
HTTPRequest *http.Request
|
||||
|
@ -120,6 +122,7 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
|
|||
Handlers: handlers.Copy(),
|
||||
|
||||
Retryer: retryer,
|
||||
AttemptTime: time.Now(),
|
||||
Time: time.Now(),
|
||||
ExpireTime: 0,
|
||||
Operation: operation,
|
||||
|
@ -224,6 +227,9 @@ func (r *Request) SetContext(ctx aws.Context) {
|
|||
|
||||
// WillRetry returns if the request's can be retried.
|
||||
func (r *Request) WillRetry() bool {
|
||||
if !aws.IsReaderSeekable(r.Body) && r.HTTPRequest.Body != NoBody {
|
||||
return false
|
||||
}
|
||||
return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
|
||||
}
|
||||
|
||||
|
@ -255,6 +261,7 @@ func (r *Request) SetStringBody(s string) {
|
|||
// SetReaderBody will set the request's body reader.
|
||||
func (r *Request) SetReaderBody(reader io.ReadSeeker) {
|
||||
r.Body = reader
|
||||
r.BodyStart, _ = reader.Seek(0, sdkio.SeekCurrent) // Get the Bodies current offset.
|
||||
r.ResetBody()
|
||||
}
|
||||
|
||||
|
@ -292,6 +299,11 @@ func (r *Request) PresignRequest(expire time.Duration) (string, http.Header, err
|
|||
return getPresignedURL(r, expire)
|
||||
}
|
||||
|
||||
// IsPresigned returns true if the request represents a presigned API url.
|
||||
func (r *Request) IsPresigned() bool {
|
||||
return r.ExpireTime != 0
|
||||
}
|
||||
|
||||
func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, error) {
|
||||
if expire <= 0 {
|
||||
return "", nil, awserr.New(
|
||||
|
@ -332,7 +344,7 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) {
|
|||
|
||||
// Build will build the request's object so it can be signed and sent
|
||||
// to the service. Build will also validate all the request's parameters.
|
||||
// Anny additional build Handlers set on this request will be run
|
||||
// Any additional build Handlers set on this request will be run
|
||||
// in the order they were set.
|
||||
//
|
||||
// The request will only be built once. Multiple calls to build will have
|
||||
|
@ -358,9 +370,9 @@ func (r *Request) Build() error {
|
|||
return r.Error
|
||||
}
|
||||
|
||||
// Sign will sign the request returning error if errors are encountered.
|
||||
// Sign will sign the request, returning error if errors are encountered.
|
||||
//
|
||||
// Send will build the request prior to signing. All Sign Handlers will
|
||||
// Sign will build the request prior to signing. All Sign Handlers will
|
||||
// be executed in the order they were set.
|
||||
func (r *Request) Sign() error {
|
||||
r.Build()
|
||||
|
@ -393,7 +405,7 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
|
|||
// of the SDK if they used that field.
|
||||
//
|
||||
// Related golang/go#18257
|
||||
l, err := computeBodyLength(r.Body)
|
||||
l, err := aws.SeekerLen(r.Body)
|
||||
if err != nil {
|
||||
return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err)
|
||||
}
|
||||
|
@ -411,7 +423,8 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
|
|||
// Transfer-Encoding: chunked bodies for these methods.
|
||||
//
|
||||
// This would only happen if a aws.ReaderSeekerCloser was used with
|
||||
// a io.Reader that was not also an io.Seeker.
|
||||
// a io.Reader that was not also an io.Seeker, or did not implement
|
||||
// Len() method.
|
||||
switch r.Operation.HTTPMethod {
|
||||
case "GET", "HEAD", "DELETE":
|
||||
body = NoBody
|
||||
|
@ -423,49 +436,13 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
|
|||
return body, nil
|
||||
}
|
||||
|
||||
// Attempts to compute the length of the body of the reader using the
|
||||
// io.Seeker interface. If the value is not seekable because of being
|
||||
// a ReaderSeekerCloser without an unerlying Seeker -1 will be returned.
|
||||
// If no error occurs the length of the body will be returned.
|
||||
func computeBodyLength(r io.ReadSeeker) (int64, error) {
|
||||
seekable := true
|
||||
// Determine if the seeker is actually seekable. ReaderSeekerCloser
|
||||
// hides the fact that a io.Readers might not actually be seekable.
|
||||
switch v := r.(type) {
|
||||
case aws.ReaderSeekerCloser:
|
||||
seekable = v.IsSeeker()
|
||||
case *aws.ReaderSeekerCloser:
|
||||
seekable = v.IsSeeker()
|
||||
}
|
||||
if !seekable {
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
curOffset, err := r.Seek(0, 1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
endOffset, err := r.Seek(0, 2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = r.Seek(curOffset, 0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return endOffset - curOffset, nil
|
||||
}
|
||||
|
||||
// GetBody will return an io.ReadSeeker of the Request's underlying
|
||||
// input body with a concurrency safe wrapper.
|
||||
func (r *Request) GetBody() io.ReadSeeker {
|
||||
return r.safeBody
|
||||
}
|
||||
|
||||
// Send will send the request returning error if errors are encountered.
|
||||
// Send will send the request, returning error if errors are encountered.
|
||||
//
|
||||
// Send will sign the request prior to sending. All Send Handlers will
|
||||
// be executed in the order they were set.
|
||||
|
@ -486,6 +463,7 @@ func (r *Request) Send() error {
|
|||
}()
|
||||
|
||||
for {
|
||||
r.AttemptTime = time.Now()
|
||||
if aws.BoolValue(r.Retryable) {
|
||||
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
|
||||
r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
|
||||
|
|
|
@ -21,7 +21,7 @@ func (noBody) WriteTo(io.Writer) (int64, error) { return 0, nil }
|
|||
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
|
||||
// sets 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
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
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
|
||||
// sets 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
|
||||
|
|
|
@ -35,8 +35,12 @@ type Pagination struct {
|
|||
// 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)
|
||||
// EndPageOnSameToken, when enabled, will allow the paginator to stop on
|
||||
// token that are the same as its previous tokens.
|
||||
EndPageOnSameToken bool
|
||||
|
||||
started bool
|
||||
prevTokens []interface{}
|
||||
nextTokens []interface{}
|
||||
|
||||
err error
|
||||
|
@ -49,7 +53,15 @@ type Pagination struct {
|
|||
//
|
||||
// Will always return true if Next has not been called yet.
|
||||
func (p *Pagination) HasNextPage() bool {
|
||||
return !(p.started && len(p.nextTokens) == 0)
|
||||
if !p.started {
|
||||
return true
|
||||
}
|
||||
|
||||
hasNextPage := len(p.nextTokens) != 0
|
||||
if p.EndPageOnSameToken {
|
||||
return hasNextPage && !awsutil.DeepEqual(p.nextTokens, p.prevTokens)
|
||||
}
|
||||
return hasNextPage
|
||||
}
|
||||
|
||||
// Err returns the error Pagination encountered when retrieving the next page.
|
||||
|
@ -96,6 +108,7 @@ func (p *Pagination) Next() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
p.prevTokens = p.nextTokens
|
||||
p.nextTokens = req.nextPageTokens()
|
||||
p.curPage = req.Data
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ func isNestedErrorRetryable(parentErr awserr.Error) bool {
|
|||
}
|
||||
|
||||
if t, ok := err.(temporaryError); ok {
|
||||
return t.Temporary()
|
||||
return t.Temporary() || isErrConnectionReset(err)
|
||||
}
|
||||
|
||||
return isErrConnectionReset(err)
|
||||
|
|
|
@ -17,6 +17,10 @@ const (
|
|||
ParamMinValueErrCode = "ParamMinValueError"
|
||||
// ParamMinLenErrCode is the error code for fields without enough elements.
|
||||
ParamMinLenErrCode = "ParamMinLenError"
|
||||
|
||||
// ParamFormatErrCode is the error code for a field with invalid
|
||||
// format or characters.
|
||||
ParamFormatErrCode = "ParamFormatInvalidError"
|
||||
)
|
||||
|
||||
// Validator provides a way for types to perform validation logic on their
|
||||
|
@ -232,3 +236,26 @@ func NewErrParamMinLen(field string, min int) *ErrParamMinLen {
|
|||
func (e *ErrParamMinLen) MinLen() int {
|
||||
return e.min
|
||||
}
|
||||
|
||||
// An ErrParamFormat represents a invalid format parameter error.
|
||||
type ErrParamFormat struct {
|
||||
errInvalidParam
|
||||
format string
|
||||
}
|
||||
|
||||
// NewErrParamFormat creates a new invalid format parameter error.
|
||||
func NewErrParamFormat(field string, format, value string) *ErrParamFormat {
|
||||
return &ErrParamFormat{
|
||||
errInvalidParam: errInvalidParam{
|
||||
code: ParamFormatErrCode,
|
||||
field: field,
|
||||
msg: fmt.Sprintf("format %v, %v", format, value),
|
||||
},
|
||||
format: format,
|
||||
}
|
||||
}
|
||||
|
||||
// Format returns the field's required format.
|
||||
func (e *ErrParamFormat) Format() string {
|
||||
return e.format
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ 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
|
||||
AWS Services. They are from a configuration file will need to include both
|
||||
aws_access_key_id and aws_secret_access_key must be provided together in the
|
||||
same file to be considered valid. The values will be ignored if not a complete
|
||||
group. aws_session_token is an optional field that can be provided if both of
|
||||
|
|
|
@ -96,9 +96,23 @@ type envConfig struct {
|
|||
//
|
||||
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
|
||||
CustomCABundle string
|
||||
|
||||
csmEnabled string
|
||||
CSMEnabled bool
|
||||
CSMPort string
|
||||
CSMClientID string
|
||||
}
|
||||
|
||||
var (
|
||||
csmEnabledEnvKey = []string{
|
||||
"AWS_CSM_ENABLED",
|
||||
}
|
||||
csmPortEnvKey = []string{
|
||||
"AWS_CSM_PORT",
|
||||
}
|
||||
csmClientIDEnvKey = []string{
|
||||
"AWS_CSM_CLIENT_ID",
|
||||
}
|
||||
credAccessEnvKey = []string{
|
||||
"AWS_ACCESS_KEY_ID",
|
||||
"AWS_ACCESS_KEY",
|
||||
|
@ -157,6 +171,12 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
|
|||
setFromEnvVal(&cfg.Creds.SecretAccessKey, credSecretEnvKey)
|
||||
setFromEnvVal(&cfg.Creds.SessionToken, credSessionEnvKey)
|
||||
|
||||
// CSM environment variables
|
||||
setFromEnvVal(&cfg.csmEnabled, csmEnabledEnvKey)
|
||||
setFromEnvVal(&cfg.CSMPort, csmPortEnvKey)
|
||||
setFromEnvVal(&cfg.CSMClientID, csmClientIDEnvKey)
|
||||
cfg.CSMEnabled = len(cfg.csmEnabled) > 0
|
||||
|
||||
// Require logical grouping of credentials
|
||||
if len(cfg.Creds.AccessKeyID) == 0 || len(cfg.Creds.SecretAccessKey) == 0 {
|
||||
cfg.Creds = credentials.Value{}
|
||||
|
|
|
@ -15,18 +15,37 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws/corehandlers"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go/aws/csm"
|
||||
"github.com/aws/aws-sdk-go/aws/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/shareddefaults"
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrCodeSharedConfig represents an error that occurs in the shared
|
||||
// configuration logic
|
||||
ErrCodeSharedConfig = "SharedConfigErr"
|
||||
)
|
||||
|
||||
// ErrSharedConfigSourceCollision will be returned if a section contains both
|
||||
// source_profile and credential_source
|
||||
var ErrSharedConfigSourceCollision = awserr.New(ErrCodeSharedConfig, "only source profile or credential source can be specified, not both", nil)
|
||||
|
||||
// ErrSharedConfigECSContainerEnvVarEmpty will be returned if the environment
|
||||
// variables are empty and Environment was set as the credential source
|
||||
var ErrSharedConfigECSContainerEnvVarEmpty = awserr.New(ErrCodeSharedConfig, "EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set", nil)
|
||||
|
||||
// ErrSharedConfigInvalidCredSource will be returned if an invalid credential source was provided
|
||||
var ErrSharedConfigInvalidCredSource = awserr.New(ErrCodeSharedConfig, "credential source values must be EcsContainer, Ec2InstanceMetadata, or Environment", nil)
|
||||
|
||||
// A Session provides a central location to create service clients from and
|
||||
// store configurations and request handlers for those services.
|
||||
//
|
||||
// Sessions are safe to create service clients concurrently, but it is not safe
|
||||
// to mutate the Session concurrently.
|
||||
//
|
||||
// The Session satisfies the service client's client.ClientConfigProvider.
|
||||
// The Session satisfies the service client's client.ConfigProvider.
|
||||
type Session struct {
|
||||
Config *aws.Config
|
||||
Handlers request.Handlers
|
||||
|
@ -81,10 +100,16 @@ func New(cfgs ...*aws.Config) *Session {
|
|||
r.Error = err
|
||||
})
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
return deprecatedNewSession(cfgs...)
|
||||
s := deprecatedNewSession(cfgs...)
|
||||
if envCfg.CSMEnabled {
|
||||
enableCSM(&s.Handlers, envCfg.CSMClientID, envCfg.CSMPort, s.Config.Logger)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// NewSession returns a new Session created from SDK defaults, config files,
|
||||
|
@ -300,10 +325,22 @@ func deprecatedNewSession(cfgs ...*aws.Config) *Session {
|
|||
}
|
||||
|
||||
initHandlers(s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func enableCSM(handlers *request.Handlers, clientID string, port string, logger aws.Logger) {
|
||||
logger.Log("Enabling CSM")
|
||||
if len(port) == 0 {
|
||||
port = csm.DefaultPort
|
||||
}
|
||||
|
||||
r, err := csm.Start(clientID, "127.0.0.1:"+port)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r.InjectHandlers(handlers)
|
||||
}
|
||||
|
||||
func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session, error) {
|
||||
cfg := defaults.Config()
|
||||
handlers := defaults.Handlers()
|
||||
|
@ -343,6 +380,9 @@ func newSession(opts Options, envCfg envConfig, cfgs ...*aws.Config) (*Session,
|
|||
}
|
||||
|
||||
initHandlers(s)
|
||||
if envCfg.CSMEnabled {
|
||||
enableCSM(&s.Handlers, envCfg.CSMClientID, envCfg.CSMPort, s.Config.Logger)
|
||||
}
|
||||
|
||||
// Setup HTTP client with custom cert bundle if enabled
|
||||
if opts.CustomCABundle != nil {
|
||||
|
@ -414,6 +454,57 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg share
|
|||
|
||||
// Configure credentials if not already set
|
||||
if cfg.Credentials == credentials.AnonymousCredentials && userCfg.Credentials == nil {
|
||||
|
||||
// inspect the profile to see if a credential source has been specified.
|
||||
if envCfg.EnableSharedConfig && len(sharedCfg.AssumeRole.CredentialSource) > 0 {
|
||||
|
||||
// if both credential_source and source_profile have been set, return an error
|
||||
// as this is undefined behavior.
|
||||
if len(sharedCfg.AssumeRole.SourceProfile) > 0 {
|
||||
return ErrSharedConfigSourceCollision
|
||||
}
|
||||
|
||||
// valid credential source values
|
||||
const (
|
||||
credSourceEc2Metadata = "Ec2InstanceMetadata"
|
||||
credSourceEnvironment = "Environment"
|
||||
credSourceECSContainer = "EcsContainer"
|
||||
)
|
||||
|
||||
switch sharedCfg.AssumeRole.CredentialSource {
|
||||
case credSourceEc2Metadata:
|
||||
cfgCp := *cfg
|
||||
p := defaults.RemoteCredProvider(cfgCp, handlers)
|
||||
cfgCp.Credentials = credentials.NewCredentials(p)
|
||||
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil {
|
||||
// AssumeRole Token provider is required if doing Assume Role
|
||||
// with MFA.
|
||||
return AssumeRoleTokenProviderNotSetError{}
|
||||
}
|
||||
|
||||
cfg.Credentials = assumeRoleCredentials(cfgCp, handlers, sharedCfg, sessOpts)
|
||||
case credSourceEnvironment:
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
envCfg.Creds,
|
||||
)
|
||||
case credSourceECSContainer:
|
||||
if len(os.Getenv(shareddefaults.ECSCredsProviderEnvVar)) == 0 {
|
||||
return ErrSharedConfigECSContainerEnvVarEmpty
|
||||
}
|
||||
|
||||
cfgCp := *cfg
|
||||
p := defaults.RemoteCredProvider(cfgCp, handlers)
|
||||
creds := credentials.NewCredentials(p)
|
||||
|
||||
cfg.Credentials = creds
|
||||
default:
|
||||
return ErrSharedConfigInvalidCredSource
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(envCfg.Creds.AccessKeyID) > 0 {
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
envCfg.Creds,
|
||||
|
@ -423,32 +514,14 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg share
|
|||
cfgCp.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
sharedCfg.AssumeRoleSource.Creds,
|
||||
)
|
||||
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 && sessOpts.AssumeRoleTokenProvider == nil {
|
||||
// AssumeRole Token provider is required if doing Assume Role
|
||||
// with MFA.
|
||||
return AssumeRoleTokenProviderNotSetError{}
|
||||
}
|
||||
cfg.Credentials = stscreds.NewCredentials(
|
||||
&Session{
|
||||
Config: &cfgCp,
|
||||
Handlers: handlers.Copy(),
|
||||
},
|
||||
sharedCfg.AssumeRole.RoleARN,
|
||||
func(opt *stscreds.AssumeRoleProvider) {
|
||||
opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName
|
||||
|
||||
// Assume role with external ID
|
||||
if len(sharedCfg.AssumeRole.ExternalID) > 0 {
|
||||
opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID)
|
||||
}
|
||||
|
||||
// Assume role with MFA
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 {
|
||||
opt.SerialNumber = aws.String(sharedCfg.AssumeRole.MFASerial)
|
||||
opt.TokenProvider = sessOpts.AssumeRoleTokenProvider
|
||||
}
|
||||
},
|
||||
)
|
||||
cfg.Credentials = assumeRoleCredentials(cfgCp, handlers, sharedCfg, sessOpts)
|
||||
} else if len(sharedCfg.Creds.AccessKeyID) > 0 {
|
||||
cfg.Credentials = credentials.NewStaticCredentialsFromCreds(
|
||||
sharedCfg.Creds,
|
||||
|
@ -471,6 +544,30 @@ func mergeConfigSrcs(cfg, userCfg *aws.Config, envCfg envConfig, sharedCfg share
|
|||
return nil
|
||||
}
|
||||
|
||||
func assumeRoleCredentials(cfg aws.Config, handlers request.Handlers, sharedCfg sharedConfig, sessOpts Options) *credentials.Credentials {
|
||||
return stscreds.NewCredentials(
|
||||
&Session{
|
||||
Config: &cfg,
|
||||
Handlers: handlers.Copy(),
|
||||
},
|
||||
sharedCfg.AssumeRole.RoleARN,
|
||||
func(opt *stscreds.AssumeRoleProvider) {
|
||||
opt.RoleSessionName = sharedCfg.AssumeRole.RoleSessionName
|
||||
|
||||
// Assume role with external ID
|
||||
if len(sharedCfg.AssumeRole.ExternalID) > 0 {
|
||||
opt.ExternalID = aws.String(sharedCfg.AssumeRole.ExternalID)
|
||||
}
|
||||
|
||||
// Assume role with MFA
|
||||
if len(sharedCfg.AssumeRole.MFASerial) > 0 {
|
||||
opt.SerialNumber = aws.String(sharedCfg.AssumeRole.MFASerial)
|
||||
opt.TokenProvider = sessOpts.AssumeRoleTokenProvider
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// AssumeRoleTokenProviderNotSetError is an error returned when creating a session when the
|
||||
// MFAToken option is not set when shared config is configured load assume a
|
||||
// role with an MFA token.
|
||||
|
@ -571,11 +668,12 @@ func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (
|
|||
}
|
||||
|
||||
return client.Config{
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningName: resolved.SigningName,
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningNameDerived: resolved.SigningNameDerived,
|
||||
SigningName: resolved.SigningName,
|
||||
}, err
|
||||
}
|
||||
|
||||
|
@ -595,10 +693,11 @@ func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Conf
|
|||
}
|
||||
|
||||
return client.Config{
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningName: resolved.SigningName,
|
||||
Config: s.Config,
|
||||
Handlers: s.Handlers,
|
||||
Endpoint: resolved.URL,
|
||||
SigningRegion: resolved.SigningRegion,
|
||||
SigningNameDerived: resolved.SigningNameDerived,
|
||||
SigningName: resolved.SigningName,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,12 @@ const (
|
|||
sessionTokenKey = `aws_session_token` // optional
|
||||
|
||||
// Assume Role Credentials group
|
||||
roleArnKey = `role_arn` // group required
|
||||
sourceProfileKey = `source_profile` // group required
|
||||
externalIDKey = `external_id` // optional
|
||||
mfaSerialKey = `mfa_serial` // optional
|
||||
roleSessionNameKey = `role_session_name` // optional
|
||||
roleArnKey = `role_arn` // group required
|
||||
sourceProfileKey = `source_profile` // group required (or credential_source)
|
||||
credentialSourceKey = `credential_source` // group required (or source_profile)
|
||||
externalIDKey = `external_id` // optional
|
||||
mfaSerialKey = `mfa_serial` // optional
|
||||
roleSessionNameKey = `role_session_name` // optional
|
||||
|
||||
// Additional Config fields
|
||||
regionKey = `region`
|
||||
|
@ -32,11 +33,12 @@ const (
|
|||
)
|
||||
|
||||
type assumeRoleConfig struct {
|
||||
RoleARN string
|
||||
SourceProfile string
|
||||
ExternalID string
|
||||
MFASerial string
|
||||
RoleSessionName string
|
||||
RoleARN string
|
||||
SourceProfile string
|
||||
CredentialSource string
|
||||
ExternalID string
|
||||
MFASerial string
|
||||
RoleSessionName string
|
||||
}
|
||||
|
||||
// sharedConfig represents the configuration fields of the SDK config files.
|
||||
|
@ -127,6 +129,13 @@ func loadSharedConfigIniFiles(filenames []string) ([]sharedConfigFile, error) {
|
|||
func (cfg *sharedConfig) setAssumeRoleSource(origProfile string, files []sharedConfigFile) error {
|
||||
var assumeRoleSrc sharedConfig
|
||||
|
||||
if len(cfg.AssumeRole.CredentialSource) > 0 {
|
||||
// setAssumeRoleSource is only called when source_profile is found.
|
||||
// If both source_profile and credential_source are set, then
|
||||
// ErrSharedConfigSourceCollision will be returned
|
||||
return ErrSharedConfigSourceCollision
|
||||
}
|
||||
|
||||
// Multiple level assume role chains are not support
|
||||
if cfg.AssumeRole.SourceProfile == origProfile {
|
||||
assumeRoleSrc = *cfg
|
||||
|
@ -195,13 +204,16 @@ func (cfg *sharedConfig) setFromIniFile(profile string, file sharedConfigFile) e
|
|||
// Assume Role
|
||||
roleArn := section.Key(roleArnKey).String()
|
||||
srcProfile := section.Key(sourceProfileKey).String()
|
||||
if len(roleArn) > 0 && len(srcProfile) > 0 {
|
||||
credentialSource := section.Key(credentialSourceKey).String()
|
||||
hasSource := len(srcProfile) > 0 || len(credentialSource) > 0
|
||||
if len(roleArn) > 0 && hasSource {
|
||||
cfg.AssumeRole = assumeRoleConfig{
|
||||
RoleARN: roleArn,
|
||||
SourceProfile: srcProfile,
|
||||
ExternalID: section.Key(externalIDKey).String(),
|
||||
MFASerial: section.Key(mfaSerialKey).String(),
|
||||
RoleSessionName: section.Key(roleSessionNameKey).String(),
|
||||
RoleARN: roleArn,
|
||||
SourceProfile: srcProfile,
|
||||
CredentialSource: credentialSource,
|
||||
ExternalID: section.Key(externalIDKey).String(),
|
||||
MFASerial: section.Key(mfaSerialKey).String(),
|
||||
RoleSessionName: section.Key(roleSessionNameKey).String(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,7 @@ import (
|
|||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkio"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||
)
|
||||
|
||||
|
@ -134,6 +135,7 @@ var requiredSignedHeaders = rules{
|
|||
"X-Amz-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||
"X-Amz-Storage-Class": struct{}{},
|
||||
"X-Amz-Website-Redirect-Location": struct{}{},
|
||||
"X-Amz-Content-Sha256": struct{}{},
|
||||
},
|
||||
},
|
||||
patterns{"X-Amz-Meta-"},
|
||||
|
@ -341,7 +343,9 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
|
|||
|
||||
ctx.sanitizeHostForHeader()
|
||||
ctx.assignAmzQueryValues()
|
||||
ctx.build(v4.DisableHeaderHoisting)
|
||||
if err := ctx.build(v4.DisableHeaderHoisting); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If the request is not presigned the body should be attached to it. This
|
||||
// prevents the confusion of wanting to send a signed request without
|
||||
|
@ -503,11 +507,13 @@ func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
|
|||
v4.Logger.Log(msg)
|
||||
}
|
||||
|
||||
func (ctx *signingCtx) build(disableHeaderHoisting bool) {
|
||||
func (ctx *signingCtx) build(disableHeaderHoisting bool) error {
|
||||
ctx.buildTime() // no depends
|
||||
ctx.buildCredentialString() // no depends
|
||||
|
||||
ctx.buildBodyDigest()
|
||||
if err := ctx.buildBodyDigest(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unsignedHeaders := ctx.Request.Header
|
||||
if ctx.isPresign {
|
||||
|
@ -535,6 +541,8 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
|
|||
}
|
||||
ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctx *signingCtx) buildTime() {
|
||||
|
@ -661,21 +669,34 @@ func (ctx *signingCtx) buildSignature() {
|
|||
ctx.signature = hex.EncodeToString(signature)
|
||||
}
|
||||
|
||||
func (ctx *signingCtx) buildBodyDigest() {
|
||||
func (ctx *signingCtx) buildBodyDigest() error {
|
||||
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
|
||||
if hash == "" {
|
||||
if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
|
||||
includeSHA256Header := ctx.unsignedPayload ||
|
||||
ctx.ServiceName == "s3" ||
|
||||
ctx.ServiceName == "glacier"
|
||||
|
||||
s3Presign := ctx.isPresign && ctx.ServiceName == "s3"
|
||||
|
||||
if ctx.unsignedPayload || s3Presign {
|
||||
hash = "UNSIGNED-PAYLOAD"
|
||||
includeSHA256Header = !s3Presign
|
||||
} else if ctx.Body == nil {
|
||||
hash = emptyStringSHA256
|
||||
} else {
|
||||
if !aws.IsReaderSeekable(ctx.Body) {
|
||||
return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body)
|
||||
}
|
||||
hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
|
||||
}
|
||||
if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
|
||||
|
||||
if includeSHA256Header {
|
||||
ctx.Request.Header.Set("X-Amz-Content-Sha256", hash)
|
||||
}
|
||||
}
|
||||
ctx.bodyDigest = hash
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isRequestSigned returns if the request is currently signed or presigned
|
||||
|
@ -715,10 +736,18 @@ func makeSha256(data []byte) []byte {
|
|||
|
||||
func makeSha256Reader(reader io.ReadSeeker) []byte {
|
||||
hash := sha256.New()
|
||||
start, _ := reader.Seek(0, 1)
|
||||
defer reader.Seek(start, 0)
|
||||
start, _ := reader.Seek(0, sdkio.SeekCurrent)
|
||||
defer reader.Seek(start, sdkio.SeekStart)
|
||||
|
||||
// Use CopyN to avoid allocating the 32KB buffer in io.Copy for bodies
|
||||
// smaller than 32KB. Fall back to io.Copy if we fail to determine the size.
|
||||
size, err := aws.SeekerLen(reader)
|
||||
if err != nil {
|
||||
io.Copy(hash, reader)
|
||||
} else {
|
||||
io.CopyN(hash, reader, size)
|
||||
}
|
||||
|
||||
io.Copy(hash, reader)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package aws
|
|||
import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/aws/aws-sdk-go/internal/sdkio"
|
||||
)
|
||||
|
||||
// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Should
|
||||
|
@ -22,6 +24,22 @@ type ReaderSeekerCloser struct {
|
|||
r io.Reader
|
||||
}
|
||||
|
||||
// IsReaderSeekable returns if the underlying reader type can be seeked. A
|
||||
// io.Reader might not actually be seekable if it is the ReaderSeekerCloser
|
||||
// type.
|
||||
func IsReaderSeekable(r io.Reader) bool {
|
||||
switch v := r.(type) {
|
||||
case ReaderSeekerCloser:
|
||||
return v.IsSeeker()
|
||||
case *ReaderSeekerCloser:
|
||||
return v.IsSeeker()
|
||||
case io.ReadSeeker:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads from the reader up to size of p. The number of bytes read, and
|
||||
// error if it occurred will be returned.
|
||||
//
|
||||
|
@ -56,6 +74,71 @@ func (r ReaderSeekerCloser) IsSeeker() bool {
|
|||
return ok
|
||||
}
|
||||
|
||||
// HasLen returns the length of the underlying reader if the value implements
|
||||
// the Len() int method.
|
||||
func (r ReaderSeekerCloser) HasLen() (int, bool) {
|
||||
type lenner interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
if lr, ok := r.r.(lenner); ok {
|
||||
return lr.Len(), true
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// GetLen returns the length of the bytes remaining in the underlying reader.
|
||||
// Checks first for Len(), then io.Seeker to determine the size of the
|
||||
// underlying reader.
|
||||
//
|
||||
// Will return -1 if the length cannot be determined.
|
||||
func (r ReaderSeekerCloser) GetLen() (int64, error) {
|
||||
if l, ok := r.HasLen(); ok {
|
||||
return int64(l), nil
|
||||
}
|
||||
|
||||
if s, ok := r.r.(io.Seeker); ok {
|
||||
return seekerLen(s)
|
||||
}
|
||||
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// SeekerLen attempts to get the number of bytes remaining at the seeker's
|
||||
// current position. Returns the number of bytes remaining or error.
|
||||
func SeekerLen(s io.Seeker) (int64, error) {
|
||||
// Determine if the seeker is actually seekable. ReaderSeekerCloser
|
||||
// hides the fact that a io.Readers might not actually be seekable.
|
||||
switch v := s.(type) {
|
||||
case ReaderSeekerCloser:
|
||||
return v.GetLen()
|
||||
case *ReaderSeekerCloser:
|
||||
return v.GetLen()
|
||||
}
|
||||
|
||||
return seekerLen(s)
|
||||
}
|
||||
|
||||
func seekerLen(s io.Seeker) (int64, error) {
|
||||
curOffset, err := s.Seek(0, sdkio.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
endOffset, err := s.Seek(0, sdkio.SeekEnd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = s.Seek(curOffset, sdkio.SeekStart)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return endOffset - curOffset, nil
|
||||
}
|
||||
|
||||
// Close closes the ReaderSeekerCloser.
|
||||
//
|
||||
// If the ReaderSeekerCloser is not an io.Closer nothing will be done.
|
||||
|
|
|
@ -5,4 +5,4 @@ package aws
|
|||
const SDKName = "aws-sdk-go"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = "1.12.72"
|
||||
const SDKVersion = "1.15.54"
|
||||
|
|
|
@ -10,7 +10,7 @@ phases:
|
|||
- mkdir -p /go/src/github.com/aws
|
||||
- ln -s $SDK_CB_ROOT $SDK_GO_ROOT
|
||||
- cd $SDK_GO_ROOT
|
||||
- make unit
|
||||
- make ci-test
|
||||
- cd $SDK_CB_ROOT
|
||||
- #echo Compiling the Go code...
|
||||
post_build:
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#/usr/bin/env bash
|
||||
|
||||
# Deletes all but the most current model version.
|
||||
for v in $(ls ./models/apis | grep -v '.go' ); do
|
||||
for vm in $(ls -r models/apis/$v/ | sed -n '1!p' ); do
|
||||
echo "rm -r models/apis/$v/$vm"
|
||||
rm -r models/apis/$v/$vm
|
||||
done
|
||||
done
|
|
@ -0,0 +1,6 @@
|
|||
module github.com/aws/aws-sdk-go
|
||||
|
||||
require (
|
||||
github.com/go-ini/ini v1.25.4
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8
|
||||
)
|
|
@ -0,0 +1,2 @@
|
|||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
|
@ -0,0 +1,57 @@
|
|||
package s3err
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// RequestFailure provides additional S3 specific metadata for the request
|
||||
// failure.
|
||||
type RequestFailure struct {
|
||||
awserr.RequestFailure
|
||||
|
||||
hostID string
|
||||
}
|
||||
|
||||
// NewRequestFailure returns a request failure error decordated with S3
|
||||
// specific metadata.
|
||||
func NewRequestFailure(err awserr.RequestFailure, hostID string) *RequestFailure {
|
||||
return &RequestFailure{RequestFailure: err, hostID: hostID}
|
||||
}
|
||||
|
||||
func (r RequestFailure) Error() string {
|
||||
extra := fmt.Sprintf("status code: %d, request id: %s, host id: %s",
|
||||
r.StatusCode(), r.RequestID(), r.hostID)
|
||||
return awserr.SprintError(r.Code(), r.Message(), extra, r.OrigErr())
|
||||
}
|
||||
func (r RequestFailure) String() string {
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// HostID returns the HostID request response value.
|
||||
func (r RequestFailure) HostID() string {
|
||||
return r.hostID
|
||||
}
|
||||
|
||||
// RequestFailureWrapperHandler returns a handler to rap an
|
||||
// awserr.RequestFailure with the S3 request ID 2 from the response.
|
||||
func RequestFailureWrapperHandler() request.NamedHandler {
|
||||
return request.NamedHandler{
|
||||
Name: "awssdk.s3.errorHandler",
|
||||
Fn: func(req *request.Request) {
|
||||
reqErr, ok := req.Error.(awserr.RequestFailure)
|
||||
if !ok || reqErr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
hostID := req.HTTPResponse.Header.Get("X-Amz-Id-2")
|
||||
if req.Error == nil {
|
||||
return
|
||||
}
|
||||
|
||||
req.Error = NewRequestFailure(reqErr, hostID)
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// +build !go1.7
|
||||
|
||||
package sdkio
|
||||
|
||||
// Copy of Go 1.7 io package's Seeker constants.
|
||||
const (
|
||||
SeekStart = 0 // seek relative to the origin of the file
|
||||
SeekCurrent = 1 // seek relative to the current offset
|
||||
SeekEnd = 2 // seek relative to the end
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
// +build go1.7
|
||||
|
||||
package sdkio
|
||||
|
||||
import "io"
|
||||
|
||||
// Alias for Go 1.7 io package Seeker constants
|
||||
const (
|
||||
SeekStart = io.SeekStart // seek relative to the origin of the file
|
||||
SeekCurrent = io.SeekCurrent // seek relative to the current offset
|
||||
SeekEnd = io.SeekEnd // seek relative to the end
|
||||
)
|
29
vendor/github.com/aws/aws-sdk-go/internal/sdkrand/locked_source.go
generated
vendored
Normal file
29
vendor/github.com/aws/aws-sdk-go/internal/sdkrand/locked_source.go
generated
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
package sdkrand
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// lockedSource is a thread-safe implementation of rand.Source
|
||||
type lockedSource struct {
|
||||
lk sync.Mutex
|
||||
src rand.Source
|
||||
}
|
||||
|
||||
func (r *lockedSource) Int63() (n int64) {
|
||||
r.lk.Lock()
|
||||
n = r.src.Int63()
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *lockedSource) Seed(seed int64) {
|
||||
r.lk.Lock()
|
||||
r.src.Seed(seed)
|
||||
r.lk.Unlock()
|
||||
}
|
||||
|
||||
// SeededRand is a new RNG using a thread safe implementation of rand.Source
|
||||
var SeededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
|
|
@ -0,0 +1,23 @@
|
|||
package sdkuri
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PathJoin will join the elements of the path delimited by the "/"
|
||||
// character. Similar to path.Join with the exception the trailing "/"
|
||||
// character is preserved if present.
|
||||
func PathJoin(elems ...string) string {
|
||||
if len(elems) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
hasTrailing := strings.HasSuffix(elems[len(elems)-1], "/")
|
||||
str := path.Join(elems...)
|
||||
if hasTrailing && str != "/" {
|
||||
str += "/"
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
12
vendor/github.com/aws/aws-sdk-go/internal/shareddefaults/ecs_container.go
generated
vendored
Normal file
12
vendor/github.com/aws/aws-sdk-go/internal/shareddefaults/ecs_container.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
package shareddefaults
|
||||
|
||||
const (
|
||||
// ECSCredsProviderEnvVar is an environmental variable key used to
|
||||
// determine which path needs to be hit.
|
||||
ECSCredsProviderEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
)
|
||||
|
||||
// ECSContainerCredentialsURI is the endpoint to retrieve container
|
||||
// credentials. This can be overriden to test to ensure the credential process
|
||||
// is behaving correctly.
|
||||
var ECSContainerCredentialsURI = "http://169.254.170.2"
|
|
@ -24,7 +24,7 @@ func Build(r *request.Request) {
|
|||
r.Error = awserr.New("SerializationError", "failed encoding EC2 Query request", err)
|
||||
}
|
||||
|
||||
if r.ExpireTime == 0 {
|
||||
if !r.IsPresigned() {
|
||||
r.HTTPRequest.Method = "POST"
|
||||
r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||
r.SetBufferBody([]byte(body.Encode()))
|
||||
|
|
|
@ -27,7 +27,11 @@ func Unmarshal(r *request.Request) {
|
|||
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||
err := xmlutil.UnmarshalXML(r.Data, decoder, "")
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed decoding EC2 Query response", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed decoding EC2 Query response", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +56,11 @@ func UnmarshalError(r *request.Request) {
|
|||
resp := &xmlErrorResponse{}
|
||||
err := xml.NewDecoder(r.HTTPResponse.Body).Decode(resp)
|
||||
if err != nil && err != io.EOF {
|
||||
r.Error = awserr.New("SerializationError", "failed decoding EC2 Query error response", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed decoding EC2 Query error response", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
} else {
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New(resp.Code, resp.Message, nil),
|
||||
|
|
144
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/debug.go
generated
vendored
Normal file
144
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/debug.go
generated
vendored
Normal file
|
@ -0,0 +1,144 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type decodedMessage struct {
|
||||
rawMessage
|
||||
Headers decodedHeaders `json:"headers"`
|
||||
}
|
||||
type jsonMessage struct {
|
||||
Length json.Number `json:"total_length"`
|
||||
HeadersLen json.Number `json:"headers_length"`
|
||||
PreludeCRC json.Number `json:"prelude_crc"`
|
||||
Headers decodedHeaders `json:"headers"`
|
||||
Payload []byte `json:"payload"`
|
||||
CRC json.Number `json:"message_crc"`
|
||||
}
|
||||
|
||||
func (d *decodedMessage) UnmarshalJSON(b []byte) (err error) {
|
||||
var jsonMsg jsonMessage
|
||||
if err = json.Unmarshal(b, &jsonMsg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Length, err = numAsUint32(jsonMsg.Length)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.HeadersLen, err = numAsUint32(jsonMsg.HeadersLen)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.PreludeCRC, err = numAsUint32(jsonMsg.PreludeCRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Headers = jsonMsg.Headers
|
||||
d.Payload = jsonMsg.Payload
|
||||
d.CRC, err = numAsUint32(jsonMsg.CRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *decodedMessage) MarshalJSON() ([]byte, error) {
|
||||
jsonMsg := jsonMessage{
|
||||
Length: json.Number(strconv.Itoa(int(d.Length))),
|
||||
HeadersLen: json.Number(strconv.Itoa(int(d.HeadersLen))),
|
||||
PreludeCRC: json.Number(strconv.Itoa(int(d.PreludeCRC))),
|
||||
Headers: d.Headers,
|
||||
Payload: d.Payload,
|
||||
CRC: json.Number(strconv.Itoa(int(d.CRC))),
|
||||
}
|
||||
|
||||
return json.Marshal(jsonMsg)
|
||||
}
|
||||
|
||||
func numAsUint32(n json.Number) (uint32, error) {
|
||||
v, err := n.Int64()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get int64 json number, %v", err)
|
||||
}
|
||||
|
||||
return uint32(v), nil
|
||||
}
|
||||
|
||||
func (d decodedMessage) Message() Message {
|
||||
return Message{
|
||||
Headers: Headers(d.Headers),
|
||||
Payload: d.Payload,
|
||||
}
|
||||
}
|
||||
|
||||
type decodedHeaders Headers
|
||||
|
||||
func (hs *decodedHeaders) UnmarshalJSON(b []byte) error {
|
||||
var jsonHeaders []struct {
|
||||
Name string `json:"name"`
|
||||
Type valueType `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(b))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&jsonHeaders); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var headers Headers
|
||||
for _, h := range jsonHeaders {
|
||||
value, err := valueFromType(h.Type, h.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headers.Set(h.Name, value)
|
||||
}
|
||||
(*hs) = decodedHeaders(headers)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func valueFromType(typ valueType, val interface{}) (Value, error) {
|
||||
switch typ {
|
||||
case trueValueType:
|
||||
return BoolValue(true), nil
|
||||
case falseValueType:
|
||||
return BoolValue(false), nil
|
||||
case int8ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int8Value(int8(v)), err
|
||||
case int16ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int16Value(int16(v)), err
|
||||
case int32ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int32Value(int32(v)), err
|
||||
case int64ValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return Int64Value(v), err
|
||||
case bytesValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
return BytesValue(v), err
|
||||
case stringValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
return StringValue(string(v)), err
|
||||
case timestampValueType:
|
||||
v, err := val.(json.Number).Int64()
|
||||
return TimestampValue(timeFromEpochMilli(v)), err
|
||||
case uuidValueType:
|
||||
v, err := base64.StdEncoding.DecodeString(val.(string))
|
||||
var tv UUIDValue
|
||||
copy(tv[:], v)
|
||||
return tv, err
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown type, %s, %T", typ.String(), val))
|
||||
}
|
||||
}
|
199
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/decode.go
generated
vendored
Normal file
199
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/decode.go
generated
vendored
Normal file
|
@ -0,0 +1,199 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
)
|
||||
|
||||
// Decoder provides decoding of an Event Stream messages.
|
||||
type Decoder struct {
|
||||
r io.Reader
|
||||
logger aws.Logger
|
||||
}
|
||||
|
||||
// NewDecoder initializes and returns a Decoder for decoding event
|
||||
// stream messages from the reader provided.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
return &Decoder{
|
||||
r: r,
|
||||
}
|
||||
}
|
||||
|
||||
// Decode attempts to decode a single message from the event stream reader.
|
||||
// Will return the event stream message, or error if Decode fails to read
|
||||
// the message from the stream.
|
||||
func (d *Decoder) Decode(payloadBuf []byte) (m Message, err error) {
|
||||
reader := d.r
|
||||
if d.logger != nil {
|
||||
debugMsgBuf := bytes.NewBuffer(nil)
|
||||
reader = io.TeeReader(reader, debugMsgBuf)
|
||||
defer func() {
|
||||
logMessageDecode(d.logger, debugMsgBuf, m, err)
|
||||
}()
|
||||
}
|
||||
|
||||
crc := crc32.New(crc32IEEETable)
|
||||
hashReader := io.TeeReader(reader, crc)
|
||||
|
||||
prelude, err := decodePrelude(hashReader, crc)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
if prelude.HeadersLen > 0 {
|
||||
lr := io.LimitReader(hashReader, int64(prelude.HeadersLen))
|
||||
m.Headers, err = decodeHeaders(lr)
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
}
|
||||
|
||||
if payloadLen := prelude.PayloadLen(); payloadLen > 0 {
|
||||
buf, err := decodePayload(payloadBuf, io.LimitReader(hashReader, int64(payloadLen)))
|
||||
if err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
m.Payload = buf
|
||||
}
|
||||
|
||||
msgCRC := crc.Sum32()
|
||||
if err := validateCRC(reader, msgCRC); err != nil {
|
||||
return Message{}, err
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// UseLogger specifies the Logger that that the decoder should use to log the
|
||||
// message decode to.
|
||||
func (d *Decoder) UseLogger(logger aws.Logger) {
|
||||
d.logger = logger
|
||||
}
|
||||
|
||||
func logMessageDecode(logger aws.Logger, msgBuf *bytes.Buffer, msg Message, decodeErr error) {
|
||||
w := bytes.NewBuffer(nil)
|
||||
defer func() { logger.Log(w.String()) }()
|
||||
|
||||
fmt.Fprintf(w, "Raw message:\n%s\n",
|
||||
hex.Dump(msgBuf.Bytes()))
|
||||
|
||||
if decodeErr != nil {
|
||||
fmt.Fprintf(w, "Decode error: %v\n", decodeErr)
|
||||
return
|
||||
}
|
||||
|
||||
rawMsg, err := msg.rawMessage()
|
||||
if err != nil {
|
||||
fmt.Fprintf(w, "failed to create raw message, %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
decodedMsg := decodedMessage{
|
||||
rawMessage: rawMsg,
|
||||
Headers: decodedHeaders(msg.Headers),
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "Decoded message:\n")
|
||||
encoder := json.NewEncoder(w)
|
||||
if err := encoder.Encode(decodedMsg); err != nil {
|
||||
fmt.Fprintf(w, "failed to generate decoded message, %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func decodePrelude(r io.Reader, crc hash.Hash32) (messagePrelude, error) {
|
||||
var p messagePrelude
|
||||
|
||||
var err error
|
||||
p.Length, err = decodeUint32(r)
|
||||
if err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
p.HeadersLen, err = decodeUint32(r)
|
||||
if err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
if err := p.ValidateLens(); err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
preludeCRC := crc.Sum32()
|
||||
if err := validateCRC(r, preludeCRC); err != nil {
|
||||
return messagePrelude{}, err
|
||||
}
|
||||
|
||||
p.PreludeCRC = preludeCRC
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func decodePayload(buf []byte, r io.Reader) ([]byte, error) {
|
||||
w := bytes.NewBuffer(buf[0:0])
|
||||
|
||||
_, err := io.Copy(w, r)
|
||||
return w.Bytes(), err
|
||||
}
|
||||
|
||||
func decodeUint8(r io.Reader) (uint8, error) {
|
||||
type byteReader interface {
|
||||
ReadByte() (byte, error)
|
||||
}
|
||||
|
||||
if br, ok := r.(byteReader); ok {
|
||||
v, err := br.ReadByte()
|
||||
return uint8(v), err
|
||||
}
|
||||
|
||||
var b [1]byte
|
||||
_, err := io.ReadFull(r, b[:])
|
||||
return uint8(b[0]), err
|
||||
}
|
||||
func decodeUint16(r io.Reader) (uint16, error) {
|
||||
var b [2]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint16(bs), nil
|
||||
}
|
||||
func decodeUint32(r io.Reader) (uint32, error) {
|
||||
var b [4]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint32(bs), nil
|
||||
}
|
||||
func decodeUint64(r io.Reader) (uint64, error) {
|
||||
var b [8]byte
|
||||
bs := b[:]
|
||||
_, err := io.ReadFull(r, bs)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return binary.BigEndian.Uint64(bs), nil
|
||||
}
|
||||
|
||||
func validateCRC(r io.Reader, expect uint32) error {
|
||||
msgCRC, err := decodeUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if msgCRC != expect {
|
||||
return ChecksumError{}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
114
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/encode.go
generated
vendored
Normal file
114
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/encode.go
generated
vendored
Normal file
|
@ -0,0 +1,114 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Encoder provides EventStream message encoding.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
|
||||
headersBuf *bytes.Buffer
|
||||
}
|
||||
|
||||
// NewEncoder initializes and returns an Encoder to encode Event Stream
|
||||
// messages to an io.Writer.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{
|
||||
w: w,
|
||||
headersBuf: bytes.NewBuffer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
// Encode encodes a single EventStream message to the io.Writer the Encoder
|
||||
// was created with. An error is returned if writing the message fails.
|
||||
func (e *Encoder) Encode(msg Message) error {
|
||||
e.headersBuf.Reset()
|
||||
|
||||
err := encodeHeaders(e.headersBuf, msg.Headers)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crc := crc32.New(crc32IEEETable)
|
||||
hashWriter := io.MultiWriter(e.w, crc)
|
||||
|
||||
headersLen := uint32(e.headersBuf.Len())
|
||||
payloadLen := uint32(len(msg.Payload))
|
||||
|
||||
if err := encodePrelude(hashWriter, crc, headersLen, payloadLen); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if headersLen > 0 {
|
||||
if _, err := io.Copy(hashWriter, e.headersBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if payloadLen > 0 {
|
||||
if _, err := hashWriter.Write(msg.Payload); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
msgCRC := crc.Sum32()
|
||||
return binary.Write(e.w, binary.BigEndian, msgCRC)
|
||||
}
|
||||
|
||||
func encodePrelude(w io.Writer, crc hash.Hash32, headersLen, payloadLen uint32) error {
|
||||
p := messagePrelude{
|
||||
Length: minMsgLen + headersLen + payloadLen,
|
||||
HeadersLen: headersLen,
|
||||
}
|
||||
if err := p.ValidateLens(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
p.Length,
|
||||
p.HeadersLen,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PreludeCRC = crc.Sum32()
|
||||
err = binary.Write(w, binary.BigEndian, p.PreludeCRC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func encodeHeaders(w io.Writer, headers Headers) error {
|
||||
for _, h := range headers {
|
||||
hn := headerName{
|
||||
Len: uint8(len(h.Name)),
|
||||
}
|
||||
copy(hn.Name[:hn.Len], h.Name)
|
||||
if err := hn.encode(w); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := h.Value.encode(w); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func binaryWriteFields(w io.Writer, order binary.ByteOrder, vs ...interface{}) error {
|
||||
for _, v := range vs {
|
||||
if err := binary.Write(w, order, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
23
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/error.go
generated
vendored
Normal file
23
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/error.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package eventstream
|
||||
|
||||
import "fmt"
|
||||
|
||||
// LengthError provides the error for items being larger than a maximum length.
|
||||
type LengthError struct {
|
||||
Part string
|
||||
Want int
|
||||
Have int
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (e LengthError) Error() string {
|
||||
return fmt.Sprintf("%s length invalid, %d/%d, %v",
|
||||
e.Part, e.Want, e.Have, e.Value)
|
||||
}
|
||||
|
||||
// ChecksumError provides the error for message checksum invalidation errors.
|
||||
type ChecksumError struct{}
|
||||
|
||||
func (e ChecksumError) Error() string {
|
||||
return "message checksum mismatch"
|
||||
}
|
196
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/api.go
generated
vendored
Normal file
196
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/api.go
generated
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
package eventstreamapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/eventstream"
|
||||
)
|
||||
|
||||
// Unmarshaler provides the interface for unmarshaling a EventStream
|
||||
// message into a SDK type.
|
||||
type Unmarshaler interface {
|
||||
UnmarshalEvent(protocol.PayloadUnmarshaler, eventstream.Message) error
|
||||
}
|
||||
|
||||
// EventStream headers with specific meaning to async API functionality.
|
||||
const (
|
||||
MessageTypeHeader = `:message-type` // Identifies type of message.
|
||||
EventMessageType = `event`
|
||||
ErrorMessageType = `error`
|
||||
ExceptionMessageType = `exception`
|
||||
|
||||
// Message Events
|
||||
EventTypeHeader = `:event-type` // Identifies message event type e.g. "Stats".
|
||||
|
||||
// Message Error
|
||||
ErrorCodeHeader = `:error-code`
|
||||
ErrorMessageHeader = `:error-message`
|
||||
|
||||
// Message Exception
|
||||
ExceptionTypeHeader = `:exception-type`
|
||||
)
|
||||
|
||||
// EventReader provides reading from the EventStream of an reader.
|
||||
type EventReader struct {
|
||||
reader io.ReadCloser
|
||||
decoder *eventstream.Decoder
|
||||
|
||||
unmarshalerForEventType func(string) (Unmarshaler, error)
|
||||
payloadUnmarshaler protocol.PayloadUnmarshaler
|
||||
|
||||
payloadBuf []byte
|
||||
}
|
||||
|
||||
// NewEventReader returns a EventReader built from the reader and unmarshaler
|
||||
// provided. Use ReadStream method to start reading from the EventStream.
|
||||
func NewEventReader(
|
||||
reader io.ReadCloser,
|
||||
payloadUnmarshaler protocol.PayloadUnmarshaler,
|
||||
unmarshalerForEventType func(string) (Unmarshaler, error),
|
||||
) *EventReader {
|
||||
return &EventReader{
|
||||
reader: reader,
|
||||
decoder: eventstream.NewDecoder(reader),
|
||||
payloadUnmarshaler: payloadUnmarshaler,
|
||||
unmarshalerForEventType: unmarshalerForEventType,
|
||||
payloadBuf: make([]byte, 10*1024),
|
||||
}
|
||||
}
|
||||
|
||||
// UseLogger instructs the EventReader to use the logger and log level
|
||||
// specified.
|
||||
func (r *EventReader) UseLogger(logger aws.Logger, logLevel aws.LogLevelType) {
|
||||
if logger != nil && logLevel.Matches(aws.LogDebugWithEventStreamBody) {
|
||||
r.decoder.UseLogger(logger)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadEvent attempts to read a message from the EventStream and return the
|
||||
// unmarshaled event value that the message is for.
|
||||
//
|
||||
// For EventStream API errors check if the returned error satisfies the
|
||||
// awserr.Error interface to get the error's Code and Message components.
|
||||
//
|
||||
// EventUnmarshalers called with EventStream messages must take copies of the
|
||||
// message's Payload. The payload will is reused between events read.
|
||||
func (r *EventReader) ReadEvent() (event interface{}, err error) {
|
||||
msg, err := r.decoder.Decode(r.payloadBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
// Reclaim payload buffer for next message read.
|
||||
r.payloadBuf = msg.Payload[0:0]
|
||||
}()
|
||||
|
||||
typ, err := GetHeaderString(msg, MessageTypeHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch typ {
|
||||
case EventMessageType:
|
||||
return r.unmarshalEventMessage(msg)
|
||||
case ExceptionMessageType:
|
||||
err = r.unmarshalEventException(msg)
|
||||
return nil, err
|
||||
case ErrorMessageType:
|
||||
return nil, r.unmarshalErrorMessage(msg)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown eventstream message type, %v", typ)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *EventReader) unmarshalEventMessage(
|
||||
msg eventstream.Message,
|
||||
) (event interface{}, err error) {
|
||||
eventType, err := GetHeaderString(msg, EventTypeHeader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ev, err := r.unmarshalerForEventType(eventType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ev.UnmarshalEvent(r.payloadUnmarshaler, msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ev, nil
|
||||
}
|
||||
|
||||
func (r *EventReader) unmarshalEventException(
|
||||
msg eventstream.Message,
|
||||
) (err error) {
|
||||
eventType, err := GetHeaderString(msg, ExceptionTypeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ev, err := r.unmarshalerForEventType(eventType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ev.UnmarshalEvent(r.payloadUnmarshaler, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var ok bool
|
||||
err, ok = ev.(error)
|
||||
if !ok {
|
||||
err = messageError{
|
||||
code: "SerializationError",
|
||||
msg: fmt.Sprintf(
|
||||
"event stream exception %s mapped to non-error %T, %v",
|
||||
eventType, ev, ev,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *EventReader) unmarshalErrorMessage(msg eventstream.Message) (err error) {
|
||||
var msgErr messageError
|
||||
|
||||
msgErr.code, err = GetHeaderString(msg, ErrorCodeHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msgErr.msg, err = GetHeaderString(msg, ErrorMessageHeader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return msgErr
|
||||
}
|
||||
|
||||
// Close closes the EventReader's EventStream reader.
|
||||
func (r *EventReader) Close() error {
|
||||
return r.reader.Close()
|
||||
}
|
||||
|
||||
// GetHeaderString returns the value of the header as a string. If the header
|
||||
// is not set or the value is not a string an error will be returned.
|
||||
func GetHeaderString(msg eventstream.Message, headerName string) (string, error) {
|
||||
headerVal := msg.Headers.Get(headerName)
|
||||
if headerVal == nil {
|
||||
return "", fmt.Errorf("error header %s not present", headerName)
|
||||
}
|
||||
|
||||
v, ok := headerVal.Get().(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("error header value is not a string, %T", headerVal)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
24
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/error.go
generated
vendored
Normal file
24
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/eventstreamapi/error.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package eventstreamapi
|
||||
|
||||
import "fmt"
|
||||
|
||||
type messageError struct {
|
||||
code string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e messageError) Code() string {
|
||||
return e.code
|
||||
}
|
||||
|
||||
func (e messageError) Message() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e messageError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.code, e.msg)
|
||||
}
|
||||
|
||||
func (e messageError) OrigErr() error {
|
||||
return nil
|
||||
}
|
166
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/header.go
generated
vendored
Normal file
166
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/header.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Headers are a collection of EventStream header values.
|
||||
type Headers []Header
|
||||
|
||||
// Header is a single EventStream Key Value header pair.
|
||||
type Header struct {
|
||||
Name string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// Set associates the name with a value. If the header name already exists in
|
||||
// the Headers the value will be replaced with the new one.
|
||||
func (hs *Headers) Set(name string, value Value) {
|
||||
var i int
|
||||
for ; i < len(*hs); i++ {
|
||||
if (*hs)[i].Name == name {
|
||||
(*hs)[i].Value = value
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
*hs = append(*hs, Header{
|
||||
Name: name, Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// Get returns the Value associated with the header. Nil is returned if the
|
||||
// value does not exist.
|
||||
func (hs Headers) Get(name string) Value {
|
||||
for i := 0; i < len(hs); i++ {
|
||||
if h := hs[i]; h.Name == name {
|
||||
return h.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Del deletes the value in the Headers if it exists.
|
||||
func (hs *Headers) Del(name string) {
|
||||
for i := 0; i < len(*hs); i++ {
|
||||
if (*hs)[i].Name == name {
|
||||
copy((*hs)[i:], (*hs)[i+1:])
|
||||
(*hs) = (*hs)[:len(*hs)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodeHeaders(r io.Reader) (Headers, error) {
|
||||
hs := Headers{}
|
||||
|
||||
for {
|
||||
name, err := decodeHeaderName(r)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// EOF while getting header name means no more headers
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
value, err := decodeHeaderValue(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hs.Set(name, value)
|
||||
}
|
||||
|
||||
return hs, nil
|
||||
}
|
||||
|
||||
func decodeHeaderName(r io.Reader) (string, error) {
|
||||
var n headerName
|
||||
|
||||
var err error
|
||||
n.Len, err = decodeUint8(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
name := n.Name[:n.Len]
|
||||
if _, err := io.ReadFull(r, name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(name), nil
|
||||
}
|
||||
|
||||
func decodeHeaderValue(r io.Reader) (Value, error) {
|
||||
var raw rawValue
|
||||
|
||||
typ, err := decodeUint8(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw.Type = valueType(typ)
|
||||
|
||||
var v Value
|
||||
|
||||
switch raw.Type {
|
||||
case trueValueType:
|
||||
v = BoolValue(true)
|
||||
case falseValueType:
|
||||
v = BoolValue(false)
|
||||
case int8ValueType:
|
||||
var tv Int8Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int16ValueType:
|
||||
var tv Int16Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int32ValueType:
|
||||
var tv Int32Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case int64ValueType:
|
||||
var tv Int64Value
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case bytesValueType:
|
||||
var tv BytesValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case stringValueType:
|
||||
var tv StringValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case timestampValueType:
|
||||
var tv TimestampValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
case uuidValueType:
|
||||
var tv UUIDValue
|
||||
err = tv.decode(r)
|
||||
v = tv
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown value type %d", raw.Type))
|
||||
}
|
||||
|
||||
// Error could be EOF, let caller deal with it
|
||||
return v, err
|
||||
}
|
||||
|
||||
const maxHeaderNameLen = 255
|
||||
|
||||
type headerName struct {
|
||||
Len uint8
|
||||
Name [maxHeaderNameLen]byte
|
||||
}
|
||||
|
||||
func (v headerName) encode(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.BigEndian, v.Len); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := w.Write(v.Name[:v.Len])
|
||||
return err
|
||||
}
|
501
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/header_value.go
generated
vendored
Normal file
501
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/header_value.go
generated
vendored
Normal file
|
@ -0,0 +1,501 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const maxHeaderValueLen = 1<<15 - 1 // 2^15-1 or 32KB - 1
|
||||
|
||||
// valueType is the EventStream header value type.
|
||||
type valueType uint8
|
||||
|
||||
// Header value types
|
||||
const (
|
||||
trueValueType valueType = iota
|
||||
falseValueType
|
||||
int8ValueType // Byte
|
||||
int16ValueType // Short
|
||||
int32ValueType // Integer
|
||||
int64ValueType // Long
|
||||
bytesValueType
|
||||
stringValueType
|
||||
timestampValueType
|
||||
uuidValueType
|
||||
)
|
||||
|
||||
func (t valueType) String() string {
|
||||
switch t {
|
||||
case trueValueType:
|
||||
return "bool"
|
||||
case falseValueType:
|
||||
return "bool"
|
||||
case int8ValueType:
|
||||
return "int8"
|
||||
case int16ValueType:
|
||||
return "int16"
|
||||
case int32ValueType:
|
||||
return "int32"
|
||||
case int64ValueType:
|
||||
return "int64"
|
||||
case bytesValueType:
|
||||
return "byte_array"
|
||||
case stringValueType:
|
||||
return "string"
|
||||
case timestampValueType:
|
||||
return "timestamp"
|
||||
case uuidValueType:
|
||||
return "uuid"
|
||||
default:
|
||||
return fmt.Sprintf("unknown value type %d", uint8(t))
|
||||
}
|
||||
}
|
||||
|
||||
type rawValue struct {
|
||||
Type valueType
|
||||
Len uint16 // Only set for variable length slices
|
||||
Value []byte // byte representation of value, BigEndian encoding.
|
||||
}
|
||||
|
||||
func (r rawValue) encodeScalar(w io.Writer, v interface{}) error {
|
||||
return binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
v,
|
||||
)
|
||||
}
|
||||
|
||||
func (r rawValue) encodeFixedSlice(w io.Writer, v []byte) error {
|
||||
binary.Write(w, binary.BigEndian, r.Type)
|
||||
|
||||
_, err := w.Write(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r rawValue) encodeBytes(w io.Writer, v []byte) error {
|
||||
if len(v) > maxHeaderValueLen {
|
||||
return LengthError{
|
||||
Part: "header value",
|
||||
Want: maxHeaderValueLen, Have: len(v),
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
r.Len = uint16(len(v))
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
r.Len,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = w.Write(v)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r rawValue) encodeString(w io.Writer, v string) error {
|
||||
if len(v) > maxHeaderValueLen {
|
||||
return LengthError{
|
||||
Part: "header value",
|
||||
Want: maxHeaderValueLen, Have: len(v),
|
||||
Value: v,
|
||||
}
|
||||
}
|
||||
r.Len = uint16(len(v))
|
||||
|
||||
type stringWriter interface {
|
||||
WriteString(string) (int, error)
|
||||
}
|
||||
|
||||
err := binaryWriteFields(w, binary.BigEndian,
|
||||
r.Type,
|
||||
r.Len,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sw, ok := w.(stringWriter); ok {
|
||||
_, err = sw.WriteString(v)
|
||||
} else {
|
||||
_, err = w.Write([]byte(v))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeFixedBytesValue(r io.Reader, buf []byte) error {
|
||||
_, err := io.ReadFull(r, buf)
|
||||
return err
|
||||
}
|
||||
|
||||
func decodeBytesValue(r io.Reader) ([]byte, error) {
|
||||
var raw rawValue
|
||||
var err error
|
||||
raw.Len, err = decodeUint16(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := make([]byte, raw.Len)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func decodeStringValue(r io.Reader) (string, error) {
|
||||
v, err := decodeBytesValue(r)
|
||||
return string(v), err
|
||||
}
|
||||
|
||||
// Value represents the abstract header value.
|
||||
type Value interface {
|
||||
Get() interface{}
|
||||
String() string
|
||||
valueType() valueType
|
||||
encode(io.Writer) error
|
||||
}
|
||||
|
||||
// An BoolValue provides eventstream encoding, and representation
|
||||
// of a Go bool value.
|
||||
type BoolValue bool
|
||||
|
||||
// Get returns the underlying type
|
||||
func (v BoolValue) Get() interface{} {
|
||||
return bool(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (v BoolValue) valueType() valueType {
|
||||
if v {
|
||||
return trueValueType
|
||||
}
|
||||
return falseValueType
|
||||
}
|
||||
|
||||
func (v BoolValue) String() string {
|
||||
return strconv.FormatBool(bool(v))
|
||||
}
|
||||
|
||||
// encode encodes the BoolValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v BoolValue) encode(w io.Writer) error {
|
||||
return binary.Write(w, binary.BigEndian, v.valueType())
|
||||
}
|
||||
|
||||
// An Int8Value provides eventstream encoding, and representation of a Go
|
||||
// int8 value.
|
||||
type Int8Value int8
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int8Value) Get() interface{} {
|
||||
return int8(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int8Value) valueType() valueType {
|
||||
return int8ValueType
|
||||
}
|
||||
|
||||
func (v Int8Value) String() string {
|
||||
return fmt.Sprintf("0x%02x", int8(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int8Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int8Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int8Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint8(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int8Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int16Value provides eventstream encoding, and representation of a Go
|
||||
// int16 value.
|
||||
type Int16Value int16
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int16Value) Get() interface{} {
|
||||
return int16(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int16Value) valueType() valueType {
|
||||
return int16ValueType
|
||||
}
|
||||
|
||||
func (v Int16Value) String() string {
|
||||
return fmt.Sprintf("0x%04x", int16(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int16Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int16Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int16Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint16(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int16Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int32Value provides eventstream encoding, and representation of a Go
|
||||
// int32 value.
|
||||
type Int32Value int32
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int32Value) Get() interface{} {
|
||||
return int32(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int32Value) valueType() valueType {
|
||||
return int32ValueType
|
||||
}
|
||||
|
||||
func (v Int32Value) String() string {
|
||||
return fmt.Sprintf("0x%08x", int32(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int32Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int32Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int32Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint32(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int32Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An Int64Value provides eventstream encoding, and representation of a Go
|
||||
// int64 value.
|
||||
type Int64Value int64
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v Int64Value) Get() interface{} {
|
||||
return int64(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (Int64Value) valueType() valueType {
|
||||
return int64ValueType
|
||||
}
|
||||
|
||||
func (v Int64Value) String() string {
|
||||
return fmt.Sprintf("0x%016x", int64(v))
|
||||
}
|
||||
|
||||
// encode encodes the Int64Value into an eventstream binary value
|
||||
// representation.
|
||||
func (v Int64Value) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
return raw.encodeScalar(w, v)
|
||||
}
|
||||
|
||||
func (v *Int64Value) decode(r io.Reader) error {
|
||||
n, err := decodeUint64(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = Int64Value(n)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An BytesValue provides eventstream encoding, and representation of a Go
|
||||
// byte slice.
|
||||
type BytesValue []byte
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v BytesValue) Get() interface{} {
|
||||
return []byte(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (BytesValue) valueType() valueType {
|
||||
return bytesValueType
|
||||
}
|
||||
|
||||
func (v BytesValue) String() string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
// encode encodes the BytesValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v BytesValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeBytes(w, []byte(v))
|
||||
}
|
||||
|
||||
func (v *BytesValue) decode(r io.Reader) error {
|
||||
buf, err := decodeBytesValue(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = BytesValue(buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An StringValue provides eventstream encoding, and representation of a Go
|
||||
// string.
|
||||
type StringValue string
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v StringValue) Get() interface{} {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (StringValue) valueType() valueType {
|
||||
return stringValueType
|
||||
}
|
||||
|
||||
func (v StringValue) String() string {
|
||||
return string(v)
|
||||
}
|
||||
|
||||
// encode encodes the StringValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v StringValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeString(w, string(v))
|
||||
}
|
||||
|
||||
func (v *StringValue) decode(r io.Reader) error {
|
||||
s, err := decodeStringValue(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = StringValue(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
// An TimestampValue provides eventstream encoding, and representation of a Go
|
||||
// timestamp.
|
||||
type TimestampValue time.Time
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v TimestampValue) Get() interface{} {
|
||||
return time.Time(v)
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (TimestampValue) valueType() valueType {
|
||||
return timestampValueType
|
||||
}
|
||||
|
||||
func (v TimestampValue) epochMilli() int64 {
|
||||
nano := time.Time(v).UnixNano()
|
||||
msec := nano / int64(time.Millisecond)
|
||||
return msec
|
||||
}
|
||||
|
||||
func (v TimestampValue) String() string {
|
||||
msec := v.epochMilli()
|
||||
return strconv.FormatInt(msec, 10)
|
||||
}
|
||||
|
||||
// encode encodes the TimestampValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v TimestampValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
msec := v.epochMilli()
|
||||
return raw.encodeScalar(w, msec)
|
||||
}
|
||||
|
||||
func (v *TimestampValue) decode(r io.Reader) error {
|
||||
n, err := decodeUint64(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*v = TimestampValue(timeFromEpochMilli(int64(n)))
|
||||
return nil
|
||||
}
|
||||
|
||||
func timeFromEpochMilli(t int64) time.Time {
|
||||
secs := t / 1e3
|
||||
msec := t % 1e3
|
||||
return time.Unix(secs, msec*int64(time.Millisecond)).UTC()
|
||||
}
|
||||
|
||||
// An UUIDValue provides eventstream encoding, and representation of a UUID
|
||||
// value.
|
||||
type UUIDValue [16]byte
|
||||
|
||||
// Get returns the underlying value.
|
||||
func (v UUIDValue) Get() interface{} {
|
||||
return v[:]
|
||||
}
|
||||
|
||||
// valueType returns the EventStream header value type value.
|
||||
func (UUIDValue) valueType() valueType {
|
||||
return uuidValueType
|
||||
}
|
||||
|
||||
func (v UUIDValue) String() string {
|
||||
return fmt.Sprintf(`%X-%X-%X-%X-%X`, v[0:4], v[4:6], v[6:8], v[8:10], v[10:])
|
||||
}
|
||||
|
||||
// encode encodes the UUIDValue into an eventstream binary value
|
||||
// representation.
|
||||
func (v UUIDValue) encode(w io.Writer) error {
|
||||
raw := rawValue{
|
||||
Type: v.valueType(),
|
||||
}
|
||||
|
||||
return raw.encodeFixedSlice(w, v[:])
|
||||
}
|
||||
|
||||
func (v *UUIDValue) decode(r io.Reader) error {
|
||||
tv := (*v)[:]
|
||||
return decodeFixedBytesValue(r, tv)
|
||||
}
|
103
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/message.go
generated
vendored
Normal file
103
vendor/github.com/aws/aws-sdk-go/private/protocol/eventstream/message.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package eventstream
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
const preludeLen = 8
|
||||
const preludeCRCLen = 4
|
||||
const msgCRCLen = 4
|
||||
const minMsgLen = preludeLen + preludeCRCLen + msgCRCLen
|
||||
const maxPayloadLen = 1024 * 1024 * 16 // 16MB
|
||||
const maxHeadersLen = 1024 * 128 // 128KB
|
||||
const maxMsgLen = minMsgLen + maxHeadersLen + maxPayloadLen
|
||||
|
||||
var crc32IEEETable = crc32.MakeTable(crc32.IEEE)
|
||||
|
||||
// A Message provides the eventstream message representation.
|
||||
type Message struct {
|
||||
Headers Headers
|
||||
Payload []byte
|
||||
}
|
||||
|
||||
func (m *Message) rawMessage() (rawMessage, error) {
|
||||
var raw rawMessage
|
||||
|
||||
if len(m.Headers) > 0 {
|
||||
var headers bytes.Buffer
|
||||
if err := encodeHeaders(&headers, m.Headers); err != nil {
|
||||
return rawMessage{}, err
|
||||
}
|
||||
raw.Headers = headers.Bytes()
|
||||
raw.HeadersLen = uint32(len(raw.Headers))
|
||||
}
|
||||
|
||||
raw.Length = raw.HeadersLen + uint32(len(m.Payload)) + minMsgLen
|
||||
|
||||
hash := crc32.New(crc32IEEETable)
|
||||
binaryWriteFields(hash, binary.BigEndian, raw.Length, raw.HeadersLen)
|
||||
raw.PreludeCRC = hash.Sum32()
|
||||
|
||||
binaryWriteFields(hash, binary.BigEndian, raw.PreludeCRC)
|
||||
|
||||
if raw.HeadersLen > 0 {
|
||||
hash.Write(raw.Headers)
|
||||
}
|
||||
|
||||
// Read payload bytes and update hash for it as well.
|
||||
if len(m.Payload) > 0 {
|
||||
raw.Payload = m.Payload
|
||||
hash.Write(raw.Payload)
|
||||
}
|
||||
|
||||
raw.CRC = hash.Sum32()
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
type messagePrelude struct {
|
||||
Length uint32
|
||||
HeadersLen uint32
|
||||
PreludeCRC uint32
|
||||
}
|
||||
|
||||
func (p messagePrelude) PayloadLen() uint32 {
|
||||
return p.Length - p.HeadersLen - minMsgLen
|
||||
}
|
||||
|
||||
func (p messagePrelude) ValidateLens() error {
|
||||
if p.Length == 0 || p.Length > maxMsgLen {
|
||||
return LengthError{
|
||||
Part: "message prelude",
|
||||
Want: maxMsgLen,
|
||||
Have: int(p.Length),
|
||||
}
|
||||
}
|
||||
if p.HeadersLen > maxHeadersLen {
|
||||
return LengthError{
|
||||
Part: "message headers",
|
||||
Want: maxHeadersLen,
|
||||
Have: int(p.HeadersLen),
|
||||
}
|
||||
}
|
||||
if payloadLen := p.PayloadLen(); payloadLen > maxPayloadLen {
|
||||
return LengthError{
|
||||
Part: "message payload",
|
||||
Want: maxPayloadLen,
|
||||
Have: int(payloadLen),
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type rawMessage struct {
|
||||
messagePrelude
|
||||
|
||||
Headers []byte
|
||||
Payload []byte
|
||||
|
||||
CRC uint32
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package protocol
|
||||
|
||||
// ValidHostLabel returns if the label is a valid RFC 1123 Section 2.1 domain
|
||||
// host label name.
|
||||
func ValidHostLabel(label string) bool {
|
||||
if l := len(label); l == 0 || l > 63 {
|
||||
return false
|
||||
}
|
||||
for _, r := range label {
|
||||
switch {
|
||||
case r >= '0' && r <= '9':
|
||||
case r >= 'A' && r <= 'Z':
|
||||
case r >= 'a' && r <= 'z':
|
||||
case r == '-':
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -216,7 +216,17 @@ func buildScalar(v reflect.Value, buf *bytes.Buffer, tag reflect.StructTag) erro
|
|||
default:
|
||||
switch converted := value.Interface().(type) {
|
||||
case time.Time:
|
||||
buf.Write(strconv.AppendInt(scratch[:0], converted.UTC().Unix(), 10))
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.UnixTimeFormatName
|
||||
}
|
||||
|
||||
ts := protocol.FormatTime(format, converted)
|
||||
if format != protocol.UnixTimeFormatName {
|
||||
ts = `"` + ts + `"`
|
||||
}
|
||||
|
||||
buf.WriteString(ts)
|
||||
case []byte:
|
||||
if !value.IsNil() {
|
||||
buf.WriteByte('"')
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
|
@ -17,16 +16,10 @@ import (
|
|||
func UnmarshalJSON(v interface{}, stream io.Reader) error {
|
||||
var out interface{}
|
||||
|
||||
b, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(b) == 0 {
|
||||
err := json.NewDecoder(stream).Decode(&out)
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &out); err != nil {
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -172,9 +165,6 @@ func unmarshalMap(value reflect.Value, data interface{}, tag reflect.StructTag)
|
|||
}
|
||||
|
||||
func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTag) error {
|
||||
errf := func() error {
|
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||
}
|
||||
|
||||
switch d := data.(type) {
|
||||
case nil:
|
||||
|
@ -189,6 +179,17 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
|
|||
return err
|
||||
}
|
||||
value.Set(reflect.ValueOf(b))
|
||||
case *time.Time:
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.ISO8601TimeFormatName
|
||||
}
|
||||
|
||||
t, err := protocol.ParseTime(format, d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value.Set(reflect.ValueOf(&t))
|
||||
case aws.JSONValue:
|
||||
// No need to use escaping as the value is a non-quoted string.
|
||||
v, err := protocol.DecodeJSONValue(d, protocol.NoEscape)
|
||||
|
@ -197,7 +198,7 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
|
|||
}
|
||||
value.Set(reflect.ValueOf(v))
|
||||
default:
|
||||
return errf()
|
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||
}
|
||||
case float64:
|
||||
switch value.Interface().(type) {
|
||||
|
@ -207,17 +208,18 @@ func unmarshalScalar(value reflect.Value, data interface{}, tag reflect.StructTa
|
|||
case *float64:
|
||||
value.Set(reflect.ValueOf(&d))
|
||||
case *time.Time:
|
||||
// Time unmarshaled from a float64 can only be epoch seconds
|
||||
t := time.Unix(int64(d), 0).UTC()
|
||||
value.Set(reflect.ValueOf(&t))
|
||||
default:
|
||||
return errf()
|
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||
}
|
||||
case bool:
|
||||
switch value.Interface().(type) {
|
||||
case *bool:
|
||||
value.Set(reflect.ValueOf(&d))
|
||||
default:
|
||||
return errf()
|
||||
return fmt.Errorf("unsupported value: %v (%s)", value.Interface(), value.Type())
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported JSON value (%v)", data)
|
||||
|
|
|
@ -7,7 +7,7 @@ package jsonrpc
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
|
@ -64,7 +64,11 @@ func Unmarshal(req *request.Request) {
|
|||
if req.DataFilled() {
|
||||
err := jsonutil.UnmarshalJSON(req.Data, req.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC response", err)
|
||||
req.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed decoding JSON RPC response", err),
|
||||
req.HTTPResponse.StatusCode,
|
||||
req.RequestID,
|
||||
)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -78,22 +82,22 @@ func UnmarshalMeta(req *request.Request) {
|
|||
// UnmarshalError unmarshals an error response for a JSON RPC service.
|
||||
func UnmarshalError(req *request.Request) {
|
||||
defer req.HTTPResponse.Body.Close()
|
||||
bodyBytes, err := ioutil.ReadAll(req.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
req.Error = awserr.New("SerializationError", "failed reading JSON RPC error response", err)
|
||||
return
|
||||
}
|
||||
if len(bodyBytes) == 0 {
|
||||
|
||||
var jsonErr jsonErrorResponse
|
||||
err := json.NewDecoder(req.HTTPResponse.Body).Decode(&jsonErr)
|
||||
if err == io.EOF {
|
||||
req.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", req.HTTPResponse.Status, nil),
|
||||
req.HTTPResponse.StatusCode,
|
||||
"",
|
||||
req.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
var jsonErr jsonErrorResponse
|
||||
if err := json.Unmarshal(bodyBytes, &jsonErr); err != nil {
|
||||
req.Error = awserr.New("SerializationError", "failed decoding JSON RPC error response", err)
|
||||
} else if err != nil {
|
||||
req.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed decoding JSON RPC error response", err),
|
||||
req.HTTPResponse.StatusCode,
|
||||
req.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/client/metadata"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// PayloadUnmarshaler provides the interface for unmarshaling a payload's
|
||||
// reader into a SDK shape.
|
||||
type PayloadUnmarshaler interface {
|
||||
UnmarshalPayload(io.Reader, interface{}) error
|
||||
}
|
||||
|
||||
// HandlerPayloadUnmarshal implements the PayloadUnmarshaler from a
|
||||
// HandlerList. This provides the support for unmarshaling a payload reader to
|
||||
// a shape without needing a SDK request first.
|
||||
type HandlerPayloadUnmarshal struct {
|
||||
Unmarshalers request.HandlerList
|
||||
}
|
||||
|
||||
// UnmarshalPayload unmarshals the io.Reader payload into the SDK shape using
|
||||
// the Unmarshalers HandlerList provided. Returns an error if unable
|
||||
// unmarshaling fails.
|
||||
func (h HandlerPayloadUnmarshal) UnmarshalPayload(r io.Reader, v interface{}) error {
|
||||
req := &request.Request{
|
||||
HTTPRequest: &http.Request{},
|
||||
HTTPResponse: &http.Response{
|
||||
StatusCode: 200,
|
||||
Header: http.Header{},
|
||||
Body: ioutil.NopCloser(r),
|
||||
},
|
||||
Data: v,
|
||||
}
|
||||
|
||||
h.Unmarshalers.Run(req)
|
||||
|
||||
return req.Error
|
||||
}
|
||||
|
||||
// PayloadMarshaler provides the interface for marshaling a SDK shape into and
|
||||
// io.Writer.
|
||||
type PayloadMarshaler interface {
|
||||
MarshalPayload(io.Writer, interface{}) error
|
||||
}
|
||||
|
||||
// HandlerPayloadMarshal implements the PayloadMarshaler from a HandlerList.
|
||||
// This provides support for marshaling a SDK shape into an io.Writer without
|
||||
// needing a SDK request first.
|
||||
type HandlerPayloadMarshal struct {
|
||||
Marshalers request.HandlerList
|
||||
}
|
||||
|
||||
// MarshalPayload marshals the SDK shape into the io.Writer using the
|
||||
// Marshalers HandlerList provided. Returns an error if unable if marshal
|
||||
// fails.
|
||||
func (h HandlerPayloadMarshal) MarshalPayload(w io.Writer, v interface{}) error {
|
||||
req := request.New(
|
||||
aws.Config{},
|
||||
metadata.ClientInfo{},
|
||||
request.Handlers{},
|
||||
nil,
|
||||
&request.Operation{HTTPMethod: "GET"},
|
||||
v,
|
||||
nil,
|
||||
)
|
||||
|
||||
h.Marshalers.Run(req)
|
||||
|
||||
if req.Error != nil {
|
||||
return req.Error
|
||||
}
|
||||
|
||||
io.Copy(w, req.GetBody())
|
||||
|
||||
return nil
|
||||
}
|
|
@ -25,7 +25,7 @@ func Build(r *request.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if r.ExpireTime == 0 {
|
||||
if !r.IsPresigned() {
|
||||
r.HTTPRequest.Method = "POST"
|
||||
r.HTTPRequest.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=utf-8")
|
||||
r.SetBufferBody([]byte(body.Encode()))
|
||||
|
|
|
@ -233,7 +233,12 @@ func (q *queryParser) parseScalar(v url.Values, r reflect.Value, name string, ta
|
|||
v.Set(name, strconv.FormatFloat(float64(value), 'f', -1, 32))
|
||||
case time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
v.Set(name, value.UTC().Format(ISO8601UTC))
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.ISO8601TimeFormatName
|
||||
}
|
||||
|
||||
v.Set(name, protocol.FormatTime(format, value))
|
||||
default:
|
||||
return fmt.Errorf("unsupported value for param %s: %v (%s)", name, r.Interface(), r.Type().Name())
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@ func Unmarshal(r *request.Request) {
|
|||
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||
err := xmlutil.UnmarshalXML(r.Data, decoder, r.Operation.Name+"Result")
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed decoding Query response", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed decoding Query response", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,11 @@ func UnmarshalError(r *request.Request) {
|
|||
|
||||
bodyBytes, err := ioutil.ReadAll(r.HTTPResponse.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed to read from query HTTP response body", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed to read from query HTTP response body", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -61,6 +65,10 @@ func UnmarshalError(r *request.Request) {
|
|||
}
|
||||
|
||||
// Failed to retrieve any error message from the response body
|
||||
r.Error = awserr.New("SerializationError",
|
||||
"failed to decode query XML error response", decodeErr)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError",
|
||||
"failed to decode query XML error response", decodeErr),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@ import (
|
|||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
)
|
||||
|
||||
// RFC822 returns an RFC822 formatted timestamp for AWS protocols
|
||||
const RFC822 = "Mon, 2 Jan 2006 15:04:05 GMT"
|
||||
|
||||
// Whether the byte value can be sent without escaping in AWS URLs
|
||||
var noEscape [256]bool
|
||||
|
||||
|
@ -270,7 +267,14 @@ func convertType(v reflect.Value, tag reflect.StructTag) (str string, err error)
|
|||
case float64:
|
||||
str = strconv.FormatFloat(value, 'f', -1, 64)
|
||||
case time.Time:
|
||||
str = value.UTC().Format(RFC822)
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.RFC822TimeFormatName
|
||||
if tag.Get("location") == "querystring" {
|
||||
format = protocol.ISO8601TimeFormatName
|
||||
}
|
||||
}
|
||||
str = protocol.FormatTime(format, value)
|
||||
case aws.JSONValue:
|
||||
if len(value) == 0 {
|
||||
return "", errValueNotSet
|
||||
|
|
|
@ -198,7 +198,11 @@ func unmarshalHeader(v reflect.Value, header string, tag reflect.StructTag) erro
|
|||
}
|
||||
v.Set(reflect.ValueOf(&f))
|
||||
case *time.Time:
|
||||
t, err := time.Parse(RFC822, header)
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.RFC822TimeFormatName
|
||||
}
|
||||
t, err := protocol.ParseTime(format, header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -36,7 +36,11 @@ func Build(r *request.Request) {
|
|||
var buf bytes.Buffer
|
||||
err := xmlutil.BuildXML(r.Params, xml.NewEncoder(&buf))
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed to encode rest XML request", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed to encode rest XML request", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
r.SetBufferBody(buf.Bytes())
|
||||
|
@ -50,7 +54,11 @@ func Unmarshal(r *request.Request) {
|
|||
decoder := xml.NewDecoder(r.HTTPResponse.Body)
|
||||
err := xmlutil.UnmarshalXML(r.Data, decoder, "")
|
||||
if err != nil {
|
||||
r.Error = awserr.New("SerializationError", "failed to decode REST XML response", err)
|
||||
r.Error = awserr.NewRequestFailure(
|
||||
awserr.New("SerializationError", "failed to decode REST XML response", err),
|
||||
r.HTTPResponse.StatusCode,
|
||||
r.RequestID,
|
||||
)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Names of time formats supported by the SDK
|
||||
const (
|
||||
RFC822TimeFormatName = "rfc822"
|
||||
ISO8601TimeFormatName = "iso8601"
|
||||
UnixTimeFormatName = "unixTimestamp"
|
||||
)
|
||||
|
||||
// Time formats supported by the SDK
|
||||
const (
|
||||
// RFC 7231#section-7.1.1.1 timetamp format. e.g Tue, 29 Apr 2014 18:30:38 GMT
|
||||
RFC822TimeFormat = "Mon, 2 Jan 2006 15:04:05 GMT"
|
||||
|
||||
// RFC3339 a subset of the ISO8601 timestamp format. e.g 2014-04-29T18:30:38Z
|
||||
ISO8601TimeFormat = "2006-01-02T15:04:05Z"
|
||||
)
|
||||
|
||||
// IsKnownTimestampFormat returns if the timestamp format name
|
||||
// is know to the SDK's protocols.
|
||||
func IsKnownTimestampFormat(name string) bool {
|
||||
switch name {
|
||||
case RFC822TimeFormatName:
|
||||
fallthrough
|
||||
case ISO8601TimeFormatName:
|
||||
fallthrough
|
||||
case UnixTimeFormatName:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// FormatTime returns a string value of the time.
|
||||
func FormatTime(name string, t time.Time) string {
|
||||
t = t.UTC()
|
||||
|
||||
switch name {
|
||||
case RFC822TimeFormatName:
|
||||
return t.Format(RFC822TimeFormat)
|
||||
case ISO8601TimeFormatName:
|
||||
return t.Format(ISO8601TimeFormat)
|
||||
case UnixTimeFormatName:
|
||||
return strconv.FormatInt(t.Unix(), 10)
|
||||
default:
|
||||
panic("unknown timestamp format name, " + name)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseTime attempts to parse the time given the format. Returns
|
||||
// the time if it was able to be parsed, and fails otherwise.
|
||||
func ParseTime(formatName, value string) (time.Time, error) {
|
||||
switch formatName {
|
||||
case RFC822TimeFormatName:
|
||||
return time.Parse(RFC822TimeFormat, value)
|
||||
case ISO8601TimeFormatName:
|
||||
return time.Parse(ISO8601TimeFormat, value)
|
||||
case UnixTimeFormatName:
|
||||
v, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
return time.Unix(int64(v), 0), nil
|
||||
default:
|
||||
panic("unknown timestamp format name, " + formatName)
|
||||
}
|
||||
}
|
|
@ -13,9 +13,13 @@ import (
|
|||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
)
|
||||
|
||||
// BuildXML will serialize params into an xml.Encoder.
|
||||
// Error will be returned if the serialization of any of the params or nested values fails.
|
||||
// BuildXML will serialize params into an xml.Encoder. Error will be returned
|
||||
// if the serialization of any of the params or nested values fails.
|
||||
func BuildXML(params interface{}, e *xml.Encoder) error {
|
||||
return buildXML(params, e, false)
|
||||
}
|
||||
|
||||
func buildXML(params interface{}, e *xml.Encoder, sorted bool) error {
|
||||
b := xmlBuilder{encoder: e, namespaces: map[string]string{}}
|
||||
root := NewXMLElement(xml.Name{})
|
||||
if err := b.buildValue(reflect.ValueOf(params), root, ""); err != nil {
|
||||
|
@ -23,7 +27,7 @@ func BuildXML(params interface{}, e *xml.Encoder) error {
|
|||
}
|
||||
for _, c := range root.Children {
|
||||
for _, v := range c {
|
||||
return StructToXML(e, v, false)
|
||||
return StructToXML(e, v, sorted)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -90,8 +94,6 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
|
|||
return nil
|
||||
}
|
||||
|
||||
fieldAdded := false
|
||||
|
||||
// unwrap payloads
|
||||
if payload := tag.Get("payload"); payload != "" {
|
||||
field, _ := value.Type().FieldByName(payload)
|
||||
|
@ -119,6 +121,8 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
|
|||
child.Attr = append(child.Attr, ns)
|
||||
}
|
||||
|
||||
var payloadFields, nonPayloadFields int
|
||||
|
||||
t := value.Type()
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
member := elemOf(value.Field(i))
|
||||
|
@ -133,8 +137,10 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
|
|||
|
||||
mTag := field.Tag
|
||||
if mTag.Get("location") != "" { // skip non-body members
|
||||
nonPayloadFields++
|
||||
continue
|
||||
}
|
||||
payloadFields++
|
||||
|
||||
if protocol.CanSetIdempotencyToken(value.Field(i), field) {
|
||||
token := protocol.GetIdempotencyToken()
|
||||
|
@ -149,11 +155,11 @@ func (b *xmlBuilder) buildStruct(value reflect.Value, current *XMLNode, tag refl
|
|||
if err := b.buildValue(member, child, mTag); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fieldAdded = true
|
||||
}
|
||||
|
||||
if fieldAdded { // only append this child if we have one ore more valid members
|
||||
// Only case where the child shape is not added is if the shape only contains
|
||||
// non-payload fields, e.g headers/query.
|
||||
if !(payloadFields == 0 && nonPayloadFields > 0) {
|
||||
current.AddChild(child)
|
||||
}
|
||||
|
||||
|
@ -278,8 +284,12 @@ func (b *xmlBuilder) buildScalar(value reflect.Value, current *XMLNode, tag refl
|
|||
case float32:
|
||||
str = strconv.FormatFloat(float64(converted), 'f', -1, 32)
|
||||
case time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
str = converted.UTC().Format(ISO8601UTC)
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.ISO8601TimeFormatName
|
||||
}
|
||||
|
||||
str = protocol.FormatTime(format, converted)
|
||||
default:
|
||||
return fmt.Errorf("unsupported value for param %s: %v (%s)",
|
||||
tag.Get("locationName"), value.Interface(), value.Type().Name())
|
||||
|
|
|
@ -9,6 +9,8 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/private/protocol"
|
||||
)
|
||||
|
||||
// UnmarshalXML deserializes an xml.Decoder into the container v. V
|
||||
|
@ -52,9 +54,15 @@ func parse(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|||
if t == "" {
|
||||
switch rtype.Kind() {
|
||||
case reflect.Struct:
|
||||
t = "structure"
|
||||
// also it can't be a time object
|
||||
if _, ok := r.Interface().(*time.Time); !ok {
|
||||
t = "structure"
|
||||
}
|
||||
case reflect.Slice:
|
||||
t = "list"
|
||||
// also it can't be a byte slice
|
||||
if _, ok := r.Interface().([]byte); !ok {
|
||||
t = "list"
|
||||
}
|
||||
case reflect.Map:
|
||||
t = "map"
|
||||
}
|
||||
|
@ -247,8 +255,12 @@ func parseScalar(r reflect.Value, node *XMLNode, tag reflect.StructTag) error {
|
|||
}
|
||||
r.Set(reflect.ValueOf(&v))
|
||||
case *time.Time:
|
||||
const ISO8601UTC = "2006-01-02T15:04:05Z"
|
||||
t, err := time.Parse(ISO8601UTC, node.Text)
|
||||
format := tag.Get("timestampFormat")
|
||||
if len(format) == 0 {
|
||||
format = protocol.ISO8601TimeFormatName
|
||||
}
|
||||
|
||||
t, err := protocol.ParseTime(format, node.Text)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ func NewXMLElement(name xml.Name) *XMLNode {
|
|||
|
||||
// AddChild adds child to the XMLNode.
|
||||
func (n *XMLNode) AddChild(child *XMLNode) {
|
||||
child.parent = n
|
||||
if _, ok := n.Children[child.Name.Local]; !ok {
|
||||
n.Children[child.Name.Local] = []*XMLNode{}
|
||||
}
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
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...))
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -5,11 +5,64 @@ import (
|
|||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awsutil"
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/endpoints"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkrand"
|
||||
)
|
||||
|
||||
type retryer struct {
|
||||
client.DefaultRetryer
|
||||
}
|
||||
|
||||
func (d retryer) RetryRules(r *request.Request) time.Duration {
|
||||
switch r.Operation.Name {
|
||||
case opModifyNetworkInterfaceAttribute:
|
||||
fallthrough
|
||||
case opAssignPrivateIpAddresses:
|
||||
return customRetryRule(r)
|
||||
default:
|
||||
return d.DefaultRetryer.RetryRules(r)
|
||||
}
|
||||
}
|
||||
|
||||
func customRetryRule(r *request.Request) time.Duration {
|
||||
retryTimes := []time.Duration{
|
||||
time.Second,
|
||||
3 * time.Second,
|
||||
5 * time.Second,
|
||||
}
|
||||
|
||||
count := r.RetryCount
|
||||
if count >= len(retryTimes) {
|
||||
count = len(retryTimes) - 1
|
||||
}
|
||||
|
||||
minTime := int(retryTimes[count])
|
||||
return time.Duration(sdkrand.SeededRand.Intn(minTime) + minTime)
|
||||
}
|
||||
|
||||
func setCustomRetryer(c *client.Client) {
|
||||
maxRetries := aws.IntValue(c.Config.MaxRetries)
|
||||
if c.Config.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
|
||||
maxRetries = 3
|
||||
}
|
||||
|
||||
c.Retryer = retryer{
|
||||
DefaultRetryer: client.DefaultRetryer{
|
||||
NumMaxRetries: maxRetries,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
initClient = func(c *client.Client) {
|
||||
if c.Config.Retryer == nil {
|
||||
// Only override the retryer with a custom one if the config
|
||||
// does not already contain a retryer
|
||||
setCustomRetryer(c)
|
||||
}
|
||||
}
|
||||
initRequest = func(r *request.Request) {
|
||||
if r.Operation.Name == opCopySnapshot { // fill the PresignedURL parameter
|
||||
r.Handlers.Build.PushFront(fillPresignedURL)
|
||||
|
|
|
@ -3,9 +3,22 @@
|
|||
// 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 AWS Cloud. Using Amazon EC2 eliminates your need to invest in hardware
|
||||
// up front, so you can develop and deploy applications faster.
|
||||
// Amazon Elastic Compute Cloud (Amazon EC2) provides secure and resizable computing
|
||||
// capacity in the AWS cloud. Using Amazon EC2 eliminates the need to invest
|
||||
// in hardware up front, so you can develop and deploy applications faster.
|
||||
//
|
||||
// To learn more about Amazon EC2, Amazon EBS, and Amazon VPC, see the following
|
||||
// resources:
|
||||
//
|
||||
// * Amazon EC2 product page (http://aws.amazon.com/ec2)
|
||||
//
|
||||
// * Amazon EC2 documentation (http://aws.amazon.com/documentation/ec2)
|
||||
//
|
||||
// * Amazon EBS product page (http://aws.amazon.com/ebs)
|
||||
//
|
||||
// * Amazon VPC product page (http://aws.amazon.com/vpc)
|
||||
//
|
||||
// * Amazon VPC documentation (http://aws.amazon.com/documentation/vpc)
|
||||
//
|
||||
// See https://docs.aws.amazon.com/goto/WebAPI/ec2-2016-11-15 for more information on this service.
|
||||
//
|
||||
|
|
|
@ -208,6 +208,10 @@ type EC2API interface {
|
|||
CreateEgressOnlyInternetGatewayWithContext(aws.Context, *ec2.CreateEgressOnlyInternetGatewayInput, ...request.Option) (*ec2.CreateEgressOnlyInternetGatewayOutput, error)
|
||||
CreateEgressOnlyInternetGatewayRequest(*ec2.CreateEgressOnlyInternetGatewayInput) (*request.Request, *ec2.CreateEgressOnlyInternetGatewayOutput)
|
||||
|
||||
CreateFleet(*ec2.CreateFleetInput) (*ec2.CreateFleetOutput, error)
|
||||
CreateFleetWithContext(aws.Context, *ec2.CreateFleetInput, ...request.Option) (*ec2.CreateFleetOutput, error)
|
||||
CreateFleetRequest(*ec2.CreateFleetInput) (*request.Request, *ec2.CreateFleetOutput)
|
||||
|
||||
CreateFlowLogs(*ec2.CreateFlowLogsInput) (*ec2.CreateFlowLogsOutput, error)
|
||||
CreateFlowLogsWithContext(aws.Context, *ec2.CreateFlowLogsInput, ...request.Option) (*ec2.CreateFlowLogsOutput, error)
|
||||
CreateFlowLogsRequest(*ec2.CreateFlowLogsInput) (*request.Request, *ec2.CreateFlowLogsOutput)
|
||||
|
@ -344,6 +348,10 @@ type EC2API interface {
|
|||
DeleteEgressOnlyInternetGatewayWithContext(aws.Context, *ec2.DeleteEgressOnlyInternetGatewayInput, ...request.Option) (*ec2.DeleteEgressOnlyInternetGatewayOutput, error)
|
||||
DeleteEgressOnlyInternetGatewayRequest(*ec2.DeleteEgressOnlyInternetGatewayInput) (*request.Request, *ec2.DeleteEgressOnlyInternetGatewayOutput)
|
||||
|
||||
DeleteFleets(*ec2.DeleteFleetsInput) (*ec2.DeleteFleetsOutput, error)
|
||||
DeleteFleetsWithContext(aws.Context, *ec2.DeleteFleetsInput, ...request.Option) (*ec2.DeleteFleetsOutput, error)
|
||||
DeleteFleetsRequest(*ec2.DeleteFleetsInput) (*request.Request, *ec2.DeleteFleetsOutput)
|
||||
|
||||
DeleteFlowLogs(*ec2.DeleteFlowLogsInput) (*ec2.DeleteFlowLogsOutput, error)
|
||||
DeleteFlowLogsWithContext(aws.Context, *ec2.DeleteFlowLogsInput, ...request.Option) (*ec2.DeleteFlowLogsOutput, error)
|
||||
DeleteFlowLogsRequest(*ec2.DeleteFlowLogsInput) (*request.Request, *ec2.DeleteFlowLogsOutput)
|
||||
|
@ -468,6 +476,10 @@ type EC2API interface {
|
|||
DescribeAddressesWithContext(aws.Context, *ec2.DescribeAddressesInput, ...request.Option) (*ec2.DescribeAddressesOutput, error)
|
||||
DescribeAddressesRequest(*ec2.DescribeAddressesInput) (*request.Request, *ec2.DescribeAddressesOutput)
|
||||
|
||||
DescribeAggregateIdFormat(*ec2.DescribeAggregateIdFormatInput) (*ec2.DescribeAggregateIdFormatOutput, error)
|
||||
DescribeAggregateIdFormatWithContext(aws.Context, *ec2.DescribeAggregateIdFormatInput, ...request.Option) (*ec2.DescribeAggregateIdFormatOutput, error)
|
||||
DescribeAggregateIdFormatRequest(*ec2.DescribeAggregateIdFormatInput) (*request.Request, *ec2.DescribeAggregateIdFormatOutput)
|
||||
|
||||
DescribeAvailabilityZones(*ec2.DescribeAvailabilityZonesInput) (*ec2.DescribeAvailabilityZonesOutput, error)
|
||||
DescribeAvailabilityZonesWithContext(aws.Context, *ec2.DescribeAvailabilityZonesInput, ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error)
|
||||
DescribeAvailabilityZonesRequest(*ec2.DescribeAvailabilityZonesInput) (*request.Request, *ec2.DescribeAvailabilityZonesOutput)
|
||||
|
@ -504,6 +516,18 @@ type EC2API interface {
|
|||
DescribeExportTasksWithContext(aws.Context, *ec2.DescribeExportTasksInput, ...request.Option) (*ec2.DescribeExportTasksOutput, error)
|
||||
DescribeExportTasksRequest(*ec2.DescribeExportTasksInput) (*request.Request, *ec2.DescribeExportTasksOutput)
|
||||
|
||||
DescribeFleetHistory(*ec2.DescribeFleetHistoryInput) (*ec2.DescribeFleetHistoryOutput, error)
|
||||
DescribeFleetHistoryWithContext(aws.Context, *ec2.DescribeFleetHistoryInput, ...request.Option) (*ec2.DescribeFleetHistoryOutput, error)
|
||||
DescribeFleetHistoryRequest(*ec2.DescribeFleetHistoryInput) (*request.Request, *ec2.DescribeFleetHistoryOutput)
|
||||
|
||||
DescribeFleetInstances(*ec2.DescribeFleetInstancesInput) (*ec2.DescribeFleetInstancesOutput, error)
|
||||
DescribeFleetInstancesWithContext(aws.Context, *ec2.DescribeFleetInstancesInput, ...request.Option) (*ec2.DescribeFleetInstancesOutput, error)
|
||||
DescribeFleetInstancesRequest(*ec2.DescribeFleetInstancesInput) (*request.Request, *ec2.DescribeFleetInstancesOutput)
|
||||
|
||||
DescribeFleets(*ec2.DescribeFleetsInput) (*ec2.DescribeFleetsOutput, error)
|
||||
DescribeFleetsWithContext(aws.Context, *ec2.DescribeFleetsInput, ...request.Option) (*ec2.DescribeFleetsOutput, error)
|
||||
DescribeFleetsRequest(*ec2.DescribeFleetsInput) (*request.Request, *ec2.DescribeFleetsOutput)
|
||||
|
||||
DescribeFlowLogs(*ec2.DescribeFlowLogsInput) (*ec2.DescribeFlowLogsOutput, error)
|
||||
DescribeFlowLogsWithContext(aws.Context, *ec2.DescribeFlowLogsInput, ...request.Option) (*ec2.DescribeFlowLogsOutput, error)
|
||||
DescribeFlowLogsRequest(*ec2.DescribeFlowLogsInput) (*request.Request, *ec2.DescribeFlowLogsOutput)
|
||||
|
@ -621,6 +645,9 @@ type EC2API interface {
|
|||
DescribeNetworkInterfacesWithContext(aws.Context, *ec2.DescribeNetworkInterfacesInput, ...request.Option) (*ec2.DescribeNetworkInterfacesOutput, error)
|
||||
DescribeNetworkInterfacesRequest(*ec2.DescribeNetworkInterfacesInput) (*request.Request, *ec2.DescribeNetworkInterfacesOutput)
|
||||
|
||||
DescribeNetworkInterfacesPages(*ec2.DescribeNetworkInterfacesInput, func(*ec2.DescribeNetworkInterfacesOutput, bool) bool) error
|
||||
DescribeNetworkInterfacesPagesWithContext(aws.Context, *ec2.DescribeNetworkInterfacesInput, func(*ec2.DescribeNetworkInterfacesOutput, bool) bool, ...request.Option) error
|
||||
|
||||
DescribePlacementGroups(*ec2.DescribePlacementGroupsInput) (*ec2.DescribePlacementGroupsOutput, error)
|
||||
DescribePlacementGroupsWithContext(aws.Context, *ec2.DescribePlacementGroupsInput, ...request.Option) (*ec2.DescribePlacementGroupsOutput, error)
|
||||
DescribePlacementGroupsRequest(*ec2.DescribePlacementGroupsInput) (*request.Request, *ec2.DescribePlacementGroupsOutput)
|
||||
|
@ -629,6 +656,10 @@ type EC2API interface {
|
|||
DescribePrefixListsWithContext(aws.Context, *ec2.DescribePrefixListsInput, ...request.Option) (*ec2.DescribePrefixListsOutput, error)
|
||||
DescribePrefixListsRequest(*ec2.DescribePrefixListsInput) (*request.Request, *ec2.DescribePrefixListsOutput)
|
||||
|
||||
DescribePrincipalIdFormat(*ec2.DescribePrincipalIdFormatInput) (*ec2.DescribePrincipalIdFormatOutput, error)
|
||||
DescribePrincipalIdFormatWithContext(aws.Context, *ec2.DescribePrincipalIdFormatInput, ...request.Option) (*ec2.DescribePrincipalIdFormatOutput, error)
|
||||
DescribePrincipalIdFormatRequest(*ec2.DescribePrincipalIdFormatInput) (*request.Request, *ec2.DescribePrincipalIdFormatOutput)
|
||||
|
||||
DescribeRegions(*ec2.DescribeRegionsInput) (*ec2.DescribeRegionsOutput, error)
|
||||
DescribeRegionsWithContext(aws.Context, *ec2.DescribeRegionsInput, ...request.Option) (*ec2.DescribeRegionsOutput, error)
|
||||
DescribeRegionsRequest(*ec2.DescribeRegionsInput) (*request.Request, *ec2.DescribeRegionsOutput)
|
||||
|
@ -917,6 +948,10 @@ type EC2API interface {
|
|||
ImportVolumeWithContext(aws.Context, *ec2.ImportVolumeInput, ...request.Option) (*ec2.ImportVolumeOutput, error)
|
||||
ImportVolumeRequest(*ec2.ImportVolumeInput) (*request.Request, *ec2.ImportVolumeOutput)
|
||||
|
||||
ModifyFleet(*ec2.ModifyFleetInput) (*ec2.ModifyFleetOutput, error)
|
||||
ModifyFleetWithContext(aws.Context, *ec2.ModifyFleetInput, ...request.Option) (*ec2.ModifyFleetOutput, error)
|
||||
ModifyFleetRequest(*ec2.ModifyFleetInput) (*request.Request, *ec2.ModifyFleetOutput)
|
||||
|
||||
ModifyFpgaImageAttribute(*ec2.ModifyFpgaImageAttributeInput) (*ec2.ModifyFpgaImageAttributeOutput, error)
|
||||
ModifyFpgaImageAttributeWithContext(aws.Context, *ec2.ModifyFpgaImageAttributeInput, ...request.Option) (*ec2.ModifyFpgaImageAttributeOutput, error)
|
||||
ModifyFpgaImageAttributeRequest(*ec2.ModifyFpgaImageAttributeInput) (*request.Request, *ec2.ModifyFpgaImageAttributeOutput)
|
||||
|
|
|
@ -29,8 +29,9 @@ var initRequest func(*request.Request)
|
|||
|
||||
// Service information constants
|
||||
const (
|
||||
ServiceName = "ec2" // Service endpoint prefix API calls made to.
|
||||
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
|
||||
ServiceName = "ec2" // Name of service.
|
||||
EndpointsID = ServiceName // ID to lookup a service endpoint with.
|
||||
ServiceID = "EC2" // ServiceID is a unique identifer of a specific service.
|
||||
)
|
||||
|
||||
// New creates a new instance of the EC2 client with a session.
|
||||
|
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
ServiceID: ServiceID,
|
||||
SigningName: signingName,
|
||||
SigningRegion: signingRegion,
|
||||
Endpoint: endpoint,
|
||||
|
|
|
@ -14,8 +14,8 @@ const opBatchCheckLayerAvailability = "BatchCheckLayerAvailability"
|
|||
|
||||
// BatchCheckLayerAvailabilityRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the BatchCheckLayerAvailability operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -106,8 +106,8 @@ const opBatchDeleteImage = "BatchDeleteImage"
|
|||
|
||||
// BatchDeleteImageRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the BatchDeleteImage operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -201,8 +201,8 @@ const opBatchGetImage = "BatchGetImage"
|
|||
|
||||
// BatchGetImageRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the BatchGetImage operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -289,8 +289,8 @@ const opCompleteLayerUpload = "CompleteLayerUpload"
|
|||
|
||||
// CompleteLayerUploadRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the CompleteLayerUpload operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -399,8 +399,8 @@ const opCreateRepository = "CreateRepository"
|
|||
|
||||
// CreateRepositoryRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the CreateRepository operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -463,7 +463,7 @@ func (c *ECR) CreateRepositoryRequest(input *CreateRepositoryInput) (req *reques
|
|||
// The operation did not succeed because it would have exceeded a service limit
|
||||
// for your account. For more information, see Amazon ECR Default Service Limits
|
||||
// (http://docs.aws.amazon.com/AmazonECR/latest/userguide/service_limits.html)
|
||||
// in the Amazon EC2 Container Registry User Guide.
|
||||
// in the Amazon Elastic Container Registry User Guide.
|
||||
//
|
||||
// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/CreateRepository
|
||||
func (c *ECR) CreateRepository(input *CreateRepositoryInput) (*CreateRepositoryOutput, error) {
|
||||
|
@ -491,8 +491,8 @@ const opDeleteLifecyclePolicy = "DeleteLifecyclePolicy"
|
|||
|
||||
// DeleteLifecyclePolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the DeleteLifecyclePolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -581,8 +581,8 @@ const opDeleteRepository = "DeleteRepository"
|
|||
|
||||
// DeleteRepositoryRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the DeleteRepository operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -673,8 +673,8 @@ const opDeleteRepositoryPolicy = "DeleteRepositoryPolicy"
|
|||
|
||||
// DeleteRepositoryPolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the DeleteRepositoryPolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -764,8 +764,8 @@ const opDescribeImages = "DescribeImages"
|
|||
|
||||
// DescribeImagesRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the DescribeImages operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -916,8 +916,8 @@ const opDescribeRepositories = "DescribeRepositories"
|
|||
|
||||
// DescribeRepositoriesRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the DescribeRepositories operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1059,8 +1059,8 @@ const opGetAuthorizationToken = "GetAuthorizationToken"
|
|||
|
||||
// GetAuthorizationTokenRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the GetAuthorizationToken operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1149,8 +1149,8 @@ const opGetDownloadUrlForLayer = "GetDownloadUrlForLayer"
|
|||
|
||||
// GetDownloadUrlForLayerRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the GetDownloadUrlForLayer operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1249,8 +1249,8 @@ const opGetLifecyclePolicy = "GetLifecyclePolicy"
|
|||
|
||||
// GetLifecyclePolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the GetLifecyclePolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1339,8 +1339,8 @@ const opGetLifecyclePolicyPreview = "GetLifecyclePolicyPreview"
|
|||
|
||||
// GetLifecyclePolicyPreviewRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the GetLifecyclePolicyPreview operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1429,8 +1429,8 @@ const opGetRepositoryPolicy = "GetRepositoryPolicy"
|
|||
|
||||
// GetRepositoryPolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the GetRepositoryPolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1520,8 +1520,8 @@ const opInitiateLayerUpload = "InitiateLayerUpload"
|
|||
|
||||
// InitiateLayerUploadRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the InitiateLayerUpload operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1611,8 +1611,8 @@ const opListImages = "ListImages"
|
|||
|
||||
// ListImagesRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the ListImages operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1760,8 +1760,8 @@ const opPutImage = "PutImage"
|
|||
|
||||
// PutImageRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the PutImage operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1837,7 +1837,7 @@ func (c *ECR) PutImageRequest(input *PutImageInput) (req *request.Request, outpu
|
|||
// The operation did not succeed because it would have exceeded a service limit
|
||||
// for your account. For more information, see Amazon ECR Default Service Limits
|
||||
// (http://docs.aws.amazon.com/AmazonECR/latest/userguide/service_limits.html)
|
||||
// in the Amazon EC2 Container Registry User Guide.
|
||||
// in the Amazon Elastic Container Registry User Guide.
|
||||
//
|
||||
// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/PutImage
|
||||
func (c *ECR) PutImage(input *PutImageInput) (*PutImageOutput, error) {
|
||||
|
@ -1865,8 +1865,8 @@ const opPutLifecyclePolicy = "PutLifecyclePolicy"
|
|||
|
||||
// PutLifecyclePolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the PutLifecyclePolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -1905,7 +1905,8 @@ func (c *ECR) PutLifecyclePolicyRequest(input *PutLifecyclePolicyInput) (req *re
|
|||
|
||||
// PutLifecyclePolicy API operation for Amazon EC2 Container Registry.
|
||||
//
|
||||
// Creates or updates a lifecycle policy.
|
||||
// Creates or updates a lifecycle policy. For information about lifecycle policy
|
||||
// syntax, see Lifecycle Policy Template (http://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html).
|
||||
//
|
||||
// Returns awserr.Error for service API and SDK errors. Use runtime type assertions
|
||||
// with awserr.Error's Code and Message methods to get detailed information about
|
||||
|
@ -1952,8 +1953,8 @@ const opSetRepositoryPolicy = "SetRepositoryPolicy"
|
|||
|
||||
// SetRepositoryPolicyRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the SetRepositoryPolicy operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -2039,8 +2040,8 @@ const opStartLifecyclePolicyPreview = "StartLifecyclePolicyPreview"
|
|||
|
||||
// StartLifecyclePolicyPreviewRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the StartLifecyclePolicyPreview operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -2134,8 +2135,8 @@ const opUploadLayerPart = "UploadLayerPart"
|
|||
|
||||
// UploadLayerPartRequest generates a "aws/request.Request" representing the
|
||||
// client's request for the UploadLayerPart operation. The "output" return
|
||||
// value will be populated with the request's response once the request complets
|
||||
// successfuly.
|
||||
// value will be populated with the request's response once the request completes
|
||||
// successfully.
|
||||
//
|
||||
// Use "Send" method on the returned Request to send the API call to the service.
|
||||
// the "output" return value is not valid until after Send returns without error.
|
||||
|
@ -2211,7 +2212,7 @@ func (c *ECR) UploadLayerPartRequest(input *UploadLayerPartInput) (req *request.
|
|||
// The operation did not succeed because it would have exceeded a service limit
|
||||
// for your account. For more information, see Amazon ECR Default Service Limits
|
||||
// (http://docs.aws.amazon.com/AmazonECR/latest/userguide/service_limits.html)
|
||||
// in the Amazon EC2 Container Registry User Guide.
|
||||
// in the Amazon Elastic Container Registry User Guide.
|
||||
//
|
||||
// See also, https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21/UploadLayerPart
|
||||
func (c *ECR) UploadLayerPart(input *UploadLayerPartInput) (*UploadLayerPartOutput, error) {
|
||||
|
@ -2246,7 +2247,7 @@ type AuthorizationData struct {
|
|||
|
||||
// The Unix time in seconds and milliseconds when the authorization token expires.
|
||||
// Authorization tokens are valid for 12 hours.
|
||||
ExpiresAt *time.Time `locationName:"expiresAt" type:"timestamp" timestampFormat:"unix"`
|
||||
ExpiresAt *time.Time `locationName:"expiresAt" type:"timestamp"`
|
||||
|
||||
// The registry URL to use for this authorization token in a docker login command.
|
||||
// The Amazon ECR registry URL format is https://aws_account_id.dkr.ecr.region.amazonaws.com.
|
||||
|
@ -2808,8 +2809,7 @@ type DeleteLifecyclePolicyInput struct {
|
|||
// If you do not specify a registry, the default registry is assumed.
|
||||
RegistryId *string `locationName:"registryId" type:"string"`
|
||||
|
||||
// The name of the repository that is associated with the repository policy
|
||||
// to
delete.
|
||||
// The name of the repository.
|
||||
//
|
||||
// RepositoryName is a required field
|
||||
RepositoryName *string `locationName:"repositoryName" min:"2" type:"string" required:"true"`
|
||||
|
@ -2857,9 +2857,9 @@ type DeleteLifecyclePolicyOutput struct {
|
|||
_ struct{} `type:"structure"`
|
||||
|
||||
// The time stamp of the last time that the lifecycle policy was run.
|
||||
LastEvaluatedAt *time.Time `locationName:"lastEvaluatedAt" type:"timestamp" timestampFormat:"unix"`
|
||||
LastEvaluatedAt *time.Time `locationName:"lastEvaluatedAt" type:"timestamp"`
|
||||
|
||||
// The JSON repository policy text.
|
||||
// The JSON lifecycle policy text.
|
||||
LifecyclePolicyText *string `locationName:"lifecyclePolicyText" min:"100" type:"string"`
|
||||
|
||||
// The registry ID associated with the request.
|
||||
|
@ -3120,13 +3120,15 @@ type DescribeImagesInput struct {
|
|||
// results of the initial request can be seen by sending another DescribeImages
|
||||
// request with the returned nextToken value. This value can be between 1 and
|
||||
// 100. If this parameter is not used, then DescribeImages returns up to 100
|
||||
// results and a nextToken value, if applicable.
|
||||
// results and a nextToken value, if applicable. This option cannot be used
|
||||
// when you specify images with imageIds.
|
||||
MaxResults *int64 `locationName:"maxResults" min:"1" type:"integer"`
|
||||
|
||||
// The nextToken value returned from a previous paginated DescribeImages request
|
||||
// where maxResults was used and the results exceeded the value of that parameter.
|
||||
// Pagination continues from the end of the previous results that returned the
|
||||
// nextToken value. This value is null when there are no more results to return.
|
||||
// This option cannot be used when you specify images with imageIds.
|
||||
NextToken *string `locationName:"nextToken" type:"string"`
|
||||
|
||||
// The AWS account ID associated with the registry that contains the repository
|
||||
|
@ -3253,14 +3255,16 @@ type DescribeRepositoriesInput struct {
|
|||
// element. The remaining results of the initial request can be seen by sending
|
||||
// another DescribeRepositories request with the returned nextToken value. This
|
||||
// value can be between 1 and 100. If this parameter is not used, then DescribeRepositories
|
||||
// returns up to 100 results and a nextToken value, if applicable.
|
||||
// returns up to 100 results and a nextToken value, if applicable. This option
|
||||
// cannot be used when you specify repositories with repositoryNames.
|
||||
MaxResults *int64 `locationName:"maxResults" min:"1" type:"integer"`
|
||||
|
||||
// The nextToken value returned from a previous paginated DescribeRepositories
|
||||
// request where maxResults was used and the results exceeded the value of that
|
||||
// parameter. Pagination continues from the end of the previous results that
|
||||
// returned the nextToken value. This value is null when there are no more results
|
||||
// to return.
|
||||
// to return. This option cannot be used when you specify repositories with
|
||||
// repositoryNames.
|
||||
//
|
||||
// This token should be treated as an opaque identifier that is only used to
|
||||
// retrieve the next items in a list and not for other programmatic purposes.
|
||||
|
@ -3527,7 +3531,7 @@ type GetLifecyclePolicyInput struct {
|
|||
// If you do not specify a registry, the default registry is assumed.
|
||||
RegistryId *string `locationName:"registryId" type:"string"`
|
||||
|
||||
// The name of the repository with the policy to retrieve.
|
||||
// The name of the repository.
|
||||
//
|
||||
// RepositoryName is a required field
|
||||
RepositoryName *string `locationName:"repositoryName" min:"2" type:"string" required:"true"`
|
||||
|
@ -3575,9 +3579,9 @@ type GetLifecyclePolicyOutput struct {
|
|||
_ struct{} `type:"structure"`
|
||||
|
||||
// The time stamp of the last time that the lifecycle policy was run.
|
||||
LastEvaluatedAt *time.Time `locationName:"lastEvaluatedAt" type:"timestamp" timestampFormat:"unix"`
|
||||
LastEvaluatedAt *time.Time `locationName:"lastEvaluatedAt" type:"timestamp"`
|
||||
|
||||
// The JSON repository policy text.
|
||||
// The JSON lifecycle policy text.
|
||||
LifecyclePolicyText *string `locationName:"lifecyclePolicyText" min:"100" type:"string"`
|
||||
|
||||
// The registry ID associated with the request.
|
||||
|
@ -3638,21 +3642,23 @@ type GetLifecyclePolicyPreviewInput struct {
|
|||
// by sending
another GetLifecyclePolicyPreviewRequest request with the returned
|
||||
// nextToken
value. This value can be between 1 and 100. If this
parameter
|
||||
// is not used, then GetLifecyclePolicyPreviewRequest returns up to
100 results
|
||||
// and a nextToken value, if
applicable.
|
||||
// and a nextToken value, if
applicable. This option cannot be used when you
|
||||
// specify images with imageIds.
|
||||
MaxResults *int64 `locationName:"maxResults" min:"1" type:"integer"`
|
||||
|
||||
// The nextToken value returned from a previous paginated
GetLifecyclePolicyPreviewRequest
|
||||
// request where maxResults was used and the
results exceeded the value of
|
||||
// that parameter. Pagination continues from the end of the
previous results
|
||||
// that returned the nextToken value. This value is
null when there are no
|
||||
// more results to return.
|
||||
// more results to return. This option cannot be used when you specify images
|
||||
// with imageIds.
|
||||
NextToken *string `locationName:"nextToken" type:"string"`
|
||||
|
||||
// The AWS account ID associated with the registry that contains the repository.
|
||||
// If you do not specify a registry, the default registry is assumed.
|
||||
RegistryId *string `locationName:"registryId" type:"string"`
|
||||
|
||||
// The name of the repository with the policy to retrieve.
|
||||
// The name of the repository.
|
||||
//
|
||||
// RepositoryName is a required field
|
||||
RepositoryName *string `locationName:"repositoryName" min:"2" type:"string" required:"true"`
|
||||
|
@ -3729,7 +3735,7 @@ func (s *GetLifecyclePolicyPreviewInput) SetRepositoryName(v string) *GetLifecyc
|
|||
type GetLifecyclePolicyPreviewOutput struct {
|
||||
_ struct{} `type:"structure"`
|
||||
|
||||
// The JSON repository policy text.
|
||||
// The JSON lifecycle policy text.
|
||||
LifecyclePolicyText *string `locationName:"lifecyclePolicyText" min:"100" type:"string"`
|
||||
|
||||
// The nextToken value to include in a future GetLifecyclePolicyPreview request.
|
||||
|
@ -3958,7 +3964,7 @@ type ImageDetail struct {
|
|||
|
||||
// The date and time, expressed in standard JavaScript date format, at which
|
||||
// the current image was pushed to the repository.
|
||||
ImagePushedAt *time.Time `locationName:"imagePushedAt" type:"timestamp" timestampFormat:"unix"`
|
||||
ImagePushedAt *time.Time `locationName:"imagePushedAt" type:"timestamp"`
|
||||
|
||||
// The size, in bytes, of the image in the repository.
|
||||
//
|
||||
|
@ -4317,7 +4323,7 @@ type LifecyclePolicyPreviewResult struct {
|
|||
|
||||
// The date and time, expressed in standard JavaScript date format, at which
|
||||
// the current image was pushed to the repository.
|
||||
ImagePushedAt *time.Time `locationName:"imagePushedAt" type:"timestamp" timestampFormat:"unix"`
|
||||
ImagePushedAt *time.Time `locationName:"imagePushedAt" type:"timestamp"`
|
||||
|
||||
// The list of tags associated with this image.
|
||||
ImageTags []*string `locationName:"imageTags" type:"list"`
|
||||
|
@ -4778,7 +4784,7 @@ type Repository struct {
|
|||
_ struct{} `type:"structure"`
|
||||
|
||||
// The date and time, in JavaScript date format, when the repository was created.
|
||||
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp" timestampFormat:"unix"`
|
||||
CreatedAt *time.Time `locationName:"createdAt" type:"timestamp"`
|
||||
|
||||
// The AWS account ID associated with the registry that contains the repository.
|
||||
RegistryId *string `locationName:"registryId" type:"string"`
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
// Package ecr provides the client and types for making API
|
||||
// requests to Amazon EC2 Container Registry.
|
||||
//
|
||||
// Amazon EC2 Container Registry (Amazon ECR) is a managed Docker registry service.
|
||||
// Customers can use the familiar Docker CLI to push, pull, and manage images.
|
||||
// Amazon ECR provides a secure, scalable, and reliable registry. Amazon ECR
|
||||
// supports private Docker repositories with resource-based permissions using
|
||||
// IAM so that specific users or Amazon EC2 instances can access repositories
|
||||
// Amazon Elastic Container Registry (Amazon ECR) is a managed Docker registry
|
||||
// service. Customers can use the familiar Docker CLI to push, pull, and manage
|
||||
// images. Amazon ECR provides a secure, scalable, and reliable registry. Amazon
|
||||
// ECR supports private Docker repositories with resource-based permissions
|
||||
// using IAM so that specific users or Amazon EC2 instances can access repositories
|
||||
// and images. Developers can use the Docker CLI to author and manage images.
|
||||
//
|
||||
// See https://docs.aws.amazon.com/goto/WebAPI/ecr-2015-09-21 for more information on this service.
|
||||
|
|
|
@ -95,7 +95,7 @@ const (
|
|||
// The operation did not succeed because it would have exceeded a service limit
|
||||
// for your account. For more information, see Amazon ECR Default Service Limits
|
||||
// (http://docs.aws.amazon.com/AmazonECR/latest/userguide/service_limits.html)
|
||||
// in the Amazon EC2 Container Registry User Guide.
|
||||
// in the Amazon Elastic Container Registry User Guide.
|
||||
ErrCodeLimitExceededException = "LimitExceededException"
|
||||
|
||||
// ErrCodeRepositoryAlreadyExistsException for service response error code
|
||||
|
|
|
@ -29,8 +29,9 @@ var initRequest func(*request.Request)
|
|||
|
||||
// Service information constants
|
||||
const (
|
||||
ServiceName = "ecr" // Service endpoint prefix API calls made to.
|
||||
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
|
||||
ServiceName = "ecr" // Name of service.
|
||||
EndpointsID = ServiceName // ID to lookup a service endpoint with.
|
||||
ServiceID = "ECR" // ServiceID is a unique identifer of a specific service.
|
||||
)
|
||||
|
||||
// New creates a new instance of the ECR client with a session.
|
||||
|
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
ServiceID: ServiceID,
|
||||
SigningName: signingName,
|
||||
SigningRegion: signingRegion,
|
||||
Endpoint: endpoint,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,249 @@
|
|||
package s3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/sdkio"
|
||||
)
|
||||
|
||||
const (
|
||||
contentMD5Header = "Content-Md5"
|
||||
contentSha256Header = "X-Amz-Content-Sha256"
|
||||
amzTeHeader = "X-Amz-Te"
|
||||
amzTxEncodingHeader = "X-Amz-Transfer-Encoding"
|
||||
|
||||
appendMD5TxEncoding = "append-md5"
|
||||
)
|
||||
|
||||
// contentMD5 computes and sets the HTTP Content-MD5 header for requests that
|
||||
// require it.
|
||||
func contentMD5(r *request.Request) {
|
||||
h := md5.New()
|
||||
|
||||
if !aws.IsReaderSeekable(r.Body) {
|
||||
if r.Config.Logger != nil {
|
||||
r.Config.Logger.Log(fmt.Sprintf(
|
||||
"Unable to compute Content-MD5 for unseekable body, S3.%s",
|
||||
r.Operation.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := copySeekableBody(h, r.Body); err != nil {
|
||||
r.Error = awserr.New("ContentMD5", "failed to compute body MD5", err)
|
||||
return
|
||||
}
|
||||
|
||||
// encode the md5 checksum in base64 and set the request header.
|
||||
v := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
r.HTTPRequest.Header.Set(contentMD5Header, v)
|
||||
}
|
||||
|
||||
// computeBodyHashes will add Content MD5 and Content Sha256 hashes to the
|
||||
// request. If the body is not seekable or S3DisableContentMD5Validation set
|
||||
// this handler will be ignored.
|
||||
func computeBodyHashes(r *request.Request) {
|
||||
if aws.BoolValue(r.Config.S3DisableContentMD5Validation) {
|
||||
return
|
||||
}
|
||||
if r.IsPresigned() {
|
||||
return
|
||||
}
|
||||
if r.Error != nil || !aws.IsReaderSeekable(r.Body) {
|
||||
return
|
||||
}
|
||||
|
||||
var md5Hash, sha256Hash hash.Hash
|
||||
hashers := make([]io.Writer, 0, 2)
|
||||
|
||||
// Determine upfront which hashes can be set without overriding user
|
||||
// provide header data.
|
||||
if v := r.HTTPRequest.Header.Get(contentMD5Header); len(v) == 0 {
|
||||
md5Hash = md5.New()
|
||||
hashers = append(hashers, md5Hash)
|
||||
}
|
||||
|
||||
if v := r.HTTPRequest.Header.Get(contentSha256Header); len(v) == 0 {
|
||||
sha256Hash = sha256.New()
|
||||
hashers = append(hashers, sha256Hash)
|
||||
}
|
||||
|
||||
// Create the destination writer based on the hashes that are not already
|
||||
// provided by the user.
|
||||
var dst io.Writer
|
||||
switch len(hashers) {
|
||||
case 0:
|
||||
return
|
||||
case 1:
|
||||
dst = hashers[0]
|
||||
default:
|
||||
dst = io.MultiWriter(hashers...)
|
||||
}
|
||||
|
||||
if _, err := copySeekableBody(dst, r.Body); err != nil {
|
||||
r.Error = awserr.New("BodyHashError", "failed to compute body hashes", err)
|
||||
return
|
||||
}
|
||||
|
||||
// For the hashes created, set the associated headers that the user did not
|
||||
// already provide.
|
||||
if md5Hash != nil {
|
||||
sum := make([]byte, md5.Size)
|
||||
encoded := make([]byte, md5Base64EncLen)
|
||||
|
||||
base64.StdEncoding.Encode(encoded, md5Hash.Sum(sum[0:0]))
|
||||
r.HTTPRequest.Header[contentMD5Header] = []string{string(encoded)}
|
||||
}
|
||||
|
||||
if sha256Hash != nil {
|
||||
encoded := make([]byte, sha256HexEncLen)
|
||||
sum := make([]byte, sha256.Size)
|
||||
|
||||
hex.Encode(encoded, sha256Hash.Sum(sum[0:0]))
|
||||
r.HTTPRequest.Header[contentSha256Header] = []string{string(encoded)}
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
md5Base64EncLen = (md5.Size + 2) / 3 * 4 // base64.StdEncoding.EncodedLen
|
||||
sha256HexEncLen = sha256.Size * 2 // hex.EncodedLen
|
||||
)
|
||||
|
||||
func copySeekableBody(dst io.Writer, src io.ReadSeeker) (int64, error) {
|
||||
curPos, err := src.Seek(0, sdkio.SeekCurrent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// hash the body. seek back to the first position after reading to reset
|
||||
// the body for transmission. copy errors may be assumed to be from the
|
||||
// body.
|
||||
n, err := io.Copy(dst, src)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
_, err = src.Seek(curPos, sdkio.SeekStart)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Adds the x-amz-te: append_md5 header to the request. This requests the service
|
||||
// responds with a trailing MD5 checksum.
|
||||
//
|
||||
// Will not ask for append MD5 if disabled, the request is presigned or,
|
||||
// or the API operation does not support content MD5 validation.
|
||||
func askForTxEncodingAppendMD5(r *request.Request) {
|
||||
if aws.BoolValue(r.Config.S3DisableContentMD5Validation) {
|
||||
return
|
||||
}
|
||||
if r.IsPresigned() {
|
||||
return
|
||||
}
|
||||
r.HTTPRequest.Header.Set(amzTeHeader, appendMD5TxEncoding)
|
||||
}
|
||||
|
||||
func useMD5ValidationReader(r *request.Request) {
|
||||
if r.Error != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v := r.HTTPResponse.Header.Get(amzTxEncodingHeader); v != appendMD5TxEncoding {
|
||||
return
|
||||
}
|
||||
|
||||
var bodyReader *io.ReadCloser
|
||||
var contentLen int64
|
||||
switch tv := r.Data.(type) {
|
||||
case *GetObjectOutput:
|
||||
bodyReader = &tv.Body
|
||||
contentLen = aws.Int64Value(tv.ContentLength)
|
||||
// Update ContentLength hiden the trailing MD5 checksum.
|
||||
tv.ContentLength = aws.Int64(contentLen - md5.Size)
|
||||
tv.ContentRange = aws.String(r.HTTPResponse.Header.Get("X-Amz-Content-Range"))
|
||||
default:
|
||||
r.Error = awserr.New("ChecksumValidationError",
|
||||
fmt.Sprintf("%s: %s header received on unsupported API, %s",
|
||||
amzTxEncodingHeader, appendMD5TxEncoding, r.Operation.Name,
|
||||
), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if contentLen < md5.Size {
|
||||
r.Error = awserr.New("ChecksumValidationError",
|
||||
fmt.Sprintf("invalid Content-Length %d for %s %s",
|
||||
contentLen, appendMD5TxEncoding, amzTxEncodingHeader,
|
||||
), nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Wrap and swap the response body reader with the validation reader.
|
||||
*bodyReader = newMD5ValidationReader(*bodyReader, contentLen-md5.Size)
|
||||
}
|
||||
|
||||
type md5ValidationReader struct {
|
||||
rawReader io.ReadCloser
|
||||
payload io.Reader
|
||||
hash hash.Hash
|
||||
|
||||
payloadLen int64
|
||||
read int64
|
||||
}
|
||||
|
||||
func newMD5ValidationReader(reader io.ReadCloser, payloadLen int64) *md5ValidationReader {
|
||||
h := md5.New()
|
||||
return &md5ValidationReader{
|
||||
rawReader: reader,
|
||||
payload: io.TeeReader(&io.LimitedReader{R: reader, N: payloadLen}, h),
|
||||
hash: h,
|
||||
payloadLen: payloadLen,
|
||||
}
|
||||
}
|
||||
|
||||
func (v *md5ValidationReader) Read(p []byte) (n int, err error) {
|
||||
n, err = v.payload.Read(p)
|
||||
if err != nil && err != io.EOF {
|
||||
return n, err
|
||||
}
|
||||
|
||||
v.read += int64(n)
|
||||
|
||||
if err == io.EOF {
|
||||
if v.read != v.payloadLen {
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
expectSum := make([]byte, md5.Size)
|
||||
actualSum := make([]byte, md5.Size)
|
||||
if _, sumReadErr := io.ReadFull(v.rawReader, expectSum); sumReadErr != nil {
|
||||
return n, sumReadErr
|
||||
}
|
||||
actualSum = v.hash.Sum(actualSum[0:0])
|
||||
if !bytes.Equal(expectSum, actualSum) {
|
||||
return n, awserr.New("InvalidChecksum",
|
||||
fmt.Sprintf("expected MD5 checksum %s, got %s",
|
||||
hex.EncodeToString(expectSum),
|
||||
hex.EncodeToString(actualSum),
|
||||
),
|
||||
nil)
|
||||
}
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (v *md5ValidationReader) Close() error {
|
||||
return v.rawReader.Close()
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package s3
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws/awserr"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
)
|
||||
|
||||
// contentMD5 computes and sets the HTTP Content-MD5 header for requests that
|
||||
// require it.
|
||||
func contentMD5(r *request.Request) {
|
||||
h := md5.New()
|
||||
|
||||
// hash the body. seek back to the first position after reading to reset
|
||||
// the body for transmission. copy errors may be assumed to be from the
|
||||
// body.
|
||||
_, err := io.Copy(h, r.Body)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("ContentMD5", "failed to read body", err)
|
||||
return
|
||||
}
|
||||
_, err = r.Body.Seek(0, 0)
|
||||
if err != nil {
|
||||
r.Error = awserr.New("ContentMD5", "failed to seek body", err)
|
||||
return
|
||||
}
|
||||
|
||||
// encode the md5 checksum in base64 and set the request header.
|
||||
sum := h.Sum(nil)
|
||||
sum64 := make([]byte, base64.StdEncoding.EncodedLen(len(sum)))
|
||||
base64.StdEncoding.Encode(sum64, sum)
|
||||
r.HTTPRequest.Header.Set("Content-MD5", string(sum64))
|
||||
}
|
|
@ -3,6 +3,7 @@ package s3
|
|||
import (
|
||||
"github.com/aws/aws-sdk-go/aws/client"
|
||||
"github.com/aws/aws-sdk-go/aws/request"
|
||||
"github.com/aws/aws-sdk-go/internal/s3err"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -21,6 +22,7 @@ func defaultInitClientFn(c *client.Client) {
|
|||
// S3 uses custom error unmarshaling logic
|
||||
c.Handlers.UnmarshalError.Clear()
|
||||
c.Handlers.UnmarshalError.PushBack(unmarshalError)
|
||||
c.Handlers.UnmarshalError.PushBackNamed(s3err.RequestFailureWrapperHandler())
|
||||
}
|
||||
|
||||
func defaultInitRequestFn(r *request.Request) {
|
||||
|
@ -42,6 +44,13 @@ func defaultInitRequestFn(r *request.Request) {
|
|||
r.Handlers.Validate.PushFront(populateLocationConstraint)
|
||||
case opCopyObject, opUploadPartCopy, opCompleteMultipartUpload:
|
||||
r.Handlers.Unmarshal.PushFront(copyMultipartStatusOKUnmarhsalError)
|
||||
r.Handlers.Unmarshal.PushBackNamed(s3err.RequestFailureWrapperHandler())
|
||||
case opPutObject, opUploadPart:
|
||||
r.Handlers.Build.PushBack(computeBodyHashes)
|
||||
// Disabled until #1837 root issue is resolved.
|
||||
// case opGetObject:
|
||||
// r.Handlers.Build.PushBack(askForTxEncodingAppendMD5)
|
||||
// r.Handlers.Unmarshal.PushBack(useMD5ValidationReader)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -375,6 +375,10 @@ type S3API interface {
|
|||
RestoreObjectWithContext(aws.Context, *s3.RestoreObjectInput, ...request.Option) (*s3.RestoreObjectOutput, error)
|
||||
RestoreObjectRequest(*s3.RestoreObjectInput) (*request.Request, *s3.RestoreObjectOutput)
|
||||
|
||||
SelectObjectContent(*s3.SelectObjectContentInput) (*s3.SelectObjectContentOutput, error)
|
||||
SelectObjectContentWithContext(aws.Context, *s3.SelectObjectContentInput, ...request.Option) (*s3.SelectObjectContentOutput, error)
|
||||
SelectObjectContentRequest(*s3.SelectObjectContentInput) (*request.Request, *s3.SelectObjectContentOutput)
|
||||
|
||||
UploadPart(*s3.UploadPartInput) (*s3.UploadPartOutput, error)
|
||||
UploadPartWithContext(aws.Context, *s3.UploadPartInput, ...request.Option) (*s3.UploadPartOutput, error)
|
||||
UploadPartRequest(*s3.UploadPartInput) (*request.Request, *s3.UploadPartOutput)
|
||||
|
|
|
@ -64,7 +64,7 @@ func (err *Error) Error() string {
|
|||
if err.OrigErr != nil {
|
||||
origErr = ":\n" + err.OrigErr.Error()
|
||||
}
|
||||
return fmt.Sprintf("failed to upload %q to %q%s",
|
||||
return fmt.Sprintf("failed to perform batch operation on %q to %q%s",
|
||||
aws.StringValue(err.Key),
|
||||
aws.StringValue(err.Bucket),
|
||||
origErr,
|
||||
|
@ -320,7 +320,7 @@ func (d *BatchDelete) Delete(ctx aws.Context, iter BatchDeleteIterator) error {
|
|||
}
|
||||
|
||||
if len(input.Delete.Objects) == d.BatchSize || !parity {
|
||||
if err := deleteBatch(d, input, objects); err != nil {
|
||||
if err := deleteBatch(ctx, d, input, objects); err != nil {
|
||||
errs = append(errs, err...)
|
||||
}
|
||||
|
||||
|
@ -338,8 +338,13 @@ func (d *BatchDelete) Delete(ctx aws.Context, iter BatchDeleteIterator) error {
|
|||
}
|
||||
}
|
||||
|
||||
// iter.Next() could return false (above) plus populate iter.Err()
|
||||
if iter.Err() != nil {
|
||||
errs = append(errs, newError(iter.Err(), nil, nil))
|
||||
}
|
||||
|
||||
if input != nil && len(input.Delete.Objects) > 0 {
|
||||
if err := deleteBatch(d, input, objects); err != nil {
|
||||
if err := deleteBatch(ctx, d, input, objects); err != nil {
|
||||
errs = append(errs, err...)
|
||||
}
|
||||
}
|
||||
|
@ -359,17 +364,33 @@ func initDeleteObjectsInput(o *s3.DeleteObjectInput) *s3.DeleteObjectsInput {
|
|||
}
|
||||
}
|
||||
|
||||
const (
|
||||
// ErrDeleteBatchFailCode represents an error code which will be returned
|
||||
// only when DeleteObjects.Errors has an error that does not contain a code.
|
||||
ErrDeleteBatchFailCode = "DeleteBatchError"
|
||||
errDefaultDeleteBatchMessage = "failed to delete"
|
||||
)
|
||||
|
||||
// deleteBatch will delete a batch of items in the objects parameters.
|
||||
func deleteBatch(d *BatchDelete, input *s3.DeleteObjectsInput, objects []BatchDeleteObject) []Error {
|
||||
func deleteBatch(ctx aws.Context, d *BatchDelete, input *s3.DeleteObjectsInput, objects []BatchDeleteObject) []Error {
|
||||
errs := []Error{}
|
||||
|
||||
if result, err := d.Client.DeleteObjects(input); err != nil {
|
||||
if result, err := d.Client.DeleteObjectsWithContext(ctx, input); err != nil {
|
||||
for i := 0; i < len(input.Delete.Objects); i++ {
|
||||
errs = append(errs, newError(err, input.Bucket, input.Delete.Objects[i].Key))
|
||||
}
|
||||
} else if len(result.Errors) > 0 {
|
||||
for i := 0; i < len(result.Errors); i++ {
|
||||
errs = append(errs, newError(err, input.Bucket, result.Errors[i].Key))
|
||||
code := ErrDeleteBatchFailCode
|
||||
msg := errDefaultDeleteBatchMessage
|
||||
if result.Errors[i].Message != nil {
|
||||
msg = *result.Errors[i].Message
|
||||
}
|
||||
if result.Errors[i].Code != nil {
|
||||
code = *result.Errors[i].Code
|
||||
}
|
||||
|
||||
errs = append(errs, newError(awserr.New(code, msg, err), input.Bucket, result.Errors[i].Key))
|
||||
}
|
||||
}
|
||||
for _, object := range objects {
|
||||
|
|
|
@ -14,8 +14,11 @@ import (
|
|||
//
|
||||
// The request will not be signed, and will not use your AWS credentials.
|
||||
//
|
||||
// A "NotFound" error code will be returned if the bucket does not exist in
|
||||
// the AWS partition the regionHint belongs to.
|
||||
// A "NotFound" error code will be returned if the bucket does not exist in the
|
||||
// AWS partition the regionHint belongs to. If the regionHint parameter is an
|
||||
// empty string GetBucketRegion will fallback to the ConfigProvider's region
|
||||
// config. If the regionHint is empty, and the ConfigProvider does not have a
|
||||
// region value, an error will be returned..
|
||||
//
|
||||
// For example to get the region of a bucket which exists in "eu-central-1"
|
||||
// you could provide a region hint of "us-west-2".
|
||||
|
@ -33,9 +36,11 @@ import (
|
|||
// fmt.Printf("Bucket %s is in %s region\n", bucket, region)
|
||||
//
|
||||
func GetBucketRegion(ctx aws.Context, c client.ConfigProvider, bucket, regionHint string, opts ...request.Option) (string, error) {
|
||||
svc := s3.New(c, &aws.Config{
|
||||
Region: aws.String(regionHint),
|
||||
})
|
||||
var cfg aws.Config
|
||||
if len(regionHint) != 0 {
|
||||
cfg.Region = aws.String(regionHint)
|
||||
}
|
||||
svc := s3.New(c, &cfg)
|
||||
return GetBucketRegionWithClient(ctx, svc, bucket, opts...)
|
||||
}
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ type Downloader struct {
|
|||
// The number of goroutines to spin up in parallel when sending parts.
|
||||
// If this is set to zero, the DefaultDownloadConcurrency value will be used.
|
||||
//
|
||||
// Concurrency of 1 will download the parts sequentially.
|
||||
//
|
||||
// Concurrency is ignored if the Range input parameter is provided.
|
||||
Concurrency int
|
||||
|
||||
|
@ -135,6 +137,9 @@ type maxRetrier interface {
|
|||
// The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
|
||||
// downloads, or in memory []byte wrapper using aws.WriteAtBuffer.
|
||||
//
|
||||
// Specifying a Downloader.Concurrency of 1 will cause the Downloader to
|
||||
// download the parts from S3 sequentially.
|
||||
//
|
||||
// If the GetObjectInput's Range value is provided that will cause the downloader
|
||||
// to perform a single GetObjectInput request for that object's range. This will
|
||||
// caused the part size, and concurrency configurations to be ignored.
|
||||
|
@ -147,7 +152,7 @@ func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ..
|
|||
//
|
||||
// DownloadWithContext is the same as Download with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the Context to add deadlining, timeouts, etc. The
|
||||
// DownloadWithContext may create sub-contexts for individual underlying
|
||||
// requests.
|
||||
//
|
||||
|
@ -160,6 +165,9 @@ func (d Downloader) Download(w io.WriterAt, input *s3.GetObjectInput, options ..
|
|||
// The w io.WriterAt can be satisfied by an os.File to do multipart concurrent
|
||||
// downloads, or in memory []byte wrapper using aws.WriteAtBuffer.
|
||||
//
|
||||
// Specifying a Downloader.Concurrency of 1 will cause the Downloader to
|
||||
// download the parts from S3 sequentially.
|
||||
//
|
||||
// It is safe to call this method concurrently across goroutines.
|
||||
//
|
||||
// If the GetObjectInput's Range value is provided that will cause the downloader
|
||||
|
@ -207,14 +215,14 @@ func (d Downloader) DownloadWithContext(ctx aws.Context, w io.WriterAt, input *s
|
|||
//
|
||||
// objects := []s3manager.BatchDownloadObject {
|
||||
// {
|
||||
// Input: &s3.GetObjectInput {
|
||||
// Object: &s3.GetObjectInput {
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("foo"),
|
||||
// },
|
||||
// Writer: fooFile,
|
||||
// },
|
||||
// {
|
||||
// Input: &s3.GetObjectInput {
|
||||
// Object: &s3.GetObjectInput {
|
||||
// Bucket: aws.String("bucket"),
|
||||
// Key: aws.String("bar"),
|
||||
// },
|
||||
|
|
|
@ -241,6 +241,8 @@ type Uploader struct {
|
|||
// E.g: 5GB file, with MaxUploadParts set to 100, will upload the file
|
||||
// as 100, 50MB parts.
|
||||
// With a limited of s3.MaxUploadParts (10,000 parts).
|
||||
//
|
||||
// Defaults to package const's MaxUploadParts value.
|
||||
MaxUploadParts int
|
||||
|
||||
// The client to use when uploading to S3.
|
||||
|
@ -357,7 +359,7 @@ func (u Uploader) Upload(input *UploadInput, options ...func(*Uploader)) (*Uploa
|
|||
//
|
||||
// UploadWithContext is the same as Upload with the additional support for
|
||||
// Context input parameters. The Context must not be nil. A nil Context will
|
||||
// cause a panic. Use the context to add deadlining, timeouts, ect. The
|
||||
// cause a panic. Use the context to add deadlining, timeouts, etc. The
|
||||
// UploadWithContext may create sub-contexts for individual underlying requests.
|
||||
//
|
||||
// Additional functional options can be provided to configure the individual
|
||||
|
@ -395,7 +397,7 @@ func (u Uploader) UploadWithContext(ctx aws.Context, input *UploadInput, opts ..
|
|||
// },
|
||||
// }
|
||||
//
|
||||
// iter := &s3managee.UploadObjectsIterator{Objects: objects}
|
||||
// iter := &s3manager.UploadObjectsIterator{Objects: objects}
|
||||
// if err := svc.UploadWithIterator(aws.BackgroundContext(), iter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
@ -443,6 +445,8 @@ type uploader struct {
|
|||
|
||||
readerPos int64 // current reader position
|
||||
totalSize int64 // set to -1 if the size is not known
|
||||
|
||||
bufferPool sync.Pool
|
||||
}
|
||||
|
||||
// internal logic for deciding whether to upload a single part or use a
|
||||
|
@ -456,7 +460,7 @@ func (u *uploader) upload() (*UploadOutput, error) {
|
|||
}
|
||||
|
||||
// Do one read to determine if we have more than one part
|
||||
reader, _, err := u.nextReader()
|
||||
reader, _, part, err := u.nextReader()
|
||||
if err == io.EOF { // single part
|
||||
return u.singlePart(reader)
|
||||
} else if err != nil {
|
||||
|
@ -464,7 +468,7 @@ func (u *uploader) upload() (*UploadOutput, error) {
|
|||
}
|
||||
|
||||
mu := multiuploader{uploader: u}
|
||||
return mu.upload(reader)
|
||||
return mu.upload(reader, part)
|
||||
}
|
||||
|
||||
// init will initialize all default options.
|
||||
|
@ -475,6 +479,13 @@ func (u *uploader) init() {
|
|||
if u.cfg.PartSize == 0 {
|
||||
u.cfg.PartSize = DefaultUploadPartSize
|
||||
}
|
||||
if u.cfg.MaxUploadParts == 0 {
|
||||
u.cfg.MaxUploadParts = MaxUploadParts
|
||||
}
|
||||
|
||||
u.bufferPool = sync.Pool{
|
||||
New: func() interface{} { return make([]byte, u.cfg.PartSize) },
|
||||
}
|
||||
|
||||
// Try to get the total size for some optimizations
|
||||
u.initSize()
|
||||
|
@ -487,10 +498,7 @@ func (u *uploader) initSize() {
|
|||
|
||||
switch r := u.in.Body.(type) {
|
||||
case io.Seeker:
|
||||
pos, _ := r.Seek(0, 1)
|
||||
defer r.Seek(pos, 0)
|
||||
|
||||
n, err := r.Seek(0, 2)
|
||||
n, err := aws.SeekerLen(r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -510,7 +518,7 @@ func (u *uploader) initSize() {
|
|||
// This operation increases the shared u.readerPos counter, but note that it
|
||||
// does not need to be wrapped in a mutex because nextReader is only called
|
||||
// from the main thread.
|
||||
func (u *uploader) nextReader() (io.ReadSeeker, int, error) {
|
||||
func (u *uploader) nextReader() (io.ReadSeeker, int, []byte, error) {
|
||||
type readerAtSeeker interface {
|
||||
io.ReaderAt
|
||||
io.ReadSeeker
|
||||
|
@ -532,14 +540,14 @@ func (u *uploader) nextReader() (io.ReadSeeker, int, error) {
|
|||
reader := io.NewSectionReader(r, u.readerPos, n)
|
||||
u.readerPos += n
|
||||
|
||||
return reader, int(n), err
|
||||
return reader, int(n), nil, err
|
||||
|
||||
default:
|
||||
part := make([]byte, u.cfg.PartSize)
|
||||
part := u.bufferPool.Get().([]byte)
|
||||
n, err := readFillBuf(r, part)
|
||||
u.readerPos += int64(n)
|
||||
|
||||
return bytes.NewReader(part[0:n]), n, err
|
||||
return bytes.NewReader(part[0:n]), n, part, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,8 +597,9 @@ type multiuploader struct {
|
|||
|
||||
// keeps track of a single chunk of data being sent to S3.
|
||||
type chunk struct {
|
||||
buf io.ReadSeeker
|
||||
num int64
|
||||
buf io.ReadSeeker
|
||||
part []byte
|
||||
num int64
|
||||
}
|
||||
|
||||
// completedParts is a wrapper to make parts sortable by their part number,
|
||||
|
@ -603,7 +612,7 @@ func (a completedParts) Less(i, j int) bool { return *a[i].PartNumber < *a[j].Pa
|
|||
|
||||
// upload will perform a multipart upload using the firstBuf buffer containing
|
||||
// the first chunk of data.
|
||||
func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
|
||||
func (u *multiuploader) upload(firstBuf io.ReadSeeker, firstPart []byte) (*UploadOutput, error) {
|
||||
params := &s3.CreateMultipartUploadInput{}
|
||||
awsutil.Copy(params, u.in)
|
||||
|
||||
|
@ -623,7 +632,7 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
|
|||
|
||||
// Send part 1 to the workers
|
||||
var num int64 = 1
|
||||
ch <- chunk{buf: firstBuf, num: num}
|
||||
ch <- chunk{buf: firstBuf, part: firstPart, num: num}
|
||||
|
||||
// Read and queue the rest of the parts
|
||||
for u.geterr() == nil && err == nil {
|
||||
|
@ -644,7 +653,8 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
|
|||
|
||||
var reader io.ReadSeeker
|
||||
var nextChunkLen int
|
||||
reader, nextChunkLen, err = u.nextReader()
|
||||
var part []byte
|
||||
reader, nextChunkLen, part, err = u.nextReader()
|
||||
|
||||
if err != nil && err != io.EOF {
|
||||
u.seterr(awserr.New(
|
||||
|
@ -661,7 +671,7 @@ func (u *multiuploader) upload(firstBuf io.ReadSeeker) (*UploadOutput, error) {
|
|||
break
|
||||
}
|
||||
|
||||
ch <- chunk{buf: reader, num: num}
|
||||
ch <- chunk{buf: reader, part: part, num: num}
|
||||
}
|
||||
|
||||
// Close the channel, wait for workers, and complete upload
|
||||
|
@ -717,6 +727,8 @@ func (u *multiuploader) send(c chunk) error {
|
|||
PartNumber: &c.num,
|
||||
}
|
||||
resp, err := u.cfg.S3.UploadPartWithContext(u.ctx, params, u.cfg.RequestOptions...)
|
||||
// put the byte array back into the pool to conserve memory
|
||||
u.bufferPool.Put(c.part)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,8 +29,9 @@ var initRequest func(*request.Request)
|
|||
|
||||
// Service information constants
|
||||
const (
|
||||
ServiceName = "s3" // Service endpoint prefix API calls made to.
|
||||
EndpointsID = ServiceName // Service ID for Regions and Endpoints metadata.
|
||||
ServiceName = "s3" // Name of service.
|
||||
EndpointsID = ServiceName // ID to lookup a service endpoint with.
|
||||
ServiceID = "S3" // ServiceID is a unique identifer of a specific service.
|
||||
)
|
||||
|
||||
// New creates a new instance of the S3 client with a session.
|
||||
|
@ -55,6 +56,7 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
cfg,
|
||||
metadata.ClientInfo{
|
||||
ServiceName: ServiceName,
|
||||
ServiceID: ServiceID,
|
||||
SigningName: signingName,
|
||||
SigningRegion: signingRegion,
|
||||
Endpoint: endpoint,
|
||||
|
@ -65,12 +67,16 @@ func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
|
|||
}
|
||||
|
||||
// Handlers
|
||||
svc.Handlers.Sign.PushBackNamed(v4.SignRequestHandler)
|
||||
svc.Handlers.Sign.PushBackNamed(v4.BuildNamedHandler(v4.SignRequestHandler.Name, func(s *v4.Signer) {
|
||||
s.DisableURIPathEscaping = true
|
||||
}))
|
||||
svc.Handlers.Build.PushBackNamed(restxml.BuildHandler)
|
||||
svc.Handlers.Unmarshal.PushBackNamed(restxml.UnmarshalHandler)
|
||||
svc.Handlers.UnmarshalMeta.PushBackNamed(restxml.UnmarshalMetaHandler)
|
||||
svc.Handlers.UnmarshalError.PushBackNamed(restxml.UnmarshalErrorHandler)
|
||||
|
||||
svc.Handlers.UnmarshalStream.PushBackNamed(restxml.UnmarshalHandler)
|
||||
|
||||
// Run custom client initialization if present
|
||||
if initClient != nil {
|
||||
initClient(svc.Client)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue