mirror of https://github.com/docker/buildx.git
s3 cache client-side support
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
6804bcbf12
commit
57d22a7bd1
|
@ -410,6 +410,10 @@ func toSolveOpt(ctx context.Context, di DriverInfo, multiDriver bool, opt Option
|
|||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) {
|
||||
continue
|
||||
}
|
||||
} else if e.Type == "s3" {
|
||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
cacheTo = append(cacheTo, e)
|
||||
}
|
||||
|
@ -420,6 +424,10 @@ func toSolveOpt(ctx context.Context, di DriverInfo, multiDriver bool, opt Option
|
|||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.gha")) {
|
||||
continue
|
||||
}
|
||||
} else if e.Type == "s3" {
|
||||
if !bopts.LLBCaps.Contains(apicaps.CapID("cache.s3")) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
cacheFrom = append(cacheFrom, e)
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ Same as [`buildx --builder`](buildx.md#builder).
|
|||
```
|
||||
|
||||
Use an external cache source for a build. Supported types are `registry`,
|
||||
`local` and `gha`.
|
||||
`local`, `gha` and `s3`.
|
||||
|
||||
- [`registry` source](https://github.com/moby/buildkit#registry-push-image-and-cache-separately)
|
||||
can import cache from a cache manifest or (special) image configuration on the
|
||||
|
@ -180,6 +180,9 @@ Use an external cache source for a build. Supported types are `registry`,
|
|||
- [`gha` source](https://github.com/moby/buildkit#github-actions-cache-experimental)
|
||||
can import cache from a previously exported cache with `--cache-to` in your
|
||||
GitHub repository
|
||||
- [`s3` source](https://github.com/moby/buildkit#s3-cache-experimental)
|
||||
can import cache from a previously exported cache with `--cache-to` in your
|
||||
S3 bucket
|
||||
|
||||
If no type is specified, `registry` exporter is used with a specified reference.
|
||||
|
||||
|
@ -191,6 +194,7 @@ $ docker buildx build --cache-from=user/app .
|
|||
$ docker buildx build --cache-from=type=registry,ref=user/app .
|
||||
$ docker buildx build --cache-from=type=local,src=path/to/cache .
|
||||
$ docker buildx build --cache-from=type=gha .
|
||||
$ docker buildx build --cache-from=type=s3,region=eu-west-1,bucket=mybucket .
|
||||
```
|
||||
|
||||
More info about cache exporters and available attributes: https://github.com/moby/buildkit#export-cache
|
||||
|
@ -202,15 +206,17 @@ More info about cache exporters and available attributes: https://github.com/mob
|
|||
```
|
||||
|
||||
Export build cache to an external cache destination. Supported types are
|
||||
`registry`, `local`, `inline` and `gha`.
|
||||
`registry`, `local`, `inline`, `gha` and `s3`.
|
||||
|
||||
- [`registry` type](https://github.com/moby/buildkit#registry-push-image-and-cache-separately) exports build cache to a cache manifest in the registry.
|
||||
- [`local` type](https://github.com/moby/buildkit#local-directory-1) type
|
||||
exports cache to a local directory on the client.
|
||||
- [`local` type](https://github.com/moby/buildkit#local-directory-1) exports
|
||||
cache to a local directory on the client.
|
||||
- [`inline` type](https://github.com/moby/buildkit#inline-push-image-and-cache-together)
|
||||
type writes the cache metadata into the image configuration.
|
||||
writes the cache metadata into the image configuration.
|
||||
- [`gha` type](https://github.com/moby/buildkit#github-actions-cache-experimental)
|
||||
type exports cache through the [Github Actions Cache service API](https://github.com/tonistiigi/go-actions-cache/blob/master/api.md#authentication).
|
||||
exports cache through the [GitHub Actions Cache service API](https://github.com/tonistiigi/go-actions-cache/blob/master/api.md#authentication).
|
||||
- [`s3` type](https://github.com/moby/buildkit#s3-cache-experimental) exports
|
||||
cache to a S3 bucket.
|
||||
|
||||
`docker` driver currently only supports exporting inline cache metadata to image
|
||||
configuration. Alternatively, `--build-arg BUILDKIT_INLINE_CACHE=1` can be used
|
||||
|
@ -228,6 +234,7 @@ $ docker buildx build --cache-to=type=inline .
|
|||
$ docker buildx build --cache-to=type=registry,ref=user/app .
|
||||
$ docker buildx build --cache-to=type=local,dest=path/to/cache .
|
||||
$ docker buildx build --cache-to=type=gha .
|
||||
$ docker buildx build --cache-to=type=s3,region=eu-west-1,bucket=mybucket .
|
||||
```
|
||||
|
||||
More info about cache exporters and available attributes: https://github.com/moby/buildkit#export-cache
|
||||
|
|
12
go.mod
12
go.mod
|
@ -3,6 +3,7 @@ module github.com/docker/buildx
|
|||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/aws/aws-sdk-go-v2/config v1.15.5
|
||||
github.com/compose-spec/compose-go v1.5.0
|
||||
github.com/containerd/console v1.0.3
|
||||
github.com/containerd/containerd v1.6.6
|
||||
|
@ -53,6 +54,16 @@ require (
|
|||
github.com/apparentlymart/go-cidr v1.0.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 // indirect
|
||||
github.com/aws/smithy-go v1.11.2 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bugsnag/bugsnag-go v1.4.1 // indirect
|
||||
github.com/bugsnag/panicwrap v1.2.0 // indirect
|
||||
|
@ -155,6 +166,7 @@ require (
|
|||
)
|
||||
|
||||
replace (
|
||||
github.com/aws/aws-sdk-go-v2/config => github.com/aws/aws-sdk-go-v2/config v1.15.5 // same as buildkit
|
||||
github.com/docker/cli => github.com/docker/cli v20.10.3-0.20220803220330-418ca3b4d46f+incompatible // master (v22.06-dev)
|
||||
github.com/docker/docker => github.com/docker/docker v20.10.3-0.20220720171342-a60b458179aa+incompatible // 22.06 branch (v22.06-dev)
|
||||
k8s.io/api => k8s.io/api v0.22.4
|
||||
|
|
24
go.sum
24
go.sum
|
@ -87,6 +87,28 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6
|
|||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.3 h1:0W1TSJ7O6OzwuEvIXAtJGvOeQ0SGAhcpxPN2/NK5EhM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.16.3/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.15.5 h1:P+xwhr6kabhxDTXTVH9YoHkqjLJ0wVVpIUHtFNr2hjU=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.15.5/go.mod h1:ZijHHh0xd/A+ZY53az0qzC5tT46kt4JVCePf2NX9Lk4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.0 h1:4R/NqlcRFSkR0wxOhgHi+agGpbEr5qMCjn7VqUIJY+E=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.12.0/go.mod h1:9YWk7VW+eyKsoIL6/CljkTrNVWBSK9pkqOPUuijid4A=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 h1:FP8gquGeGHHdfY6G5llaMQDF+HAf20VKc8opRwmjf04=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4/go.mod h1:u/s5/Z+ohUQOPXl00m2yJVyioWDECsbpXTQlaqSlufc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10 h1:uFWgo6mGJI1n17nbcvSc6fxVuR3xLNqvXt12JCnEcT8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4 h1:cnsvEKSoHN4oAN7spMMr0zhEW2MHnhAVpmqQg8E6UcM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4/go.mod h1:8glyUqVIM4AmeenIsPo0oVh3+NUwnsQml2OFupfQW+0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11 h1:6cZRymlLEIlDTEB0+5+An6Zj1CKt6rSE69tOmFeu1nk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11/go.mod h1:0MR+sS1b/yxsfAPvAESrw8NfwUoxMinDyw6EYR9BS2U=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 h1:b16QW0XWl0jWjLABFc1A+uh145Oqv+xDcObNk0iQgUk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4/go.mod h1:uKkN7qmSIsNJVyMtxNQoCEYMvFEXbOg9fwCJPdfp2u8=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 h1:Uw5wBybFQ1UeA9ts0Y07gbv0ncZnIAyw858tDW0NP2o=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.11.4/go.mod h1:cPDwJwsP4Kff9mldCXAmddjJL6JGQqtA3Mzer2zyr88=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 h1:+xtV90n3abQmgzk1pS++FdxZTrPEDgQng6e4/56WR2A=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.16.4/go.mod h1:lfSYenAXtavyX2A1LsViglqlG9eEFYxNryTZS5rn3QE=
|
||||
github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE=
|
||||
github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
@ -361,6 +383,8 @@ github.com/jinzhu/gorm v1.9.2 h1:lCvgEaqe/HVE+tjAR2mt4HbbHAZsQOv3XAZiEZV37iw=
|
|||
github.com/jinzhu/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k=
|
||||
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package buildflags
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
awsconfig "github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/moby/buildkit/client"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -49,6 +51,7 @@ func ParseCacheEntry(in []string) ([]client.CacheOptionsEntry, error) {
|
|||
if !addGithubToken(&im) {
|
||||
continue
|
||||
}
|
||||
addAwsCredentials(&im)
|
||||
imports = append(imports, im)
|
||||
}
|
||||
return imports, nil
|
||||
|
@ -79,3 +82,27 @@ func addGithubToken(ci *client.CacheOptionsEntry) bool {
|
|||
}
|
||||
return ci.Attrs["token"] != "" && ci.Attrs["url"] != ""
|
||||
}
|
||||
|
||||
func addAwsCredentials(ci *client.CacheOptionsEntry) {
|
||||
if ci.Type != "s3" {
|
||||
return
|
||||
}
|
||||
ctx := context.TODO()
|
||||
awsConfig, err := awsconfig.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
credentials, err := awsConfig.Credentials.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, ok := ci.Attrs["access_key_id"]; !ok && credentials.AccessKeyID != "" {
|
||||
ci.Attrs["access_key_id"] = credentials.AccessKeyID
|
||||
}
|
||||
if _, ok := ci.Attrs["secret_access_key"]; !ok && credentials.SecretAccessKey != "" {
|
||||
ci.Attrs["secret_access_key"] = credentials.SecretAccessKey
|
||||
}
|
||||
if _, ok := ci.Attrs["session_token"]; !ok && credentials.SessionToken != "" {
|
||||
ci.Attrs["session_token"] = credentials.SessionToken
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
dist
|
||||
/doc
|
||||
/doc-staging
|
||||
.yardoc
|
||||
Gemfile.lock
|
||||
/internal/awstesting/integration/smoke/**/importmarker__.go
|
||||
/internal/awstesting/integration/smoke/_test/
|
||||
/vendor
|
||||
/private/model/cli/gen-api/gen-api
|
||||
.gradle/
|
||||
build/
|
|
@ -0,0 +1,27 @@
|
|||
[run]
|
||||
concurrency = 4
|
||||
timeout = "1m"
|
||||
issues-exit-code = 0
|
||||
modules-download-mode = "readonly"
|
||||
allow-parallel-runners = true
|
||||
skip-dirs = ["internal/repotools"]
|
||||
skip-dirs-use-default = true
|
||||
|
||||
[output]
|
||||
format = "github-actions"
|
||||
|
||||
[linters-settings.cyclop]
|
||||
skip-tests = false
|
||||
|
||||
[linters-settings.errcheck]
|
||||
check-blank = true
|
||||
|
||||
[linters]
|
||||
disable-all = true
|
||||
enable = ["errcheck"]
|
||||
fast = false
|
||||
|
||||
[issues]
|
||||
exclude-use-default = false
|
||||
|
||||
# Refer config definitions at https://golangci-lint.run/usage/configuration/#config-file
|
|
@ -0,0 +1,31 @@
|
|||
language: go
|
||||
sudo: true
|
||||
dist: bionic
|
||||
|
||||
branches:
|
||||
only:
|
||||
- main
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
# Travis doesn't work with windows and Go tip
|
||||
#- windows
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install make; fi
|
||||
- (cd /tmp/; go get golang.org/x/lint/golint)
|
||||
|
||||
env:
|
||||
- EACHMODULE_CONCURRENCY=4
|
||||
|
||||
script:
|
||||
- make ci-test-no-generate;
|
||||
|
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.
|
|
@ -0,0 +1,178 @@
|
|||
# Contributing to the AWS SDK for Go
|
||||
|
||||
Thank you for your interest in contributing to the AWS SDK for Go!
|
||||
We work hard to provide a high-quality and useful SDK, and we greatly value
|
||||
feedback and contributions from our community. Whether it's a bug report,
|
||||
new feature, correction, or additional documentation, we welcome your issues
|
||||
and pull requests. Please read through this document before submitting any
|
||||
[issues] or [pull requests][pr] to ensure we have all the necessary information to
|
||||
effectively respond to your bug report or contribution.
|
||||
|
||||
Jump To:
|
||||
|
||||
* [Bug Reports](#bug-reports)
|
||||
* [Feature Requests](#feature-requests)
|
||||
* [Code Contributions](#code-contributions)
|
||||
|
||||
|
||||
## How to contribute
|
||||
|
||||
*Before you send us a pull request, please be sure that:*
|
||||
|
||||
1. You're working from the latest source on the master branch.
|
||||
2. You check existing open, and recently closed, pull requests to be sure
|
||||
that someone else hasn't already addressed the problem.
|
||||
3. You create an issue before working on a contribution that will take a
|
||||
significant amount of your time.
|
||||
|
||||
*Creating a Pull Request*
|
||||
|
||||
1. Fork the repository.
|
||||
2. In your fork, make your change in a branch that's based on this repo's master branch.
|
||||
3. Commit the change to your fork, using a clear and descriptive commit message.
|
||||
4. Create a pull request, answering any questions in the pull request form.
|
||||
|
||||
For contributions that will take a significant amount of time, open a new
|
||||
issue to pitch your idea before you get started. Explain the problem and
|
||||
describe the content you want to see added to the documentation. Let us know
|
||||
if you'll write it yourself or if you'd like us to help. We'll discuss your
|
||||
proposal with you and let you know whether we're likely to accept it.
|
||||
|
||||
## Bug Reports
|
||||
|
||||
You can file bug reports against the SDK on the [GitHub issues][issues] page.
|
||||
|
||||
If you are filing a report for a bug or regression in the SDK, it's extremely
|
||||
helpful to provide as much information as possible when opening the original
|
||||
issue. This helps us reproduce and investigate the possible bug without having
|
||||
to wait for this extra information to be provided. Please read the following
|
||||
guidelines prior to filing a bug report.
|
||||
|
||||
1. Search through existing [issues][] to ensure that your specific issue has
|
||||
not yet been reported. If it is a common issue, it is likely there is
|
||||
already a bug report for your problem.
|
||||
|
||||
2. Ensure that you have tested the latest version of the SDK. Although you
|
||||
may have an issue against an older version of the SDK, we cannot provide
|
||||
bug fixes for old versions. It's also possible that the bug may have been
|
||||
fixed in the latest release.
|
||||
|
||||
3. Provide as much information about your environment, SDK version, and
|
||||
relevant dependencies as possible. For example, let us know what version
|
||||
of Go you are using, which and version of the operating system, and the
|
||||
the environment your code is running in. e.g Container.
|
||||
|
||||
4. Provide a minimal test case that reproduces your issue or any error
|
||||
information you related to your problem. We can provide feedback much
|
||||
more quickly if we know what operations you are calling in the SDK. If
|
||||
you cannot provide a full test case, provide as much code as you can
|
||||
to help us diagnose the problem. Any relevant information should be provided
|
||||
as well, like whether this is a persistent issue, or if it only occurs
|
||||
some of the time.
|
||||
|
||||
## Feature Requests
|
||||
|
||||
Open an [issue][issues] with the following:
|
||||
|
||||
* A short, descriptive title. Ideally, other community members should be able
|
||||
to get a good idea of the feature just from reading the title.
|
||||
* A detailed description of the the proposed feature.
|
||||
* Why it should be added to the SDK.
|
||||
* If possible, example code to illustrate how it should work.
|
||||
* Use Markdown to make the request easier to read;
|
||||
* If you intend to implement this feature, indicate that you'd like to the issue to be assigned to you.
|
||||
|
||||
## Code Contributions
|
||||
|
||||
We are always happy to receive code and documentation contributions to the SDK.
|
||||
Please be aware of the following notes prior to opening a pull request:
|
||||
|
||||
1. The SDK is released under the [Apache license][license]. Any code you submit
|
||||
will be released under that license. For substantial contributions, we may
|
||||
ask you to sign a [Contributor License Agreement (CLA)][cla].
|
||||
|
||||
2. If you would like to implement support for a significant feature that is not
|
||||
yet available in the SDK, please talk to us beforehand to avoid any
|
||||
duplication of effort.
|
||||
|
||||
3. Wherever possible, pull requests should contain tests as appropriate.
|
||||
Bugfixes should contain tests that exercise the corrected behavior (i.e., the
|
||||
test should fail without the bugfix and pass with it), and new features
|
||||
should be accompanied by tests exercising the feature.
|
||||
|
||||
4. Pull requests that contain failing tests will not be merged until the test
|
||||
failures are addressed. Pull requests that cause a significant drop in the
|
||||
SDK's test coverage percentage are unlikely to be merged until tests have
|
||||
been added.
|
||||
|
||||
5. The JSON files under the SDK's `models` folder are sourced from outside the SDK.
|
||||
Such as `models/apis/ec2/2016-11-15/api.json`. We will not accept pull requests
|
||||
directly on these models. If you discover an issue with the models please
|
||||
create a [GitHub issue][issues] describing the issue.
|
||||
|
||||
### Testing
|
||||
|
||||
To run the tests locally, running the `make unit` command will `go get` the
|
||||
SDK's testing dependencies, and run vet, link and unit tests for the SDK.
|
||||
|
||||
```
|
||||
make unit
|
||||
```
|
||||
|
||||
Standard go testing functionality is supported as well. To test SDK code that
|
||||
is tagged with `codegen` you'll need to set the build tag in the go test
|
||||
command. The `make unit` command will do this automatically.
|
||||
|
||||
```
|
||||
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
|
||||
unit testing within environments configured for specific Go versions.
|
||||
|
||||
```
|
||||
make sandbox-test-go18
|
||||
```
|
||||
|
||||
To run all sandbox environments use the following make recipe
|
||||
|
||||
```
|
||||
# Optionally update the Go tip that will be used during the batch testing
|
||||
make update-aws-golang-tip
|
||||
|
||||
# Run all SDK tests for supported Go versions in sandboxes
|
||||
make sandbox-test
|
||||
```
|
||||
|
||||
In addition the sandbox environment include make recipes for interactive modes
|
||||
so you can run command within the Docker container and context of the SDK.
|
||||
|
||||
```
|
||||
make sandbox-go18
|
||||
```
|
||||
|
||||
### Changelog Documents
|
||||
|
||||
You can see all release changes in the `CHANGELOG.md` file at the root of the
|
||||
repository. The release notes added to this file will contain service client
|
||||
updates, and major SDK changes. When submitting a pull request please include an entry in `CHANGELOG_PENDING.md` under the appropriate changelog type so your changelog entry is included on the following release.
|
||||
|
||||
#### Changelog Types
|
||||
|
||||
* `SDK Features` - For major additive features, internal changes that have
|
||||
outward impact, or updates to the SDK foundations. This will result in a minor
|
||||
version change.
|
||||
* `SDK Enhancements` - For minor additive features or incremental sized changes.
|
||||
This will result in a patch version change.
|
||||
* `SDK Bugs` - For minor changes that resolve an issue. This will result in a
|
||||
patch version change.
|
||||
|
||||
[issues]: https://github.com/aws/aws-sdk-go/issues
|
||||
[pr]: https://github.com/aws/aws-sdk-go/pulls
|
||||
[license]: http://aws.amazon.com/apache2.0/
|
||||
[cla]: http://en.wikipedia.org/wiki/Contributor_License_Agreement
|
||||
[releasenotes]: https://github.com/aws/aws-sdk-go/releases
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
Open Discussions
|
||||
---
|
||||
The following issues are currently open for community feedback.
|
||||
All discourse must adhere to the [Code of Conduct] policy.
|
||||
|
||||
* [Refactoring API Client Paginators](https://github.com/aws/aws-sdk-go-v2/issues/439)
|
||||
* [Refactoring API Client Waiters](https://github.com/aws/aws-sdk-go-v2/issues/442)
|
||||
* [Refactoring API Client Enums and Types to Discrete Packages](https://github.com/aws/aws-sdk-go-v2/issues/445)
|
||||
* [SDK Modularization](https://github.com/aws/aws-sdk-go-v2/issues/444)
|
||||
|
||||
Past Discussions
|
||||
---
|
||||
The issues listed here are for documentation purposes, and is used to capture issues and their associated discussions.
|
||||
|
||||
[Code of Conduct]: https://github.com/aws/aws-sdk-go-v2/blob/master/CODE_OF_CONDUCT.md
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,507 @@
|
|||
# Lint rules to ignore
|
||||
LINTIGNORESINGLEFIGHT='internal/sync/singleflight/singleflight.go:.+error should be the last type'
|
||||
LINT_IGNORE_S3MANAGER_INPUT='feature/s3/manager/upload.go:.+struct field SSEKMSKeyId should be SSEKMSKeyID'
|
||||
|
||||
UNIT_TEST_TAGS=
|
||||
BUILD_TAGS=-tags "example,codegen,integration,ec2env,perftest"
|
||||
|
||||
SMITHY_GO_SRC ?= $(shell pwd)/../smithy-go
|
||||
|
||||
SDK_MIN_GO_VERSION ?= 1.15
|
||||
|
||||
EACHMODULE_FAILFAST ?= true
|
||||
EACHMODULE_FAILFAST_FLAG=-fail-fast=${EACHMODULE_FAILFAST}
|
||||
|
||||
EACHMODULE_CONCURRENCY ?= 1
|
||||
EACHMODULE_CONCURRENCY_FLAG=-c ${EACHMODULE_CONCURRENCY}
|
||||
|
||||
EACHMODULE_SKIP ?=
|
||||
EACHMODULE_SKIP_FLAG=-skip="${EACHMODULE_SKIP}"
|
||||
|
||||
EACHMODULE_FLAGS=${EACHMODULE_CONCURRENCY_FLAG} ${EACHMODULE_FAILFAST_FLAG} ${EACHMODULE_SKIP_FLAG}
|
||||
|
||||
# SDK's Core and client packages that are compatible with Go 1.9+.
|
||||
SDK_CORE_PKGS=./aws/... ./internal/...
|
||||
SDK_CLIENT_PKGS=./service/...
|
||||
SDK_COMPA_PKGS=${SDK_CORE_PKGS} ${SDK_CLIENT_PKGS}
|
||||
|
||||
# SDK additional packages that are used for development of the SDK.
|
||||
SDK_EXAMPLES_PKGS=
|
||||
SDK_ALL_PKGS=${SDK_COMPA_PKGS} ${SDK_EXAMPLES_PKGS}
|
||||
|
||||
RUN_NONE=-run NONE
|
||||
RUN_INTEG=-run '^TestInteg_'
|
||||
|
||||
CODEGEN_RESOURCES_PATH=$(shell pwd)/codegen/smithy-aws-go-codegen/src/main/resources/software/amazon/smithy/aws/go/codegen
|
||||
CODEGEN_API_MODELS_PATH=$(shell pwd)/codegen/sdk-codegen/aws-models
|
||||
ENDPOINTS_JSON=${CODEGEN_RESOURCES_PATH}/endpoints.json
|
||||
ENDPOINT_PREFIX_JSON=${CODEGEN_RESOURCES_PATH}/endpoint-prefix.json
|
||||
|
||||
LICENSE_FILE=$(shell pwd)/LICENSE.txt
|
||||
|
||||
SMITHY_GO_VERSION ?=
|
||||
PRE_RELEASE_VERSION ?=
|
||||
RELEASE_MANIFEST_FILE ?=
|
||||
RELEASE_CHGLOG_DESC_FILE ?=
|
||||
|
||||
REPOTOOLS_VERSION ?= latest
|
||||
REPOTOOLS_MODULE = github.com/awslabs/aws-go-multi-module-repository-tools
|
||||
REPOTOOLS_CMD_ANNOTATE_STABLE_GEN = ${REPOTOOLS_MODULE}/cmd/annotatestablegen@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_MAKE_RELATIVE = ${REPOTOOLS_MODULE}/cmd/makerelative@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_CALCULATE_RELEASE = ${REPOTOOLS_MODULE}/cmd/calculaterelease@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_UPDATE_REQUIRES = ${REPOTOOLS_MODULE}/cmd/updaterequires@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_UPDATE_MODULE_METADATA = ${REPOTOOLS_MODULE}/cmd/updatemodulemeta@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_GENERATE_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/generatechangelog@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_CHANGELOG = ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_TAG_RELEASE = ${REPOTOOLS_MODULE}/cmd/tagrelease@${REPOTOOLS_VERSION}
|
||||
REPOTOOLS_CMD_EDIT_MODULE_DEPENDENCY = ${REPOTOOLS_MODULE}/cmd/editmoduledependency@${REPOTOOLS_VERSION}
|
||||
|
||||
REPOTOOLS_CALCULATE_RELEASE_VERBOSE ?= false
|
||||
REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG=-v=${REPOTOOLS_CALCULATE_RELEASE_VERBOSE}
|
||||
|
||||
REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS ?=
|
||||
|
||||
ifneq ($(PRE_RELEASE_VERSION),)
|
||||
REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS += -preview=${PRE_RELEASE_VERSION}
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: generate unit
|
||||
|
||||
###################
|
||||
# Code Generation #
|
||||
###################
|
||||
.PHONY: generate smithy-generate smithy-build smithy-build-% smithy-clean smithy-go-publish-local format \
|
||||
gen-config-asserts gen-repo-mod-replace gen-mod-replace-smithy gen-mod-dropreplace-smithy gen-aws-ptrs tidy-modules-% \
|
||||
add-module-license-files sync-models sync-endpoints-model sync-endpoints.json clone-v1-models gen-internal-codegen \
|
||||
sync-api-models copy-attributevalue-feature min-go-version-% update-requires smithy-annotate-stable \
|
||||
update-module-metadata download-modules-%
|
||||
|
||||
generate: smithy-generate update-requires gen-repo-mod-replace update-module-metadata smithy-annotate-stable \
|
||||
gen-config-asserts gen-internal-codegen copy-attributevalue-feature gen-mod-dropreplace-smithy min-go-version-. \
|
||||
tidy-modules-. add-module-license-files gen-aws-ptrs format
|
||||
|
||||
smithy-generate:
|
||||
cd codegen && ./gradlew clean build -Plog-tests && ./gradlew clean
|
||||
|
||||
smithy-build: gen-repo-mod-replace
|
||||
cd codegen && ./gradlew clean build -Plog-tests
|
||||
|
||||
smithy-build-%: gen-repo-mod-replace
|
||||
@# smithy-build- command that uses the pattern to define build filter that
|
||||
@# the smithy API model service id starts with. Strips off the
|
||||
@# "smithy-build-".
|
||||
@#
|
||||
@# e.g. smithy-build-com.amazonaws.rds
|
||||
@# e.g. smithy-build-com.amazonaws.rds#AmazonRDSv19
|
||||
cd codegen && \
|
||||
SMITHY_GO_BUILD_API="$(subst smithy-build-,,$@)" ./gradlew clean build -Plog-tests
|
||||
|
||||
smithy-annotate-stable:
|
||||
go run ${REPOTOOLS_CMD_ANNOTATE_STABLE_GEN}
|
||||
|
||||
smithy-clean:
|
||||
cd codegen && ./gradlew clean
|
||||
|
||||
smithy-go-publish-local:
|
||||
rm -rf /tmp/smithy-go-local
|
||||
git clone https://github.com/aws/smithy-go /tmp/smithy-go-local
|
||||
make -C /tmp/smithy-go-local smithy-clean smithy-publish-local
|
||||
|
||||
format:
|
||||
gofmt -w -s .
|
||||
|
||||
gen-config-asserts:
|
||||
@echo "Generating SDK config package implementor assertions"
|
||||
cd config \
|
||||
&& go mod tidy \
|
||||
&& go generate
|
||||
|
||||
gen-internal-codegen:
|
||||
@echo "Generating internal/codegen"
|
||||
cd internal/codegen \
|
||||
&& go generate
|
||||
|
||||
gen-repo-mod-replace:
|
||||
@echo "Generating go.mod replace for repo modules"
|
||||
go run ${REPOTOOLS_CMD_MAKE_RELATIVE}
|
||||
|
||||
gen-mod-replace-smithy:
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . "go mod edit -replace github.com/aws/smithy-go=${SMITHY_GO_SRC}"
|
||||
|
||||
gen-mod-dropreplace-smithy:
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . "go mod edit -dropreplace github.com/aws/smithy-go"
|
||||
|
||||
gen-aws-ptrs:
|
||||
cd aws && go generate
|
||||
|
||||
tidy-modules-%:
|
||||
@# tidy command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "tidy-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. tidy-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst tidy-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go mod tidy"
|
||||
|
||||
download-modules-%:
|
||||
@# download command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "download-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. download-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst download-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go mod download all"
|
||||
|
||||
add-module-license-files:
|
||||
cd internal/repotools/cmd/eachmodule && \
|
||||
go run . -skip-root \
|
||||
"cp $(LICENSE_FILE) ."
|
||||
|
||||
sync-models: sync-endpoints-model sync-api-models
|
||||
|
||||
sync-endpoints-model: sync-endpoints.json
|
||||
|
||||
sync-endpoints.json:
|
||||
[[ ! -z "${ENDPOINTS_MODEL}" ]] && cp ${ENDPOINTS_MODEL} ${ENDPOINTS_JSON} || echo "ENDPOINTS_MODEL not set, must not be empty"
|
||||
|
||||
clone-v1-models:
|
||||
rm -rf /tmp/aws-sdk-go-model-sync
|
||||
git clone https://github.com/aws/aws-sdk-go.git --depth 1 /tmp/aws-sdk-go-model-sync
|
||||
|
||||
sync-api-models:
|
||||
cd internal/repotools/cmd/syncAPIModels && \
|
||||
go run . \
|
||||
-m ${API_MODELS} \
|
||||
-o ${CODEGEN_API_MODELS_PATH}
|
||||
|
||||
copy-attributevalue-feature:
|
||||
cd ./feature/dynamodbstreams/attributevalue && \
|
||||
find . -name "*.go" | grep -v "doc.go" | xargs -I % rm % && \
|
||||
find ../../dynamodb/attributevalue -name "*.go" | grep -v "doc.go" | xargs -I % cp % . && \
|
||||
ls *.go | grep -v "convert.go" | grep -v "doc.go" | \
|
||||
xargs -I % sed -i.bk -E 's:github.com/aws/aws-sdk-go-v2/(service|feature)/dynamodb:github.com/aws/aws-sdk-go-v2/\1/dynamodbstreams:g' % && \
|
||||
ls *.go | grep -v "convert.go" | grep -v "doc.go" | \
|
||||
xargs -I % sed -i.bk 's:DynamoDB:DynamoDBStreams:g' % && \
|
||||
ls *.go | grep -v "doc.go" | \
|
||||
xargs -I % sed -i.bk 's:dynamodb\.:dynamodbstreams.:g' % && \
|
||||
sed -i.bk 's:streams\.:ddbtypes.:g' "convert.go" && \
|
||||
sed -i.bk 's:ddb\.:streams.:g' "convert.go" && \
|
||||
sed -i.bk 's:ddbtypes\.:ddb.:g' "convert.go" &&\
|
||||
sed -i.bk 's:Streams::g' "convert.go" && \
|
||||
rm -rf ./*.bk && \
|
||||
go mod tidy && \
|
||||
gofmt -w -s . && \
|
||||
go test .
|
||||
|
||||
min-go-version-%:
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst min-go-version-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go mod edit -go=${SDK_MIN_GO_VERSION}"
|
||||
|
||||
update-requires:
|
||||
go run ${REPOTOOLS_CMD_UPDATE_REQUIRES}
|
||||
|
||||
update-module-metadata:
|
||||
go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA}
|
||||
|
||||
################
|
||||
# Unit Testing #
|
||||
################
|
||||
.PHONY: unit unit-race unit-test unit-race-test unit-race-modules-% unit-modules-% build build-modules-% \
|
||||
go-build-modules-% test test-race-modules-% test-modules-% cachedep cachedep-modules-% api-diff-modules-%
|
||||
|
||||
unit: lint unit-modules-.
|
||||
unit-race: lint unit-race-modules-.
|
||||
|
||||
unit-test: test-modules-.
|
||||
unit-race-test: test-race-modules-.
|
||||
|
||||
unit-race-modules-%:
|
||||
@# unit command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "unit-race-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. unit-race-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst unit-race-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go vet ${BUILD_TAGS} --all ./..." \
|
||||
"go test ${BUILD_TAGS} ${RUN_NONE} ./..." \
|
||||
"go test -timeout=1m ${UNIT_TEST_TAGS} -race -cpu=4 ./..."
|
||||
|
||||
|
||||
unit-modules-%:
|
||||
@# unit command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "unit-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. unit-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst unit-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go vet ${BUILD_TAGS} --all ./..." \
|
||||
"go test ${BUILD_TAGS} ${RUN_NONE} ./..." \
|
||||
"go test -timeout=1m ${UNIT_TEST_TAGS} ./..."
|
||||
|
||||
build: build-modules-.
|
||||
|
||||
build-modules-%:
|
||||
@# build command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "build-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. build-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst build-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go test ${BUILD_TAGS} ${RUN_NONE} ./..."
|
||||
|
||||
go-build-modules-%:
|
||||
@# build command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "build-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# Validates that all modules in the repo have buildable Go files.
|
||||
@#
|
||||
@# e.g. go-build-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst go-build-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go build ${BUILD_TAGS} ./..."
|
||||
|
||||
test: test-modules-.
|
||||
|
||||
test-race-modules-%:
|
||||
@# Test command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "test-race-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. test-race-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst test-race-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go test -timeout=1m ${UNIT_TEST_TAGS} -race -cpu=4 ./..."
|
||||
|
||||
test-modules-%:
|
||||
@# Test command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "test-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. test-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst test-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go test -timeout=1m ${UNIT_TEST_TAGS} ./..."
|
||||
|
||||
cachedep: cachedep-modules-.
|
||||
|
||||
cachedep-modules-%:
|
||||
@# build command that uses the pattern to define the root path that the
|
||||
@# module caching will start from. Strips off the "cachedep-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. cachedep-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst cachedep-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go mod download"
|
||||
|
||||
api-diff-modules-%:
|
||||
@# Command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "api-diff-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# Requires golang.org/x/exp/cmd/gorelease to be available in the GOPATH.
|
||||
@#
|
||||
@# e.g. api-diff-modules-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst api-diff-modules-,,$@)) \
|
||||
-fail-fast=true \
|
||||
-c 1 \
|
||||
-skip="internal/repotools" \
|
||||
"$$(go env GOPATH)/bin/gorelease"
|
||||
|
||||
##############
|
||||
# CI Testing #
|
||||
##############
|
||||
.PHONY: ci-test ci-test-no-generate ci-test-generate-validate
|
||||
|
||||
ci-test: generate unit-race ci-test-generate-validate
|
||||
ci-test-no-generate: unit-race
|
||||
|
||||
ci-test-generate-validate:
|
||||
@echo "CI test validate no generated code changes"
|
||||
git update-index --assume-unchanged go.mod go.sum
|
||||
git add . -A
|
||||
gitstatus=`git diff --cached --ignore-space-change`; \
|
||||
echo "$$gitstatus"; \
|
||||
if [ "$$gitstatus" != "" ] && [ "$$gitstatus" != "skipping validation" ]; then echo "$$gitstatus"; exit 1; fi
|
||||
git update-index --no-assume-unchanged go.mod go.sum
|
||||
|
||||
ci-lint: ci-lint-.
|
||||
|
||||
ci-lint-%:
|
||||
@# Run golangci-lint command that uses the pattern to define the root path that the
|
||||
@# module check will start from. Strips off the "ci-lint-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. ci-lint-internal_protocoltest
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst ci-lint-,,$@)) \
|
||||
-fail-fast=false \
|
||||
-c 1 \
|
||||
-skip="internal/repotools" \
|
||||
"golangci-lint run"
|
||||
|
||||
ci-lint-install:
|
||||
@# Installs golangci-lint at GoPATH.
|
||||
@# This should be used to run golangci-lint locally.
|
||||
@#
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
|
||||
#######################
|
||||
# Integration Testing #
|
||||
#######################
|
||||
.PHONY: integration integ-modules-% cleanup-integ-buckets
|
||||
|
||||
integration: integ-modules-service
|
||||
|
||||
integ-modules-%:
|
||||
@# integration command that uses the pattern to define the root path that
|
||||
@# the module testing will start from. Strips off the "integ-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. test-modules-service_dynamodb
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst integ-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go test -timeout=10m -tags "integration" -v ${RUN_INTEG} -count 1 ./..."
|
||||
|
||||
cleanup-integ-buckets:
|
||||
@echo "Cleaning up SDK integration resources"
|
||||
go run -tags "integration" ./internal/awstesting/cmd/bucket_cleanup/main.go "aws-sdk-go-integration"
|
||||
|
||||
##############
|
||||
# Benchmarks #
|
||||
##############
|
||||
.PHONY: bench bench-modules-%
|
||||
|
||||
bench: bench-modules-.
|
||||
|
||||
bench-modules-%:
|
||||
@# benchmark command that uses the pattern to define the root path that
|
||||
@# the module testing will start from. Strips off the "bench-modules-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# e.g. bench-modules-service_dynamodb
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst bench-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go test -timeout=10m -bench . --benchmem ${BUILD_TAGS} ${RUN_NONE} ./..."
|
||||
|
||||
|
||||
#####################
|
||||
# Release Process #
|
||||
#####################
|
||||
.PHONY: preview-release pre-release-validation release
|
||||
|
||||
ls-changes:
|
||||
go run ${REPOTOOLS_CMD_CHANGELOG} ls
|
||||
|
||||
preview-release:
|
||||
go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} ${REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS}
|
||||
|
||||
pre-release-validation:
|
||||
@if [[ -z "${RELEASE_MANIFEST_FILE}" ]]; then \
|
||||
echo "RELEASE_MANIFEST_FILE is required to specify the file to write the release manifest" && false; \
|
||||
fi
|
||||
@if [[ -z "${RELEASE_CHGLOG_DESC_FILE}" ]]; then \
|
||||
echo "RELEASE_CHGLOG_DESC_FILE is required to specify the file to write the release notes" && false; \
|
||||
fi
|
||||
|
||||
release: pre-release-validation
|
||||
go run ${REPOTOOLS_CMD_CALCULATE_RELEASE} -o ${RELEASE_MANIFEST_FILE} ${REPOTOOLS_CALCULATE_RELEASE_VERBOSE_FLAG} ${REPOTOOLS_CALCULATE_RELEASE_ADDITIONAL_ARGS}
|
||||
go run ${REPOTOOLS_CMD_UPDATE_REQUIRES} -release ${RELEASE_MANIFEST_FILE}
|
||||
go run ${REPOTOOLS_CMD_UPDATE_MODULE_METADATA} -release ${RELEASE_MANIFEST_FILE}
|
||||
go run ${REPOTOOLS_CMD_GENERATE_CHANGELOG} -release ${RELEASE_MANIFEST_FILE} -o ${RELEASE_CHGLOG_DESC_FILE}
|
||||
go run ${REPOTOOLS_CMD_CHANGELOG} rm -all
|
||||
go run ${REPOTOOLS_CMD_TAG_RELEASE} -release ${RELEASE_MANIFEST_FILE}
|
||||
|
||||
##############
|
||||
# Repo Tools #
|
||||
##############
|
||||
.PHONY: install-repotools
|
||||
|
||||
install-repotools:
|
||||
go install ${REPOTOOLS_MODULE}/cmd/changelog@${REPOTOOLS_VERSION}
|
||||
|
||||
set-smithy-go-version:
|
||||
@if [[ -z "${SMITHY_GO_VERSION}" ]]; then \
|
||||
echo "SMITHY_GO_VERSION is required to update SDK's smithy-go module dependency version" && false; \
|
||||
fi
|
||||
go run ${REPOTOOLS_CMD_EDIT_MODULE_DEPENDENCY} -s "github.com/aws/smithy-go" -v "${SMITHY_GO_VERSION}"
|
||||
|
||||
##################
|
||||
# Linting/Verify #
|
||||
##################
|
||||
.PHONY: verify lint vet vet-modules-% sdkv1check
|
||||
|
||||
verify: lint vet sdkv1check
|
||||
|
||||
lint:
|
||||
@echo "go lint SDK and vendor packages"
|
||||
@lint=`golint ./...`; \
|
||||
dolint=`echo "$$lint" | grep -E -v \
|
||||
-e ${LINT_IGNORE_S3MANAGER_INPUT} \
|
||||
-e ${LINTIGNORESINGLEFIGHT}`; \
|
||||
echo "$$dolint"; \
|
||||
if [ "$$dolint" != "" ]; then exit 1; fi
|
||||
|
||||
vet: vet-modules-.
|
||||
|
||||
vet-modules-%:
|
||||
cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst vet-modules-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go vet ${BUILD_TAGS} --all ./..."
|
||||
|
||||
sdkv1check:
|
||||
@echo "Checking for usage of AWS SDK for Go v1"
|
||||
@sdkv1usage=`go list -test -f '''{{ if not .Standard }}{{ range $$_, $$name := .Imports }} * {{ $$.ImportPath }} -> {{ $$name }}{{ print "\n" }}{{ end }}{{ range $$_, $$name := .TestImports }} *: {{ $$.ImportPath }} -> {{ $$name }}{{ print "\n" }}{{ end }}{{ end}}''' ./... | sort -u | grep '''/aws-sdk-go/'''`; \
|
||||
echo "$$sdkv1usage"; \
|
||||
if [ "$$sdkv1usage" != "" ]; then exit 1; fi
|
||||
|
||||
list-deps: list-deps-.
|
||||
|
||||
list-deps-%:
|
||||
@# command that uses the pattern to define the root path that the
|
||||
@# module testing will start from. Strips off the "list-deps-" and
|
||||
@# replaces all "_" with "/".
|
||||
@#
|
||||
@# Trim output to only include stdout for list of dependencies only.
|
||||
@# make list-deps 2>&-
|
||||
@#
|
||||
@# e.g. list-deps-internal_protocoltest
|
||||
@cd ./internal/repotools/cmd/eachmodule \
|
||||
&& go run . -p $(subst _,/,$(subst list-deps-,,$@)) ${EACHMODULE_FLAGS} \
|
||||
"go list -m all | grep -v 'github.com/aws/aws-sdk-go-v2'" | sort -u
|
||||
|
||||
###################
|
||||
# Sandbox Testing #
|
||||
###################
|
||||
.PHONY: sandbox-tests sandbox-build-% sandbox-run-% sandbox-test-% update-aws-golang-tip
|
||||
|
||||
sandbox-tests: sandbox-test-go1.15 sandbox-test-go1.16 sandbox-test-go1.17 sandbox-test-gotip
|
||||
|
||||
sandbox-build-%:
|
||||
@# sandbox-build-go1.17
|
||||
@# sandbox-build-gotip
|
||||
docker build \
|
||||
-f ./internal/awstesting/sandbox/Dockerfile.test.$(subst sandbox-build-,,$@) \
|
||||
-t "aws-sdk-go-$(subst sandbox-build-,,$@)" .
|
||||
sandbox-run-%: sandbox-build-%
|
||||
@# sandbox-run-go1.17
|
||||
@# sandbox-run-gotip
|
||||
docker run -i -t "aws-sdk-go-$(subst sandbox-run-,,$@)" bash
|
||||
sandbox-test-%: sandbox-build-%
|
||||
@# sandbox-test-go1.17
|
||||
@# sandbox-test-gotip
|
||||
docker run -t "aws-sdk-go-$(subst sandbox-test-,,$@)"
|
||||
|
||||
update-aws-golang-tip:
|
||||
docker build --no-cache=true -f ./internal/awstesting/sandbox/Dockerfile.golang-tip -t "aws-golang:tip" .
|
|
@ -0,0 +1,3 @@
|
|||
AWS SDK for Go
|
||||
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||
Copyright 2014-2015 Stripe, Inc.
|
|
@ -0,0 +1,155 @@
|
|||
# AWS SDK for Go v2
|
||||
|
||||
[![Go Build status](https://github.com/aws/aws-sdk-go-v2/actions/workflows/go.yml/badge.svg?branch=main)](https://github.com/aws/aws-sdk-go-v2/actions/workflows/go.yml)[![Codegen Build status](https://github.com/aws/aws-sdk-go-v2/actions/workflows/codegen.yml/badge.svg?branch=main)](https://github.com/aws/aws-sdk-go-v2/actions/workflows/codegen.yml) [![SDK Documentation](https://img.shields.io/badge/SDK-Documentation-blue)](https://aws.github.io/aws-sdk-go-v2/docs/) [![Migration Guide](https://img.shields.io/badge/Migration-Guide-blue)](https://aws.github.io/aws-sdk-go-v2/docs/migrating/) [![API Reference](https://img.shields.io/badge/api-reference-blue.svg)](https://pkg.go.dev/mod/github.com/aws/aws-sdk-go-v2) [![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-go-v2` is the v2 AWS SDK for the Go programming language.
|
||||
|
||||
The v2 SDK requires a minimum version of `Go 1.15`.
|
||||
|
||||
Checkout out the [release notes](https://github.com/aws/aws-sdk-go-v2/blob/main/CHANGELOG.md) for information about the latest bug
|
||||
fixes, updates, and features added to the SDK.
|
||||
|
||||
Jump To:
|
||||
* [Getting Started](#getting-started)
|
||||
* [Getting Help](#getting-help)
|
||||
* [Contributing](#feedback-and-contributing)
|
||||
* [More Resources](#resources)
|
||||
|
||||
## Maintenance and support for SDK major versions
|
||||
|
||||
For information about maintenance and support for SDK major versions and their underlying dependencies, see the
|
||||
following in the AWS SDKs and Tools Shared Configuration and Credentials Reference Guide:
|
||||
|
||||
* [AWS SDKs and Tools Maintenance Policy](https://docs.aws.amazon.com/credref/latest/refdocs/maint-policy.html)
|
||||
* [AWS SDKs and Tools Version Support Matrix](https://docs.aws.amazon.com/credref/latest/refdocs/version-support-matrix.html)
|
||||
|
||||
## Getting started
|
||||
To get started working with the SDK setup your project for Go modules, and retrieve the SDK dependencies with `go get`.
|
||||
This example shows how you can use the v2 SDK to make an API request using the SDK's [Amazon DynamoDB] client.
|
||||
|
||||
###### Initialize Project
|
||||
```sh
|
||||
$ mkdir ~/helloaws
|
||||
$ cd ~/helloaws
|
||||
$ go mod init helloaws
|
||||
```
|
||||
###### Add SDK Dependencies
|
||||
```sh
|
||||
$ go get github.com/aws/aws-sdk-go-v2/aws
|
||||
$ go get github.com/aws/aws-sdk-go-v2/config
|
||||
$ go get github.com/aws/aws-sdk-go-v2/service/dynamodb
|
||||
```
|
||||
|
||||
###### Write Code
|
||||
In your preferred editor add the following content to `main.go`
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Using the SDK's default configuration, loading additional config
|
||||
// and credentials values from the environment variables, shared
|
||||
// credentials, and shared configuration files
|
||||
cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-west-2"))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to load SDK config, %v", err)
|
||||
}
|
||||
|
||||
// Using the Config value, create the DynamoDB client
|
||||
svc := dynamodb.NewFromConfig(cfg)
|
||||
|
||||
// Build the request with its input parameters
|
||||
resp, err := svc.ListTables(context.TODO(), &dynamodb.ListTablesInput{
|
||||
Limit: aws.Int32(5),
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("failed to list tables, %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Tables:")
|
||||
for _, tableName := range resp.TableNames {
|
||||
fmt.Println(tableName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
###### Compile and Execute
|
||||
```sh
|
||||
$ go run .
|
||||
Table:
|
||||
tableOne
|
||||
tableTwo
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
Please use these community resources for getting help. We use the GitHub issues
|
||||
for tracking bugs and feature requests.
|
||||
|
||||
* Ask a question on [StackOverflow](http://stackoverflow.com/) and tag it with the [`aws-sdk-go`](http://stackoverflow.com/questions/tagged/aws-sdk-go) tag.
|
||||
* Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html).
|
||||
* If you think you may have found a bug, please open an [issue](https://github.com/aws/aws-sdk-go-v2/issues/new/choose).
|
||||
|
||||
This SDK implements AWS service APIs. For general issues regarding the AWS services and their limitations, you may also take a look at the [Amazon Web Services Discussion Forums](https://forums.aws.amazon.com/).
|
||||
|
||||
### Opening Issues
|
||||
|
||||
If you encounter a bug with the AWS SDK for Go we would like to hear about it.
|
||||
Search the [existing issues][Issues] and see
|
||||
if others are also experiencing the issue before opening a new issue. Please
|
||||
include the version of AWS SDK for Go, Go language, and OS you’re using. Please
|
||||
also include reproduction case when appropriate.
|
||||
|
||||
The GitHub issues are intended for bug reports and feature requests. For help
|
||||
and questions with using AWS SDK for Go please make use of the resources listed
|
||||
in the [Getting Help](#getting-help) section.
|
||||
Keeping the list of open issues lean will help us respond in a timely manner.
|
||||
|
||||
## Feedback and contributing
|
||||
|
||||
The v2 SDK will use GitHub [Issues] to track feature requests and issues with the SDK. In addition, we'll use GitHub [Projects] to track large tasks spanning multiple pull requests, such as refactoring the SDK's internal request lifecycle. You can provide feedback to us in several ways.
|
||||
|
||||
**GitHub issues**. To provide feedback or report bugs, file GitHub [Issues] on the SDK. This is the preferred mechanism to give feedback so that other users can engage in the conversation, +1 issues, etc. Issues you open will be evaluated, and included in our roadmap for the GA launch.
|
||||
|
||||
**Contributing**. You can open pull requests for fixes or additions to the AWS SDK for Go 2.0. All pull requests must be submitted under the Apache 2.0 license and will be reviewed by an SDK team member before being merged in. Accompanying unit tests, where possible, are appreciated.
|
||||
|
||||
## Resources
|
||||
|
||||
[SDK Developer Guide](https://aws.github.io/aws-sdk-go-v2/docs/) - Use this document to learn how to get started and
|
||||
use the AWS SDK for Go V2.
|
||||
|
||||
[SDK API Reference Documentation](https://pkg.go.dev/mod/github.com/aws/aws-sdk-go-v2) - Use this
|
||||
document to look up all API operation input and output parameters for AWS
|
||||
services supported by the SDK. The API reference also includes documentation of
|
||||
the SDK, and examples how to using the SDK, service client API operations, and
|
||||
API operation require parameters.
|
||||
|
||||
[Service Documentation](https://aws.amazon.com/documentation/) - Use this
|
||||
documentation to learn how to interface with AWS services. These guides are
|
||||
great for getting started with a service, or when looking for more
|
||||
information about a service. While this document is not required for coding,
|
||||
services may supply helpful samples to look out for.
|
||||
|
||||
[Forum](https://forums.aws.amazon.com/forum.jspa?forumID=293) - Ask questions, get help, and give feedback
|
||||
|
||||
[Issues] - Report issues, submit pull requests, and get involved
|
||||
(see [Apache 2.0 License][license])
|
||||
|
||||
[Dep]: https://github.com/golang/dep
|
||||
[Issues]: https://github.com/aws/aws-sdk-go-v2/issues
|
||||
[Projects]: https://github.com/aws/aws-sdk-go-v2/projects
|
||||
[CHANGELOG]: https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md
|
||||
[Amazon DynamoDB]: https://aws.amazon.com/dynamodb/
|
||||
[design]: https://github.com/aws/aws-sdk-go-v2/blob/master/DESIGN.md
|
||||
[license]: http://aws.amazon.com/apache2.0/
|
|
@ -0,0 +1,166 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// HTTPClient provides the interface to provide custom HTTPClients. Generally
|
||||
// *http.Client is sufficient for most use cases. The HTTPClient should not
|
||||
// follow redirects.
|
||||
type HTTPClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// A Config provides service configuration for service clients.
|
||||
type Config struct {
|
||||
// The region to send requests to. This parameter is required and must
|
||||
// be configured globally or on a per-client basis unless otherwise
|
||||
// noted. A full list of regions is found in the "Regions and Endpoints"
|
||||
// document.
|
||||
//
|
||||
// See http://docs.aws.amazon.com/general/latest/gr/rande.html for
|
||||
// information on AWS regions.
|
||||
Region string
|
||||
|
||||
// The credentials object to use when signing requests. Defaults to a
|
||||
// chain of credential providers to search for credentials in environment
|
||||
// variables, shared credential file, and EC2 Instance Roles.
|
||||
Credentials CredentialsProvider
|
||||
|
||||
// The HTTP Client the SDK's API clients will use to invoke HTTP requests.
|
||||
// The SDK defaults to a BuildableClient allowing API clients to create
|
||||
// copies of the HTTP Client for service specific customizations.
|
||||
//
|
||||
// Use a (*http.Client) for custom behavior. Using a custom http.Client
|
||||
// will prevent the SDK from modifying the HTTP client.
|
||||
HTTPClient HTTPClient
|
||||
|
||||
// An endpoint resolver that can be used to provide or override an endpoint
|
||||
// for the given service and region.
|
||||
//
|
||||
// See the `aws.EndpointResolver` documentation for additional usage
|
||||
// information.
|
||||
//
|
||||
// Deprecated: See Config.EndpointResolverWithOptions
|
||||
EndpointResolver EndpointResolver
|
||||
|
||||
// An endpoint resolver that can be used to provide or override an endpoint
|
||||
// for the given service and region.
|
||||
//
|
||||
// When EndpointResolverWithOptions is specified, it will be used by a
|
||||
// service client rather than using EndpointResolver if also specified.
|
||||
//
|
||||
// See the `aws.EndpointResolverWithOptions` documentation for additional
|
||||
// usage information.
|
||||
EndpointResolverWithOptions EndpointResolverWithOptions
|
||||
|
||||
// RetryMaxAttempts specifies the maximum number attempts an API client
|
||||
// will call an operation that fails with a retryable error.
|
||||
//
|
||||
// API Clients will only use this value to construct a retryer if the
|
||||
// Config.Retryer member is not nil. This value will be ignored if
|
||||
// Retryer is not nil.
|
||||
RetryMaxAttempts int
|
||||
|
||||
// RetryMode specifies the retry model the API client will be created with.
|
||||
//
|
||||
// API Clients will only use this value to construct a retryer if the
|
||||
// Config.Retryer member is not nil. This value will be ignored if
|
||||
// Retryer is not nil.
|
||||
RetryMode RetryMode
|
||||
|
||||
// Retryer is a function that provides a Retryer implementation. A Retryer
|
||||
// guides how HTTP requests should be retried in case of recoverable
|
||||
// failures. When nil the API client will use a default retryer.
|
||||
//
|
||||
// In general, the provider function should return a new instance of a
|
||||
// Retryer if you are attempting to provide a consistent Retryer
|
||||
// configuration across all clients. This will ensure that each client will
|
||||
// be provided a new instance of the Retryer implementation, and will avoid
|
||||
// issues such as sharing the same retry token bucket across services.
|
||||
//
|
||||
// If not nil, RetryMaxAttempts, and RetryMode will be ignored by API
|
||||
// clients.
|
||||
Retryer func() Retryer
|
||||
|
||||
// ConfigSources are the sources that were used to construct the Config.
|
||||
// Allows for additional configuration to be loaded by clients.
|
||||
ConfigSources []interface{}
|
||||
|
||||
// APIOptions provides the set of middleware mutations modify how the API
|
||||
// client requests will be handled. This is useful for adding additional
|
||||
// tracing data to a request, or changing behavior of the SDK's client.
|
||||
APIOptions []func(*middleware.Stack) error
|
||||
|
||||
// The logger writer interface to write logging messages to. Defaults to
|
||||
// standard error.
|
||||
Logger logging.Logger
|
||||
|
||||
// Configures the events that will be sent to the configured logger. This
|
||||
// can be used to configure the logging of signing, retries, request, and
|
||||
// responses of the SDK clients.
|
||||
//
|
||||
// See the ClientLogMode type documentation for the complete set of logging
|
||||
// modes and available configuration.
|
||||
ClientLogMode ClientLogMode
|
||||
|
||||
// The configured DefaultsMode. If not specified, service clients will
|
||||
// default to legacy.
|
||||
//
|
||||
// Supported modes are: auto, cross-region, in-region, legacy, mobile,
|
||||
// standard
|
||||
DefaultsMode DefaultsMode
|
||||
|
||||
// The RuntimeEnvironment configuration, only populated if the DefaultsMode
|
||||
// is set to DefaultsModeAuto and is initialized by
|
||||
// `config.LoadDefaultConfig`. You should not populate this structure
|
||||
// programmatically, or rely on the values here within your applications.
|
||||
RuntimeEnvironment RuntimeEnvironment
|
||||
}
|
||||
|
||||
// NewConfig returns a new Config pointer that can be chained with builder
|
||||
// methods to set multiple configuration values inline without using pointers.
|
||||
func NewConfig() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
|
||||
// Copy will return a shallow copy of the Config object. If any additional
|
||||
// configurations are provided they will be merged into the new config returned.
|
||||
func (c Config) Copy() Config {
|
||||
cp := c
|
||||
return cp
|
||||
}
|
||||
|
||||
// EndpointDiscoveryEnableState indicates if endpoint discovery is
|
||||
// enabled, disabled, auto or unset state.
|
||||
//
|
||||
// Default behavior (Auto or Unset) indicates operations that require endpoint
|
||||
// discovery will use Endpoint Discovery by default. Operations that
|
||||
// optionally use Endpoint Discovery will not use Endpoint Discovery
|
||||
// unless EndpointDiscovery is explicitly enabled.
|
||||
type EndpointDiscoveryEnableState uint
|
||||
|
||||
// Enumeration values for EndpointDiscoveryEnableState
|
||||
const (
|
||||
// EndpointDiscoveryUnset represents EndpointDiscoveryEnableState is unset.
|
||||
// Users do not need to use this value explicitly. The behavior for unset
|
||||
// is the same as for EndpointDiscoveryAuto.
|
||||
EndpointDiscoveryUnset EndpointDiscoveryEnableState = iota
|
||||
|
||||
// EndpointDiscoveryAuto represents an AUTO state that allows endpoint
|
||||
// discovery only when required by the api. This is the default
|
||||
// configuration resolved by the client if endpoint discovery is neither
|
||||
// enabled or disabled.
|
||||
EndpointDiscoveryAuto // default state
|
||||
|
||||
// EndpointDiscoveryDisabled indicates client MUST not perform endpoint
|
||||
// discovery even when required.
|
||||
EndpointDiscoveryDisabled
|
||||
|
||||
// EndpointDiscoveryEnabled indicates client MUST always perform endpoint
|
||||
// discovery if supported for the operation.
|
||||
EndpointDiscoveryEnabled
|
||||
)
|
|
@ -0,0 +1,22 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type suppressedContext struct {
|
||||
context.Context
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Deadline() (deadline time.Time, ok bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *suppressedContext) Err() error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
sdkrand "github.com/aws/aws-sdk-go-v2/internal/rand"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sync/singleflight"
|
||||
)
|
||||
|
||||
// CredentialsCacheOptions are the options
|
||||
type CredentialsCacheOptions struct {
|
||||
|
||||
// ExpiryWindow will allow the credentials to trigger refreshing prior to
|
||||
// the credentials actually expiring. This is beneficial so race conditions
|
||||
// with expiring credentials do not cause request to fail unexpectedly
|
||||
// due to ExpiredTokenException exceptions.
|
||||
//
|
||||
// An ExpiryWindow of 10s would cause calls to IsExpired() to return true
|
||||
// 10 seconds before the credentials are actually expired. This can cause an
|
||||
// increased number of requests to refresh the credentials to occur.
|
||||
//
|
||||
// If ExpiryWindow is 0 or less it will be ignored.
|
||||
ExpiryWindow time.Duration
|
||||
|
||||
// ExpiryWindowJitterFrac provides a mechanism for randomizing the
|
||||
// expiration of credentials within the configured ExpiryWindow by a random
|
||||
// percentage. Valid values are between 0.0 and 1.0.
|
||||
//
|
||||
// As an example if ExpiryWindow is 60 seconds and ExpiryWindowJitterFrac
|
||||
// is 0.5 then credentials will be set to expire between 30 to 60 seconds
|
||||
// prior to their actual expiration time.
|
||||
//
|
||||
// If ExpiryWindow is 0 or less then ExpiryWindowJitterFrac is ignored.
|
||||
// If ExpiryWindowJitterFrac is 0 then no randomization will be applied to the window.
|
||||
// If ExpiryWindowJitterFrac < 0 the value will be treated as 0.
|
||||
// If ExpiryWindowJitterFrac > 1 the value will be treated as 1.
|
||||
ExpiryWindowJitterFrac float64
|
||||
}
|
||||
|
||||
// CredentialsCache provides caching and concurrency safe credentials retrieval
|
||||
// via the provider's retrieve method.
|
||||
//
|
||||
// CredentialsCache will look for optional interfaces on the Provider to adjust
|
||||
// how the credential cache handles credentials caching.
|
||||
//
|
||||
// * HandleFailRefreshCredentialsCacheStrategy - Allows provider to handle
|
||||
// credential refresh failures. This could return an updated Credentials
|
||||
// value, or attempt another means of retrieving credentials.
|
||||
//
|
||||
// * AdjustExpiresByCredentialsCacheStrategy - Allows provider to adjust how
|
||||
// credentials Expires is modified. This could modify how the Credentials
|
||||
// Expires is adjusted based on the CredentialsCache ExpiryWindow option.
|
||||
// Such as providing a floor not to reduce the Expires below.
|
||||
type CredentialsCache struct {
|
||||
provider CredentialsProvider
|
||||
|
||||
options CredentialsCacheOptions
|
||||
creds atomic.Value
|
||||
sf singleflight.Group
|
||||
}
|
||||
|
||||
// NewCredentialsCache returns a CredentialsCache that wraps provider. Provider
|
||||
// is expected to not be nil. A variadic list of one or more functions can be
|
||||
// provided to modify the CredentialsCache configuration. This allows for
|
||||
// configuration of credential expiry window and jitter.
|
||||
func NewCredentialsCache(provider CredentialsProvider, optFns ...func(options *CredentialsCacheOptions)) *CredentialsCache {
|
||||
options := CredentialsCacheOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
if options.ExpiryWindow < 0 {
|
||||
options.ExpiryWindow = 0
|
||||
}
|
||||
|
||||
if options.ExpiryWindowJitterFrac < 0 {
|
||||
options.ExpiryWindowJitterFrac = 0
|
||||
} else if options.ExpiryWindowJitterFrac > 1 {
|
||||
options.ExpiryWindowJitterFrac = 1
|
||||
}
|
||||
|
||||
return &CredentialsCache{
|
||||
provider: provider,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve returns the credentials. If the credentials have already been
|
||||
// retrieved, and not expired the cached credentials will be returned. If the
|
||||
// credentials have not been retrieved yet, or expired the provider's Retrieve
|
||||
// method will be called.
|
||||
//
|
||||
// Returns and error if the provider's retrieve method returns an error.
|
||||
func (p *CredentialsCache) Retrieve(ctx context.Context) (Credentials, error) {
|
||||
if creds, ok := p.getCreds(); ok && !creds.Expired() {
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
resCh := p.sf.DoChan("", func() (interface{}, error) {
|
||||
return p.singleRetrieve(&suppressedContext{ctx})
|
||||
})
|
||||
select {
|
||||
case res := <-resCh:
|
||||
return res.Val.(Credentials), res.Err
|
||||
case <-ctx.Done():
|
||||
return Credentials{}, &RequestCanceledError{Err: ctx.Err()}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *CredentialsCache) singleRetrieve(ctx context.Context) (interface{}, error) {
|
||||
currCreds, ok := p.getCreds()
|
||||
if ok && !currCreds.Expired() {
|
||||
return currCreds, nil
|
||||
}
|
||||
|
||||
newCreds, err := p.provider.Retrieve(ctx)
|
||||
if err != nil {
|
||||
handleFailToRefresh := defaultHandleFailToRefresh
|
||||
if cs, ok := p.provider.(HandleFailRefreshCredentialsCacheStrategy); ok {
|
||||
handleFailToRefresh = cs.HandleFailToRefresh
|
||||
}
|
||||
newCreds, err = handleFailToRefresh(ctx, currCreds, err)
|
||||
if err != nil {
|
||||
return Credentials{}, fmt.Errorf("failed to refresh cached credentials, %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if newCreds.CanExpire && p.options.ExpiryWindow > 0 {
|
||||
adjustExpiresBy := defaultAdjustExpiresBy
|
||||
if cs, ok := p.provider.(AdjustExpiresByCredentialsCacheStrategy); ok {
|
||||
adjustExpiresBy = cs.AdjustExpiresBy
|
||||
}
|
||||
|
||||
randFloat64, err := sdkrand.CryptoRandFloat64()
|
||||
if err != nil {
|
||||
return Credentials{}, fmt.Errorf("failed to get random provider, %w", err)
|
||||
}
|
||||
|
||||
var jitter time.Duration
|
||||
if p.options.ExpiryWindowJitterFrac > 0 {
|
||||
jitter = time.Duration(randFloat64 *
|
||||
p.options.ExpiryWindowJitterFrac * float64(p.options.ExpiryWindow))
|
||||
}
|
||||
|
||||
newCreds, err = adjustExpiresBy(newCreds, -(p.options.ExpiryWindow - jitter))
|
||||
if err != nil {
|
||||
return Credentials{}, fmt.Errorf("failed to adjust credentials expires, %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
p.creds.Store(&newCreds)
|
||||
return newCreds, nil
|
||||
}
|
||||
|
||||
// getCreds returns the currently stored credentials and true. Returning false
|
||||
// if no credentials were stored.
|
||||
func (p *CredentialsCache) getCreds() (Credentials, bool) {
|
||||
v := p.creds.Load()
|
||||
if v == nil {
|
||||
return Credentials{}, false
|
||||
}
|
||||
|
||||
c := v.(*Credentials)
|
||||
if c == nil || !c.HasKeys() {
|
||||
return Credentials{}, false
|
||||
}
|
||||
|
||||
return *c, true
|
||||
}
|
||||
|
||||
// Invalidate will invalidate the cached credentials. The next call to Retrieve
|
||||
// will cause the provider's Retrieve method to be called.
|
||||
func (p *CredentialsCache) Invalidate() {
|
||||
p.creds.Store((*Credentials)(nil))
|
||||
}
|
||||
|
||||
// HandleFailRefreshCredentialsCacheStrategy is an interface for
|
||||
// CredentialsCache to allow CredentialsProvider how failed to refresh
|
||||
// credentials is handled.
|
||||
type HandleFailRefreshCredentialsCacheStrategy interface {
|
||||
// Given the previously cached Credentials, if any, and refresh error, may
|
||||
// returns new or modified set of Credentials, or error.
|
||||
//
|
||||
// Credential caches may use default implementation if nil.
|
||||
HandleFailToRefresh(context.Context, Credentials, error) (Credentials, error)
|
||||
}
|
||||
|
||||
// defaultHandleFailToRefresh returns the passed in error.
|
||||
func defaultHandleFailToRefresh(ctx context.Context, _ Credentials, err error) (Credentials, error) {
|
||||
return Credentials{}, err
|
||||
}
|
||||
|
||||
// AdjustExpiresByCredentialsCacheStrategy is an interface for CredentialCache
|
||||
// to allow CredentialsProvider to intercept adjustments to Credentials expiry
|
||||
// based on expectations and use cases of CredentialsProvider.
|
||||
//
|
||||
// Credential caches may use default implementation if nil.
|
||||
type AdjustExpiresByCredentialsCacheStrategy interface {
|
||||
// Given a Credentials as input, applying any mutations and
|
||||
// returning the potentially updated Credentials, or error.
|
||||
AdjustExpiresBy(Credentials, time.Duration) (Credentials, error)
|
||||
}
|
||||
|
||||
// defaultAdjustExpiresBy adds the duration to the passed in credentials Expires,
|
||||
// and returns the updated credentials value. If Credentials value's CanExpire
|
||||
// is false, the passed in credentials are returned unchanged.
|
||||
func defaultAdjustExpiresBy(creds Credentials, dur time.Duration) (Credentials, error) {
|
||||
if !creds.CanExpire {
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
creds.Expires = creds.Expires.Add(dur)
|
||||
return creds, nil
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
)
|
||||
|
||||
// AnonymousCredentials provides a sentinel CredentialsProvider that should be
|
||||
// used to instruct the SDK's signing middleware to not sign the request.
|
||||
//
|
||||
// Using `nil` credentials when configuring an API client will achieve the same
|
||||
// result. The AnonymousCredentials type allows you to configure the SDK's
|
||||
// external config loading to not attempt to source credentials from the shared
|
||||
// config or environment.
|
||||
//
|
||||
// For example you can use this CredentialsProvider with an API client's
|
||||
// Options to instruct the client not to sign a request for accessing public
|
||||
// S3 bucket objects.
|
||||
//
|
||||
// The following example demonstrates using the AnonymousCredentials to prevent
|
||||
// SDK's external config loading attempt to resolve credentials.
|
||||
//
|
||||
// cfg, err := config.LoadDefaultConfig(context.TODO(),
|
||||
// config.WithCredentialsProvider(aws.AnonymousCredentials{}),
|
||||
// )
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed to load config, %v", err)
|
||||
// }
|
||||
//
|
||||
// client := s3.NewFromConfig(cfg)
|
||||
//
|
||||
// Alternatively you can leave the API client Option's `Credential` member to
|
||||
// nil. If using the `NewFromConfig` constructor you'll need to explicitly set
|
||||
// the `Credentials` member to nil, if the external config resolved a
|
||||
// credential provider.
|
||||
//
|
||||
// client := s3.New(s3.Options{
|
||||
// // Credentials defaults to a nil value.
|
||||
// })
|
||||
//
|
||||
// This can also be configured for specific operations calls too.
|
||||
//
|
||||
// cfg, err := config.LoadDefaultConfig(context.TODO())
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed to load config, %v", err)
|
||||
// }
|
||||
//
|
||||
// client := s3.NewFromConfig(config)
|
||||
//
|
||||
// result, err := client.GetObject(context.TODO(), s3.GetObject{
|
||||
// Bucket: aws.String("example-bucket"),
|
||||
// Key: aws.String("example-key"),
|
||||
// }, func(o *s3.Options) {
|
||||
// o.Credentials = nil
|
||||
// // Or
|
||||
// o.Credentials = aws.AnonymousCredentials{}
|
||||
// })
|
||||
type AnonymousCredentials struct{}
|
||||
|
||||
// Retrieve implements the CredentialsProvider interface, but will always
|
||||
// return error, and cannot be used to sign a request. The AnonymousCredentials
|
||||
// type is used as a sentinel type instructing the AWS request signing
|
||||
// middleware to not sign a request.
|
||||
func (AnonymousCredentials) Retrieve(context.Context) (Credentials, error) {
|
||||
return Credentials{Source: "AnonymousCredentials"},
|
||||
fmt.Errorf("the AnonymousCredentials is not a valid credential provider, and cannot be used to sign AWS requests with")
|
||||
}
|
||||
|
||||
// A Credentials is the AWS credentials value for individual credential fields.
|
||||
type Credentials struct {
|
||||
// AWS Access key ID
|
||||
AccessKeyID string
|
||||
|
||||
// AWS Secret Access Key
|
||||
SecretAccessKey string
|
||||
|
||||
// AWS Session Token
|
||||
SessionToken string
|
||||
|
||||
// Source of the credentials
|
||||
Source string
|
||||
|
||||
// States if the credentials can expire or not.
|
||||
CanExpire bool
|
||||
|
||||
// The time the credentials will expire at. Should be ignored if CanExpire
|
||||
// is false.
|
||||
Expires time.Time
|
||||
}
|
||||
|
||||
// Expired returns if the credentials have expired.
|
||||
func (v Credentials) Expired() bool {
|
||||
if v.CanExpire {
|
||||
// Calling Round(0) on the current time will truncate the monotonic
|
||||
// reading only. Ensures credential expiry time is always based on
|
||||
// reported wall-clock time.
|
||||
return !v.Expires.After(sdk.NowTime().Round(0))
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// HasKeys returns if the credentials keys are set.
|
||||
func (v Credentials) HasKeys() bool {
|
||||
return len(v.AccessKeyID) > 0 && len(v.SecretAccessKey) > 0
|
||||
}
|
||||
|
||||
// A CredentialsProvider is the interface for any component which will provide
|
||||
// credentials Credentials. A CredentialsProvider is required to manage its own
|
||||
// Expired state, and what to be expired means.
|
||||
//
|
||||
// A credentials provider implementation can be wrapped with a CredentialCache
|
||||
// to cache the credential value retrieved. Without the cache the SDK will
|
||||
// attempt to retrieve the credentials for every request.
|
||||
type CredentialsProvider interface {
|
||||
// Retrieve returns nil if it successfully retrieved the value.
|
||||
// Error is returned if the value were not obtainable, or empty.
|
||||
Retrieve(ctx context.Context) (Credentials, error)
|
||||
}
|
||||
|
||||
// CredentialsProviderFunc provides a helper wrapping a function value to
|
||||
// satisfy the CredentialsProvider interface.
|
||||
type CredentialsProviderFunc func(context.Context) (Credentials, error)
|
||||
|
||||
// Retrieve delegates to the function value the CredentialsProviderFunc wraps.
|
||||
func (fn CredentialsProviderFunc) Retrieve(ctx context.Context) (Credentials, error) {
|
||||
return fn(ctx)
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package defaults
|
||||
|
||||
import (
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var getGOOS = func() string {
|
||||
return runtime.GOOS
|
||||
}
|
||||
|
||||
// ResolveDefaultsModeAuto is used to determine the effective aws.DefaultsMode when the mode
|
||||
// is set to aws.DefaultsModeAuto.
|
||||
func ResolveDefaultsModeAuto(region string, environment aws.RuntimeEnvironment) aws.DefaultsMode {
|
||||
goos := getGOOS()
|
||||
if goos == "android" || goos == "ios" {
|
||||
return aws.DefaultsModeMobile
|
||||
}
|
||||
|
||||
var currentRegion string
|
||||
if len(environment.EnvironmentIdentifier) > 0 {
|
||||
currentRegion = environment.Region
|
||||
}
|
||||
|
||||
if len(currentRegion) == 0 && len(environment.EC2InstanceMetadataRegion) > 0 {
|
||||
currentRegion = environment.EC2InstanceMetadataRegion
|
||||
}
|
||||
|
||||
if len(region) > 0 && len(currentRegion) > 0 {
|
||||
if strings.EqualFold(region, currentRegion) {
|
||||
return aws.DefaultsModeInRegion
|
||||
}
|
||||
return aws.DefaultsModeCrossRegion
|
||||
}
|
||||
|
||||
return aws.DefaultsModeStandard
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package defaults
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// Configuration is the set of SDK configuration options that are determined based
|
||||
// on the configured DefaultsMode.
|
||||
type Configuration struct {
|
||||
// RetryMode is the configuration's default retry mode API clients should
|
||||
// use for constructing a Retryer.
|
||||
RetryMode aws.RetryMode
|
||||
|
||||
// ConnectTimeout is the maximum amount of time a dial will wait for
|
||||
// a connect to complete.
|
||||
//
|
||||
// See https://pkg.go.dev/net#Dialer.Timeout
|
||||
ConnectTimeout *time.Duration
|
||||
|
||||
// TLSNegotiationTimeout specifies the maximum amount of time waiting to
|
||||
// wait for a TLS handshake.
|
||||
//
|
||||
// See https://pkg.go.dev/net/http#Transport.TLSHandshakeTimeout
|
||||
TLSNegotiationTimeout *time.Duration
|
||||
}
|
||||
|
||||
// GetConnectTimeout returns the ConnectTimeout value, returns false if the value is not set.
|
||||
func (c *Configuration) GetConnectTimeout() (time.Duration, bool) {
|
||||
if c.ConnectTimeout == nil {
|
||||
return 0, false
|
||||
}
|
||||
return *c.ConnectTimeout, true
|
||||
}
|
||||
|
||||
// GetTLSNegotiationTimeout returns the TLSNegotiationTimeout value, returns false if the value is not set.
|
||||
func (c *Configuration) GetTLSNegotiationTimeout() (time.Duration, bool) {
|
||||
if c.TLSNegotiationTimeout == nil {
|
||||
return 0, false
|
||||
}
|
||||
return *c.TLSNegotiationTimeout, true
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
// Code generated by github.com/aws/aws-sdk-go-v2/internal/codegen/cmd/defaultsconfig. DO NOT EDIT.
|
||||
|
||||
package defaults
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GetModeConfiguration returns the default Configuration descriptor for the given mode.
|
||||
//
|
||||
// Supports the following modes: cross-region, in-region, mobile, standard
|
||||
func GetModeConfiguration(mode aws.DefaultsMode) (Configuration, error) {
|
||||
var mv aws.DefaultsMode
|
||||
mv.SetFromString(string(mode))
|
||||
|
||||
switch mv {
|
||||
case aws.DefaultsModeCrossRegion:
|
||||
settings := Configuration{
|
||||
ConnectTimeout: aws.Duration(3100 * time.Millisecond),
|
||||
RetryMode: aws.RetryMode("standard"),
|
||||
TLSNegotiationTimeout: aws.Duration(3100 * time.Millisecond),
|
||||
}
|
||||
return settings, nil
|
||||
case aws.DefaultsModeInRegion:
|
||||
settings := Configuration{
|
||||
ConnectTimeout: aws.Duration(1100 * time.Millisecond),
|
||||
RetryMode: aws.RetryMode("standard"),
|
||||
TLSNegotiationTimeout: aws.Duration(1100 * time.Millisecond),
|
||||
}
|
||||
return settings, nil
|
||||
case aws.DefaultsModeMobile:
|
||||
settings := Configuration{
|
||||
ConnectTimeout: aws.Duration(30000 * time.Millisecond),
|
||||
RetryMode: aws.RetryMode("standard"),
|
||||
TLSNegotiationTimeout: aws.Duration(30000 * time.Millisecond),
|
||||
}
|
||||
return settings, nil
|
||||
case aws.DefaultsModeStandard:
|
||||
settings := Configuration{
|
||||
ConnectTimeout: aws.Duration(3100 * time.Millisecond),
|
||||
RetryMode: aws.RetryMode("standard"),
|
||||
TLSNegotiationTimeout: aws.Duration(3100 * time.Millisecond),
|
||||
}
|
||||
return settings, nil
|
||||
default:
|
||||
return Configuration{}, fmt.Errorf("unsupported defaults mode: %v", mode)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
// Package defaults provides recommended configuration values for AWS SDKs and CLIs.
|
||||
package defaults
|
|
@ -0,0 +1,95 @@
|
|||
// Code generated by github.com/aws/aws-sdk-go-v2/internal/codegen/cmd/defaultsmode. DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// DefaultsMode is the SDK defaults mode setting.
|
||||
type DefaultsMode string
|
||||
|
||||
// The DefaultsMode constants.
|
||||
const (
|
||||
// DefaultsModeAuto is an experimental mode that builds on the standard mode.
|
||||
// The SDK will attempt to discover the execution environment to determine the
|
||||
// appropriate settings automatically.
|
||||
//
|
||||
// Note that the auto detection is heuristics-based and does not guarantee 100%
|
||||
// accuracy. STANDARD mode will be used if the execution environment cannot
|
||||
// be determined. The auto detection might query EC2 Instance Metadata service
|
||||
// (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html),
|
||||
// which might introduce latency. Therefore we recommend choosing an explicit
|
||||
// defaults_mode instead if startup latency is critical to your application
|
||||
DefaultsModeAuto DefaultsMode = "auto"
|
||||
|
||||
// DefaultsModeCrossRegion builds on the standard mode and includes optimization
|
||||
// tailored for applications which call AWS services in a different region
|
||||
//
|
||||
// Note that the default values vended from this mode might change as best practices
|
||||
// may evolve. As a result, it is encouraged to perform tests when upgrading
|
||||
// the SDK
|
||||
DefaultsModeCrossRegion DefaultsMode = "cross-region"
|
||||
|
||||
// DefaultsModeInRegion builds on the standard mode and includes optimization
|
||||
// tailored for applications which call AWS services from within the same AWS
|
||||
// region
|
||||
//
|
||||
// Note that the default values vended from this mode might change as best practices
|
||||
// may evolve. As a result, it is encouraged to perform tests when upgrading
|
||||
// the SDK
|
||||
DefaultsModeInRegion DefaultsMode = "in-region"
|
||||
|
||||
// DefaultsModeLegacy provides default settings that vary per SDK and were used
|
||||
// prior to establishment of defaults_mode
|
||||
DefaultsModeLegacy DefaultsMode = "legacy"
|
||||
|
||||
// DefaultsModeMobile builds on the standard mode and includes optimization
|
||||
// tailored for mobile applications
|
||||
//
|
||||
// Note that the default values vended from this mode might change as best practices
|
||||
// may evolve. As a result, it is encouraged to perform tests when upgrading
|
||||
// the SDK
|
||||
DefaultsModeMobile DefaultsMode = "mobile"
|
||||
|
||||
// DefaultsModeStandard provides the latest recommended default values that
|
||||
// should be safe to run in most scenarios
|
||||
//
|
||||
// Note that the default values vended from this mode might change as best practices
|
||||
// may evolve. As a result, it is encouraged to perform tests when upgrading
|
||||
// the SDK
|
||||
DefaultsModeStandard DefaultsMode = "standard"
|
||||
)
|
||||
|
||||
// SetFromString sets the DefaultsMode value to one of the pre-defined constants that matches
|
||||
// the provided string when compared using EqualFold. If the value does not match a known
|
||||
// constant it will be set to as-is and the function will return false. As a special case, if the
|
||||
// provided value is a zero-length string, the mode will be set to LegacyDefaultsMode.
|
||||
func (d *DefaultsMode) SetFromString(v string) (ok bool) {
|
||||
switch {
|
||||
case strings.EqualFold(v, string(DefaultsModeAuto)):
|
||||
*d = DefaultsModeAuto
|
||||
ok = true
|
||||
case strings.EqualFold(v, string(DefaultsModeCrossRegion)):
|
||||
*d = DefaultsModeCrossRegion
|
||||
ok = true
|
||||
case strings.EqualFold(v, string(DefaultsModeInRegion)):
|
||||
*d = DefaultsModeInRegion
|
||||
ok = true
|
||||
case strings.EqualFold(v, string(DefaultsModeLegacy)):
|
||||
*d = DefaultsModeLegacy
|
||||
ok = true
|
||||
case strings.EqualFold(v, string(DefaultsModeMobile)):
|
||||
*d = DefaultsModeMobile
|
||||
ok = true
|
||||
case strings.EqualFold(v, string(DefaultsModeStandard)):
|
||||
*d = DefaultsModeStandard
|
||||
ok = true
|
||||
case len(v) == 0:
|
||||
*d = DefaultsModeLegacy
|
||||
ok = true
|
||||
default:
|
||||
*d = DefaultsMode(v)
|
||||
}
|
||||
return ok
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// Package aws provides the core SDK's utilities and shared types. Use this package's
|
||||
// utilities to simplify setting and reading API operations parameters.
|
||||
//
|
||||
// Value and Pointer Conversion Utilities
|
||||
//
|
||||
// This package includes a helper conversion utility for each scalar type the SDK's
|
||||
// API use. These utilities make getting a pointer of the scalar, and dereferencing
|
||||
// a pointer easier.
|
||||
//
|
||||
// Each conversion utility comes in two forms. Value to Pointer and Pointer to Value.
|
||||
// The Pointer to value will safely dereference the pointer and return its value.
|
||||
// If the pointer was nil, the scalar's zero value will be returned.
|
||||
//
|
||||
// The value to pointer functions will be named after the scalar type. So get a
|
||||
// *string from a string value use the "String" function. This makes it easy to
|
||||
// to get pointer of a literal string value, because getting the address of a
|
||||
// literal requires assigning the value to a variable first.
|
||||
//
|
||||
// var strPtr *string
|
||||
//
|
||||
// // Without the SDK's conversion functions
|
||||
// str := "my string"
|
||||
// strPtr = &str
|
||||
//
|
||||
// // With the SDK's conversion functions
|
||||
// strPtr = aws.String("my string")
|
||||
//
|
||||
// // Convert *string to string value
|
||||
// str = aws.ToString(strPtr)
|
||||
//
|
||||
// In addition to scalars the aws package also includes conversion utilities for
|
||||
// map and slice for commonly types used in API parameters. The map and slice
|
||||
// conversion functions use similar naming pattern as the scalar conversion
|
||||
// functions.
|
||||
//
|
||||
// var strPtrs []*string
|
||||
// var strs []string = []string{"Go", "Gophers", "Go"}
|
||||
//
|
||||
// // Convert []string to []*string
|
||||
// strPtrs = aws.StringSlice(strs)
|
||||
//
|
||||
// // Convert []*string to []string
|
||||
// strs = aws.ToStringSlice(strPtrs)
|
||||
//
|
||||
// SDK Default HTTP Client
|
||||
//
|
||||
// The SDK will use the http.DefaultClient if a HTTP client is not provided to
|
||||
// the SDK's Session, or service client constructor. This means that if the
|
||||
// http.DefaultClient is modified by other components of your application the
|
||||
// modifications will be picked up by the SDK as well.
|
||||
//
|
||||
// In some cases this might be intended, but it is a better practice to create
|
||||
// a custom HTTP Client to share explicitly through your application. You can
|
||||
// configure the SDK to use the custom HTTP Client by setting the HTTPClient
|
||||
// value of the SDK's Config type when creating a Session or service client.
|
||||
package aws
|
||||
|
||||
// generate.go uses a build tag of "ignore", go run doesn't need to specify
|
||||
// this because go run ignores all build flags when running a go file directly.
|
||||
//go:generate go run -tags codegen generate.go
|
||||
//go:generate go run -tags codegen logging_generate.go
|
||||
//go:generate gofmt -w -s .
|
|
@ -0,0 +1,229 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// DualStackEndpointState is a constant to describe the dual-stack endpoint resolution behavior.
|
||||
type DualStackEndpointState uint
|
||||
|
||||
const (
|
||||
// DualStackEndpointStateUnset is the default value behavior for dual-stack endpoint resolution.
|
||||
DualStackEndpointStateUnset DualStackEndpointState = iota
|
||||
|
||||
// DualStackEndpointStateEnabled enables dual-stack endpoint resolution for service endpoints.
|
||||
DualStackEndpointStateEnabled
|
||||
|
||||
// DualStackEndpointStateDisabled disables dual-stack endpoint resolution for endpoints.
|
||||
DualStackEndpointStateDisabled
|
||||
)
|
||||
|
||||
// GetUseDualStackEndpoint takes a service's EndpointResolverOptions and returns the UseDualStackEndpoint value.
|
||||
// Returns boolean false if the provided options does not have a method to retrieve the DualStackEndpointState.
|
||||
func GetUseDualStackEndpoint(options ...interface{}) (value DualStackEndpointState, found bool) {
|
||||
type iface interface {
|
||||
GetUseDualStackEndpoint() DualStackEndpointState
|
||||
}
|
||||
for _, option := range options {
|
||||
if i, ok := option.(iface); ok {
|
||||
value = i.GetUseDualStackEndpoint()
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return value, found
|
||||
}
|
||||
|
||||
// FIPSEndpointState is a constant to describe the FIPS endpoint resolution behavior.
|
||||
type FIPSEndpointState uint
|
||||
|
||||
const (
|
||||
// FIPSEndpointStateUnset is the default value behavior for FIPS endpoint resolution.
|
||||
FIPSEndpointStateUnset FIPSEndpointState = iota
|
||||
|
||||
// FIPSEndpointStateEnabled enables FIPS endpoint resolution for service endpoints.
|
||||
FIPSEndpointStateEnabled
|
||||
|
||||
// FIPSEndpointStateDisabled disables FIPS endpoint resolution for endpoints.
|
||||
FIPSEndpointStateDisabled
|
||||
)
|
||||
|
||||
// GetUseFIPSEndpoint takes a service's EndpointResolverOptions and returns the UseDualStackEndpoint value.
|
||||
// Returns boolean false if the provided options does not have a method to retrieve the DualStackEndpointState.
|
||||
func GetUseFIPSEndpoint(options ...interface{}) (value FIPSEndpointState, found bool) {
|
||||
type iface interface {
|
||||
GetUseFIPSEndpoint() FIPSEndpointState
|
||||
}
|
||||
for _, option := range options {
|
||||
if i, ok := option.(iface); ok {
|
||||
value = i.GetUseFIPSEndpoint()
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return value, found
|
||||
}
|
||||
|
||||
// Endpoint represents the endpoint a service client should make API operation
|
||||
// calls to.
|
||||
//
|
||||
// The SDK will automatically resolve these endpoints per API client using an
|
||||
// internal endpoint resolvers. If you'd like to provide custom endpoint
|
||||
// resolving behavior you can implement the EndpointResolver interface.
|
||||
type Endpoint struct {
|
||||
// The base URL endpoint the SDK API clients will use to make API calls to.
|
||||
// The SDK will suffix URI path and query elements to this endpoint.
|
||||
URL string
|
||||
|
||||
// Specifies if the endpoint's hostname can be modified by the SDK's API
|
||||
// client.
|
||||
//
|
||||
// If the hostname is mutable the SDK API clients may modify any part of
|
||||
// the hostname based on the requirements of the API, (e.g. adding, or
|
||||
// removing content in the hostname). Such as, Amazon S3 API client
|
||||
// prefixing "bucketname" to the hostname, or changing the
|
||||
// hostname service name component from "s3." to "s3-accesspoint.dualstack."
|
||||
// for the dualstack endpoint of an S3 Accesspoint resource.
|
||||
//
|
||||
// Care should be taken when providing a custom endpoint for an API. If the
|
||||
// endpoint hostname is mutable, and the client cannot modify the endpoint
|
||||
// correctly, the operation call will most likely fail, or have undefined
|
||||
// behavior.
|
||||
//
|
||||
// If hostname is immutable, the SDK API clients will not modify the
|
||||
// hostname of the URL. This may cause the API client not to function
|
||||
// correctly if the API requires the operation specific hostname values
|
||||
// to be used by the client.
|
||||
//
|
||||
// This flag does not modify the API client's behavior if this endpoint
|
||||
// will be used instead of Endpoint Discovery, or if the endpoint will be
|
||||
// used to perform Endpoint Discovery. That behavior is configured via the
|
||||
// API Client's Options.
|
||||
HostnameImmutable bool
|
||||
|
||||
// The AWS partition the endpoint belongs to.
|
||||
PartitionID string
|
||||
|
||||
// The service name that should be used for signing the requests to the
|
||||
// endpoint.
|
||||
SigningName string
|
||||
|
||||
// The region that should be used for signing the request to the endpoint.
|
||||
SigningRegion string
|
||||
|
||||
// The signing method that should be used for signing the requests to the
|
||||
// endpoint.
|
||||
SigningMethod string
|
||||
|
||||
// The source of the Endpoint. By default, this will be EndpointSourceServiceMetadata.
|
||||
// When providing a custom endpoint, you should set the source as EndpointSourceCustom.
|
||||
// If source is not provided when providing a custom endpoint, the SDK may not
|
||||
// perform required host mutations correctly. Source should be used along with
|
||||
// HostnameImmutable property as per the usage requirement.
|
||||
Source EndpointSource
|
||||
}
|
||||
|
||||
// EndpointSource is the endpoint source type.
|
||||
type EndpointSource int
|
||||
|
||||
const (
|
||||
// EndpointSourceServiceMetadata denotes service modeled endpoint metadata is used as Endpoint Source.
|
||||
EndpointSourceServiceMetadata EndpointSource = iota
|
||||
|
||||
// EndpointSourceCustom denotes endpoint is a custom endpoint. This source should be used when
|
||||
// user provides a custom endpoint to be used by the SDK.
|
||||
EndpointSourceCustom
|
||||
)
|
||||
|
||||
// EndpointNotFoundError is a sentinel error to indicate that the
|
||||
// EndpointResolver implementation was unable to resolve an endpoint for the
|
||||
// given service and region. Resolvers should use this to indicate that an API
|
||||
// client should fallback and attempt to use it's internal default resolver to
|
||||
// resolve the endpoint.
|
||||
type EndpointNotFoundError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error is the error message.
|
||||
func (e *EndpointNotFoundError) Error() string {
|
||||
return fmt.Sprintf("endpoint not found, %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error.
|
||||
func (e *EndpointNotFoundError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// EndpointResolver is an endpoint resolver that can be used to provide or
|
||||
// override an endpoint for the given service and region. API clients will
|
||||
// attempt to use the EndpointResolver first to resolve an endpoint if
|
||||
// available. If the EndpointResolver returns an EndpointNotFoundError error,
|
||||
// API clients will fallback to attempting to resolve the endpoint using its
|
||||
// internal default endpoint resolver.
|
||||
//
|
||||
// Deprecated: See EndpointResolverWithOptions
|
||||
type EndpointResolver interface {
|
||||
ResolveEndpoint(service, region string) (Endpoint, error)
|
||||
}
|
||||
|
||||
// EndpointResolverFunc wraps a function to satisfy the EndpointResolver interface.
|
||||
//
|
||||
// Deprecated: See EndpointResolverWithOptionsFunc
|
||||
type EndpointResolverFunc func(service, region string) (Endpoint, error)
|
||||
|
||||
// ResolveEndpoint calls the wrapped function and returns the results.
|
||||
//
|
||||
// Deprecated: See EndpointResolverWithOptions.ResolveEndpoint
|
||||
func (e EndpointResolverFunc) ResolveEndpoint(service, region string) (Endpoint, error) {
|
||||
return e(service, region)
|
||||
}
|
||||
|
||||
// EndpointResolverWithOptions is an endpoint resolver that can be used to provide or
|
||||
// override an endpoint for the given service, region, and the service client's EndpointOptions. API clients will
|
||||
// attempt to use the EndpointResolverWithOptions first to resolve an endpoint if
|
||||
// available. If the EndpointResolverWithOptions returns an EndpointNotFoundError error,
|
||||
// API clients will fallback to attempting to resolve the endpoint using its
|
||||
// internal default endpoint resolver.
|
||||
type EndpointResolverWithOptions interface {
|
||||
ResolveEndpoint(service, region string, options ...interface{}) (Endpoint, error)
|
||||
}
|
||||
|
||||
// EndpointResolverWithOptionsFunc wraps a function to satisfy the EndpointResolverWithOptions interface.
|
||||
type EndpointResolverWithOptionsFunc func(service, region string, options ...interface{}) (Endpoint, error)
|
||||
|
||||
// ResolveEndpoint calls the wrapped function and returns the results.
|
||||
func (e EndpointResolverWithOptionsFunc) ResolveEndpoint(service, region string, options ...interface{}) (Endpoint, error) {
|
||||
return e(service, region, options...)
|
||||
}
|
||||
|
||||
// GetDisableHTTPS takes a service's EndpointResolverOptions and returns the DisableHTTPS value.
|
||||
// Returns boolean false if the provided options does not have a method to retrieve the DisableHTTPS.
|
||||
func GetDisableHTTPS(options ...interface{}) (value bool, found bool) {
|
||||
type iface interface {
|
||||
GetDisableHTTPS() bool
|
||||
}
|
||||
for _, option := range options {
|
||||
if i, ok := option.(iface); ok {
|
||||
value = i.GetDisableHTTPS()
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return value, found
|
||||
}
|
||||
|
||||
// GetResolvedRegion takes a service's EndpointResolverOptions and returns the ResolvedRegion value.
|
||||
// Returns boolean false if the provided options does not have a method to retrieve the ResolvedRegion.
|
||||
func GetResolvedRegion(options ...interface{}) (value string, found bool) {
|
||||
type iface interface {
|
||||
GetResolvedRegion() string
|
||||
}
|
||||
for _, option := range options {
|
||||
if i, ok := option.(iface); ok {
|
||||
value = i.GetResolvedRegion()
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return value, found
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package aws
|
||||
|
||||
// MissingRegionError is an error that is returned if region configuration
|
||||
// value was not found.
|
||||
type MissingRegionError struct{}
|
||||
|
||||
func (*MissingRegionError) Error() string {
|
||||
return "an AWS region is required, but was not found"
|
||||
}
|
|
@ -0,0 +1,365 @@
|
|||
// Code generated by aws/generate.go DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ToBool returns bool value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a bool zero value if the
|
||||
// pointer was nil.
|
||||
func ToBool(p *bool) (v bool) {
|
||||
return ptr.ToBool(p)
|
||||
}
|
||||
|
||||
// ToBoolSlice returns a slice of bool values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a bool
|
||||
// zero value if the pointer was nil.
|
||||
func ToBoolSlice(vs []*bool) []bool {
|
||||
return ptr.ToBoolSlice(vs)
|
||||
}
|
||||
|
||||
// ToBoolMap returns a map of bool values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The bool
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToBoolMap(vs map[string]*bool) map[string]bool {
|
||||
return ptr.ToBoolMap(vs)
|
||||
}
|
||||
|
||||
// ToByte returns byte value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a byte zero value if the
|
||||
// pointer was nil.
|
||||
func ToByte(p *byte) (v byte) {
|
||||
return ptr.ToByte(p)
|
||||
}
|
||||
|
||||
// ToByteSlice returns a slice of byte values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a byte
|
||||
// zero value if the pointer was nil.
|
||||
func ToByteSlice(vs []*byte) []byte {
|
||||
return ptr.ToByteSlice(vs)
|
||||
}
|
||||
|
||||
// ToByteMap returns a map of byte values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The byte
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToByteMap(vs map[string]*byte) map[string]byte {
|
||||
return ptr.ToByteMap(vs)
|
||||
}
|
||||
|
||||
// ToString returns string value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a string zero value if the
|
||||
// pointer was nil.
|
||||
func ToString(p *string) (v string) {
|
||||
return ptr.ToString(p)
|
||||
}
|
||||
|
||||
// ToStringSlice returns a slice of string values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a string
|
||||
// zero value if the pointer was nil.
|
||||
func ToStringSlice(vs []*string) []string {
|
||||
return ptr.ToStringSlice(vs)
|
||||
}
|
||||
|
||||
// ToStringMap returns a map of string values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The string
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToStringMap(vs map[string]*string) map[string]string {
|
||||
return ptr.ToStringMap(vs)
|
||||
}
|
||||
|
||||
// ToInt returns int value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a int zero value if the
|
||||
// pointer was nil.
|
||||
func ToInt(p *int) (v int) {
|
||||
return ptr.ToInt(p)
|
||||
}
|
||||
|
||||
// ToIntSlice returns a slice of int values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a int
|
||||
// zero value if the pointer was nil.
|
||||
func ToIntSlice(vs []*int) []int {
|
||||
return ptr.ToIntSlice(vs)
|
||||
}
|
||||
|
||||
// ToIntMap returns a map of int values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The int
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToIntMap(vs map[string]*int) map[string]int {
|
||||
return ptr.ToIntMap(vs)
|
||||
}
|
||||
|
||||
// ToInt8 returns int8 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a int8 zero value if the
|
||||
// pointer was nil.
|
||||
func ToInt8(p *int8) (v int8) {
|
||||
return ptr.ToInt8(p)
|
||||
}
|
||||
|
||||
// ToInt8Slice returns a slice of int8 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a int8
|
||||
// zero value if the pointer was nil.
|
||||
func ToInt8Slice(vs []*int8) []int8 {
|
||||
return ptr.ToInt8Slice(vs)
|
||||
}
|
||||
|
||||
// ToInt8Map returns a map of int8 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The int8
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToInt8Map(vs map[string]*int8) map[string]int8 {
|
||||
return ptr.ToInt8Map(vs)
|
||||
}
|
||||
|
||||
// ToInt16 returns int16 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a int16 zero value if the
|
||||
// pointer was nil.
|
||||
func ToInt16(p *int16) (v int16) {
|
||||
return ptr.ToInt16(p)
|
||||
}
|
||||
|
||||
// ToInt16Slice returns a slice of int16 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a int16
|
||||
// zero value if the pointer was nil.
|
||||
func ToInt16Slice(vs []*int16) []int16 {
|
||||
return ptr.ToInt16Slice(vs)
|
||||
}
|
||||
|
||||
// ToInt16Map returns a map of int16 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The int16
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToInt16Map(vs map[string]*int16) map[string]int16 {
|
||||
return ptr.ToInt16Map(vs)
|
||||
}
|
||||
|
||||
// ToInt32 returns int32 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a int32 zero value if the
|
||||
// pointer was nil.
|
||||
func ToInt32(p *int32) (v int32) {
|
||||
return ptr.ToInt32(p)
|
||||
}
|
||||
|
||||
// ToInt32Slice returns a slice of int32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a int32
|
||||
// zero value if the pointer was nil.
|
||||
func ToInt32Slice(vs []*int32) []int32 {
|
||||
return ptr.ToInt32Slice(vs)
|
||||
}
|
||||
|
||||
// ToInt32Map returns a map of int32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The int32
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToInt32Map(vs map[string]*int32) map[string]int32 {
|
||||
return ptr.ToInt32Map(vs)
|
||||
}
|
||||
|
||||
// ToInt64 returns int64 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a int64 zero value if the
|
||||
// pointer was nil.
|
||||
func ToInt64(p *int64) (v int64) {
|
||||
return ptr.ToInt64(p)
|
||||
}
|
||||
|
||||
// ToInt64Slice returns a slice of int64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a int64
|
||||
// zero value if the pointer was nil.
|
||||
func ToInt64Slice(vs []*int64) []int64 {
|
||||
return ptr.ToInt64Slice(vs)
|
||||
}
|
||||
|
||||
// ToInt64Map returns a map of int64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The int64
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToInt64Map(vs map[string]*int64) map[string]int64 {
|
||||
return ptr.ToInt64Map(vs)
|
||||
}
|
||||
|
||||
// ToUint returns uint value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a uint zero value if the
|
||||
// pointer was nil.
|
||||
func ToUint(p *uint) (v uint) {
|
||||
return ptr.ToUint(p)
|
||||
}
|
||||
|
||||
// ToUintSlice returns a slice of uint values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a uint
|
||||
// zero value if the pointer was nil.
|
||||
func ToUintSlice(vs []*uint) []uint {
|
||||
return ptr.ToUintSlice(vs)
|
||||
}
|
||||
|
||||
// ToUintMap returns a map of uint values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The uint
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToUintMap(vs map[string]*uint) map[string]uint {
|
||||
return ptr.ToUintMap(vs)
|
||||
}
|
||||
|
||||
// ToUint8 returns uint8 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a uint8 zero value if the
|
||||
// pointer was nil.
|
||||
func ToUint8(p *uint8) (v uint8) {
|
||||
return ptr.ToUint8(p)
|
||||
}
|
||||
|
||||
// ToUint8Slice returns a slice of uint8 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a uint8
|
||||
// zero value if the pointer was nil.
|
||||
func ToUint8Slice(vs []*uint8) []uint8 {
|
||||
return ptr.ToUint8Slice(vs)
|
||||
}
|
||||
|
||||
// ToUint8Map returns a map of uint8 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The uint8
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToUint8Map(vs map[string]*uint8) map[string]uint8 {
|
||||
return ptr.ToUint8Map(vs)
|
||||
}
|
||||
|
||||
// ToUint16 returns uint16 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a uint16 zero value if the
|
||||
// pointer was nil.
|
||||
func ToUint16(p *uint16) (v uint16) {
|
||||
return ptr.ToUint16(p)
|
||||
}
|
||||
|
||||
// ToUint16Slice returns a slice of uint16 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a uint16
|
||||
// zero value if the pointer was nil.
|
||||
func ToUint16Slice(vs []*uint16) []uint16 {
|
||||
return ptr.ToUint16Slice(vs)
|
||||
}
|
||||
|
||||
// ToUint16Map returns a map of uint16 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The uint16
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToUint16Map(vs map[string]*uint16) map[string]uint16 {
|
||||
return ptr.ToUint16Map(vs)
|
||||
}
|
||||
|
||||
// ToUint32 returns uint32 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a uint32 zero value if the
|
||||
// pointer was nil.
|
||||
func ToUint32(p *uint32) (v uint32) {
|
||||
return ptr.ToUint32(p)
|
||||
}
|
||||
|
||||
// ToUint32Slice returns a slice of uint32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a uint32
|
||||
// zero value if the pointer was nil.
|
||||
func ToUint32Slice(vs []*uint32) []uint32 {
|
||||
return ptr.ToUint32Slice(vs)
|
||||
}
|
||||
|
||||
// ToUint32Map returns a map of uint32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The uint32
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToUint32Map(vs map[string]*uint32) map[string]uint32 {
|
||||
return ptr.ToUint32Map(vs)
|
||||
}
|
||||
|
||||
// ToUint64 returns uint64 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a uint64 zero value if the
|
||||
// pointer was nil.
|
||||
func ToUint64(p *uint64) (v uint64) {
|
||||
return ptr.ToUint64(p)
|
||||
}
|
||||
|
||||
// ToUint64Slice returns a slice of uint64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a uint64
|
||||
// zero value if the pointer was nil.
|
||||
func ToUint64Slice(vs []*uint64) []uint64 {
|
||||
return ptr.ToUint64Slice(vs)
|
||||
}
|
||||
|
||||
// ToUint64Map returns a map of uint64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The uint64
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToUint64Map(vs map[string]*uint64) map[string]uint64 {
|
||||
return ptr.ToUint64Map(vs)
|
||||
}
|
||||
|
||||
// ToFloat32 returns float32 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a float32 zero value if the
|
||||
// pointer was nil.
|
||||
func ToFloat32(p *float32) (v float32) {
|
||||
return ptr.ToFloat32(p)
|
||||
}
|
||||
|
||||
// ToFloat32Slice returns a slice of float32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a float32
|
||||
// zero value if the pointer was nil.
|
||||
func ToFloat32Slice(vs []*float32) []float32 {
|
||||
return ptr.ToFloat32Slice(vs)
|
||||
}
|
||||
|
||||
// ToFloat32Map returns a map of float32 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The float32
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToFloat32Map(vs map[string]*float32) map[string]float32 {
|
||||
return ptr.ToFloat32Map(vs)
|
||||
}
|
||||
|
||||
// ToFloat64 returns float64 value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a float64 zero value if the
|
||||
// pointer was nil.
|
||||
func ToFloat64(p *float64) (v float64) {
|
||||
return ptr.ToFloat64(p)
|
||||
}
|
||||
|
||||
// ToFloat64Slice returns a slice of float64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a float64
|
||||
// zero value if the pointer was nil.
|
||||
func ToFloat64Slice(vs []*float64) []float64 {
|
||||
return ptr.ToFloat64Slice(vs)
|
||||
}
|
||||
|
||||
// ToFloat64Map returns a map of float64 values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The float64
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToFloat64Map(vs map[string]*float64) map[string]float64 {
|
||||
return ptr.ToFloat64Map(vs)
|
||||
}
|
||||
|
||||
// ToTime returns time.Time value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a time.Time zero value if the
|
||||
// pointer was nil.
|
||||
func ToTime(p *time.Time) (v time.Time) {
|
||||
return ptr.ToTime(p)
|
||||
}
|
||||
|
||||
// ToTimeSlice returns a slice of time.Time values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a time.Time
|
||||
// zero value if the pointer was nil.
|
||||
func ToTimeSlice(vs []*time.Time) []time.Time {
|
||||
return ptr.ToTimeSlice(vs)
|
||||
}
|
||||
|
||||
// ToTimeMap returns a map of time.Time values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The time.Time
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToTimeMap(vs map[string]*time.Time) map[string]time.Time {
|
||||
return ptr.ToTimeMap(vs)
|
||||
}
|
||||
|
||||
// ToDuration returns time.Duration value dereferenced if the passed
|
||||
// in pointer was not nil. Returns a time.Duration zero value if the
|
||||
// pointer was nil.
|
||||
func ToDuration(p *time.Duration) (v time.Duration) {
|
||||
return ptr.ToDuration(p)
|
||||
}
|
||||
|
||||
// ToDurationSlice returns a slice of time.Duration values, that are
|
||||
// dereferenced if the passed in pointer was not nil. Returns a time.Duration
|
||||
// zero value if the pointer was nil.
|
||||
func ToDurationSlice(vs []*time.Duration) []time.Duration {
|
||||
return ptr.ToDurationSlice(vs)
|
||||
}
|
||||
|
||||
// ToDurationMap returns a map of time.Duration values, that are
|
||||
// dereferenced if the passed in pointer was not nil. The time.Duration
|
||||
// zero value is used if the pointer was nil.
|
||||
func ToDurationMap(vs map[string]*time.Duration) map[string]time.Duration {
|
||||
return ptr.ToDurationMap(vs)
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.16.3"
|
|
@ -0,0 +1,117 @@
|
|||
// Code generated by aws/logging_generate.go DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
// ClientLogMode represents the logging mode of SDK clients. The client logging mode is a bit-field where
|
||||
// each bit is a flag that describes the logging behavior for one or more client components.
|
||||
// The entire 64-bit group is reserved for later expansion by the SDK.
|
||||
//
|
||||
// Example: Setting ClientLogMode to enable logging of retries and requests
|
||||
// clientLogMode := aws.LogRetries | aws.LogRequest
|
||||
//
|
||||
// Example: Adding an additional log mode to an existing ClientLogMode value
|
||||
// clientLogMode |= aws.LogResponse
|
||||
type ClientLogMode uint64
|
||||
|
||||
// Supported ClientLogMode bits that can be configured to toggle logging of specific SDK events.
|
||||
const (
|
||||
LogSigning ClientLogMode = 1 << (64 - 1 - iota)
|
||||
LogRetries
|
||||
LogRequest
|
||||
LogRequestWithBody
|
||||
LogResponse
|
||||
LogResponseWithBody
|
||||
LogDeprecatedUsage
|
||||
LogRequestEventMessage
|
||||
LogResponseEventMessage
|
||||
)
|
||||
|
||||
// IsSigning returns whether the Signing logging mode bit is set
|
||||
func (m ClientLogMode) IsSigning() bool {
|
||||
return m&LogSigning != 0
|
||||
}
|
||||
|
||||
// IsRetries returns whether the Retries logging mode bit is set
|
||||
func (m ClientLogMode) IsRetries() bool {
|
||||
return m&LogRetries != 0
|
||||
}
|
||||
|
||||
// IsRequest returns whether the Request logging mode bit is set
|
||||
func (m ClientLogMode) IsRequest() bool {
|
||||
return m&LogRequest != 0
|
||||
}
|
||||
|
||||
// IsRequestWithBody returns whether the RequestWithBody logging mode bit is set
|
||||
func (m ClientLogMode) IsRequestWithBody() bool {
|
||||
return m&LogRequestWithBody != 0
|
||||
}
|
||||
|
||||
// IsResponse returns whether the Response logging mode bit is set
|
||||
func (m ClientLogMode) IsResponse() bool {
|
||||
return m&LogResponse != 0
|
||||
}
|
||||
|
||||
// IsResponseWithBody returns whether the ResponseWithBody logging mode bit is set
|
||||
func (m ClientLogMode) IsResponseWithBody() bool {
|
||||
return m&LogResponseWithBody != 0
|
||||
}
|
||||
|
||||
// IsDeprecatedUsage returns whether the DeprecatedUsage logging mode bit is set
|
||||
func (m ClientLogMode) IsDeprecatedUsage() bool {
|
||||
return m&LogDeprecatedUsage != 0
|
||||
}
|
||||
|
||||
// IsRequestEventMessage returns whether the RequestEventMessage logging mode bit is set
|
||||
func (m ClientLogMode) IsRequestEventMessage() bool {
|
||||
return m&LogRequestEventMessage != 0
|
||||
}
|
||||
|
||||
// IsResponseEventMessage returns whether the ResponseEventMessage logging mode bit is set
|
||||
func (m ClientLogMode) IsResponseEventMessage() bool {
|
||||
return m&LogResponseEventMessage != 0
|
||||
}
|
||||
|
||||
// ClearSigning clears the Signing logging mode bit
|
||||
func (m *ClientLogMode) ClearSigning() {
|
||||
*m &^= LogSigning
|
||||
}
|
||||
|
||||
// ClearRetries clears the Retries logging mode bit
|
||||
func (m *ClientLogMode) ClearRetries() {
|
||||
*m &^= LogRetries
|
||||
}
|
||||
|
||||
// ClearRequest clears the Request logging mode bit
|
||||
func (m *ClientLogMode) ClearRequest() {
|
||||
*m &^= LogRequest
|
||||
}
|
||||
|
||||
// ClearRequestWithBody clears the RequestWithBody logging mode bit
|
||||
func (m *ClientLogMode) ClearRequestWithBody() {
|
||||
*m &^= LogRequestWithBody
|
||||
}
|
||||
|
||||
// ClearResponse clears the Response logging mode bit
|
||||
func (m *ClientLogMode) ClearResponse() {
|
||||
*m &^= LogResponse
|
||||
}
|
||||
|
||||
// ClearResponseWithBody clears the ResponseWithBody logging mode bit
|
||||
func (m *ClientLogMode) ClearResponseWithBody() {
|
||||
*m &^= LogResponseWithBody
|
||||
}
|
||||
|
||||
// ClearDeprecatedUsage clears the DeprecatedUsage logging mode bit
|
||||
func (m *ClientLogMode) ClearDeprecatedUsage() {
|
||||
*m &^= LogDeprecatedUsage
|
||||
}
|
||||
|
||||
// ClearRequestEventMessage clears the RequestEventMessage logging mode bit
|
||||
func (m *ClientLogMode) ClearRequestEventMessage() {
|
||||
*m &^= LogRequestEventMessage
|
||||
}
|
||||
|
||||
// ClearResponseEventMessage clears the ResponseEventMessage logging mode bit
|
||||
func (m *ClientLogMode) ClearResponseEventMessage() {
|
||||
*m &^= LogResponseEventMessage
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
//go:build clientlogmode
|
||||
// +build clientlogmode
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var config = struct {
|
||||
ModeBits []string
|
||||
}{
|
||||
// Items should be appended only to keep bit-flag positions stable
|
||||
ModeBits: []string{
|
||||
"Signing",
|
||||
"Retries",
|
||||
"Request",
|
||||
"RequestWithBody",
|
||||
"Response",
|
||||
"ResponseWithBody",
|
||||
"DeprecatedUsage",
|
||||
"RequestEventMessage",
|
||||
"ResponseEventMessage",
|
||||
},
|
||||
}
|
||||
|
||||
func bitName(name string) string {
|
||||
return strings.ToUpper(name[:1]) + name[1:]
|
||||
}
|
||||
|
||||
var tmpl = template.Must(template.New("ClientLogMode").Funcs(map[string]interface{}{
|
||||
"symbolName": func(name string) string {
|
||||
return "Log" + bitName(name)
|
||||
},
|
||||
"bitName": bitName,
|
||||
}).Parse(`// Code generated by aws/logging_generate.go DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
// ClientLogMode represents the logging mode of SDK clients. The client logging mode is a bit-field where
|
||||
// each bit is a flag that describes the logging behavior for one or more client components.
|
||||
// The entire 64-bit group is reserved for later expansion by the SDK.
|
||||
//
|
||||
// Example: Setting ClientLogMode to enable logging of retries and requests
|
||||
// clientLogMode := aws.LogRetries | aws.LogRequest
|
||||
//
|
||||
// Example: Adding an additional log mode to an existing ClientLogMode value
|
||||
// clientLogMode |= aws.LogResponse
|
||||
type ClientLogMode uint64
|
||||
|
||||
// Supported ClientLogMode bits that can be configured to toggle logging of specific SDK events.
|
||||
const (
|
||||
{{- range $index, $field := .ModeBits }}
|
||||
{{ (symbolName $field) }}{{- if (eq 0 $index) }} ClientLogMode = 1 << (64 - 1 - iota){{- end }}
|
||||
{{- end }}
|
||||
)
|
||||
{{ range $_, $field := .ModeBits }}
|
||||
// Is{{- bitName $field }} returns whether the {{ bitName $field }} logging mode bit is set
|
||||
func (m ClientLogMode) Is{{- bitName $field }}() bool {
|
||||
return m&{{- (symbolName $field) }} != 0
|
||||
}
|
||||
{{ end }}
|
||||
{{- range $_, $field := .ModeBits }}
|
||||
// Clear{{- bitName $field }} clears the {{ bitName $field }} logging mode bit
|
||||
func (m *ClientLogMode) Clear{{- bitName $field }}() {
|
||||
*m &^= {{ (symbolName $field) }}
|
||||
}
|
||||
{{ end -}}
|
||||
`))
|
||||
|
||||
func main() {
|
||||
uniqueBitFields := make(map[string]struct{})
|
||||
|
||||
for _, bitName := range config.ModeBits {
|
||||
if _, ok := uniqueBitFields[strings.ToLower(bitName)]; ok {
|
||||
panic(fmt.Sprintf("duplicate bit field: %s", bitName))
|
||||
}
|
||||
uniqueBitFields[bitName] = struct{}{}
|
||||
}
|
||||
|
||||
file, err := os.Create("logging.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
err = tmpl.Execute(file, config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// RegisterServiceMetadata registers metadata about the service and operation into the middleware context
|
||||
// so that it is available at runtime for other middleware to introspect.
|
||||
type RegisterServiceMetadata struct {
|
||||
ServiceID string
|
||||
SigningName string
|
||||
Region string
|
||||
OperationName string
|
||||
}
|
||||
|
||||
// ID returns the middleware identifier.
|
||||
func (s *RegisterServiceMetadata) ID() string {
|
||||
return "RegisterServiceMetadata"
|
||||
}
|
||||
|
||||
// HandleInitialize registers service metadata information into the middleware context, allowing for introspection.
|
||||
func (s RegisterServiceMetadata) HandleInitialize(
|
||||
ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler,
|
||||
) (out middleware.InitializeOutput, metadata middleware.Metadata, err error) {
|
||||
if len(s.ServiceID) > 0 {
|
||||
ctx = SetServiceID(ctx, s.ServiceID)
|
||||
}
|
||||
if len(s.SigningName) > 0 {
|
||||
ctx = SetSigningName(ctx, s.SigningName)
|
||||
}
|
||||
if len(s.Region) > 0 {
|
||||
ctx = setRegion(ctx, s.Region)
|
||||
}
|
||||
if len(s.OperationName) > 0 {
|
||||
ctx = setOperationName(ctx, s.OperationName)
|
||||
}
|
||||
return next.HandleInitialize(ctx, in)
|
||||
}
|
||||
|
||||
// service metadata keys for storing and lookup of runtime stack information.
|
||||
type (
|
||||
serviceIDKey struct{}
|
||||
signingNameKey struct{}
|
||||
signingRegionKey struct{}
|
||||
regionKey struct{}
|
||||
operationNameKey struct{}
|
||||
partitionIDKey struct{}
|
||||
)
|
||||
|
||||
// GetServiceID retrieves the service id from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetServiceID(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, serviceIDKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// GetSigningName retrieves the service signing name from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetSigningName(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, signingNameKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// GetSigningRegion retrieves the region from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetSigningRegion(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, signingRegionKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// GetRegion retrieves the endpoint region from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetRegion(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, regionKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// GetOperationName retrieves the service operation metadata from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetOperationName(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, operationNameKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// GetPartitionID retrieves the endpoint partition id from the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetPartitionID(ctx context.Context) string {
|
||||
v, _ := middleware.GetStackValue(ctx, partitionIDKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// SetSigningName set or modifies the signing name on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetSigningName(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, signingNameKey{}, value)
|
||||
}
|
||||
|
||||
// SetSigningRegion sets or modifies the region on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetSigningRegion(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, signingRegionKey{}, value)
|
||||
}
|
||||
|
||||
// SetServiceID sets the service id on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetServiceID(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, serviceIDKey{}, value)
|
||||
}
|
||||
|
||||
// setRegion sets the endpoint region on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func setRegion(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, regionKey{}, value)
|
||||
}
|
||||
|
||||
// setOperationName sets the service operation on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func setOperationName(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, operationNameKey{}, value)
|
||||
}
|
||||
|
||||
// SetPartitionID sets the partition id of a resolved region on the context
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetPartitionID(ctx context.Context, value string) context.Context {
|
||||
return middleware.WithStackValue(ctx, partitionIDKey{}, value)
|
||||
}
|
||||
|
||||
// EndpointSource key
|
||||
type endpointSourceKey struct{}
|
||||
|
||||
// GetEndpointSource returns an endpoint source if set on context
|
||||
func GetEndpointSource(ctx context.Context) (v aws.EndpointSource) {
|
||||
v, _ = middleware.GetStackValue(ctx, endpointSourceKey{}).(aws.EndpointSource)
|
||||
return v
|
||||
}
|
||||
|
||||
// SetEndpointSource sets endpoint source on context
|
||||
func SetEndpointSource(ctx context.Context, value aws.EndpointSource) context.Context {
|
||||
return middleware.WithStackValue(ctx, endpointSourceKey{}, value)
|
||||
}
|
||||
|
||||
type signingCredentialsKey struct{}
|
||||
|
||||
// GetSigningCredentials returns the credentials that were used for signing if set on context.
|
||||
func GetSigningCredentials(ctx context.Context) (v aws.Credentials) {
|
||||
v, _ = middleware.GetStackValue(ctx, signingCredentialsKey{}).(aws.Credentials)
|
||||
return v
|
||||
}
|
||||
|
||||
// SetSigningCredentials sets the credentails used for signing on the context.
|
||||
func SetSigningCredentials(ctx context.Context, value aws.Credentials) context.Context {
|
||||
return middleware.WithStackValue(ctx, signingCredentialsKey{}, value)
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/internal/rand"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyrand "github.com/aws/smithy-go/rand"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// ClientRequestID is a Smithy BuildMiddleware that will generate a unique ID for logical API operation
|
||||
// invocation.
|
||||
type ClientRequestID struct{}
|
||||
|
||||
// ID the identifier for the ClientRequestID
|
||||
func (r *ClientRequestID) ID() string {
|
||||
return "ClientRequestID"
|
||||
}
|
||||
|
||||
// HandleBuild attaches a unique operation invocation id for the operation to the request
|
||||
func (r ClientRequestID) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown transport type %T", req)
|
||||
}
|
||||
|
||||
invocationID, err := smithyrand.NewUUID(rand.Reader).GetUUID()
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
const invocationIDHeader = "Amz-Sdk-Invocation-Id"
|
||||
req.Header[invocationIDHeader] = append(req.Header[invocationIDHeader][:0], invocationID)
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// RecordResponseTiming records the response timing for the SDK client requests.
|
||||
type RecordResponseTiming struct{}
|
||||
|
||||
// ID is the middleware identifier
|
||||
func (a *RecordResponseTiming) ID() string {
|
||||
return "RecordResponseTiming"
|
||||
}
|
||||
|
||||
// HandleDeserialize calculates response metadata and clock skew
|
||||
func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
responseAt := sdk.NowTime()
|
||||
setResponseAt(&metadata, responseAt)
|
||||
|
||||
var serverTime time.Time
|
||||
|
||||
switch resp := out.RawResponse.(type) {
|
||||
case *smithyhttp.Response:
|
||||
respDateHeader := resp.Header.Get("Date")
|
||||
if len(respDateHeader) == 0 {
|
||||
break
|
||||
}
|
||||
var parseErr error
|
||||
serverTime, parseErr = smithyhttp.ParseTime(respDateHeader)
|
||||
if parseErr != nil {
|
||||
logger := middleware.GetLogger(ctx)
|
||||
logger.Logf(logging.Warn, "failed to parse response Date header value, got %v",
|
||||
parseErr.Error())
|
||||
break
|
||||
}
|
||||
setServerTime(&metadata, serverTime)
|
||||
}
|
||||
|
||||
if !serverTime.IsZero() {
|
||||
attemptSkew := serverTime.Sub(responseAt)
|
||||
setAttemptSkew(&metadata, attemptSkew)
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
type responseAtKey struct{}
|
||||
|
||||
// GetResponseAt returns the time response was received at.
|
||||
func GetResponseAt(metadata middleware.Metadata) (v time.Time, ok bool) {
|
||||
v, ok = metadata.Get(responseAtKey{}).(time.Time)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// setResponseAt sets the response time on the metadata.
|
||||
func setResponseAt(metadata *middleware.Metadata, v time.Time) {
|
||||
metadata.Set(responseAtKey{}, v)
|
||||
}
|
||||
|
||||
type serverTimeKey struct{}
|
||||
|
||||
// GetServerTime returns the server time for response.
|
||||
func GetServerTime(metadata middleware.Metadata) (v time.Time, ok bool) {
|
||||
v, ok = metadata.Get(serverTimeKey{}).(time.Time)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// setServerTime sets the server time on the metadata.
|
||||
func setServerTime(metadata *middleware.Metadata, v time.Time) {
|
||||
metadata.Set(serverTimeKey{}, v)
|
||||
}
|
||||
|
||||
type attemptSkewKey struct{}
|
||||
|
||||
// GetAttemptSkew returns Attempt clock skew for response from metadata.
|
||||
func GetAttemptSkew(metadata middleware.Metadata) (v time.Duration, ok bool) {
|
||||
v, ok = metadata.Get(attemptSkewKey{}).(time.Duration)
|
||||
return v, ok
|
||||
}
|
||||
|
||||
// setAttemptSkew sets the attempt clock skew on the metadata.
|
||||
func setAttemptSkew(metadata *middleware.Metadata, v time.Duration) {
|
||||
metadata.Set(attemptSkewKey{}, v)
|
||||
}
|
||||
|
||||
// AddClientRequestIDMiddleware adds ClientRequestID to the middleware stack
|
||||
func AddClientRequestIDMiddleware(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(&ClientRequestID{}, middleware.After)
|
||||
}
|
||||
|
||||
// AddRecordResponseTiming adds RecordResponseTiming middleware to the
|
||||
// middleware stack.
|
||||
func AddRecordResponseTiming(stack *middleware.Stack) error {
|
||||
return stack.Deserialize.Add(&RecordResponseTiming{}, middleware.After)
|
||||
}
|
||||
|
||||
// rawResponseKey is the accessor key used to store and access the
|
||||
// raw response within the response metadata.
|
||||
type rawResponseKey struct{}
|
||||
|
||||
// addRawResponse middleware adds raw response on to the metadata
|
||||
type addRawResponse struct{}
|
||||
|
||||
// ID the identifier for the ClientRequestID
|
||||
func (m *addRawResponse) ID() string {
|
||||
return "AddRawResponseToMetadata"
|
||||
}
|
||||
|
||||
// HandleDeserialize adds raw response on the middleware metadata
|
||||
func (m addRawResponse) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
metadata.Set(rawResponseKey{}, out.RawResponse)
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
// AddRawResponseToMetadata adds middleware to the middleware stack that
|
||||
// store raw response on to the metadata.
|
||||
func AddRawResponseToMetadata(stack *middleware.Stack) error {
|
||||
return stack.Deserialize.Add(&addRawResponse{}, middleware.Before)
|
||||
}
|
||||
|
||||
// GetRawResponse returns raw response set on metadata
|
||||
func GetRawResponse(metadata middleware.Metadata) interface{} {
|
||||
return metadata.Get(rawResponseKey{})
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
//go:build go1.16
|
||||
// +build go1.16
|
||||
|
||||
package middleware
|
||||
|
||||
import "runtime"
|
||||
|
||||
func getNormalizedOSName() (os string) {
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
os = "android"
|
||||
case "linux":
|
||||
os = "linux"
|
||||
case "windows":
|
||||
os = "windows"
|
||||
case "darwin":
|
||||
os = "macos"
|
||||
case "ios":
|
||||
os = "ios"
|
||||
default:
|
||||
os = "other"
|
||||
}
|
||||
return os
|
||||
}
|
24
vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/osname_go115.go
generated
vendored
Normal file
24
vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/osname_go115.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
//go:build !go1.16
|
||||
// +build !go1.16
|
||||
|
||||
package middleware
|
||||
|
||||
import "runtime"
|
||||
|
||||
func getNormalizedOSName() (os string) {
|
||||
switch runtime.GOOS {
|
||||
case "android":
|
||||
os = "android"
|
||||
case "linux":
|
||||
os = "linux"
|
||||
case "windows":
|
||||
os = "windows"
|
||||
case "darwin":
|
||||
// Due to Apple M1 we can't distinguish between macOS and iOS when GOOS/GOARCH is darwin/amd64
|
||||
// For now declare this as "other" until we have a better detection mechanism.
|
||||
fallthrough
|
||||
default:
|
||||
os = "other"
|
||||
}
|
||||
return os
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// requestIDKey is used to retrieve request id from response metadata
|
||||
type requestIDKey struct{}
|
||||
|
||||
// SetRequestIDMetadata sets the provided request id over middleware metadata
|
||||
func SetRequestIDMetadata(metadata *middleware.Metadata, id string) {
|
||||
metadata.Set(requestIDKey{}, id)
|
||||
}
|
||||
|
||||
// GetRequestIDMetadata retrieves the request id from middleware metadata
|
||||
// returns string and bool indicating value of request id, whether request id was set.
|
||||
func GetRequestIDMetadata(metadata middleware.Metadata) (string, bool) {
|
||||
if !metadata.Has(requestIDKey{}) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
v, ok := metadata.Get(requestIDKey{}).(string)
|
||||
if !ok {
|
||||
return "", true
|
||||
}
|
||||
return v, true
|
||||
}
|
49
vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/request_id_retriever.go
generated
vendored
Normal file
49
vendor/github.com/aws/aws-sdk-go-v2/aws/middleware/request_id_retriever.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// AddRequestIDRetrieverMiddleware adds request id retriever middleware
|
||||
func AddRequestIDRetrieverMiddleware(stack *middleware.Stack) error {
|
||||
// add error wrapper middleware before operation deserializers so that it can wrap the error response
|
||||
// returned by operation deserializers
|
||||
return stack.Deserialize.Insert(&requestIDRetriever{}, "OperationDeserializer", middleware.Before)
|
||||
}
|
||||
|
||||
type requestIDRetriever struct {
|
||||
}
|
||||
|
||||
// ID returns the middleware identifier
|
||||
func (m *requestIDRetriever) ID() string {
|
||||
return "RequestIDRetriever"
|
||||
}
|
||||
|
||||
func (m *requestIDRetriever) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
|
||||
resp, ok := out.RawResponse.(*smithyhttp.Response)
|
||||
if !ok {
|
||||
// No raw response to wrap with.
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
// Different header which can map to request id
|
||||
requestIDHeaderList := []string{"X-Amzn-Requestid", "X-Amz-RequestId"}
|
||||
|
||||
for _, h := range requestIDHeaderList {
|
||||
// check for headers known to contain Request id
|
||||
if v := resp.Header.Get(h); len(v) != 0 {
|
||||
// set reqID on metadata for successful responses.
|
||||
SetRequestIDMetadata(&metadata, v)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
var languageVersion = strings.TrimPrefix(runtime.Version(), "go")
|
||||
|
||||
// SDKAgentKeyType is the metadata type to add to the SDK agent string
|
||||
type SDKAgentKeyType int
|
||||
|
||||
// The set of valid SDKAgentKeyType constants. If an unknown value is assigned for SDKAgentKeyType it will
|
||||
// be mapped to AdditionalMetadata.
|
||||
const (
|
||||
_ SDKAgentKeyType = iota
|
||||
APIMetadata
|
||||
OperatingSystemMetadata
|
||||
LanguageMetadata
|
||||
EnvironmentMetadata
|
||||
FeatureMetadata
|
||||
ConfigMetadata
|
||||
FrameworkMetadata
|
||||
AdditionalMetadata
|
||||
ApplicationIdentifier
|
||||
)
|
||||
|
||||
func (k SDKAgentKeyType) string() string {
|
||||
switch k {
|
||||
case APIMetadata:
|
||||
return "api"
|
||||
case OperatingSystemMetadata:
|
||||
return "os"
|
||||
case LanguageMetadata:
|
||||
return "lang"
|
||||
case EnvironmentMetadata:
|
||||
return "exec-env"
|
||||
case FeatureMetadata:
|
||||
return "ft"
|
||||
case ConfigMetadata:
|
||||
return "cfg"
|
||||
case FrameworkMetadata:
|
||||
return "lib"
|
||||
case ApplicationIdentifier:
|
||||
return "app"
|
||||
case AdditionalMetadata:
|
||||
fallthrough
|
||||
default:
|
||||
return "md"
|
||||
}
|
||||
}
|
||||
|
||||
const execEnvVar = `AWS_EXECUTION_ENV`
|
||||
|
||||
// requestUserAgent is a build middleware that set the User-Agent for the request.
|
||||
type requestUserAgent struct {
|
||||
sdkAgent, userAgent *smithyhttp.UserAgentBuilder
|
||||
}
|
||||
|
||||
// newRequestUserAgent returns a new requestUserAgent which will set the User-Agent and X-Amz-User-Agent for the
|
||||
// request.
|
||||
//
|
||||
// User-Agent example:
|
||||
// aws-sdk-go-v2/1.2.3
|
||||
//
|
||||
// X-Amz-User-Agent example:
|
||||
// aws-sdk-go-v2/1.2.3 md/GOOS/linux md/GOARCH/amd64 lang/go/1.15
|
||||
func newRequestUserAgent() *requestUserAgent {
|
||||
userAgent, sdkAgent := smithyhttp.NewUserAgentBuilder(), smithyhttp.NewUserAgentBuilder()
|
||||
addProductName(userAgent)
|
||||
addProductName(sdkAgent)
|
||||
|
||||
r := &requestUserAgent{
|
||||
sdkAgent: sdkAgent,
|
||||
userAgent: userAgent,
|
||||
}
|
||||
|
||||
addSDKMetadata(r)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func addSDKMetadata(r *requestUserAgent) {
|
||||
r.AddSDKAgentKey(OperatingSystemMetadata, getNormalizedOSName())
|
||||
r.AddSDKAgentKeyValue(LanguageMetadata, "go", languageVersion)
|
||||
r.AddSDKAgentKeyValue(AdditionalMetadata, "GOOS", runtime.GOOS)
|
||||
r.AddSDKAgentKeyValue(AdditionalMetadata, "GOARCH", runtime.GOARCH)
|
||||
if ev := os.Getenv(execEnvVar); len(ev) > 0 {
|
||||
r.AddSDKAgentKey(EnvironmentMetadata, ev)
|
||||
}
|
||||
}
|
||||
|
||||
func addProductName(builder *smithyhttp.UserAgentBuilder) {
|
||||
builder.AddKeyValue(aws.SDKName, aws.SDKVersion)
|
||||
}
|
||||
|
||||
// AddUserAgentKey retrieves a requestUserAgent from the provided stack, or initializes one.
|
||||
func AddUserAgentKey(key string) func(*middleware.Stack) error {
|
||||
return func(stack *middleware.Stack) error {
|
||||
requestUserAgent, err := getOrAddRequestUserAgent(stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestUserAgent.AddUserAgentKey(key)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddUserAgentKeyValue retrieves a requestUserAgent from the provided stack, or initializes one.
|
||||
func AddUserAgentKeyValue(key, value string) func(*middleware.Stack) error {
|
||||
return func(stack *middleware.Stack) error {
|
||||
requestUserAgent, err := getOrAddRequestUserAgent(stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestUserAgent.AddUserAgentKeyValue(key, value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddSDKAgentKey retrieves a requestUserAgent from the provided stack, or initializes one.
|
||||
func AddSDKAgentKey(keyType SDKAgentKeyType, key string) func(*middleware.Stack) error {
|
||||
return func(stack *middleware.Stack) error {
|
||||
requestUserAgent, err := getOrAddRequestUserAgent(stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestUserAgent.AddSDKAgentKey(keyType, key)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddSDKAgentKeyValue retrieves a requestUserAgent from the provided stack, or initializes one.
|
||||
func AddSDKAgentKeyValue(keyType SDKAgentKeyType, key, value string) func(*middleware.Stack) error {
|
||||
return func(stack *middleware.Stack) error {
|
||||
requestUserAgent, err := getOrAddRequestUserAgent(stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestUserAgent.AddSDKAgentKeyValue(keyType, key, value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AddRequestUserAgentMiddleware registers a requestUserAgent middleware on the stack if not present.
|
||||
func AddRequestUserAgentMiddleware(stack *middleware.Stack) error {
|
||||
_, err := getOrAddRequestUserAgent(stack)
|
||||
return err
|
||||
}
|
||||
|
||||
func getOrAddRequestUserAgent(stack *middleware.Stack) (*requestUserAgent, error) {
|
||||
id := (*requestUserAgent)(nil).ID()
|
||||
bm, ok := stack.Build.Get(id)
|
||||
if !ok {
|
||||
bm = newRequestUserAgent()
|
||||
err := stack.Build.Add(bm, middleware.After)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
requestUserAgent, ok := bm.(*requestUserAgent)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%T for %s middleware did not match expected type", bm, id)
|
||||
}
|
||||
|
||||
return requestUserAgent, nil
|
||||
}
|
||||
|
||||
// AddUserAgentKey adds the component identified by name to the User-Agent string.
|
||||
func (u *requestUserAgent) AddUserAgentKey(key string) {
|
||||
u.userAgent.AddKey(key)
|
||||
}
|
||||
|
||||
// AddUserAgentKeyValue adds the key identified by the given name and value to the User-Agent string.
|
||||
func (u *requestUserAgent) AddUserAgentKeyValue(key, value string) {
|
||||
u.userAgent.AddKeyValue(key, value)
|
||||
}
|
||||
|
||||
// AddUserAgentKey adds the component identified by name to the User-Agent string.
|
||||
func (u *requestUserAgent) AddSDKAgentKey(keyType SDKAgentKeyType, key string) {
|
||||
// TODO: should target sdkAgent
|
||||
u.userAgent.AddKey(keyType.string() + "/" + key)
|
||||
}
|
||||
|
||||
// AddUserAgentKeyValue adds the key identified by the given name and value to the User-Agent string.
|
||||
func (u *requestUserAgent) AddSDKAgentKeyValue(keyType SDKAgentKeyType, key, value string) {
|
||||
// TODO: should target sdkAgent
|
||||
u.userAgent.AddKeyValue(keyType.string()+"/"+key, value)
|
||||
}
|
||||
|
||||
// ID the name of the middleware.
|
||||
func (u *requestUserAgent) ID() string {
|
||||
return "UserAgent"
|
||||
}
|
||||
|
||||
// HandleBuild adds or appends the constructed user agent to the request.
|
||||
func (u *requestUserAgent) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
switch req := in.Request.(type) {
|
||||
case *smithyhttp.Request:
|
||||
u.addHTTPUserAgent(req)
|
||||
// TODO: To be re-enabled
|
||||
// u.addHTTPSDKAgent(req)
|
||||
default:
|
||||
return out, metadata, fmt.Errorf("unknown transport type %T", in)
|
||||
}
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
func (u *requestUserAgent) addHTTPUserAgent(request *smithyhttp.Request) {
|
||||
const userAgent = "User-Agent"
|
||||
updateHTTPHeader(request, userAgent, u.userAgent.Build())
|
||||
}
|
||||
|
||||
func (u *requestUserAgent) addHTTPSDKAgent(request *smithyhttp.Request) {
|
||||
const sdkAgent = "X-Amz-User-Agent"
|
||||
updateHTTPHeader(request, sdkAgent, u.sdkAgent.Build())
|
||||
}
|
||||
|
||||
func updateHTTPHeader(request *smithyhttp.Request, header string, value string) {
|
||||
var current string
|
||||
if v := request.Header[header]; len(v) > 0 {
|
||||
current = v[0]
|
||||
}
|
||||
if len(current) > 0 {
|
||||
current = value + " " + current
|
||||
} else {
|
||||
current = value
|
||||
}
|
||||
request.Header[header] = append(request.Header[header][:0], current)
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Array represents the encoding of Query lists and sets. A Query array is a
|
||||
// representation of a list of values of a fixed type. A serialized array might
|
||||
// look like the following:
|
||||
//
|
||||
// ListName.member.1=foo
|
||||
// &ListName.member.2=bar
|
||||
// &Listname.member.3=baz
|
||||
type Array struct {
|
||||
// The query values to add the array to.
|
||||
values url.Values
|
||||
// The array's prefix, which includes the names of all parent structures
|
||||
// and ends with the name of the list. For example, the prefix might be
|
||||
// "ParentStructure.ListName". This prefix will be used to form the full
|
||||
// keys for each element in the list. For example, an entry might have the
|
||||
// key "ParentStructure.ListName.member.MemberName.1".
|
||||
//
|
||||
// While this is currently represented as a string that gets added to, it
|
||||
// could also be represented as a stack that only gets condensed into a
|
||||
// string when a finalized key is created. This could potentially reduce
|
||||
// allocations.
|
||||
prefix string
|
||||
// Whether the list is flat or not. A list that is not flat will produce the
|
||||
// following entry to the url.Values for a given entry:
|
||||
// ListName.MemberName.1=value
|
||||
// A list that is flat will produce the following:
|
||||
// ListName.1=value
|
||||
flat bool
|
||||
// The location name of the member. In most cases this should be "member".
|
||||
memberName string
|
||||
// Elements are stored in values, so we keep track of the list size here.
|
||||
size int32
|
||||
}
|
||||
|
||||
func newArray(values url.Values, prefix string, flat bool, memberName string) *Array {
|
||||
return &Array{
|
||||
values: values,
|
||||
prefix: prefix,
|
||||
flat: flat,
|
||||
memberName: memberName,
|
||||
}
|
||||
}
|
||||
|
||||
// Value adds a new element to the Query Array. Returns a Value type used to
|
||||
// encode the array element.
|
||||
func (a *Array) Value() Value {
|
||||
// Query lists start a 1, so adjust the size first
|
||||
a.size++
|
||||
prefix := a.prefix
|
||||
if !a.flat {
|
||||
prefix = fmt.Sprintf("%s.%s", prefix, a.memberName)
|
||||
}
|
||||
// Lists can't have flat members
|
||||
return newValue(a.values, fmt.Sprintf("%s.%d", prefix, a.size), false)
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Encoder is a Query encoder that supports construction of Query body
|
||||
// values using methods.
|
||||
type Encoder struct {
|
||||
// The query values that will be built up to manage encoding.
|
||||
values url.Values
|
||||
// The writer that the encoded body will be written to.
|
||||
writer io.Writer
|
||||
Value
|
||||
}
|
||||
|
||||
// NewEncoder returns a new Query body encoder
|
||||
func NewEncoder(writer io.Writer) *Encoder {
|
||||
values := url.Values{}
|
||||
return &Encoder{
|
||||
values: values,
|
||||
writer: writer,
|
||||
Value: newBaseValue(values),
|
||||
}
|
||||
}
|
||||
|
||||
// Encode returns the []byte slice representing the current
|
||||
// state of the Query encoder.
|
||||
func (e Encoder) Encode() error {
|
||||
ws, ok := e.writer.(interface{ WriteString(string) (int, error) })
|
||||
if !ok {
|
||||
// Fall back to less optimal byte slice casting if WriteString isn't available.
|
||||
ws = &wrapWriteString{writer: e.writer}
|
||||
}
|
||||
|
||||
// Get the keys and sort them to have a stable output
|
||||
keys := make([]string, 0, len(e.values))
|
||||
for k := range e.values {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
isFirstEntry := true
|
||||
for _, key := range keys {
|
||||
queryValues := e.values[key]
|
||||
escapedKey := url.QueryEscape(key)
|
||||
for _, value := range queryValues {
|
||||
if !isFirstEntry {
|
||||
if _, err := ws.WriteString(`&`); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
isFirstEntry = false
|
||||
}
|
||||
if _, err := ws.WriteString(escapedKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ws.WriteString(`=`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ws.WriteString(url.QueryEscape(value)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrapWriteString wraps an io.Writer to provide a WriteString method
|
||||
// where one is not available.
|
||||
type wrapWriteString struct {
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// WriteString writes a string to the wrapped writer by casting it to
|
||||
// a byte array first.
|
||||
func (w wrapWriteString) WriteString(v string) (int, error) {
|
||||
return w.writer.Write([]byte(v))
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Map represents the encoding of Query maps. A Query map is a representation
|
||||
// of a mapping of arbitrary string keys to arbitrary values of a fixed type.
|
||||
// A Map differs from an Object in that the set of keys is not fixed, in that
|
||||
// the values must all be of the same type, and that map entries are ordered.
|
||||
// A serialized map might look like the following:
|
||||
//
|
||||
// MapName.entry.1.key=Foo
|
||||
// &MapName.entry.1.value=spam
|
||||
// &MapName.entry.2.key=Bar
|
||||
// &MapName.entry.2.value=eggs
|
||||
type Map struct {
|
||||
// The query values to add the map to.
|
||||
values url.Values
|
||||
// The map's prefix, which includes the names of all parent structures
|
||||
// and ends with the name of the object. For example, the prefix might be
|
||||
// "ParentStructure.MapName". This prefix will be used to form the full
|
||||
// keys for each key-value pair of the map. For example, a value might have
|
||||
// the key "ParentStructure.MapName.1.value".
|
||||
//
|
||||
// While this is currently represented as a string that gets added to, it
|
||||
// could also be represented as a stack that only gets condensed into a
|
||||
// string when a finalized key is created. This could potentially reduce
|
||||
// allocations.
|
||||
prefix string
|
||||
// Whether the map is flat or not. A map that is not flat will produce the
|
||||
// following entries to the url.Values for a given key-value pair:
|
||||
// MapName.entry.1.KeyLocationName=mykey
|
||||
// MapName.entry.1.ValueLocationName=myvalue
|
||||
// A map that is flat will produce the following:
|
||||
// MapName.1.KeyLocationName=mykey
|
||||
// MapName.1.ValueLocationName=myvalue
|
||||
flat bool
|
||||
// The location name of the key. In most cases this should be "key".
|
||||
keyLocationName string
|
||||
// The location name of the value. In most cases this should be "value".
|
||||
valueLocationName string
|
||||
// Elements are stored in values, so we keep track of the list size here.
|
||||
size int32
|
||||
}
|
||||
|
||||
func newMap(values url.Values, prefix string, flat bool, keyLocationName string, valueLocationName string) *Map {
|
||||
return &Map{
|
||||
values: values,
|
||||
prefix: prefix,
|
||||
flat: flat,
|
||||
keyLocationName: keyLocationName,
|
||||
valueLocationName: valueLocationName,
|
||||
}
|
||||
}
|
||||
|
||||
// Key adds the given named key to the Query map.
|
||||
// Returns a Value encoder that should be used to encode a Query value type.
|
||||
func (m *Map) Key(name string) Value {
|
||||
// Query lists start a 1, so adjust the size first
|
||||
m.size++
|
||||
var key string
|
||||
var value string
|
||||
if m.flat {
|
||||
key = fmt.Sprintf("%s.%d.%s", m.prefix, m.size, m.keyLocationName)
|
||||
value = fmt.Sprintf("%s.%d.%s", m.prefix, m.size, m.valueLocationName)
|
||||
} else {
|
||||
key = fmt.Sprintf("%s.entry.%d.%s", m.prefix, m.size, m.keyLocationName)
|
||||
value = fmt.Sprintf("%s.entry.%d.%s", m.prefix, m.size, m.valueLocationName)
|
||||
}
|
||||
|
||||
// The key can only be a string, so we just go ahead and set it here
|
||||
newValue(m.values, key, false).String(name)
|
||||
|
||||
// Maps can't have flat members
|
||||
return newValue(m.values, value, false)
|
||||
}
|
62
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/middleware.go
generated
vendored
Normal file
62
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/query/middleware.go
generated
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// AddAsGetRequestMiddleware adds a middleware to the Serialize stack after the
|
||||
// operation serializer that will convert the query request body to a GET
|
||||
// operation with the query message in the HTTP request querystring.
|
||||
func AddAsGetRequestMiddleware(stack *middleware.Stack) error {
|
||||
return stack.Serialize.Insert(&asGetRequest{}, "OperationSerializer", middleware.After)
|
||||
}
|
||||
|
||||
type asGetRequest struct{}
|
||||
|
||||
func (*asGetRequest) ID() string { return "Query:AsGetRequest" }
|
||||
|
||||
func (m *asGetRequest) HandleSerialize(
|
||||
ctx context.Context, input middleware.SerializeInput, next middleware.SerializeHandler,
|
||||
) (
|
||||
out middleware.SerializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := input.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("expect smithy HTTP Request, got %T", input.Request)
|
||||
}
|
||||
|
||||
req.Method = "GET"
|
||||
|
||||
// If the stream is not set, nothing else to do.
|
||||
stream := req.GetStream()
|
||||
if stream == nil {
|
||||
return next.HandleSerialize(ctx, input)
|
||||
}
|
||||
|
||||
// Clear the stream since there will not be any body.
|
||||
req.Header.Del("Content-Type")
|
||||
req, err = req.SetStream(nil)
|
||||
if err != nil {
|
||||
return out, metadata, fmt.Errorf("unable update request body %w", err)
|
||||
}
|
||||
input.Request = req
|
||||
|
||||
// Update request query with the body's query string value.
|
||||
delim := ""
|
||||
if len(req.URL.RawQuery) != 0 {
|
||||
delim = "&"
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(stream)
|
||||
if err != nil {
|
||||
return out, metadata, fmt.Errorf("unable to get request body %w", err)
|
||||
}
|
||||
req.URL.RawQuery += delim + string(b)
|
||||
|
||||
return next.HandleSerialize(ctx, input)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Object represents the encoding of Query structures and unions. A Query
|
||||
// object is a representation of a mapping of string keys to arbitrary
|
||||
// values where there is a fixed set of keys whose values each have their
|
||||
// own known type. A serialized object might look like the following:
|
||||
//
|
||||
// ObjectName.Foo=value
|
||||
// &ObjectName.Bar=5
|
||||
type Object struct {
|
||||
// The query values to add the object to.
|
||||
values url.Values
|
||||
// The object's prefix, which includes the names of all parent structures
|
||||
// and ends with the name of the object. For example, the prefix might be
|
||||
// "ParentStructure.ObjectName". This prefix will be used to form the full
|
||||
// keys for each member of the object. For example, a member might have the
|
||||
// key "ParentStructure.ObjectName.MemberName".
|
||||
//
|
||||
// While this is currently represented as a string that gets added to, it
|
||||
// could also be represented as a stack that only gets condensed into a
|
||||
// string when a finalized key is created. This could potentially reduce
|
||||
// allocations.
|
||||
prefix string
|
||||
}
|
||||
|
||||
func newObject(values url.Values, prefix string) *Object {
|
||||
return &Object{
|
||||
values: values,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
// Key adds the given named key to the Query object.
|
||||
// Returns a Value encoder that should be used to encode a Query value type.
|
||||
func (o *Object) Key(name string) Value {
|
||||
return o.key(name, false)
|
||||
}
|
||||
|
||||
// FlatKey adds the given named key to the Query object.
|
||||
// Returns a Value encoder that should be used to encode a Query value type. The
|
||||
// value will be flattened if it is a map or array.
|
||||
func (o *Object) FlatKey(name string) Value {
|
||||
return o.key(name, true)
|
||||
}
|
||||
|
||||
func (o *Object) key(name string, flatValue bool) Value {
|
||||
if o.prefix != "" {
|
||||
return newValue(o.values, fmt.Sprintf("%s.%s", o.prefix, name), flatValue)
|
||||
}
|
||||
return newValue(o.values, name, flatValue)
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package query
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"net/url"
|
||||
|
||||
"github.com/aws/smithy-go/encoding/httpbinding"
|
||||
)
|
||||
|
||||
// Value represents a Query Value type.
|
||||
type Value struct {
|
||||
// The query values to add the value to.
|
||||
values url.Values
|
||||
// The value's key, which will form the prefix for complex types.
|
||||
key string
|
||||
// Whether the value should be flattened or not if it's a flattenable type.
|
||||
flat bool
|
||||
queryValue httpbinding.QueryValue
|
||||
}
|
||||
|
||||
func newValue(values url.Values, key string, flat bool) Value {
|
||||
return Value{
|
||||
values: values,
|
||||
key: key,
|
||||
flat: flat,
|
||||
queryValue: httpbinding.NewQueryValue(values, key, false),
|
||||
}
|
||||
}
|
||||
|
||||
func newBaseValue(values url.Values) Value {
|
||||
return Value{
|
||||
values: values,
|
||||
queryValue: httpbinding.NewQueryValue(nil, "", false),
|
||||
}
|
||||
}
|
||||
|
||||
// Array returns a new Array encoder.
|
||||
func (qv Value) Array(locationName string) *Array {
|
||||
return newArray(qv.values, qv.key, qv.flat, locationName)
|
||||
}
|
||||
|
||||
// Object returns a new Object encoder.
|
||||
func (qv Value) Object() *Object {
|
||||
return newObject(qv.values, qv.key)
|
||||
}
|
||||
|
||||
// Map returns a new Map encoder.
|
||||
func (qv Value) Map(keyLocationName string, valueLocationName string) *Map {
|
||||
return newMap(qv.values, qv.key, qv.flat, keyLocationName, valueLocationName)
|
||||
}
|
||||
|
||||
// Base64EncodeBytes encodes v as a base64 query string value.
|
||||
// This is intended to enable compatibility with the JSON encoder.
|
||||
func (qv Value) Base64EncodeBytes(v []byte) {
|
||||
qv.queryValue.Blob(v)
|
||||
}
|
||||
|
||||
// Boolean encodes v as a query string value
|
||||
func (qv Value) Boolean(v bool) {
|
||||
qv.queryValue.Boolean(v)
|
||||
}
|
||||
|
||||
// String encodes v as a query string value
|
||||
func (qv Value) String(v string) {
|
||||
qv.queryValue.String(v)
|
||||
}
|
||||
|
||||
// Byte encodes v as a query string value
|
||||
func (qv Value) Byte(v int8) {
|
||||
qv.queryValue.Byte(v)
|
||||
}
|
||||
|
||||
// Short encodes v as a query string value
|
||||
func (qv Value) Short(v int16) {
|
||||
qv.queryValue.Short(v)
|
||||
}
|
||||
|
||||
// Integer encodes v as a query string value
|
||||
func (qv Value) Integer(v int32) {
|
||||
qv.queryValue.Integer(v)
|
||||
}
|
||||
|
||||
// Long encodes v as a query string value
|
||||
func (qv Value) Long(v int64) {
|
||||
qv.queryValue.Long(v)
|
||||
}
|
||||
|
||||
// Float encodes v as a query string value
|
||||
func (qv Value) Float(v float32) {
|
||||
qv.queryValue.Float(v)
|
||||
}
|
||||
|
||||
// Double encodes v as a query string value
|
||||
func (qv Value) Double(v float64) {
|
||||
qv.queryValue.Double(v)
|
||||
}
|
||||
|
||||
// BigInteger encodes v as a query string value
|
||||
func (qv Value) BigInteger(v *big.Int) {
|
||||
qv.queryValue.BigInteger(v)
|
||||
}
|
||||
|
||||
// BigDecimal encodes v as a query string value
|
||||
func (qv Value) BigDecimal(v *big.Float) {
|
||||
qv.queryValue.BigDecimal(v)
|
||||
}
|
85
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/restjson/decoder_util.go
generated
vendored
Normal file
85
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/restjson/decoder_util.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package restjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/smithy-go"
|
||||
)
|
||||
|
||||
// GetErrorInfo util looks for code, __type, and message members in the
|
||||
// json body. These members are optionally available, and the function
|
||||
// returns the value of member if it is available. This function is useful to
|
||||
// identify the error code, msg in a REST JSON error response.
|
||||
func GetErrorInfo(decoder *json.Decoder) (errorType string, message string, err error) {
|
||||
var errInfo struct {
|
||||
Code string
|
||||
Type string `json:"__type"`
|
||||
Message string
|
||||
}
|
||||
|
||||
err = decoder.Decode(&errInfo)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return errorType, message, nil
|
||||
}
|
||||
return errorType, message, err
|
||||
}
|
||||
|
||||
// assign error type
|
||||
if len(errInfo.Code) != 0 {
|
||||
errorType = errInfo.Code
|
||||
} else if len(errInfo.Type) != 0 {
|
||||
errorType = errInfo.Type
|
||||
}
|
||||
|
||||
// assign error message
|
||||
if len(errInfo.Message) != 0 {
|
||||
message = errInfo.Message
|
||||
}
|
||||
|
||||
// sanitize error
|
||||
if len(errorType) != 0 {
|
||||
errorType = SanitizeErrorCode(errorType)
|
||||
}
|
||||
|
||||
return errorType, message, nil
|
||||
}
|
||||
|
||||
// SanitizeErrorCode sanitizes the errorCode string .
|
||||
// The rule for sanitizing is if a `:` character is present, then take only the
|
||||
// contents before the first : character in the value.
|
||||
// If a # character is present, then take only the contents after the
|
||||
// first # character in the value.
|
||||
func SanitizeErrorCode(errorCode string) string {
|
||||
if strings.ContainsAny(errorCode, ":") {
|
||||
errorCode = strings.SplitN(errorCode, ":", 2)[0]
|
||||
}
|
||||
|
||||
if strings.ContainsAny(errorCode, "#") {
|
||||
errorCode = strings.SplitN(errorCode, "#", 2)[1]
|
||||
}
|
||||
|
||||
return errorCode
|
||||
}
|
||||
|
||||
// GetSmithyGenericAPIError returns smithy generic api error and an error interface.
|
||||
// Takes in json decoder, and error Code string as args. The function retrieves error message
|
||||
// and error code from the decoder body. If errorCode of length greater than 0 is passed in as
|
||||
// an argument, it is used instead.
|
||||
func GetSmithyGenericAPIError(decoder *json.Decoder, errorCode string) (*smithy.GenericAPIError, error) {
|
||||
errorType, message, err := GetErrorInfo(decoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(errorCode) == 0 {
|
||||
errorCode = errorType
|
||||
}
|
||||
|
||||
return &smithy.GenericAPIError{
|
||||
Code: errorCode,
|
||||
Message: message,
|
||||
}, nil
|
||||
}
|
56
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/xml/error_utils.go
generated
vendored
Normal file
56
vendor/github.com/aws/aws-sdk-go-v2/aws/protocol/xml/error_utils.go
generated
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
package xml
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ErrorComponents represents the error response fields
|
||||
// that will be deserialized from an xml error response body
|
||||
type ErrorComponents struct {
|
||||
Code string
|
||||
Message string
|
||||
RequestID string
|
||||
}
|
||||
|
||||
// GetErrorResponseComponents returns the error fields from an xml error response body
|
||||
func GetErrorResponseComponents(r io.Reader, noErrorWrapping bool) (ErrorComponents, error) {
|
||||
if noErrorWrapping {
|
||||
var errResponse noWrappedErrorResponse
|
||||
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
|
||||
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
|
||||
}
|
||||
return ErrorComponents{
|
||||
Code: errResponse.Code,
|
||||
Message: errResponse.Message,
|
||||
RequestID: errResponse.RequestID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var errResponse wrappedErrorResponse
|
||||
if err := xml.NewDecoder(r).Decode(&errResponse); err != nil && err != io.EOF {
|
||||
return ErrorComponents{}, fmt.Errorf("error while deserializing xml error response: %w", err)
|
||||
}
|
||||
return ErrorComponents{
|
||||
Code: errResponse.Code,
|
||||
Message: errResponse.Message,
|
||||
RequestID: errResponse.RequestID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// noWrappedErrorResponse represents the error response body with
|
||||
// no internal <Error></Error wrapping
|
||||
type noWrappedErrorResponse struct {
|
||||
Code string `xml:"Code"`
|
||||
Message string `xml:"Message"`
|
||||
RequestID string `xml:"RequestId"`
|
||||
}
|
||||
|
||||
// wrappedErrorResponse represents the error response body
|
||||
// wrapped within <Error>...</Error>
|
||||
type wrappedErrorResponse struct {
|
||||
Code string `xml:"Error>Code"`
|
||||
Message string `xml:"Error>Message"`
|
||||
RequestID string `xml:"RequestId"`
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package ratelimit
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// TokenBucket provides a concurrency safe utility for adding and removing
|
||||
// tokens from the available token bucket.
|
||||
type TokenBucket struct {
|
||||
remainingTokens uint
|
||||
maxCapacity uint
|
||||
minCapacity uint
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// NewTokenBucket returns an initialized TokenBucket with the capacity
|
||||
// specified.
|
||||
func NewTokenBucket(i uint) *TokenBucket {
|
||||
return &TokenBucket{
|
||||
remainingTokens: i,
|
||||
maxCapacity: i,
|
||||
minCapacity: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve attempts to reduce the available tokens by the amount requested. If
|
||||
// there are tokens available true will be returned along with the number of
|
||||
// available tokens remaining. If amount requested is larger than the available
|
||||
// capacity, false will be returned along with the available capacity. If the
|
||||
// amount is less than the available capacity, the capacity will be reduced by
|
||||
// that amount, and the remaining capacity and true will be returned.
|
||||
func (t *TokenBucket) Retrieve(amount uint) (available uint, retrieved bool) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if amount > t.remainingTokens {
|
||||
return t.remainingTokens, false
|
||||
}
|
||||
|
||||
t.remainingTokens -= amount
|
||||
return t.remainingTokens, true
|
||||
}
|
||||
|
||||
// Refund returns the amount of tokens back to the available token bucket, up
|
||||
// to the initial capacity.
|
||||
func (t *TokenBucket) Refund(amount uint) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
// Capacity cannot exceed max capacity.
|
||||
t.remainingTokens = uintMin(t.remainingTokens+amount, t.maxCapacity)
|
||||
}
|
||||
|
||||
// Capacity returns the maximum capacity of tokens that the bucket could
|
||||
// contain.
|
||||
func (t *TokenBucket) Capacity() uint {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
return t.maxCapacity
|
||||
}
|
||||
|
||||
// Remaining returns the number of tokens that remaining in the bucket.
|
||||
func (t *TokenBucket) Remaining() uint {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
return t.remainingTokens
|
||||
}
|
||||
|
||||
// Resize adjusts the size of the token bucket. Returns the capacity remaining.
|
||||
func (t *TokenBucket) Resize(size uint) uint {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.maxCapacity = uintMax(size, t.minCapacity)
|
||||
|
||||
// Capacity needs to be capped at max capacity, if max size reduced.
|
||||
t.remainingTokens = uintMin(t.remainingTokens, t.maxCapacity)
|
||||
|
||||
return t.remainingTokens
|
||||
}
|
||||
|
||||
func uintMin(a, b uint) uint {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func uintMax(a, b uint) uint {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
87
vendor/github.com/aws/aws-sdk-go-v2/aws/ratelimit/token_rate_limit.go
generated
vendored
Normal file
87
vendor/github.com/aws/aws-sdk-go-v2/aws/ratelimit/token_rate_limit.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
package ratelimit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type rateToken struct {
|
||||
tokenCost uint
|
||||
bucket *TokenBucket
|
||||
}
|
||||
|
||||
func (t rateToken) release() error {
|
||||
t.bucket.Refund(t.tokenCost)
|
||||
return nil
|
||||
}
|
||||
|
||||
// TokenRateLimit provides a Token Bucket RateLimiter implementation
|
||||
// that limits the overall number of retry attempts that can be made across
|
||||
// operation invocations.
|
||||
type TokenRateLimit struct {
|
||||
bucket *TokenBucket
|
||||
}
|
||||
|
||||
// NewTokenRateLimit returns an TokenRateLimit with default values.
|
||||
// Functional options can configure the retry rate limiter.
|
||||
func NewTokenRateLimit(tokens uint) *TokenRateLimit {
|
||||
return &TokenRateLimit{
|
||||
bucket: NewTokenBucket(tokens),
|
||||
}
|
||||
}
|
||||
|
||||
func isTimeoutError(error) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type canceledError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (c canceledError) CanceledError() bool { return true }
|
||||
func (c canceledError) Unwrap() error { return c.Err }
|
||||
func (c canceledError) Error() string {
|
||||
return fmt.Sprintf("canceled, %v", c.Err)
|
||||
}
|
||||
|
||||
// GetToken may cause a available pool of retry quota to be
|
||||
// decremented. Will return an error if the decremented value can not be
|
||||
// reduced from the retry quota.
|
||||
func (l *TokenRateLimit) GetToken(ctx context.Context, cost uint) (func() error, error) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, canceledError{Err: ctx.Err()}
|
||||
default:
|
||||
}
|
||||
if avail, ok := l.bucket.Retrieve(cost); !ok {
|
||||
return nil, QuotaExceededError{Available: avail, Requested: cost}
|
||||
}
|
||||
|
||||
return rateToken{
|
||||
tokenCost: cost,
|
||||
bucket: l.bucket,
|
||||
}.release, nil
|
||||
}
|
||||
|
||||
// AddTokens increments the token bucket by a fixed amount.
|
||||
func (l *TokenRateLimit) AddTokens(v uint) error {
|
||||
l.bucket.Refund(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remaining returns the number of remaining tokens in the bucket.
|
||||
func (l *TokenRateLimit) Remaining() uint {
|
||||
return l.bucket.Remaining()
|
||||
}
|
||||
|
||||
// QuotaExceededError provides the SDK error when the retries for a given
|
||||
// token bucket have been exhausted.
|
||||
type QuotaExceededError struct {
|
||||
Available uint
|
||||
Requested uint
|
||||
}
|
||||
|
||||
func (e QuotaExceededError) Error() string {
|
||||
return fmt.Sprintf("retry quota exceeded, %d available, %d requested",
|
||||
e.Available, e.Requested)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// TODO remove replace with smithy.CanceledError
|
||||
|
||||
// RequestCanceledError is the error that will be returned by an API request
|
||||
// that was canceled. Requests given a Context may return this error when
|
||||
// canceled.
|
||||
type RequestCanceledError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// CanceledError returns true to satisfy interfaces checking for canceled errors.
|
||||
func (*RequestCanceledError) CanceledError() bool { return true }
|
||||
|
||||
// Unwrap returns the underlying error, if there was one.
|
||||
func (e *RequestCanceledError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
func (e *RequestCanceledError) Error() string {
|
||||
return fmt.Sprintf("request canceled, %v", e.Err)
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultRequestCost is the cost of a single request from the adaptive
|
||||
// rate limited token bucket.
|
||||
DefaultRequestCost uint = 1
|
||||
)
|
||||
|
||||
// DefaultThrottles provides the set of errors considered throttle errors that
|
||||
// are checked by default.
|
||||
var DefaultThrottles = []IsErrorThrottle{
|
||||
ThrottleErrorCode{
|
||||
Codes: DefaultThrottleErrorCodes,
|
||||
},
|
||||
}
|
||||
|
||||
// AdaptiveModeOptions provides the functional options for configuring the
|
||||
// adaptive retry mode, and delay behavior.
|
||||
type AdaptiveModeOptions struct {
|
||||
// If the adaptive token bucket is empty, when an attempt will be made
|
||||
// AdaptiveMode will sleep until a token is available. This can occur when
|
||||
// attempts fail with throttle errors. Use this option to disable the sleep
|
||||
// until token is available, and return error immediately.
|
||||
FailOnNoAttemptTokens bool
|
||||
|
||||
// The cost of an attempt from the AdaptiveMode's adaptive token bucket.
|
||||
RequestCost uint
|
||||
|
||||
// Set of strategies to determine if the attempt failed due to a throttle
|
||||
// error.
|
||||
//
|
||||
// It is safe to append to this list in NewAdaptiveMode's functional options.
|
||||
Throttles []IsErrorThrottle
|
||||
|
||||
// Set of options for standard retry mode that AdaptiveMode is built on top
|
||||
// of. AdaptiveMode may apply its own defaults to Standard retry mode that
|
||||
// are different than the defaults of NewStandard. Use these options to
|
||||
// override the default options.
|
||||
StandardOptions []func(*StandardOptions)
|
||||
}
|
||||
|
||||
// AdaptiveMode provides an experimental retry strategy that expands on the
|
||||
// Standard retry strategy, adding client attempt rate limits. The attempt rate
|
||||
// limit is initially unrestricted, but becomes restricted when the attempt
|
||||
// fails with for a throttle error. When restricted AdaptiveMode may need to
|
||||
// sleep before an attempt is made, if too many throttles have been received.
|
||||
// AdaptiveMode's sleep can be canceled with context cancel. Set
|
||||
// AdaptiveModeOptions FailOnNoAttemptTokens to change the behavior from sleep,
|
||||
// to fail fast.
|
||||
//
|
||||
// Eventually unrestricted attempt rate limit will be restored once attempts no
|
||||
// longer are failing due to throttle errors.
|
||||
type AdaptiveMode struct {
|
||||
options AdaptiveModeOptions
|
||||
throttles IsErrorThrottles
|
||||
|
||||
retryer aws.RetryerV2
|
||||
rateLimit *adaptiveRateLimit
|
||||
}
|
||||
|
||||
// NewAdaptiveMode returns an initialized AdaptiveMode retry strategy.
|
||||
func NewAdaptiveMode(optFns ...func(*AdaptiveModeOptions)) *AdaptiveMode {
|
||||
o := AdaptiveModeOptions{
|
||||
RequestCost: DefaultRequestCost,
|
||||
Throttles: append([]IsErrorThrottle{}, DefaultThrottles...),
|
||||
}
|
||||
for _, fn := range optFns {
|
||||
fn(&o)
|
||||
}
|
||||
|
||||
return &AdaptiveMode{
|
||||
options: o,
|
||||
throttles: IsErrorThrottles(o.Throttles),
|
||||
retryer: NewStandard(o.StandardOptions...),
|
||||
rateLimit: newAdaptiveRateLimit(),
|
||||
}
|
||||
}
|
||||
|
||||
// IsErrorRetryable returns if the failed attempt is retryable. This check
|
||||
// should determine if the error can be retried, or if the error is
|
||||
// terminal.
|
||||
func (a *AdaptiveMode) IsErrorRetryable(err error) bool {
|
||||
return a.retryer.IsErrorRetryable(err)
|
||||
}
|
||||
|
||||
// MaxAttempts returns the maximum number of attempts that can be made for
|
||||
// a attempt before failing. A value of 0 implies that the attempt should
|
||||
// be retried until it succeeds if the errors are retryable.
|
||||
func (a *AdaptiveMode) MaxAttempts() int {
|
||||
return a.retryer.MaxAttempts()
|
||||
}
|
||||
|
||||
// RetryDelay returns the delay that should be used before retrying the
|
||||
// attempt. Will return error if the if the delay could not be determined.
|
||||
func (a *AdaptiveMode) RetryDelay(attempt int, opErr error) (
|
||||
time.Duration, error,
|
||||
) {
|
||||
return a.retryer.RetryDelay(attempt, opErr)
|
||||
}
|
||||
|
||||
// GetRetryToken attempts to deduct the retry cost from the retry token pool.
|
||||
// Returning the token release function, or error.
|
||||
func (a *AdaptiveMode) GetRetryToken(ctx context.Context, opErr error) (
|
||||
releaseToken func(error) error, err error,
|
||||
) {
|
||||
return a.retryer.GetRetryToken(ctx, opErr)
|
||||
}
|
||||
|
||||
// GetInitialToken returns the initial attempt token that can increment the
|
||||
// retry token pool if the attempt is successful.
|
||||
//
|
||||
// Deprecated: This method does not provide a way to block using Context,
|
||||
// nor can it return an error. Use RetryerV2, and GetAttemptToken instead. Only
|
||||
// present to implement Retryer interface.
|
||||
func (a *AdaptiveMode) GetInitialToken() (releaseToken func(error) error) {
|
||||
return nopRelease
|
||||
}
|
||||
|
||||
// GetAttemptToken returns the attempt token that can be used to rate limit
|
||||
// attempt calls. Will be used by the SDK's retry package's Attempt
|
||||
// middleware to get a attempt token prior to calling the temp and releasing
|
||||
// the attempt token after the attempt has been made.
|
||||
func (a *AdaptiveMode) GetAttemptToken(ctx context.Context) (func(error) error, error) {
|
||||
for {
|
||||
acquiredToken, waitTryAgain := a.rateLimit.AcquireToken(a.options.RequestCost)
|
||||
if acquiredToken {
|
||||
break
|
||||
}
|
||||
if a.options.FailOnNoAttemptTokens {
|
||||
return nil, fmt.Errorf(
|
||||
"unable to get attempt token, and FailOnNoAttemptTokens enables")
|
||||
}
|
||||
|
||||
if err := sdk.SleepWithContext(ctx, waitTryAgain); err != nil {
|
||||
return nil, fmt.Errorf("failed to wait for token to be available, %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return a.handleResponse, nil
|
||||
}
|
||||
|
||||
func (a *AdaptiveMode) handleResponse(opErr error) error {
|
||||
throttled := a.throttles.IsErrorThrottle(opErr).Bool()
|
||||
|
||||
a.rateLimit.Update(throttled)
|
||||
return nil
|
||||
}
|
158
vendor/github.com/aws/aws-sdk-go-v2/aws/retry/adaptive_ratelimit.go
generated
vendored
Normal file
158
vendor/github.com/aws/aws-sdk-go-v2/aws/retry/adaptive_ratelimit.go
generated
vendored
Normal file
|
@ -0,0 +1,158 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
)
|
||||
|
||||
type adaptiveRateLimit struct {
|
||||
tokenBucketEnabled bool
|
||||
|
||||
smooth float64
|
||||
beta float64
|
||||
scaleConstant float64
|
||||
minFillRate float64
|
||||
|
||||
fillRate float64
|
||||
calculatedRate float64
|
||||
lastRefilled time.Time
|
||||
measuredTxRate float64
|
||||
lastTxRateBucket float64
|
||||
requestCount int64
|
||||
lastMaxRate float64
|
||||
lastThrottleTime time.Time
|
||||
timeWindow float64
|
||||
|
||||
tokenBucket *adaptiveTokenBucket
|
||||
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newAdaptiveRateLimit() *adaptiveRateLimit {
|
||||
now := sdk.NowTime()
|
||||
return &adaptiveRateLimit{
|
||||
smooth: 0.8,
|
||||
beta: 0.7,
|
||||
scaleConstant: 0.4,
|
||||
|
||||
minFillRate: 0.5,
|
||||
|
||||
lastTxRateBucket: math.Floor(timeFloat64Seconds(now)),
|
||||
lastThrottleTime: now,
|
||||
|
||||
tokenBucket: newAdaptiveTokenBucket(0),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) Enable(v bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
a.tokenBucketEnabled = v
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) AcquireToken(amount uint) (
|
||||
tokenAcquired bool, waitTryAgain time.Duration,
|
||||
) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
if !a.tokenBucketEnabled {
|
||||
return true, 0
|
||||
}
|
||||
|
||||
a.tokenBucketRefill()
|
||||
|
||||
available, ok := a.tokenBucket.Retrieve(float64(amount))
|
||||
if !ok {
|
||||
waitDur := float64Seconds((float64(amount) - available) / a.fillRate)
|
||||
return false, waitDur
|
||||
}
|
||||
|
||||
return true, 0
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) Update(throttled bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
a.updateMeasuredRate()
|
||||
|
||||
if throttled {
|
||||
rateToUse := a.measuredTxRate
|
||||
if a.tokenBucketEnabled {
|
||||
rateToUse = math.Min(a.measuredTxRate, a.fillRate)
|
||||
}
|
||||
|
||||
a.lastMaxRate = rateToUse
|
||||
a.calculateTimeWindow()
|
||||
a.lastThrottleTime = sdk.NowTime()
|
||||
a.calculatedRate = a.cubicThrottle(rateToUse)
|
||||
a.tokenBucketEnabled = true
|
||||
} else {
|
||||
a.calculateTimeWindow()
|
||||
a.calculatedRate = a.cubicSuccess(sdk.NowTime())
|
||||
}
|
||||
|
||||
newRate := math.Min(a.calculatedRate, 2*a.measuredTxRate)
|
||||
a.tokenBucketUpdateRate(newRate)
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) cubicSuccess(t time.Time) float64 {
|
||||
dt := secondsFloat64(t.Sub(a.lastThrottleTime))
|
||||
return (a.scaleConstant * math.Pow(dt-a.timeWindow, 3)) + a.lastMaxRate
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) cubicThrottle(rateToUse float64) float64 {
|
||||
return rateToUse * a.beta
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) calculateTimeWindow() {
|
||||
a.timeWindow = math.Pow((a.lastMaxRate*(1.-a.beta))/a.scaleConstant, 1./3.)
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) tokenBucketUpdateRate(newRPS float64) {
|
||||
a.tokenBucketRefill()
|
||||
a.fillRate = math.Max(newRPS, a.minFillRate)
|
||||
a.tokenBucket.Resize(newRPS)
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) updateMeasuredRate() {
|
||||
now := sdk.NowTime()
|
||||
timeBucket := math.Floor(timeFloat64Seconds(now)*2.) / 2.
|
||||
a.requestCount++
|
||||
|
||||
if timeBucket > a.lastTxRateBucket {
|
||||
currentRate := float64(a.requestCount) / (timeBucket - a.lastTxRateBucket)
|
||||
a.measuredTxRate = (currentRate * a.smooth) + (a.measuredTxRate * (1. - a.smooth))
|
||||
a.requestCount = 0
|
||||
a.lastTxRateBucket = timeBucket
|
||||
}
|
||||
}
|
||||
|
||||
func (a *adaptiveRateLimit) tokenBucketRefill() {
|
||||
now := sdk.NowTime()
|
||||
if a.lastRefilled.IsZero() {
|
||||
a.lastRefilled = now
|
||||
return
|
||||
}
|
||||
|
||||
fillAmount := secondsFloat64(now.Sub(a.lastRefilled)) * a.fillRate
|
||||
a.tokenBucket.Refund(fillAmount)
|
||||
a.lastRefilled = now
|
||||
}
|
||||
|
||||
func float64Seconds(v float64) time.Duration {
|
||||
return time.Duration(v * float64(time.Second))
|
||||
}
|
||||
|
||||
func secondsFloat64(v time.Duration) float64 {
|
||||
return float64(v) / float64(time.Second)
|
||||
}
|
||||
|
||||
func timeFloat64Seconds(v time.Time) float64 {
|
||||
return float64(v.UnixNano()) / float64(time.Second)
|
||||
}
|
83
vendor/github.com/aws/aws-sdk-go-v2/aws/retry/adaptive_token_bucket.go
generated
vendored
Normal file
83
vendor/github.com/aws/aws-sdk-go-v2/aws/retry/adaptive_token_bucket.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// adaptiveTokenBucket provides a concurrency safe utility for adding and
|
||||
// removing tokens from the available token bucket.
|
||||
type adaptiveTokenBucket struct {
|
||||
remainingTokens float64
|
||||
maxCapacity float64
|
||||
minCapacity float64
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// newAdaptiveTokenBucket returns an initialized adaptiveTokenBucket with the
|
||||
// capacity specified.
|
||||
func newAdaptiveTokenBucket(i float64) *adaptiveTokenBucket {
|
||||
return &adaptiveTokenBucket{
|
||||
remainingTokens: i,
|
||||
maxCapacity: i,
|
||||
minCapacity: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve attempts to reduce the available tokens by the amount requested. If
|
||||
// there are tokens available true will be returned along with the number of
|
||||
// available tokens remaining. If amount requested is larger than the available
|
||||
// capacity, false will be returned along with the available capacity. If the
|
||||
// amount is less than the available capacity, the capacity will be reduced by
|
||||
// that amount, and the remaining capacity and true will be returned.
|
||||
func (t *adaptiveTokenBucket) Retrieve(amount float64) (available float64, retrieved bool) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if amount > t.remainingTokens {
|
||||
return t.remainingTokens, false
|
||||
}
|
||||
|
||||
t.remainingTokens -= amount
|
||||
return t.remainingTokens, true
|
||||
}
|
||||
|
||||
// Refund returns the amount of tokens back to the available token bucket, up
|
||||
// to the initial capacity.
|
||||
func (t *adaptiveTokenBucket) Refund(amount float64) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
// Capacity cannot exceed max capacity.
|
||||
t.remainingTokens = math.Min(t.remainingTokens+amount, t.maxCapacity)
|
||||
}
|
||||
|
||||
// Capacity returns the maximum capacity of tokens that the bucket could
|
||||
// contain.
|
||||
func (t *adaptiveTokenBucket) Capacity() float64 {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
return t.maxCapacity
|
||||
}
|
||||
|
||||
// Remaining returns the number of tokens that remaining in the bucket.
|
||||
func (t *adaptiveTokenBucket) Remaining() float64 {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
return t.remainingTokens
|
||||
}
|
||||
|
||||
// Resize adjusts the size of the token bucket. Returns the capacity remaining.
|
||||
func (t *adaptiveTokenBucket) Resize(size float64) float64 {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
t.maxCapacity = math.Max(size, t.minCapacity)
|
||||
|
||||
// Capacity needs to be capped at max capacity, if max size reduced.
|
||||
t.remainingTokens = math.Min(t.remainingTokens, t.maxCapacity)
|
||||
|
||||
return t.remainingTokens
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// Package retry provides interfaces and implementations for SDK request retry behavior.
|
||||
//
|
||||
// Retryer Interface and Implementations
|
||||
//
|
||||
// This packages defines Retryer interface that is used to either implement custom retry behavior
|
||||
// or to extend the existing retry implementations provided by the SDK. This packages provides a single
|
||||
// retry implementations: Standard.
|
||||
//
|
||||
// Standard
|
||||
//
|
||||
// Standard is the default retryer implementation used by service clients. The standard retryer is a rate limited
|
||||
// retryer that has a configurable max attempts to limit the number of retry attempts when a retryable error occurs.
|
||||
// In addition, the retryer uses a configurable token bucket to rate limit the retry attempts across the client,
|
||||
// and uses an additional delay policy to limit the time between a requests subsequent attempts.
|
||||
//
|
||||
// By default the standard retryer uses the DefaultRetryables slice of IsErrorRetryable types to determine whether
|
||||
// a given error is retryable. By default this list of retryables includes the following:
|
||||
// - Retrying errors that implement the RetryableError method, and return true.
|
||||
// - Connection Errors
|
||||
// - Errors that implement a ConnectionError, Temporary, or Timeout method that return true.
|
||||
// - Connection Reset Errors.
|
||||
// - net.OpErr types that are dialing errors or are temporary.
|
||||
// - HTTP Status Codes: 500, 502, 503, and 504.
|
||||
// - API Error Codes
|
||||
// - RequestTimeout, RequestTimeoutException
|
||||
// - Throttling, ThrottlingException, ThrottledException, RequestThrottledException, TooManyRequestsException,
|
||||
// RequestThrottled, SlowDown, EC2ThrottledException
|
||||
// - ProvisionedThroughputExceededException, RequestLimitExceeded, BandwidthLimitExceeded, LimitExceededException
|
||||
// - TransactionInProgressException, PriorRequestNotComplete
|
||||
//
|
||||
// The standard retryer will not retry a request in the event if the context associated with the request
|
||||
// has been cancelled. Applications must handle this case explicitly if they wish to retry with a different context
|
||||
// value.
|
||||
//
|
||||
// You can configure the standard retryer implementation to fit your applications by constructing a standard retryer
|
||||
// using the NewStandard function, and providing one more functional arguments that mutate the StandardOptions
|
||||
// structure. StandardOptions provides the ability to modify the token bucket rate limiter, retryable error conditions,
|
||||
// and the retry delay policy.
|
||||
//
|
||||
// For example to modify the default retry attempts for the standard retryer:
|
||||
//
|
||||
// // configure the custom retryer
|
||||
// customRetry := retry.NewStandard(func(o *retry.StandardOptions) {
|
||||
// o.MaxAttempts = 5
|
||||
// })
|
||||
//
|
||||
// // create a service client with the retryer
|
||||
// s3.NewFromConfig(cfg, func(o *s3.Options) {
|
||||
// o.Retryer = customRetry
|
||||
// })
|
||||
//
|
||||
// Utilities
|
||||
//
|
||||
// A number of package functions have been provided to easily wrap retryer implementations in an implementation agnostic
|
||||
// way. These are:
|
||||
//
|
||||
// AddWithErrorCodes - Provides the ability to add additional API error codes that should be considered retryable
|
||||
// in addition to those considered retryable by the provided retryer.
|
||||
//
|
||||
// AddWithMaxAttempts - Provides the ability to set the max number of attempts for retrying a request by wrapping
|
||||
// a retryer implementation.
|
||||
//
|
||||
// AddWithMaxBackoffDelay - Provides the ability to set the max back off delay that can occur before retrying a
|
||||
// request by wrapping a retryer implementation.
|
||||
//
|
||||
// The following package functions have been provided to easily satisfy different retry interfaces to further customize
|
||||
// a given retryer's behavior:
|
||||
//
|
||||
// BackoffDelayerFunc - Can be used to wrap a function to satisfy the BackoffDelayer interface. For example,
|
||||
// you can use this method to easily create custom back off policies to be used with the
|
||||
// standard retryer.
|
||||
//
|
||||
// IsErrorRetryableFunc - Can be used to wrap a function to satisfy the IsErrorRetryable interface. For example,
|
||||
// this can be used to extend the standard retryer to add additional logic ot determine if a
|
||||
// error should be retried.
|
||||
//
|
||||
// IsErrorTimeoutFunc - Can be used to wrap a function to satisfy IsErrorTimeout interface. For example,
|
||||
// this can be used to extend the standard retryer to add additional logic to determine if an
|
||||
// error should be considered a timeout.
|
||||
package retry
|
|
@ -0,0 +1,20 @@
|
|||
package retry
|
||||
|
||||
import "fmt"
|
||||
|
||||
// MaxAttemptsError provides the error when the maximum number of attempts have
|
||||
// been exceeded.
|
||||
type MaxAttemptsError struct {
|
||||
Attempt int
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MaxAttemptsError) Error() string {
|
||||
return fmt.Sprintf("exceeded maximum number of attempts, %d, %v", e.Attempt, e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the nested error causing the max attempts error. Provides the
|
||||
// implementation for errors.Is and errors.As to unwrap nested errors.
|
||||
func (e *MaxAttemptsError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/internal/rand"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/timeconv"
|
||||
)
|
||||
|
||||
// ExponentialJitterBackoff provides backoff delays with jitter based on the
|
||||
// number of attempts.
|
||||
type ExponentialJitterBackoff struct {
|
||||
maxBackoff time.Duration
|
||||
// precomputed number of attempts needed to reach max backoff.
|
||||
maxBackoffAttempts float64
|
||||
|
||||
randFloat64 func() (float64, error)
|
||||
}
|
||||
|
||||
// NewExponentialJitterBackoff returns an ExponentialJitterBackoff configured
|
||||
// for the max backoff.
|
||||
func NewExponentialJitterBackoff(maxBackoff time.Duration) *ExponentialJitterBackoff {
|
||||
return &ExponentialJitterBackoff{
|
||||
maxBackoff: maxBackoff,
|
||||
maxBackoffAttempts: math.Log2(
|
||||
float64(maxBackoff) / float64(time.Second)),
|
||||
randFloat64: rand.CryptoRandFloat64,
|
||||
}
|
||||
}
|
||||
|
||||
// BackoffDelay returns the duration to wait before the next attempt should be
|
||||
// made. Returns an error if unable get a duration.
|
||||
func (j *ExponentialJitterBackoff) BackoffDelay(attempt int, err error) (time.Duration, error) {
|
||||
if attempt > int(j.maxBackoffAttempts) {
|
||||
return j.maxBackoff, nil
|
||||
}
|
||||
|
||||
b, err := j.randFloat64()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// [0.0, 1.0) * 2 ^ attempts
|
||||
ri := int64(1 << uint64(attempt))
|
||||
delaySeconds := b * float64(ri)
|
||||
|
||||
return timeconv.FloatSecondsDur(delaySeconds), nil
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
awsmiddle "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// attemptResultsKey is a metadata accessor key to retrieve metadata
|
||||
// for all request attempts.
|
||||
type attemptResultsKey struct {
|
||||
}
|
||||
|
||||
// GetAttemptResults retrieves attempts results from middleware metadata.
|
||||
func GetAttemptResults(metadata middleware.Metadata) (AttemptResults, bool) {
|
||||
m, ok := metadata.Get(attemptResultsKey{}).(AttemptResults)
|
||||
return m, ok
|
||||
}
|
||||
|
||||
// AttemptResults represents struct containing metadata returned by all request attempts.
|
||||
type AttemptResults struct {
|
||||
|
||||
// Results is a slice consisting attempt result from all request attempts.
|
||||
// Results are stored in order request attempt is made.
|
||||
Results []AttemptResult
|
||||
}
|
||||
|
||||
// AttemptResult represents attempt result returned by a single request attempt.
|
||||
type AttemptResult struct {
|
||||
|
||||
// Err is the error if received for the request attempt.
|
||||
Err error
|
||||
|
||||
// Retryable denotes if request may be retried. This states if an
|
||||
// error is considered retryable.
|
||||
Retryable bool
|
||||
|
||||
// Retried indicates if this request was retried.
|
||||
Retried bool
|
||||
|
||||
// ResponseMetadata is any existing metadata passed via the response middlewares.
|
||||
ResponseMetadata middleware.Metadata
|
||||
}
|
||||
|
||||
// addAttemptResults adds attempt results to middleware metadata
|
||||
func addAttemptResults(metadata *middleware.Metadata, v AttemptResults) {
|
||||
metadata.Set(attemptResultsKey{}, v)
|
||||
}
|
||||
|
||||
// GetRawResponse returns raw response recorded for the attempt result
|
||||
func (a AttemptResult) GetRawResponse() interface{} {
|
||||
return awsmiddle.GetRawResponse(a.ResponseMetadata)
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsmiddle "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithymiddle "github.com/aws/smithy-go/middleware"
|
||||
"github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// RequestCloner is a function that can take an input request type and clone
|
||||
// the request for use in a subsequent retry attempt.
|
||||
type RequestCloner func(interface{}) interface{}
|
||||
|
||||
type retryMetadata struct {
|
||||
AttemptNum int
|
||||
AttemptTime time.Time
|
||||
MaxAttempts int
|
||||
AttemptClockSkew time.Duration
|
||||
}
|
||||
|
||||
// Attempt is a Smithy Finalize middleware that handles retry attempts using
|
||||
// the provided Retryer implementation.
|
||||
type Attempt struct {
|
||||
// Enable the logging of retry attempts performed by the SDK. This will
|
||||
// include logging retry attempts, unretryable errors, and when max
|
||||
// attempts are reached.
|
||||
LogAttempts bool
|
||||
|
||||
retryer aws.RetryerV2
|
||||
requestCloner RequestCloner
|
||||
}
|
||||
|
||||
// NewAttemptMiddleware returns a new Attempt retry middleware.
|
||||
func NewAttemptMiddleware(retryer aws.Retryer, requestCloner RequestCloner, optFns ...func(*Attempt)) *Attempt {
|
||||
m := &Attempt{
|
||||
retryer: wrapAsRetryerV2(retryer),
|
||||
requestCloner: requestCloner,
|
||||
}
|
||||
for _, fn := range optFns {
|
||||
fn(m)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// ID returns the middleware identifier
|
||||
func (r *Attempt) ID() string { return "Retry" }
|
||||
|
||||
func (r Attempt) logf(logger logging.Logger, classification logging.Classification, format string, v ...interface{}) {
|
||||
if !r.LogAttempts {
|
||||
return
|
||||
}
|
||||
logger.Logf(classification, format, v...)
|
||||
}
|
||||
|
||||
// HandleFinalize utilizes the provider Retryer implementation to attempt
|
||||
// retries over the next handler
|
||||
func (r *Attempt) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeInput, next smithymiddle.FinalizeHandler) (
|
||||
out smithymiddle.FinalizeOutput, metadata smithymiddle.Metadata, err error,
|
||||
) {
|
||||
var attemptNum int
|
||||
var attemptClockSkew time.Duration
|
||||
var attemptResults AttemptResults
|
||||
|
||||
maxAttempts := r.retryer.MaxAttempts()
|
||||
releaseRetryToken := nopRelease
|
||||
|
||||
for {
|
||||
attemptNum++
|
||||
attemptInput := in
|
||||
attemptInput.Request = r.requestCloner(attemptInput.Request)
|
||||
|
||||
// Record the metadata for the for attempt being started.
|
||||
attemptCtx := setRetryMetadata(ctx, retryMetadata{
|
||||
AttemptNum: attemptNum,
|
||||
AttemptTime: sdk.NowTime().UTC(),
|
||||
MaxAttempts: maxAttempts,
|
||||
AttemptClockSkew: attemptClockSkew,
|
||||
})
|
||||
|
||||
var attemptResult AttemptResult
|
||||
out, attemptResult, releaseRetryToken, err = r.handleAttempt(attemptCtx, attemptInput, releaseRetryToken, next)
|
||||
attemptClockSkew, _ = awsmiddle.GetAttemptSkew(attemptResult.ResponseMetadata)
|
||||
|
||||
// AttempResult Retried states that the attempt was not successful, and
|
||||
// should be retried.
|
||||
shouldRetry := attemptResult.Retried
|
||||
|
||||
// Add attempt metadata to list of all attempt metadata
|
||||
attemptResults.Results = append(attemptResults.Results, attemptResult)
|
||||
|
||||
if !shouldRetry {
|
||||
// Ensure the last response's metadata is used as the bases for result
|
||||
// metadata returned by the stack. The Slice of attempt results
|
||||
// will be added to this cloned metadata.
|
||||
metadata = attemptResult.ResponseMetadata.Clone()
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
addAttemptResults(&metadata, attemptResults)
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
// handleAttempt handles an individual request attempt.
|
||||
func (r *Attempt) handleAttempt(
|
||||
ctx context.Context, in smithymiddle.FinalizeInput, releaseRetryToken func(error) error, next smithymiddle.FinalizeHandler,
|
||||
) (
|
||||
out smithymiddle.FinalizeOutput, attemptResult AttemptResult, _ func(error) error, err error,
|
||||
) {
|
||||
defer func() {
|
||||
attemptResult.Err = err
|
||||
}()
|
||||
|
||||
// Short circuit if this attempt never can succeed because the context is
|
||||
// canceled. This reduces the chance of token pools being modified for
|
||||
// attempts that will not be made
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return out, attemptResult, nopRelease, ctx.Err()
|
||||
default:
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// Get Attempt Token
|
||||
//------------------------------
|
||||
releaseAttemptToken, err := r.retryer.GetAttemptToken(ctx)
|
||||
if err != nil {
|
||||
return out, attemptResult, nopRelease, fmt.Errorf(
|
||||
"failed to get retry Send token, %w", err)
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// Send Attempt
|
||||
//------------------------------
|
||||
logger := smithymiddle.GetLogger(ctx)
|
||||
service, operation := awsmiddle.GetServiceID(ctx), awsmiddle.GetOperationName(ctx)
|
||||
retryMetadata, _ := getRetryMetadata(ctx)
|
||||
attemptNum := retryMetadata.AttemptNum
|
||||
maxAttempts := retryMetadata.MaxAttempts
|
||||
|
||||
// Following attempts must ensure the request payload stream starts in a
|
||||
// rewound state.
|
||||
if attemptNum > 1 {
|
||||
if rewindable, ok := in.Request.(interface{ RewindStream() error }); ok {
|
||||
if rewindErr := rewindable.RewindStream(); rewindErr != nil {
|
||||
return out, attemptResult, nopRelease, fmt.Errorf(
|
||||
"failed to rewind transport stream for retry, %w", rewindErr)
|
||||
}
|
||||
}
|
||||
|
||||
r.logf(logger, logging.Debug, "retrying request %s/%s, attempt %d",
|
||||
service, operation, attemptNum)
|
||||
}
|
||||
|
||||
var metadata smithymiddle.Metadata
|
||||
out, metadata, err = next.HandleFinalize(ctx, in)
|
||||
attemptResult.ResponseMetadata = metadata
|
||||
|
||||
//------------------------------
|
||||
// Bookkeeping
|
||||
//------------------------------
|
||||
// Release the retry token based on the state of the attempt's error (if any).
|
||||
if releaseError := releaseRetryToken(err); releaseError != nil && err != nil {
|
||||
return out, attemptResult, nopRelease, fmt.Errorf(
|
||||
"failed to release retry token after request error, %w", err)
|
||||
}
|
||||
// Release the attempt token based on the state of the attempt's error (if any).
|
||||
if releaseError := releaseAttemptToken(err); releaseError != nil && err != nil {
|
||||
return out, attemptResult, nopRelease, fmt.Errorf(
|
||||
"failed to release initial token after request error, %w", err)
|
||||
}
|
||||
// If there was no error making the attempt, nothing further to do. There
|
||||
// will be nothing to retry.
|
||||
if err == nil {
|
||||
return out, attemptResult, nopRelease, err
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// Is Retryable and Should Retry
|
||||
//------------------------------
|
||||
// If the attempt failed with an unretryable error, nothing further to do
|
||||
// but return, and inform the caller about the terminal failure.
|
||||
retryable := r.retryer.IsErrorRetryable(err)
|
||||
if !retryable {
|
||||
r.logf(logger, logging.Debug, "request failed with unretryable error %v", err)
|
||||
return out, attemptResult, nopRelease, err
|
||||
}
|
||||
|
||||
// set retryable to true
|
||||
attemptResult.Retryable = true
|
||||
|
||||
// Once the maximum number of attempts have been exhausted there is nothing
|
||||
// further to do other than inform the caller about the terminal failure.
|
||||
if maxAttempts > 0 && attemptNum >= maxAttempts {
|
||||
r.logf(logger, logging.Debug, "max retry attempts exhausted, max %d", maxAttempts)
|
||||
err = &MaxAttemptsError{
|
||||
Attempt: attemptNum,
|
||||
Err: err,
|
||||
}
|
||||
return out, attemptResult, nopRelease, err
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// Get Retry (aka Retry Quota) Token
|
||||
//------------------------------
|
||||
// Get a retry token that will be released after the
|
||||
releaseRetryToken, retryTokenErr := r.retryer.GetRetryToken(ctx, err)
|
||||
if retryTokenErr != nil {
|
||||
return out, attemptResult, nopRelease, retryTokenErr
|
||||
}
|
||||
|
||||
//------------------------------
|
||||
// Retry Delay and Sleep
|
||||
//------------------------------
|
||||
// Get the retry delay before another attempt can be made, and sleep for
|
||||
// that time. Potentially early exist if the sleep is canceled via the
|
||||
// context.
|
||||
retryDelay, reqErr := r.retryer.RetryDelay(attemptNum, err)
|
||||
if reqErr != nil {
|
||||
return out, attemptResult, releaseRetryToken, reqErr
|
||||
}
|
||||
if reqErr = sdk.SleepWithContext(ctx, retryDelay); reqErr != nil {
|
||||
err = &aws.RequestCanceledError{Err: reqErr}
|
||||
return out, attemptResult, releaseRetryToken, err
|
||||
}
|
||||
|
||||
// The request should be re-attempted.
|
||||
attemptResult.Retried = true
|
||||
|
||||
return out, attemptResult, releaseRetryToken, err
|
||||
}
|
||||
|
||||
// MetricsHeader attaches SDK request metric header for retries to the transport
|
||||
type MetricsHeader struct{}
|
||||
|
||||
// ID returns the middleware identifier
|
||||
func (r *MetricsHeader) ID() string {
|
||||
return "RetryMetricsHeader"
|
||||
}
|
||||
|
||||
// HandleFinalize attaches the SDK request metric header to the transport layer
|
||||
func (r MetricsHeader) HandleFinalize(ctx context.Context, in smithymiddle.FinalizeInput, next smithymiddle.FinalizeHandler) (
|
||||
out smithymiddle.FinalizeOutput, metadata smithymiddle.Metadata, err error,
|
||||
) {
|
||||
retryMetadata, _ := getRetryMetadata(ctx)
|
||||
|
||||
const retryMetricHeader = "Amz-Sdk-Request"
|
||||
var parts []string
|
||||
|
||||
parts = append(parts, "attempt="+strconv.Itoa(retryMetadata.AttemptNum))
|
||||
if retryMetadata.MaxAttempts != 0 {
|
||||
parts = append(parts, "max="+strconv.Itoa(retryMetadata.MaxAttempts))
|
||||
}
|
||||
|
||||
var ttl time.Time
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
ttl = deadline
|
||||
}
|
||||
|
||||
// Only append the TTL if it can be determined.
|
||||
if !ttl.IsZero() && retryMetadata.AttemptClockSkew > 0 {
|
||||
const unixTimeFormat = "20060102T150405Z"
|
||||
ttl = ttl.Add(retryMetadata.AttemptClockSkew)
|
||||
parts = append(parts, "ttl="+ttl.Format(unixTimeFormat))
|
||||
}
|
||||
|
||||
switch req := in.Request.(type) {
|
||||
case *http.Request:
|
||||
req.Header[retryMetricHeader] = append(req.Header[retryMetricHeader][:0], strings.Join(parts, "; "))
|
||||
default:
|
||||
return out, metadata, fmt.Errorf("unknown transport type %T", req)
|
||||
}
|
||||
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
type retryMetadataKey struct{}
|
||||
|
||||
// getRetryMetadata retrieves retryMetadata from the context and a bool
|
||||
// indicating if it was set.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func getRetryMetadata(ctx context.Context) (metadata retryMetadata, ok bool) {
|
||||
metadata, ok = middleware.GetStackValue(ctx, retryMetadataKey{}).(retryMetadata)
|
||||
return metadata, ok
|
||||
}
|
||||
|
||||
// setRetryMetadata sets the retryMetadata on the context.
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func setRetryMetadata(ctx context.Context, metadata retryMetadata) context.Context {
|
||||
return middleware.WithStackValue(ctx, retryMetadataKey{}, metadata)
|
||||
}
|
||||
|
||||
// AddRetryMiddlewaresOptions is the set of options that can be passed to
|
||||
// AddRetryMiddlewares for configuring retry associated middleware.
|
||||
type AddRetryMiddlewaresOptions struct {
|
||||
Retryer aws.Retryer
|
||||
|
||||
// Enable the logging of retry attempts performed by the SDK. This will
|
||||
// include logging retry attempts, unretryable errors, and when max
|
||||
// attempts are reached.
|
||||
LogRetryAttempts bool
|
||||
}
|
||||
|
||||
// AddRetryMiddlewares adds retry middleware to operation middleware stack
|
||||
func AddRetryMiddlewares(stack *smithymiddle.Stack, options AddRetryMiddlewaresOptions) error {
|
||||
attempt := NewAttemptMiddleware(options.Retryer, http.RequestCloner, func(middleware *Attempt) {
|
||||
middleware.LogAttempts = options.LogRetryAttempts
|
||||
})
|
||||
|
||||
if err := stack.Finalize.Add(attempt, smithymiddle.After); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := stack.Finalize.Add(&MetricsHeader{}, smithymiddle.After); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// AddWithErrorCodes returns a Retryer with additional error codes considered
|
||||
// for determining if the error should be retried.
|
||||
func AddWithErrorCodes(r aws.Retryer, codes ...string) aws.Retryer {
|
||||
retryable := &RetryableErrorCode{
|
||||
Codes: map[string]struct{}{},
|
||||
}
|
||||
for _, c := range codes {
|
||||
retryable.Codes[c] = struct{}{}
|
||||
}
|
||||
|
||||
return &withIsErrorRetryable{
|
||||
RetryerV2: wrapAsRetryerV2(r),
|
||||
Retryable: retryable,
|
||||
}
|
||||
}
|
||||
|
||||
type withIsErrorRetryable struct {
|
||||
aws.RetryerV2
|
||||
Retryable IsErrorRetryable
|
||||
}
|
||||
|
||||
func (r *withIsErrorRetryable) IsErrorRetryable(err error) bool {
|
||||
if v := r.Retryable.IsErrorRetryable(err); v != aws.UnknownTernary {
|
||||
return v.Bool()
|
||||
}
|
||||
return r.RetryerV2.IsErrorRetryable(err)
|
||||
}
|
||||
|
||||
// AddWithMaxAttempts returns a Retryer with MaxAttempts set to the value
|
||||
// specified.
|
||||
func AddWithMaxAttempts(r aws.Retryer, max int) aws.Retryer {
|
||||
return &withMaxAttempts{
|
||||
RetryerV2: wrapAsRetryerV2(r),
|
||||
Max: max,
|
||||
}
|
||||
}
|
||||
|
||||
type withMaxAttempts struct {
|
||||
aws.RetryerV2
|
||||
Max int
|
||||
}
|
||||
|
||||
func (w *withMaxAttempts) MaxAttempts() int {
|
||||
return w.Max
|
||||
}
|
||||
|
||||
// AddWithMaxBackoffDelay returns a retryer wrapping the passed in retryer
|
||||
// overriding the RetryDelay behavior for a alternate minimum initial backoff
|
||||
// delay.
|
||||
func AddWithMaxBackoffDelay(r aws.Retryer, delay time.Duration) aws.Retryer {
|
||||
return &withMaxBackoffDelay{
|
||||
RetryerV2: wrapAsRetryerV2(r),
|
||||
backoff: NewExponentialJitterBackoff(delay),
|
||||
}
|
||||
}
|
||||
|
||||
type withMaxBackoffDelay struct {
|
||||
aws.RetryerV2
|
||||
backoff *ExponentialJitterBackoff
|
||||
}
|
||||
|
||||
func (r *withMaxBackoffDelay) RetryDelay(attempt int, err error) (time.Duration, error) {
|
||||
return r.backoff.BackoffDelay(attempt, err)
|
||||
}
|
||||
|
||||
type wrappedAsRetryerV2 struct {
|
||||
aws.Retryer
|
||||
}
|
||||
|
||||
func wrapAsRetryerV2(r aws.Retryer) aws.RetryerV2 {
|
||||
v, ok := r.(aws.RetryerV2)
|
||||
if !ok {
|
||||
v = wrappedAsRetryerV2{Retryer: r}
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (w wrappedAsRetryerV2) GetAttemptToken(context.Context) (func(error) error, error) {
|
||||
return w.Retryer.GetInitialToken(), nil
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// IsErrorRetryable provides the interface of an implementation to determine if
|
||||
// a error as the result of an operation is retryable.
|
||||
type IsErrorRetryable interface {
|
||||
IsErrorRetryable(error) aws.Ternary
|
||||
}
|
||||
|
||||
// IsErrorRetryables is a collection of checks to determine of the error is
|
||||
// retryable. Iterates through the checks and returns the state of retryable
|
||||
// if any check returns something other than unknown.
|
||||
type IsErrorRetryables []IsErrorRetryable
|
||||
|
||||
// IsErrorRetryable returns if the error is retryable if any of the checks in
|
||||
// the list return a value other than unknown.
|
||||
func (r IsErrorRetryables) IsErrorRetryable(err error) aws.Ternary {
|
||||
for _, re := range r {
|
||||
if v := re.IsErrorRetryable(err); v != aws.UnknownTernary {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
// IsErrorRetryableFunc wraps a function with the IsErrorRetryable interface.
|
||||
type IsErrorRetryableFunc func(error) aws.Ternary
|
||||
|
||||
// IsErrorRetryable returns if the error is retryable.
|
||||
func (fn IsErrorRetryableFunc) IsErrorRetryable(err error) aws.Ternary {
|
||||
return fn(err)
|
||||
}
|
||||
|
||||
// RetryableError is an IsErrorRetryable implementation which uses the
|
||||
// optional interface Retryable on the error value to determine if the error is
|
||||
// retryable.
|
||||
type RetryableError struct{}
|
||||
|
||||
// IsErrorRetryable returns if the error is retryable if it satisfies the
|
||||
// Retryable interface, and returns if the attempt should be retried.
|
||||
func (RetryableError) IsErrorRetryable(err error) aws.Ternary {
|
||||
var v interface{ RetryableError() bool }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.BoolTernary(v.RetryableError())
|
||||
}
|
||||
|
||||
// NoRetryCanceledError detects if the error was an request canceled error and
|
||||
// returns if so.
|
||||
type NoRetryCanceledError struct{}
|
||||
|
||||
// IsErrorRetryable returns the error is not retryable if the request was
|
||||
// canceled.
|
||||
func (NoRetryCanceledError) IsErrorRetryable(err error) aws.Ternary {
|
||||
var v interface{ CanceledError() bool }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
if v.CanceledError() {
|
||||
return aws.FalseTernary
|
||||
}
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
// RetryableConnectionError determines if the underlying error is an HTTP
|
||||
// connection and returns if it should be retried.
|
||||
//
|
||||
// Includes errors such as connection reset, connection refused, net dial,
|
||||
// temporary, and timeout errors.
|
||||
type RetryableConnectionError struct{}
|
||||
|
||||
// IsErrorRetryable returns if the error is caused by and HTTP connection
|
||||
// error, and should be retried.
|
||||
func (r RetryableConnectionError) IsErrorRetryable(err error) aws.Ternary {
|
||||
if err == nil {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
var retryable bool
|
||||
|
||||
var conErr interface{ ConnectionError() bool }
|
||||
var tempErr interface{ Temporary() bool }
|
||||
var timeoutErr interface{ Timeout() bool }
|
||||
var urlErr *url.Error
|
||||
var netOpErr *net.OpError
|
||||
|
||||
switch {
|
||||
case errors.As(err, &conErr) && conErr.ConnectionError():
|
||||
retryable = true
|
||||
|
||||
case strings.Contains(err.Error(), "connection reset"):
|
||||
retryable = true
|
||||
|
||||
case errors.As(err, &urlErr):
|
||||
// Refused connections should be retried as the service may not yet be
|
||||
// running on the port. Go TCP dial considers refused connections as
|
||||
// not temporary.
|
||||
if strings.Contains(urlErr.Error(), "connection refused") {
|
||||
retryable = true
|
||||
} else {
|
||||
return r.IsErrorRetryable(errors.Unwrap(urlErr))
|
||||
}
|
||||
|
||||
case errors.As(err, &netOpErr):
|
||||
// Network dial, or temporary network errors are always retryable.
|
||||
if strings.EqualFold(netOpErr.Op, "dial") || netOpErr.Temporary() {
|
||||
retryable = true
|
||||
} else {
|
||||
return r.IsErrorRetryable(errors.Unwrap(netOpErr))
|
||||
}
|
||||
|
||||
case errors.As(err, &tempErr) && tempErr.Temporary():
|
||||
// Fallback to the generic temporary check, with temporary errors
|
||||
// retryable.
|
||||
retryable = true
|
||||
|
||||
case errors.As(err, &timeoutErr) && timeoutErr.Timeout():
|
||||
// Fallback to the generic timeout check, with timeout errors
|
||||
// retryable.
|
||||
retryable = true
|
||||
|
||||
default:
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.BoolTernary(retryable)
|
||||
|
||||
}
|
||||
|
||||
// RetryableHTTPStatusCode provides a IsErrorRetryable based on HTTP status
|
||||
// codes.
|
||||
type RetryableHTTPStatusCode struct {
|
||||
Codes map[int]struct{}
|
||||
}
|
||||
|
||||
// IsErrorRetryable return if the passed in error is retryable based on the
|
||||
// HTTP status code.
|
||||
func (r RetryableHTTPStatusCode) IsErrorRetryable(err error) aws.Ternary {
|
||||
var v interface{ HTTPStatusCode() int }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
_, ok := r.Codes[v.HTTPStatusCode()]
|
||||
if !ok {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.TrueTernary
|
||||
}
|
||||
|
||||
// RetryableErrorCode determines if an attempt should be retried based on the
|
||||
// API error code.
|
||||
type RetryableErrorCode struct {
|
||||
Codes map[string]struct{}
|
||||
}
|
||||
|
||||
// IsErrorRetryable return if the error is retryable based on the error codes.
|
||||
// Returns unknown if the error doesn't have a code or it is unknown.
|
||||
func (r RetryableErrorCode) IsErrorRetryable(err error) aws.Ternary {
|
||||
var v interface{ ErrorCode() string }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
_, ok := r.Codes[v.ErrorCode()]
|
||||
if !ok {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.TrueTernary
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws/ratelimit"
|
||||
)
|
||||
|
||||
// BackoffDelayer provides the interface for determining the delay to before
|
||||
// another request attempt, that previously failed.
|
||||
type BackoffDelayer interface {
|
||||
BackoffDelay(attempt int, err error) (time.Duration, error)
|
||||
}
|
||||
|
||||
// BackoffDelayerFunc provides a wrapper around a function to determine the
|
||||
// backoff delay of an attempt retry.
|
||||
type BackoffDelayerFunc func(int, error) (time.Duration, error)
|
||||
|
||||
// BackoffDelay returns the delay before attempt to retry a request.
|
||||
func (fn BackoffDelayerFunc) BackoffDelay(attempt int, err error) (time.Duration, error) {
|
||||
return fn(attempt, err)
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultMaxAttempts is the maximum of attempts for an API request
|
||||
DefaultMaxAttempts int = 3
|
||||
|
||||
// DefaultMaxBackoff is the maximum back off delay between attempts
|
||||
DefaultMaxBackoff time.Duration = 20 * time.Second
|
||||
)
|
||||
|
||||
// Default retry token quota values.
|
||||
const (
|
||||
DefaultRetryRateTokens uint = 500
|
||||
DefaultRetryCost uint = 5
|
||||
DefaultRetryTimeoutCost uint = 10
|
||||
DefaultNoRetryIncrement uint = 1
|
||||
)
|
||||
|
||||
// DefaultRetryableHTTPStatusCodes is the default set of HTTP status codes the SDK
|
||||
// should consider as retryable errors.
|
||||
var DefaultRetryableHTTPStatusCodes = map[int]struct{}{
|
||||
500: {},
|
||||
502: {},
|
||||
503: {},
|
||||
504: {},
|
||||
}
|
||||
|
||||
// DefaultRetryableErrorCodes provides the set of API error codes that should
|
||||
// be retried.
|
||||
var DefaultRetryableErrorCodes = map[string]struct{}{
|
||||
"RequestTimeout": {},
|
||||
"RequestTimeoutException": {},
|
||||
}
|
||||
|
||||
// DefaultThrottleErrorCodes provides the set of API error codes that are
|
||||
// considered throttle errors.
|
||||
var DefaultThrottleErrorCodes = map[string]struct{}{
|
||||
"Throttling": {},
|
||||
"ThrottlingException": {},
|
||||
"ThrottledException": {},
|
||||
"RequestThrottledException": {},
|
||||
"TooManyRequestsException": {},
|
||||
"ProvisionedThroughputExceededException": {},
|
||||
"TransactionInProgressException": {},
|
||||
"RequestLimitExceeded": {},
|
||||
"BandwidthLimitExceeded": {},
|
||||
"LimitExceededException": {},
|
||||
"RequestThrottled": {},
|
||||
"SlowDown": {},
|
||||
"PriorRequestNotComplete": {},
|
||||
"EC2ThrottledException": {},
|
||||
}
|
||||
|
||||
// DefaultRetryables provides the set of retryable checks that are used by
|
||||
// default.
|
||||
var DefaultRetryables = []IsErrorRetryable{
|
||||
NoRetryCanceledError{},
|
||||
RetryableError{},
|
||||
RetryableConnectionError{},
|
||||
RetryableHTTPStatusCode{
|
||||
Codes: DefaultRetryableHTTPStatusCodes,
|
||||
},
|
||||
RetryableErrorCode{
|
||||
Codes: DefaultRetryableErrorCodes,
|
||||
},
|
||||
RetryableErrorCode{
|
||||
Codes: DefaultThrottleErrorCodes,
|
||||
},
|
||||
}
|
||||
|
||||
// DefaultTimeouts provides the set of timeout checks that are used by default.
|
||||
var DefaultTimeouts = []IsErrorTimeout{
|
||||
TimeouterError{},
|
||||
}
|
||||
|
||||
// StandardOptions provides the functional options for configuring the standard
|
||||
// retryable, and delay behavior.
|
||||
type StandardOptions struct {
|
||||
// Maximum number of attempts that should be made.
|
||||
MaxAttempts int
|
||||
|
||||
// MaxBackoff duration between retried attempts.
|
||||
MaxBackoff time.Duration
|
||||
|
||||
// Provides the backoff strategy the retryer will use to determine the
|
||||
// delay between retry attempts.
|
||||
Backoff BackoffDelayer
|
||||
|
||||
// Set of strategies to determine if the attempt should be retried based on
|
||||
// the error response received.
|
||||
//
|
||||
// It is safe to append to this list in NewStandard's functional options.
|
||||
Retryables []IsErrorRetryable
|
||||
|
||||
// Set of strategies to determine if the attempt failed due to a timeout
|
||||
// error.
|
||||
//
|
||||
// It is safe to append to this list in NewStandard's functional options.
|
||||
Timeouts []IsErrorTimeout
|
||||
|
||||
// Provides the rate limiting strategy for rate limiting attempt retries
|
||||
// across all attempts the retryer is being used with.
|
||||
RateLimiter RateLimiter
|
||||
|
||||
// The cost to deduct from the RateLimiter's token bucket per retry.
|
||||
RetryCost uint
|
||||
|
||||
// The cost to deduct from the RateLimiter's token bucket per retry caused
|
||||
// by timeout error.
|
||||
RetryTimeoutCost uint
|
||||
|
||||
// The cost to payback to the RateLimiter's token bucket for successful
|
||||
// attempts.
|
||||
NoRetryIncrement uint
|
||||
}
|
||||
|
||||
// RateLimiter provides the interface for limiting the rate of attempt retries
|
||||
// allowed by the retryer.
|
||||
type RateLimiter interface {
|
||||
GetToken(ctx context.Context, cost uint) (releaseToken func() error, err error)
|
||||
AddTokens(uint) error
|
||||
}
|
||||
|
||||
// Standard is the standard retry pattern for the SDK. It uses a set of
|
||||
// retryable checks to determine of the failed attempt should be retried, and
|
||||
// what retry delay should be used.
|
||||
type Standard struct {
|
||||
options StandardOptions
|
||||
|
||||
timeout IsErrorTimeout
|
||||
retryable IsErrorRetryable
|
||||
backoff BackoffDelayer
|
||||
}
|
||||
|
||||
// NewStandard initializes a standard retry behavior with defaults that can be
|
||||
// overridden via functional options.
|
||||
func NewStandard(fnOpts ...func(*StandardOptions)) *Standard {
|
||||
o := StandardOptions{
|
||||
MaxAttempts: DefaultMaxAttempts,
|
||||
MaxBackoff: DefaultMaxBackoff,
|
||||
Retryables: append([]IsErrorRetryable{}, DefaultRetryables...),
|
||||
Timeouts: append([]IsErrorTimeout{}, DefaultTimeouts...),
|
||||
|
||||
RateLimiter: ratelimit.NewTokenRateLimit(DefaultRetryRateTokens),
|
||||
RetryCost: DefaultRetryCost,
|
||||
RetryTimeoutCost: DefaultRetryTimeoutCost,
|
||||
NoRetryIncrement: DefaultNoRetryIncrement,
|
||||
}
|
||||
for _, fn := range fnOpts {
|
||||
fn(&o)
|
||||
}
|
||||
if o.MaxAttempts <= 0 {
|
||||
o.MaxAttempts = DefaultMaxAttempts
|
||||
}
|
||||
|
||||
backoff := o.Backoff
|
||||
if backoff == nil {
|
||||
backoff = NewExponentialJitterBackoff(o.MaxBackoff)
|
||||
}
|
||||
|
||||
return &Standard{
|
||||
options: o,
|
||||
backoff: backoff,
|
||||
retryable: IsErrorRetryables(o.Retryables),
|
||||
timeout: IsErrorTimeouts(o.Timeouts),
|
||||
}
|
||||
}
|
||||
|
||||
// MaxAttempts returns the maximum number of attempts that can be made for a
|
||||
// request before failing.
|
||||
func (s *Standard) MaxAttempts() int {
|
||||
return s.options.MaxAttempts
|
||||
}
|
||||
|
||||
// IsErrorRetryable returns if the error is can be retried or not. Should not
|
||||
// consider the number of attempts made.
|
||||
func (s *Standard) IsErrorRetryable(err error) bool {
|
||||
return s.retryable.IsErrorRetryable(err).Bool()
|
||||
}
|
||||
|
||||
// RetryDelay returns the delay to use before another request attempt is made.
|
||||
func (s *Standard) RetryDelay(attempt int, err error) (time.Duration, error) {
|
||||
return s.backoff.BackoffDelay(attempt, err)
|
||||
}
|
||||
|
||||
// GetAttemptToken returns the token to be released after then attempt completes.
|
||||
// The release token will add NoRetryIncrement to the RateLimiter token pool if
|
||||
// the attempt was successful. If the attempt failed, nothing will be done.
|
||||
func (s *Standard) GetAttemptToken(context.Context) (func(error) error, error) {
|
||||
return s.GetInitialToken(), nil
|
||||
}
|
||||
|
||||
// GetInitialToken returns a token for adding the NoRetryIncrement to the
|
||||
// RateLimiter token if the attempt completed successfully without error.
|
||||
//
|
||||
// InitialToken applies to result of the each attempt, including the first.
|
||||
// Whereas the RetryToken applies to the result of subsequent attempts.
|
||||
//
|
||||
// Deprecated: use GetAttemptToken instead.
|
||||
func (s *Standard) GetInitialToken() func(error) error {
|
||||
return releaseToken(s.noRetryIncrement).release
|
||||
}
|
||||
|
||||
func (s *Standard) noRetryIncrement() error {
|
||||
return s.options.RateLimiter.AddTokens(s.options.NoRetryIncrement)
|
||||
}
|
||||
|
||||
// GetRetryToken attempts to deduct the retry cost from the retry token pool.
|
||||
// Returning the token release function, or error.
|
||||
func (s *Standard) GetRetryToken(ctx context.Context, opErr error) (func(error) error, error) {
|
||||
cost := s.options.RetryCost
|
||||
|
||||
if s.timeout.IsErrorTimeout(opErr).Bool() {
|
||||
cost = s.options.RetryTimeoutCost
|
||||
}
|
||||
|
||||
fn, err := s.options.RateLimiter.GetToken(ctx, cost)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get rate limit token, %w", err)
|
||||
}
|
||||
|
||||
return releaseToken(fn).release, nil
|
||||
}
|
||||
|
||||
func nopRelease(error) error { return nil }
|
||||
|
||||
type releaseToken func() error
|
||||
|
||||
func (f releaseToken) release(err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return f()
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// IsErrorThrottle provides the interface of an implementation to determine if
|
||||
// a error response from an operation is a throttling error.
|
||||
type IsErrorThrottle interface {
|
||||
IsErrorThrottle(error) aws.Ternary
|
||||
}
|
||||
|
||||
// IsErrorThrottles is a collection of checks to determine of the error a
|
||||
// throttle error. Iterates through the checks and returns the state of
|
||||
// throttle if any check returns something other than unknown.
|
||||
type IsErrorThrottles []IsErrorThrottle
|
||||
|
||||
// IsErrorThrottle returns if the error is a throttle error if any of the
|
||||
// checks in the list return a value other than unknown.
|
||||
func (r IsErrorThrottles) IsErrorThrottle(err error) aws.Ternary {
|
||||
for _, re := range r {
|
||||
if v := re.IsErrorThrottle(err); v != aws.UnknownTernary {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
// IsErrorThrottleFunc wraps a function with the IsErrorThrottle interface.
|
||||
type IsErrorThrottleFunc func(error) aws.Ternary
|
||||
|
||||
// IsErrorThrottle returns if the error is a throttle error.
|
||||
func (fn IsErrorThrottleFunc) IsErrorThrottle(err error) aws.Ternary {
|
||||
return fn(err)
|
||||
}
|
||||
|
||||
// ThrottleErrorCode determines if an attempt should be retried based on the
|
||||
// API error code.
|
||||
type ThrottleErrorCode struct {
|
||||
Codes map[string]struct{}
|
||||
}
|
||||
|
||||
// IsErrorThrottle return if the error is a throttle error based on the error
|
||||
// codes. Returns unknown if the error doesn't have a code or it is unknown.
|
||||
func (r ThrottleErrorCode) IsErrorThrottle(err error) aws.Ternary {
|
||||
var v interface{ ErrorCode() string }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
_, ok := r.Codes[v.ErrorCode()]
|
||||
if !ok {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.TrueTernary
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// IsErrorTimeout provides the interface of an implementation to determine if
|
||||
// a error matches.
|
||||
type IsErrorTimeout interface {
|
||||
IsErrorTimeout(err error) aws.Ternary
|
||||
}
|
||||
|
||||
// IsErrorTimeouts is a collection of checks to determine of the error is
|
||||
// retryable. Iterates through the checks and returns the state of retryable
|
||||
// if any check returns something other than unknown.
|
||||
type IsErrorTimeouts []IsErrorTimeout
|
||||
|
||||
// IsErrorTimeout returns if the error is retryable if any of the checks in
|
||||
// the list return a value other than unknown.
|
||||
func (ts IsErrorTimeouts) IsErrorTimeout(err error) aws.Ternary {
|
||||
for _, t := range ts {
|
||||
if v := t.IsErrorTimeout(err); v != aws.UnknownTernary {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
// IsErrorTimeoutFunc wraps a function with the IsErrorTimeout interface.
|
||||
type IsErrorTimeoutFunc func(error) aws.Ternary
|
||||
|
||||
// IsErrorTimeout returns if the error is retryable.
|
||||
func (fn IsErrorTimeoutFunc) IsErrorTimeout(err error) aws.Ternary {
|
||||
return fn(err)
|
||||
}
|
||||
|
||||
// TimeouterError provides the IsErrorTimeout implementation for determining if
|
||||
// an error is a timeout based on type with the Timeout method.
|
||||
type TimeouterError struct{}
|
||||
|
||||
// IsErrorTimeout returns if the error is a timeout error.
|
||||
func (t TimeouterError) IsErrorTimeout(err error) aws.Ternary {
|
||||
var v interface{ Timeout() bool }
|
||||
|
||||
if !errors.As(err, &v) {
|
||||
return aws.UnknownTernary
|
||||
}
|
||||
|
||||
return aws.BoolTernary(v.Timeout())
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RetryMode provides the mode the API client will use to create a retryer
|
||||
// based on.
|
||||
type RetryMode string
|
||||
|
||||
const (
|
||||
// RetryModeStandard model provides rate limited retry attempts with
|
||||
// exponential backoff delay.
|
||||
RetryModeStandard RetryMode = "standard"
|
||||
|
||||
// RetryModeAdaptive model provides attempt send rate limiting on throttle
|
||||
// responses in addition to standard mode's retry rate limiting.
|
||||
//
|
||||
// Adaptive retry mode is experimental and is subject to change in the
|
||||
// future.
|
||||
RetryModeAdaptive RetryMode = "adaptive"
|
||||
)
|
||||
|
||||
// ParseRetryMode attempts to parse a RetryMode from the given string.
|
||||
// Returning error if the value is not a known RetryMode.
|
||||
func ParseRetryMode(v string) (mode RetryMode, err error) {
|
||||
switch v {
|
||||
case "standard":
|
||||
return RetryModeStandard, nil
|
||||
case "adaptive":
|
||||
return RetryModeAdaptive, nil
|
||||
default:
|
||||
return mode, fmt.Errorf("unknown RetryMode, %v", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (m RetryMode) String() string { return string(m) }
|
||||
|
||||
// Retryer is an interface to determine if a given error from a
|
||||
// attempt should be retried, and if so what backoff delay to apply. The
|
||||
// default implementation used by most services is the retry package's Standard
|
||||
// type. Which contains basic retry logic using exponential backoff.
|
||||
type Retryer interface {
|
||||
// IsErrorRetryable returns if the failed attempt is retryable. This check
|
||||
// should determine if the error can be retried, or if the error is
|
||||
// terminal.
|
||||
IsErrorRetryable(error) bool
|
||||
|
||||
// MaxAttempts returns the maximum number of attempts that can be made for
|
||||
// a attempt before failing. A value of 0 implies that the attempt should
|
||||
// be retried until it succeeds if the errors are retryable.
|
||||
MaxAttempts() int
|
||||
|
||||
// RetryDelay returns the delay that should be used before retrying the
|
||||
// attempt. Will return error if the if the delay could not be determined.
|
||||
RetryDelay(attempt int, opErr error) (time.Duration, error)
|
||||
|
||||
// GetRetryToken attempts to deduct the retry cost from the retry token pool.
|
||||
// Returning the token release function, or error.
|
||||
GetRetryToken(ctx context.Context, opErr error) (releaseToken func(error) error, err error)
|
||||
|
||||
// GetInitialToken returns the initial attempt token that can increment the
|
||||
// retry token pool if the attempt is successful.
|
||||
GetInitialToken() (releaseToken func(error) error)
|
||||
}
|
||||
|
||||
// RetryerV2 is an interface to determine if a given error from a attempt
|
||||
// should be retried, and if so what backoff delay to apply. The default
|
||||
// implementation used by most services is the retry package's Standard type.
|
||||
// Which contains basic retry logic using exponential backoff.
|
||||
//
|
||||
// RetryerV2 replaces the Retryer interface, deprecating the GetInitialToken
|
||||
// method in favor of GetAttemptToken which takes a context, and can return an error.
|
||||
//
|
||||
// The SDK's retry package's Attempt middleware, and utilities will always
|
||||
// wrap a Retryer as a RetryerV2. Delegating to GetInitialToken, only if
|
||||
// GetAttemptToken is not implemented.
|
||||
type RetryerV2 interface {
|
||||
Retryer
|
||||
|
||||
// GetInitialToken returns the initial attempt token that can increment the
|
||||
// retry token pool if the attempt is successful.
|
||||
//
|
||||
// Deprecated: This method does not provide a way to block using Context,
|
||||
// nor can it return an error. Use RetryerV2, and GetAttemptToken instead.
|
||||
GetInitialToken() (releaseToken func(error) error)
|
||||
|
||||
// GetAttemptToken returns the send token that can be used to rate limit
|
||||
// attempt calls. Will be used by the SDK's retry package's Attempt
|
||||
// middleware to get a send token prior to calling the temp and releasing
|
||||
// the send token after the attempt has been made.
|
||||
GetAttemptToken(context.Context) (func(error) error, error)
|
||||
}
|
||||
|
||||
// NopRetryer provides a RequestRetryDecider implementation that will flag
|
||||
// all attempt errors as not retryable, with a max attempts of 1.
|
||||
type NopRetryer struct{}
|
||||
|
||||
// IsErrorRetryable returns false for all error values.
|
||||
func (NopRetryer) IsErrorRetryable(error) bool { return false }
|
||||
|
||||
// MaxAttempts always returns 1 for the original attempt.
|
||||
func (NopRetryer) MaxAttempts() int { return 1 }
|
||||
|
||||
// RetryDelay is not valid for the NopRetryer. Will always return error.
|
||||
func (NopRetryer) RetryDelay(int, error) (time.Duration, error) {
|
||||
return 0, fmt.Errorf("not retrying any attempt errors")
|
||||
}
|
||||
|
||||
// GetRetryToken returns a stub function that does nothing.
|
||||
func (NopRetryer) GetRetryToken(context.Context, error) (func(error) error, error) {
|
||||
return nopReleaseToken, nil
|
||||
}
|
||||
|
||||
// GetInitialToken returns a stub function that does nothing.
|
||||
func (NopRetryer) GetInitialToken() func(error) error {
|
||||
return nopReleaseToken
|
||||
}
|
||||
|
||||
// GetAttemptToken returns a stub function that does nothing.
|
||||
func (NopRetryer) GetAttemptToken(context.Context) (func(error) error, error) {
|
||||
return nopReleaseToken, nil
|
||||
}
|
||||
|
||||
func nopReleaseToken(error) error { return nil }
|
|
@ -0,0 +1,14 @@
|
|||
package aws
|
||||
|
||||
// ExecutionEnvironmentID is the AWS execution environment runtime identifier.
|
||||
type ExecutionEnvironmentID string
|
||||
|
||||
// RuntimeEnvironment is a collection of values that are determined at runtime
|
||||
// based on the environment that the SDK is executing in. Some of these values
|
||||
// may or may not be present based on the executing environment and certain SDK
|
||||
// configuration properties that drive whether these values are populated..
|
||||
type RuntimeEnvironment struct {
|
||||
EnvironmentIdentifier ExecutionEnvironmentID
|
||||
Region string
|
||||
EC2InstanceMetadataRegion string
|
||||
}
|
115
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/cache.go
generated
vendored
Normal file
115
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/cache.go
generated
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
func lookupKey(service, region string) string {
|
||||
var s strings.Builder
|
||||
s.Grow(len(region) + len(service) + 3)
|
||||
s.WriteString(region)
|
||||
s.WriteRune('/')
|
||||
s.WriteString(service)
|
||||
return s.String()
|
||||
}
|
||||
|
||||
type derivedKey struct {
|
||||
AccessKey string
|
||||
Date time.Time
|
||||
Credential []byte
|
||||
}
|
||||
|
||||
type derivedKeyCache struct {
|
||||
values map[string]derivedKey
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
func newDerivedKeyCache() derivedKeyCache {
|
||||
return derivedKeyCache{
|
||||
values: make(map[string]derivedKey),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *derivedKeyCache) Get(credentials aws.Credentials, service, region string, signingTime SigningTime) []byte {
|
||||
key := lookupKey(service, region)
|
||||
s.mutex.RLock()
|
||||
if cred, ok := s.get(key, credentials, signingTime.Time); ok {
|
||||
s.mutex.RUnlock()
|
||||
return cred
|
||||
}
|
||||
s.mutex.RUnlock()
|
||||
|
||||
s.mutex.Lock()
|
||||
if cred, ok := s.get(key, credentials, signingTime.Time); ok {
|
||||
s.mutex.Unlock()
|
||||
return cred
|
||||
}
|
||||
cred := deriveKey(credentials.SecretAccessKey, service, region, signingTime)
|
||||
entry := derivedKey{
|
||||
AccessKey: credentials.AccessKeyID,
|
||||
Date: signingTime.Time,
|
||||
Credential: cred,
|
||||
}
|
||||
s.values[key] = entry
|
||||
s.mutex.Unlock()
|
||||
|
||||
return cred
|
||||
}
|
||||
|
||||
func (s *derivedKeyCache) get(key string, credentials aws.Credentials, signingTime time.Time) ([]byte, bool) {
|
||||
cacheEntry, ok := s.retrieveFromCache(key)
|
||||
if ok && cacheEntry.AccessKey == credentials.AccessKeyID && isSameDay(signingTime, cacheEntry.Date) {
|
||||
return cacheEntry.Credential, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (s *derivedKeyCache) retrieveFromCache(key string) (derivedKey, bool) {
|
||||
if v, ok := s.values[key]; ok {
|
||||
return v, true
|
||||
}
|
||||
return derivedKey{}, false
|
||||
}
|
||||
|
||||
// SigningKeyDeriver derives a signing key from a set of credentials
|
||||
type SigningKeyDeriver struct {
|
||||
cache derivedKeyCache
|
||||
}
|
||||
|
||||
// NewSigningKeyDeriver returns a new SigningKeyDeriver
|
||||
func NewSigningKeyDeriver() *SigningKeyDeriver {
|
||||
return &SigningKeyDeriver{
|
||||
cache: newDerivedKeyCache(),
|
||||
}
|
||||
}
|
||||
|
||||
// DeriveKey returns a derived signing key from the given credentials to be used with SigV4 signing.
|
||||
func (k *SigningKeyDeriver) DeriveKey(credential aws.Credentials, service, region string, signingTime SigningTime) []byte {
|
||||
return k.cache.Get(credential, service, region, signingTime)
|
||||
}
|
||||
|
||||
func deriveKey(secret, service, region string, t SigningTime) []byte {
|
||||
hmacDate := HMACSHA256([]byte("AWS4"+secret), []byte(t.ShortTimeFormat()))
|
||||
hmacRegion := HMACSHA256(hmacDate, []byte(region))
|
||||
hmacService := HMACSHA256(hmacRegion, []byte(service))
|
||||
return HMACSHA256(hmacService, []byte("aws4_request"))
|
||||
}
|
||||
|
||||
func isSameDay(x, y time.Time) bool {
|
||||
xYear, xMonth, xDay := x.Date()
|
||||
yYear, yMonth, yDay := y.Date()
|
||||
|
||||
if xYear != yYear {
|
||||
return false
|
||||
}
|
||||
|
||||
if xMonth != yMonth {
|
||||
return false
|
||||
}
|
||||
|
||||
return xDay == yDay
|
||||
}
|
40
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/const.go
generated
vendored
Normal file
40
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/const.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package v4
|
||||
|
||||
// Signature Version 4 (SigV4) Constants
|
||||
const (
|
||||
// EmptyStringSHA256 is the hex encoded sha256 value of an empty string
|
||||
EmptyStringSHA256 = `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
|
||||
|
||||
// UnsignedPayload indicates that the request payload body is unsigned
|
||||
UnsignedPayload = "UNSIGNED-PAYLOAD"
|
||||
|
||||
// AmzAlgorithmKey indicates the signing algorithm
|
||||
AmzAlgorithmKey = "X-Amz-Algorithm"
|
||||
|
||||
// AmzSecurityTokenKey indicates the security token to be used with temporary credentials
|
||||
AmzSecurityTokenKey = "X-Amz-Security-Token"
|
||||
|
||||
// AmzDateKey is the UTC timestamp for the request in the format YYYYMMDD'T'HHMMSS'Z'
|
||||
AmzDateKey = "X-Amz-Date"
|
||||
|
||||
// AmzCredentialKey is the access key ID and credential scope
|
||||
AmzCredentialKey = "X-Amz-Credential"
|
||||
|
||||
// AmzSignedHeadersKey is the set of headers signed for the request
|
||||
AmzSignedHeadersKey = "X-Amz-SignedHeaders"
|
||||
|
||||
// AmzSignatureKey is the query parameter to store the SigV4 signature
|
||||
AmzSignatureKey = "X-Amz-Signature"
|
||||
|
||||
// TimeFormat is the time format to be used in the X-Amz-Date header or query parameter
|
||||
TimeFormat = "20060102T150405Z"
|
||||
|
||||
// ShortTimeFormat is the shorten time format used in the credential scope
|
||||
ShortTimeFormat = "20060102"
|
||||
|
||||
// ContentSHAKey is the SHA256 of request body
|
||||
ContentSHAKey = "X-Amz-Content-Sha256"
|
||||
|
||||
// StreamingEventsPayload indicates that the request payload body is a signed event stream.
|
||||
StreamingEventsPayload = "STREAMING-AWS4-HMAC-SHA256-EVENTS"
|
||||
)
|
82
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/header_rules.go
generated
vendored
Normal file
82
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/header_rules.go
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
sdkstrings "github.com/aws/aws-sdk-go-v2/internal/strings"
|
||||
)
|
||||
|
||||
// Rules houses a set of Rule needed for validation of a
|
||||
// string value
|
||||
type Rules []Rule
|
||||
|
||||
// Rule interface allows for more flexible rules and just simply
|
||||
// checks whether or not a value adheres to that Rule
|
||||
type Rule interface {
|
||||
IsValid(value string) bool
|
||||
}
|
||||
|
||||
// IsValid will iterate through all rules and see if any rules
|
||||
// apply to the value and supports nested rules
|
||||
func (r Rules) IsValid(value string) bool {
|
||||
for _, rule := range r {
|
||||
if rule.IsValid(value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MapRule generic Rule for maps
|
||||
type MapRule map[string]struct{}
|
||||
|
||||
// IsValid for the map Rule satisfies whether it exists in the map
|
||||
func (m MapRule) IsValid(value string) bool {
|
||||
_, ok := m[value]
|
||||
return ok
|
||||
}
|
||||
|
||||
// AllowList is a generic Rule for include listing
|
||||
type AllowList struct {
|
||||
Rule
|
||||
}
|
||||
|
||||
// IsValid for AllowList checks if the value is within the AllowList
|
||||
func (w AllowList) IsValid(value string) bool {
|
||||
return w.Rule.IsValid(value)
|
||||
}
|
||||
|
||||
// ExcludeList is a generic Rule for exclude listing
|
||||
type ExcludeList struct {
|
||||
Rule
|
||||
}
|
||||
|
||||
// IsValid for AllowList checks if the value is within the AllowList
|
||||
func (b ExcludeList) IsValid(value string) bool {
|
||||
return !b.Rule.IsValid(value)
|
||||
}
|
||||
|
||||
// Patterns is a list of strings to match against
|
||||
type Patterns []string
|
||||
|
||||
// IsValid for Patterns checks each pattern and returns if a match has
|
||||
// been found
|
||||
func (p Patterns) IsValid(value string) bool {
|
||||
for _, pattern := range p {
|
||||
if sdkstrings.HasPrefixFold(value, pattern) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// InclusiveRules rules allow for rules to depend on one another
|
||||
type InclusiveRules []Rule
|
||||
|
||||
// IsValid will return true if all rules are true
|
||||
func (r InclusiveRules) IsValid(value string) bool {
|
||||
for _, rule := range r {
|
||||
if !rule.IsValid(value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
68
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/headers.go
generated
vendored
Normal file
68
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/headers.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package v4
|
||||
|
||||
// IgnoredHeaders is a list of headers that are ignored during signing
|
||||
var IgnoredHeaders = Rules{
|
||||
ExcludeList{
|
||||
MapRule{
|
||||
"Authorization": struct{}{},
|
||||
"User-Agent": struct{}{},
|
||||
"X-Amzn-Trace-Id": struct{}{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// RequiredSignedHeaders is a allow list for Build canonical headers.
|
||||
var RequiredSignedHeaders = Rules{
|
||||
AllowList{
|
||||
MapRule{
|
||||
"Cache-Control": struct{}{},
|
||||
"Content-Disposition": struct{}{},
|
||||
"Content-Encoding": struct{}{},
|
||||
"Content-Language": struct{}{},
|
||||
"Content-Md5": struct{}{},
|
||||
"Content-Type": struct{}{},
|
||||
"Expires": struct{}{},
|
||||
"If-Match": struct{}{},
|
||||
"If-Modified-Since": struct{}{},
|
||||
"If-None-Match": struct{}{},
|
||||
"If-Unmodified-Since": struct{}{},
|
||||
"Range": struct{}{},
|
||||
"X-Amz-Acl": struct{}{},
|
||||
"X-Amz-Copy-Source": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Match": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Modified-Since": struct{}{},
|
||||
"X-Amz-Copy-Source-If-None-Match": struct{}{},
|
||||
"X-Amz-Copy-Source-If-Unmodified-Since": struct{}{},
|
||||
"X-Amz-Copy-Source-Range": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||
"X-Amz-Copy-Source-Server-Side-Encryption-Customer-Key-Md5": struct{}{},
|
||||
"X-Amz-Grant-Full-control": struct{}{},
|
||||
"X-Amz-Grant-Read": struct{}{},
|
||||
"X-Amz-Grant-Read-Acp": struct{}{},
|
||||
"X-Amz-Grant-Write": struct{}{},
|
||||
"X-Amz-Grant-Write-Acp": struct{}{},
|
||||
"X-Amz-Metadata-Directive": struct{}{},
|
||||
"X-Amz-Mfa": struct{}{},
|
||||
"X-Amz-Request-Payer": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Aws-Kms-Key-Id": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Customer-Algorithm": struct{}{},
|
||||
"X-Amz-Server-Side-Encryption-Customer-Key": struct{}{},
|
||||
"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{}{},
|
||||
"X-Amz-Tagging": struct{}{},
|
||||
},
|
||||
},
|
||||
Patterns{"X-Amz-Object-Lock-"},
|
||||
Patterns{"X-Amz-Meta-"},
|
||||
}
|
||||
|
||||
// AllowedQueryHoisting is a allowed list for Build query headers. The boolean value
|
||||
// represents whether or not it is a pattern.
|
||||
var AllowedQueryHoisting = InclusiveRules{
|
||||
ExcludeList{RequiredSignedHeaders},
|
||||
Patterns{"X-Amz-"},
|
||||
}
|
13
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/hmac.go
generated
vendored
Normal file
13
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/hmac.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
// HMACSHA256 computes a HMAC-SHA256 of data given the provided key.
|
||||
func HMACSHA256(key []byte, data []byte) []byte {
|
||||
hash := hmac.New(sha256.New, key)
|
||||
hash.Write(data)
|
||||
return hash.Sum(nil)
|
||||
}
|
75
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/host.go
generated
vendored
Normal file
75
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/host.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SanitizeHostForHeader removes default port from host and updates request.Host
|
||||
func SanitizeHostForHeader(r *http.Request) {
|
||||
host := getHost(r)
|
||||
port := portOnly(host)
|
||||
if port != "" && isDefaultPort(r.URL.Scheme, port) {
|
||||
r.Host = stripPort(host)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns host from request
|
||||
func getHost(r *http.Request) string {
|
||||
if r.Host != "" {
|
||||
return r.Host
|
||||
}
|
||||
|
||||
return r.URL.Host
|
||||
}
|
||||
|
||||
// Hostname returns u.Host, without any port number.
|
||||
//
|
||||
// If Host is an IPv6 literal with a port number, Hostname returns the
|
||||
// IPv6 literal without the square brackets. IPv6 literals may include
|
||||
// a zone identifier.
|
||||
//
|
||||
// Copied from the Go 1.8 standard library (net/url)
|
||||
func stripPort(hostport string) string {
|
||||
colon := strings.IndexByte(hostport, ':')
|
||||
if colon == -1 {
|
||||
return hostport
|
||||
}
|
||||
if i := strings.IndexByte(hostport, ']'); i != -1 {
|
||||
return strings.TrimPrefix(hostport[:i], "[")
|
||||
}
|
||||
return hostport[:colon]
|
||||
}
|
||||
|
||||
// Port returns the port part of u.Host, without the leading colon.
|
||||
// If u.Host doesn't contain a port, Port returns an empty string.
|
||||
//
|
||||
// Copied from the Go 1.8 standard library (net/url)
|
||||
func portOnly(hostport string) string {
|
||||
colon := strings.IndexByte(hostport, ':')
|
||||
if colon == -1 {
|
||||
return ""
|
||||
}
|
||||
if i := strings.Index(hostport, "]:"); i != -1 {
|
||||
return hostport[i+len("]:"):]
|
||||
}
|
||||
if strings.Contains(hostport, "]") {
|
||||
return ""
|
||||
}
|
||||
return hostport[colon+len(":"):]
|
||||
}
|
||||
|
||||
// Returns true if the specified URI is using the standard port
|
||||
// (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
|
||||
func isDefaultPort(scheme, port string) bool {
|
||||
if port == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
lowerCaseScheme := strings.ToLower(scheme)
|
||||
if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
13
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/scope.go
generated
vendored
Normal file
13
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/scope.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
package v4
|
||||
|
||||
import "strings"
|
||||
|
||||
// BuildCredentialScope builds the Signature Version 4 (SigV4) signing scope
|
||||
func BuildCredentialScope(signingTime SigningTime, region, service string) string {
|
||||
return strings.Join([]string{
|
||||
signingTime.ShortTimeFormat(),
|
||||
region,
|
||||
service,
|
||||
"aws4_request",
|
||||
}, "/")
|
||||
}
|
36
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/time.go
generated
vendored
Normal file
36
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/time.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package v4
|
||||
|
||||
import "time"
|
||||
|
||||
// SigningTime provides a wrapper around a time.Time which provides cached values for SigV4 signing.
|
||||
type SigningTime struct {
|
||||
time.Time
|
||||
timeFormat string
|
||||
shortTimeFormat string
|
||||
}
|
||||
|
||||
// NewSigningTime creates a new SigningTime given a time.Time
|
||||
func NewSigningTime(t time.Time) SigningTime {
|
||||
return SigningTime{
|
||||
Time: t,
|
||||
}
|
||||
}
|
||||
|
||||
// TimeFormat provides a time formatted in the X-Amz-Date format.
|
||||
func (m *SigningTime) TimeFormat() string {
|
||||
return m.format(&m.timeFormat, TimeFormat)
|
||||
}
|
||||
|
||||
// ShortTimeFormat provides a time formatted of 20060102.
|
||||
func (m *SigningTime) ShortTimeFormat() string {
|
||||
return m.format(&m.shortTimeFormat, ShortTimeFormat)
|
||||
}
|
||||
|
||||
func (m *SigningTime) format(target *string, format string) string {
|
||||
if len(*target) > 0 {
|
||||
return *target
|
||||
}
|
||||
v := m.Time.Format(format)
|
||||
*target = v
|
||||
return v
|
||||
}
|
64
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/util.go
generated
vendored
Normal file
64
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4/util.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const doubleSpace = " "
|
||||
|
||||
// StripExcessSpaces will rewrite the passed in slice's string values to not
|
||||
// contain multiple side-by-side spaces.
|
||||
func StripExcessSpaces(str string) string {
|
||||
var j, k, l, m, spaces int
|
||||
// Trim trailing spaces
|
||||
for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- {
|
||||
}
|
||||
|
||||
// Trim leading spaces
|
||||
for k = 0; k < j && str[k] == ' '; k++ {
|
||||
}
|
||||
str = str[k : j+1]
|
||||
|
||||
// Strip multiple spaces.
|
||||
j = strings.Index(str, doubleSpace)
|
||||
if j < 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
buf := []byte(str)
|
||||
for k, m, l = j, j, len(buf); k < l; k++ {
|
||||
if buf[k] == ' ' {
|
||||
if spaces == 0 {
|
||||
// First space.
|
||||
buf[m] = buf[k]
|
||||
m++
|
||||
}
|
||||
spaces++
|
||||
} else {
|
||||
// End of multiple spaces.
|
||||
spaces = 0
|
||||
buf[m] = buf[k]
|
||||
m++
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[:m])
|
||||
}
|
||||
|
||||
// GetURIPath returns the escaped URI component from the provided URL
|
||||
func GetURIPath(u *url.URL) string {
|
||||
var uri string
|
||||
|
||||
if len(u.Opaque) > 0 {
|
||||
uri = "/" + strings.Join(strings.Split(u.Opaque, "/")[3:], "/")
|
||||
} else {
|
||||
uri = u.EscapedPath()
|
||||
}
|
||||
|
||||
if len(uri) == 0 {
|
||||
uri = "/"
|
||||
}
|
||||
|
||||
return uri
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
v4Internal "github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
const computePayloadHashMiddlewareID = "ComputePayloadHash"
|
||||
|
||||
// HashComputationError indicates an error occurred while computing the signing hash
|
||||
type HashComputationError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
// Error is the error message
|
||||
func (e *HashComputationError) Error() string {
|
||||
return fmt.Sprintf("failed to compute payload hash: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error if one is set
|
||||
func (e *HashComputationError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// SigningError indicates an error condition occurred while performing SigV4 signing
|
||||
type SigningError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *SigningError) Error() string {
|
||||
return fmt.Sprintf("failed to sign request: %v", e.Err)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying error cause
|
||||
func (e *SigningError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// UseDynamicPayloadSigningMiddleware swaps the compute payload sha256 middleware with a resolver middleware that
|
||||
// switches between unsigned and signed payload based on TLS state for request.
|
||||
// This middleware should not be used for AWS APIs that do not support unsigned payload signing auth.
|
||||
// By default, SDK uses this middleware for known AWS APIs that support such TLS based auth selection .
|
||||
//
|
||||
// Usage example -
|
||||
// S3 PutObject API allows unsigned payload signing auth usage when TLS is enabled, and uses this middleware to
|
||||
// dynamically switch between unsigned and signed payload based on TLS state for request.
|
||||
func UseDynamicPayloadSigningMiddleware(stack *middleware.Stack) error {
|
||||
_, err := stack.Build.Swap(computePayloadHashMiddlewareID, &dynamicPayloadSigningMiddleware{})
|
||||
return err
|
||||
}
|
||||
|
||||
// dynamicPayloadSigningMiddleware dynamically resolves the middleware that computes and set payload sha256 middleware.
|
||||
type dynamicPayloadSigningMiddleware struct {
|
||||
}
|
||||
|
||||
// ID returns the resolver identifier
|
||||
func (m *dynamicPayloadSigningMiddleware) ID() string {
|
||||
return computePayloadHashMiddlewareID
|
||||
}
|
||||
|
||||
// HandleBuild sets a resolver that directs to the payload sha256 compute handler.
|
||||
func (m *dynamicPayloadSigningMiddleware) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown transport type %T", in.Request)
|
||||
}
|
||||
|
||||
// if TLS is enabled, use unsigned payload when supported
|
||||
if strings.EqualFold(req.URL.Scheme, "https") {
|
||||
return (&unsignedPayload{}).HandleBuild(ctx, in, next)
|
||||
}
|
||||
|
||||
// else fall back to signed payload
|
||||
return (&computePayloadSHA256{}).HandleBuild(ctx, in, next)
|
||||
}
|
||||
|
||||
// unsignedPayload sets the SigV4 request payload hash to unsigned.
|
||||
//
|
||||
// Will not set the Unsigned Payload magic SHA value, if a SHA has already been
|
||||
// stored in the context. (e.g. application pre-computed SHA256 before making
|
||||
// API call).
|
||||
//
|
||||
// This middleware does not check the X-Amz-Content-Sha256 header, if that
|
||||
// header is serialized a middleware must translate it into the context.
|
||||
type unsignedPayload struct{}
|
||||
|
||||
// AddUnsignedPayloadMiddleware adds unsignedPayload to the operation
|
||||
// middleware stack
|
||||
func AddUnsignedPayloadMiddleware(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(&unsignedPayload{}, middleware.After)
|
||||
}
|
||||
|
||||
// ID returns the unsignedPayload identifier
|
||||
func (m *unsignedPayload) ID() string {
|
||||
return computePayloadHashMiddlewareID
|
||||
}
|
||||
|
||||
// HandleBuild sets the payload hash to be an unsigned payload
|
||||
func (m *unsignedPayload) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
// This should not compute the content SHA256 if the value is already
|
||||
// known. (e.g. application pre-computed SHA256 before making API call).
|
||||
// Does not have any tight coupling to the X-Amz-Content-Sha256 header, if
|
||||
// that header is provided a middleware must translate it into the context.
|
||||
contentSHA := GetPayloadHash(ctx)
|
||||
if len(contentSHA) == 0 {
|
||||
contentSHA = v4Internal.UnsignedPayload
|
||||
}
|
||||
|
||||
ctx = SetPayloadHash(ctx, contentSHA)
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// computePayloadSHA256 computes SHA256 payload hash to sign.
|
||||
//
|
||||
// Will not set the Unsigned Payload magic SHA value, if a SHA has already been
|
||||
// stored in the context. (e.g. application pre-computed SHA256 before making
|
||||
// API call).
|
||||
//
|
||||
// This middleware does not check the X-Amz-Content-Sha256 header, if that
|
||||
// header is serialized a middleware must translate it into the context.
|
||||
type computePayloadSHA256 struct{}
|
||||
|
||||
// AddComputePayloadSHA256Middleware adds computePayloadSHA256 to the
|
||||
// operation middleware stack
|
||||
func AddComputePayloadSHA256Middleware(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(&computePayloadSHA256{}, middleware.After)
|
||||
}
|
||||
|
||||
// RemoveComputePayloadSHA256Middleware removes computePayloadSHA256 from the
|
||||
// operation middleware stack
|
||||
func RemoveComputePayloadSHA256Middleware(stack *middleware.Stack) error {
|
||||
_, err := stack.Build.Remove(computePayloadHashMiddlewareID)
|
||||
return err
|
||||
}
|
||||
|
||||
// ID is the middleware name
|
||||
func (m *computePayloadSHA256) ID() string {
|
||||
return computePayloadHashMiddlewareID
|
||||
}
|
||||
|
||||
// HandleBuild compute the payload hash for the request payload
|
||||
func (m *computePayloadSHA256) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, &HashComputationError{
|
||||
Err: fmt.Errorf("unexpected request middleware type %T", in.Request),
|
||||
}
|
||||
}
|
||||
|
||||
// This should not compute the content SHA256 if the value is already
|
||||
// known. (e.g. application pre-computed SHA256 before making API call)
|
||||
// Does not have any tight coupling to the X-Amz-Content-Sha256 header, if
|
||||
// that header is provided a middleware must translate it into the context.
|
||||
if contentSHA := GetPayloadHash(ctx); len(contentSHA) != 0 {
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
hash := sha256.New()
|
||||
if stream := req.GetStream(); stream != nil {
|
||||
_, err = io.Copy(hash, stream)
|
||||
if err != nil {
|
||||
return out, metadata, &HashComputationError{
|
||||
Err: fmt.Errorf("failed to compute payload hash, %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
if err := req.RewindStream(); err != nil {
|
||||
return out, metadata, &HashComputationError{
|
||||
Err: fmt.Errorf("failed to seek body to start, %w", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx = SetPayloadHash(ctx, hex.EncodeToString(hash.Sum(nil)))
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// SwapComputePayloadSHA256ForUnsignedPayloadMiddleware replaces the
|
||||
// ComputePayloadSHA256 middleware with the UnsignedPayload middleware.
|
||||
//
|
||||
// Use this to disable computing the Payload SHA256 checksum and instead use
|
||||
// UNSIGNED-PAYLOAD for the SHA256 value.
|
||||
func SwapComputePayloadSHA256ForUnsignedPayloadMiddleware(stack *middleware.Stack) error {
|
||||
_, err := stack.Build.Swap(computePayloadHashMiddlewareID, &unsignedPayload{})
|
||||
return err
|
||||
}
|
||||
|
||||
// contentSHA256Header sets the X-Amz-Content-Sha256 header value to
|
||||
// the Payload hash stored in the context.
|
||||
type contentSHA256Header struct{}
|
||||
|
||||
// AddContentSHA256HeaderMiddleware adds ContentSHA256Header to the
|
||||
// operation middleware stack
|
||||
func AddContentSHA256HeaderMiddleware(stack *middleware.Stack) error {
|
||||
return stack.Build.Insert(&contentSHA256Header{}, computePayloadHashMiddlewareID, middleware.After)
|
||||
}
|
||||
|
||||
// RemoveContentSHA256HeaderMiddleware removes contentSHA256Header middleware
|
||||
// from the operation middleware stack
|
||||
func RemoveContentSHA256HeaderMiddleware(stack *middleware.Stack) error {
|
||||
_, err := stack.Build.Remove((*contentSHA256Header)(nil).ID())
|
||||
return err
|
||||
}
|
||||
|
||||
// ID returns the ContentSHA256HeaderMiddleware identifier
|
||||
func (m *contentSHA256Header) ID() string {
|
||||
return "SigV4ContentSHA256Header"
|
||||
}
|
||||
|
||||
// HandleBuild sets the X-Amz-Content-Sha256 header value to the Payload hash
|
||||
// stored in the context.
|
||||
func (m *contentSHA256Header) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, &HashComputationError{Err: fmt.Errorf("unexpected request middleware type %T", in.Request)}
|
||||
}
|
||||
|
||||
req.Header.Set(v4Internal.ContentSHAKey, GetPayloadHash(ctx))
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// SignHTTPRequestMiddlewareOptions is the configuration options for the SignHTTPRequestMiddleware middleware.
|
||||
type SignHTTPRequestMiddlewareOptions struct {
|
||||
CredentialsProvider aws.CredentialsProvider
|
||||
Signer HTTPSigner
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
// SignHTTPRequestMiddleware is a `FinalizeMiddleware` implementation for SigV4 HTTP Signing
|
||||
type SignHTTPRequestMiddleware struct {
|
||||
credentialsProvider aws.CredentialsProvider
|
||||
signer HTTPSigner
|
||||
logSigning bool
|
||||
}
|
||||
|
||||
// NewSignHTTPRequestMiddleware constructs a SignHTTPRequestMiddleware using the given Signer for signing requests
|
||||
func NewSignHTTPRequestMiddleware(options SignHTTPRequestMiddlewareOptions) *SignHTTPRequestMiddleware {
|
||||
return &SignHTTPRequestMiddleware{
|
||||
credentialsProvider: options.CredentialsProvider,
|
||||
signer: options.Signer,
|
||||
logSigning: options.LogSigning,
|
||||
}
|
||||
}
|
||||
|
||||
// ID is the SignHTTPRequestMiddleware identifier
|
||||
func (s *SignHTTPRequestMiddleware) ID() string {
|
||||
return "Signing"
|
||||
}
|
||||
|
||||
// HandleFinalize will take the provided input and sign the request using the SigV4 authentication scheme
|
||||
func (s *SignHTTPRequestMiddleware) HandleFinalize(ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
if !haveCredentialProvider(s.credentialsProvider) {
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("unexpected request middleware type %T", in.Request)}
|
||||
}
|
||||
|
||||
signingName, signingRegion := awsmiddleware.GetSigningName(ctx), awsmiddleware.GetSigningRegion(ctx)
|
||||
payloadHash := GetPayloadHash(ctx)
|
||||
if len(payloadHash) == 0 {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("computed payload hash missing from context")}
|
||||
}
|
||||
|
||||
credentials, err := s.credentialsProvider.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("failed to retrieve credentials: %w", err)}
|
||||
}
|
||||
|
||||
err = s.signer.SignHTTP(ctx, credentials, req.Request, payloadHash, signingName, signingRegion, sdk.NowTime(),
|
||||
func(o *SignerOptions) {
|
||||
o.Logger = middleware.GetLogger(ctx)
|
||||
o.LogSigning = s.logSigning
|
||||
})
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{Err: fmt.Errorf("failed to sign http request, %w", err)}
|
||||
}
|
||||
|
||||
ctx = awsmiddleware.SetSigningCredentials(ctx, credentials)
|
||||
|
||||
return next.HandleFinalize(ctx, in)
|
||||
}
|
||||
|
||||
type streamingEventsPayload struct{}
|
||||
|
||||
// AddStreamingEventsPayload adds the streamingEventsPayload middleware to the stack.
|
||||
func AddStreamingEventsPayload(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(&streamingEventsPayload{}, middleware.After)
|
||||
}
|
||||
|
||||
func (s *streamingEventsPayload) ID() string {
|
||||
return computePayloadHashMiddlewareID
|
||||
}
|
||||
|
||||
func (s *streamingEventsPayload) HandleBuild(
|
||||
ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler,
|
||||
) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
contentSHA := GetPayloadHash(ctx)
|
||||
if len(contentSHA) == 0 {
|
||||
contentSHA = v4Internal.StreamingEventsPayload
|
||||
}
|
||||
|
||||
ctx = SetPayloadHash(ctx, contentSHA)
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// GetSignedRequestSignature attempts to extract the signature of the request.
|
||||
// Returning an error if the request is unsigned, or unable to extract the
|
||||
// signature.
|
||||
func GetSignedRequestSignature(r *http.Request) ([]byte, error) {
|
||||
const authHeaderSignatureElem = "Signature="
|
||||
|
||||
if auth := r.Header.Get(authorizationHeader); len(auth) != 0 {
|
||||
ps := strings.Split(auth, ", ")
|
||||
for _, p := range ps {
|
||||
if idx := strings.Index(p, authHeaderSignatureElem); idx >= 0 {
|
||||
sig := p[len(authHeaderSignatureElem):]
|
||||
if len(sig) == 0 {
|
||||
return nil, fmt.Errorf("invalid request signature authorization header")
|
||||
}
|
||||
return hex.DecodeString(sig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sig := r.URL.Query().Get("X-Amz-Signature"); len(sig) != 0 {
|
||||
return hex.DecodeString(sig)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("request not signed")
|
||||
}
|
||||
|
||||
func haveCredentialProvider(p aws.CredentialsProvider) bool {
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
switch p.(type) {
|
||||
case aws.AnonymousCredentials,
|
||||
*aws.AnonymousCredentials:
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type payloadHashKey struct{}
|
||||
|
||||
// GetPayloadHash retrieves the payload hash to use for signing
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func GetPayloadHash(ctx context.Context) (v string) {
|
||||
v, _ = middleware.GetStackValue(ctx, payloadHashKey{}).(string)
|
||||
return v
|
||||
}
|
||||
|
||||
// SetPayloadHash sets the payload hash to be used for signing the request
|
||||
//
|
||||
// Scoped to stack values. Use github.com/aws/smithy-go/middleware#ClearStackValues
|
||||
// to clear all stack values.
|
||||
func SetPayloadHash(ctx context.Context, hash string) context.Context {
|
||||
return middleware.WithStackValue(ctx, payloadHashKey{}, hash)
|
||||
}
|
127
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/v4/presign_middleware.go
generated
vendored
Normal file
127
vendor/github.com/aws/aws-sdk-go-v2/aws/signer/v4/presign_middleware.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/aws-sdk-go-v2/internal/sdk"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyHTTP "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// HTTPPresigner is an interface to a SigV4 signer that can sign create a
|
||||
// presigned URL for a HTTP requests.
|
||||
type HTTPPresigner interface {
|
||||
PresignHTTP(
|
||||
ctx context.Context, credentials aws.Credentials, r *http.Request,
|
||||
payloadHash string, service string, region string, signingTime time.Time,
|
||||
optFns ...func(*SignerOptions),
|
||||
) (url string, signedHeader http.Header, err error)
|
||||
}
|
||||
|
||||
// PresignedHTTPRequest provides the URL and signed headers that are included
|
||||
// in the presigned URL.
|
||||
type PresignedHTTPRequest struct {
|
||||
URL string
|
||||
Method string
|
||||
SignedHeader http.Header
|
||||
}
|
||||
|
||||
// PresignHTTPRequestMiddlewareOptions is the options for the PresignHTTPRequestMiddleware middleware.
|
||||
type PresignHTTPRequestMiddlewareOptions struct {
|
||||
CredentialsProvider aws.CredentialsProvider
|
||||
Presigner HTTPPresigner
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
// PresignHTTPRequestMiddleware provides the Finalize middleware for creating a
|
||||
// presigned URL for an HTTP request.
|
||||
//
|
||||
// Will short circuit the middleware stack and not forward onto the next
|
||||
// Finalize handler.
|
||||
type PresignHTTPRequestMiddleware struct {
|
||||
credentialsProvider aws.CredentialsProvider
|
||||
presigner HTTPPresigner
|
||||
logSigning bool
|
||||
}
|
||||
|
||||
// NewPresignHTTPRequestMiddleware returns a new PresignHTTPRequestMiddleware
|
||||
// initialized with the presigner.
|
||||
func NewPresignHTTPRequestMiddleware(options PresignHTTPRequestMiddlewareOptions) *PresignHTTPRequestMiddleware {
|
||||
return &PresignHTTPRequestMiddleware{
|
||||
credentialsProvider: options.CredentialsProvider,
|
||||
presigner: options.Presigner,
|
||||
logSigning: options.LogSigning,
|
||||
}
|
||||
}
|
||||
|
||||
// ID provides the middleware ID.
|
||||
func (*PresignHTTPRequestMiddleware) ID() string { return "PresignHTTPRequest" }
|
||||
|
||||
// HandleFinalize will take the provided input and create a presigned url for
|
||||
// the http request using the SigV4 presign authentication scheme.
|
||||
//
|
||||
// Since the signed request is not a valid HTTP request
|
||||
func (s *PresignHTTPRequestMiddleware) HandleFinalize(
|
||||
ctx context.Context, in middleware.FinalizeInput, next middleware.FinalizeHandler,
|
||||
) (
|
||||
out middleware.FinalizeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyHTTP.Request)
|
||||
if !ok {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("unexpected request middleware type %T", in.Request),
|
||||
}
|
||||
}
|
||||
|
||||
httpReq := req.Build(ctx)
|
||||
if !haveCredentialProvider(s.credentialsProvider) {
|
||||
out.Result = &PresignedHTTPRequest{
|
||||
URL: httpReq.URL.String(),
|
||||
Method: httpReq.Method,
|
||||
SignedHeader: http.Header{},
|
||||
}
|
||||
|
||||
return out, metadata, nil
|
||||
}
|
||||
|
||||
signingName := awsmiddleware.GetSigningName(ctx)
|
||||
signingRegion := awsmiddleware.GetSigningRegion(ctx)
|
||||
payloadHash := GetPayloadHash(ctx)
|
||||
if len(payloadHash) == 0 {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("computed payload hash missing from context"),
|
||||
}
|
||||
}
|
||||
|
||||
credentials, err := s.credentialsProvider.Retrieve(ctx)
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("failed to retrieve credentials: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
u, h, err := s.presigner.PresignHTTP(ctx, credentials,
|
||||
httpReq, payloadHash, signingName, signingRegion, sdk.NowTime(),
|
||||
func(o *SignerOptions) {
|
||||
o.Logger = middleware.GetLogger(ctx)
|
||||
o.LogSigning = s.logSigning
|
||||
})
|
||||
if err != nil {
|
||||
return out, metadata, &SigningError{
|
||||
Err: fmt.Errorf("failed to sign http request, %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
out.Result = &PresignedHTTPRequest{
|
||||
URL: u,
|
||||
Method: httpReq.Method,
|
||||
SignedHeader: h,
|
||||
}
|
||||
|
||||
return out, metadata, nil
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package v4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
v4Internal "github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// EventStreamSigner is an AWS EventStream protocol signer.
|
||||
type EventStreamSigner interface {
|
||||
GetSignature(ctx context.Context, headers, payload []byte, signingTime time.Time, optFns ...func(*StreamSignerOptions)) ([]byte, error)
|
||||
}
|
||||
|
||||
// StreamSignerOptions is the configuration options for StreamSigner.
|
||||
type StreamSignerOptions struct{}
|
||||
|
||||
// StreamSigner implements Signature Version 4 (SigV4) signing of event stream encoded payloads.
|
||||
type StreamSigner struct {
|
||||
options StreamSignerOptions
|
||||
|
||||
credentials aws.Credentials
|
||||
service string
|
||||
region string
|
||||
|
||||
prevSignature []byte
|
||||
|
||||
signingKeyDeriver *v4Internal.SigningKeyDeriver
|
||||
}
|
||||
|
||||
// NewStreamSigner returns a new AWS EventStream protocol signer.
|
||||
func NewStreamSigner(credentials aws.Credentials, service, region string, seedSignature []byte, optFns ...func(*StreamSignerOptions)) *StreamSigner {
|
||||
o := StreamSignerOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&o)
|
||||
}
|
||||
|
||||
return &StreamSigner{
|
||||
options: o,
|
||||
credentials: credentials,
|
||||
service: service,
|
||||
region: region,
|
||||
signingKeyDeriver: v4Internal.NewSigningKeyDeriver(),
|
||||
prevSignature: seedSignature,
|
||||
}
|
||||
}
|
||||
|
||||
// GetSignature signs the provided header and payload bytes.
|
||||
func (s *StreamSigner) GetSignature(ctx context.Context, headers, payload []byte, signingTime time.Time, optFns ...func(*StreamSignerOptions)) ([]byte, error) {
|
||||
options := s.options
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
prevSignature := s.prevSignature
|
||||
|
||||
st := v4Internal.NewSigningTime(signingTime)
|
||||
|
||||
sigKey := s.signingKeyDeriver.DeriveKey(s.credentials, s.service, s.region, st)
|
||||
|
||||
scope := v4Internal.BuildCredentialScope(st, s.region, s.service)
|
||||
|
||||
stringToSign := s.buildEventStreamStringToSign(headers, payload, prevSignature, scope, &st)
|
||||
|
||||
signature := v4Internal.HMACSHA256(sigKey, []byte(stringToSign))
|
||||
s.prevSignature = signature
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
func (s *StreamSigner) buildEventStreamStringToSign(headers, payload, previousSignature []byte, credentialScope string, signingTime *v4Internal.SigningTime) string {
|
||||
hash := sha256.New()
|
||||
return strings.Join([]string{
|
||||
"AWS4-HMAC-SHA256-PAYLOAD",
|
||||
signingTime.TimeFormat(),
|
||||
credentialScope,
|
||||
hex.EncodeToString(previousSignature),
|
||||
hex.EncodeToString(makeHash(hash, headers)),
|
||||
hex.EncodeToString(makeHash(hash, payload)),
|
||||
}, "\n")
|
||||
}
|
|
@ -0,0 +1,542 @@
|
|||
// Package v4 implements signing for AWS V4 signer
|
||||
//
|
||||
// Provides request signing for request that need to be signed with
|
||||
// AWS V4 Signatures.
|
||||
//
|
||||
// Standalone Signer
|
||||
//
|
||||
// Generally using the signer outside of the SDK should not require any additional
|
||||
// The signer does this by taking advantage of the URL.EscapedPath method. If your request URI requires
|
||||
// additional escaping you many need to use the URL.Opaque to define what the raw URI should be sent
|
||||
// to the service as.
|
||||
//
|
||||
// The signer will first check the URL.Opaque field, and use its value if set.
|
||||
// The signer does require the URL.Opaque field to be set in the form of:
|
||||
//
|
||||
// "//<hostname>/<path>"
|
||||
//
|
||||
// // e.g.
|
||||
// "//example.com/some/path"
|
||||
//
|
||||
// The leading "//" and hostname are required or the URL.Opaque escaping will
|
||||
// not work correctly.
|
||||
//
|
||||
// If URL.Opaque is not set the signer will fallback to the URL.EscapedPath()
|
||||
// method and using the returned value.
|
||||
//
|
||||
// AWS v4 signature validation requires that the canonical string's URI path
|
||||
// element must be the URI escaped form of the HTTP request's path.
|
||||
// http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
//
|
||||
// The Go HTTP client will perform escaping automatically on the request. Some
|
||||
// of these escaping may cause signature validation errors because the HTTP
|
||||
// request differs from the URI path or query that the signature was generated.
|
||||
// https://golang.org/pkg/net/url/#URL.EscapedPath
|
||||
//
|
||||
// Because of this, it is recommended that when using the signer outside of the
|
||||
// SDK that explicitly escaping the request prior to being signed is preferable,
|
||||
// and will help prevent signature validation errors. This can be done by setting
|
||||
// the URL.Opaque or URL.RawPath. The SDK will use URL.Opaque first and then
|
||||
// call URL.EscapedPath() if Opaque is not set.
|
||||
//
|
||||
// Test `TestStandaloneSign` provides a complete example of using the signer
|
||||
// outside of the SDK and pre-escaping the URI path.
|
||||
package v4
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
v4Internal "github.com/aws/aws-sdk-go-v2/aws/signer/internal/v4"
|
||||
"github.com/aws/smithy-go/encoding/httpbinding"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
)
|
||||
|
||||
const (
|
||||
signingAlgorithm = "AWS4-HMAC-SHA256"
|
||||
authorizationHeader = "Authorization"
|
||||
)
|
||||
|
||||
// HTTPSigner is an interface to a SigV4 signer that can sign HTTP requests
|
||||
type HTTPSigner interface {
|
||||
SignHTTP(ctx context.Context, credentials aws.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(*SignerOptions)) error
|
||||
}
|
||||
|
||||
type keyDerivator interface {
|
||||
DeriveKey(credential aws.Credentials, service, region string, signingTime v4Internal.SigningTime) []byte
|
||||
}
|
||||
|
||||
// SignerOptions is the SigV4 Signer options.
|
||||
type SignerOptions struct {
|
||||
// Disables the Signer's moving HTTP header key/value pairs from the HTTP
|
||||
// request header to the request's query string. This is most commonly used
|
||||
// with pre-signed requests preventing headers from being added to the
|
||||
// request's query string.
|
||||
DisableHeaderHoisting bool
|
||||
|
||||
// Disables the automatic escaping of the URI path of the request for the
|
||||
// siganture's canonical string's path. For services that do not need additional
|
||||
// escaping then use this to disable the signer escaping the path.
|
||||
//
|
||||
// S3 is an example of a service that does not need additional escaping.
|
||||
//
|
||||
// http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
DisableURIPathEscaping bool
|
||||
|
||||
// The logger to send log messages to.
|
||||
Logger logging.Logger
|
||||
|
||||
// Enable logging of signed requests.
|
||||
// This will enable logging of the canonical request, the string to sign, and for presigning the subsequent
|
||||
// presigned URL.
|
||||
LogSigning bool
|
||||
}
|
||||
|
||||
// Signer applies AWS v4 signing to given request. Use this to sign requests
|
||||
// that need to be signed with AWS V4 Signatures.
|
||||
type Signer struct {
|
||||
options SignerOptions
|
||||
keyDerivator keyDerivator
|
||||
}
|
||||
|
||||
// NewSigner returns a new SigV4 Signer
|
||||
func NewSigner(optFns ...func(signer *SignerOptions)) *Signer {
|
||||
options := SignerOptions{}
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
return &Signer{options: options, keyDerivator: v4Internal.NewSigningKeyDeriver()}
|
||||
}
|
||||
|
||||
type httpSigner struct {
|
||||
Request *http.Request
|
||||
ServiceName string
|
||||
Region string
|
||||
Time v4Internal.SigningTime
|
||||
Credentials aws.Credentials
|
||||
KeyDerivator keyDerivator
|
||||
IsPreSign bool
|
||||
|
||||
PayloadHash string
|
||||
|
||||
DisableHeaderHoisting bool
|
||||
DisableURIPathEscaping bool
|
||||
}
|
||||
|
||||
func (s *httpSigner) Build() (signedRequest, error) {
|
||||
req := s.Request
|
||||
|
||||
query := req.URL.Query()
|
||||
headers := req.Header
|
||||
|
||||
s.setRequiredSigningFields(headers, query)
|
||||
|
||||
// Sort Each Query Key's Values
|
||||
for key := range query {
|
||||
sort.Strings(query[key])
|
||||
}
|
||||
|
||||
v4Internal.SanitizeHostForHeader(req)
|
||||
|
||||
credentialScope := s.buildCredentialScope()
|
||||
credentialStr := s.Credentials.AccessKeyID + "/" + credentialScope
|
||||
if s.IsPreSign {
|
||||
query.Set(v4Internal.AmzCredentialKey, credentialStr)
|
||||
}
|
||||
|
||||
unsignedHeaders := headers
|
||||
if s.IsPreSign && !s.DisableHeaderHoisting {
|
||||
var urlValues url.Values
|
||||
urlValues, unsignedHeaders = buildQuery(v4Internal.AllowedQueryHoisting, headers)
|
||||
for k := range urlValues {
|
||||
query[k] = urlValues[k]
|
||||
}
|
||||
}
|
||||
|
||||
host := req.URL.Host
|
||||
if len(req.Host) > 0 {
|
||||
host = req.Host
|
||||
}
|
||||
|
||||
signedHeaders, signedHeadersStr, canonicalHeaderStr := s.buildCanonicalHeaders(host, v4Internal.IgnoredHeaders, unsignedHeaders, s.Request.ContentLength)
|
||||
|
||||
if s.IsPreSign {
|
||||
query.Set(v4Internal.AmzSignedHeadersKey, signedHeadersStr)
|
||||
}
|
||||
|
||||
var rawQuery strings.Builder
|
||||
rawQuery.WriteString(strings.Replace(query.Encode(), "+", "%20", -1))
|
||||
|
||||
canonicalURI := v4Internal.GetURIPath(req.URL)
|
||||
if !s.DisableURIPathEscaping {
|
||||
canonicalURI = httpbinding.EscapePath(canonicalURI, false)
|
||||
}
|
||||
|
||||
canonicalString := s.buildCanonicalString(
|
||||
req.Method,
|
||||
canonicalURI,
|
||||
rawQuery.String(),
|
||||
signedHeadersStr,
|
||||
canonicalHeaderStr,
|
||||
)
|
||||
|
||||
strToSign := s.buildStringToSign(credentialScope, canonicalString)
|
||||
signingSignature, err := s.buildSignature(strToSign)
|
||||
if err != nil {
|
||||
return signedRequest{}, err
|
||||
}
|
||||
|
||||
if s.IsPreSign {
|
||||
rawQuery.WriteString("&X-Amz-Signature=")
|
||||
rawQuery.WriteString(signingSignature)
|
||||
} else {
|
||||
headers[authorizationHeader] = append(headers[authorizationHeader][:0], buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature))
|
||||
}
|
||||
|
||||
req.URL.RawQuery = rawQuery.String()
|
||||
|
||||
return signedRequest{
|
||||
Request: req,
|
||||
SignedHeaders: signedHeaders,
|
||||
CanonicalString: canonicalString,
|
||||
StringToSign: strToSign,
|
||||
PreSigned: s.IsPreSign,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildAuthorizationHeader(credentialStr, signedHeadersStr, signingSignature string) string {
|
||||
const credential = "Credential="
|
||||
const signedHeaders = "SignedHeaders="
|
||||
const signature = "Signature="
|
||||
const commaSpace = ", "
|
||||
|
||||
var parts strings.Builder
|
||||
parts.Grow(len(signingAlgorithm) + 1 +
|
||||
len(credential) + len(credentialStr) + 2 +
|
||||
len(signedHeaders) + len(signedHeadersStr) + 2 +
|
||||
len(signature) + len(signingSignature),
|
||||
)
|
||||
parts.WriteString(signingAlgorithm)
|
||||
parts.WriteRune(' ')
|
||||
parts.WriteString(credential)
|
||||
parts.WriteString(credentialStr)
|
||||
parts.WriteString(commaSpace)
|
||||
parts.WriteString(signedHeaders)
|
||||
parts.WriteString(signedHeadersStr)
|
||||
parts.WriteString(commaSpace)
|
||||
parts.WriteString(signature)
|
||||
parts.WriteString(signingSignature)
|
||||
return parts.String()
|
||||
}
|
||||
|
||||
// SignHTTP signs AWS v4 requests with the provided payload hash, service name, region the
|
||||
// request is made to, and time the request is signed at. The signTime allows
|
||||
// you to specify that a request is signed for the future, and cannot be
|
||||
// used until then.
|
||||
//
|
||||
// The payloadHash is the hex encoded SHA-256 hash of the request payload, and
|
||||
// must be provided. Even if the request has no payload (aka body). If the
|
||||
// request has no payload you should use the hex encoded SHA-256 of an empty
|
||||
// string as the payloadHash value.
|
||||
//
|
||||
// "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
//
|
||||
// Some services such as Amazon S3 accept alternative values for the payload
|
||||
// hash, such as "UNSIGNED-PAYLOAD" for requests where the body will not be
|
||||
// included in the request signature.
|
||||
//
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
||||
//
|
||||
// Sign differs from Presign in that it will sign the request using HTTP
|
||||
// header values. This type of signing is intended for http.Request values that
|
||||
// will not be shared, or are shared in a way the header values on the request
|
||||
// will not be lost.
|
||||
//
|
||||
// The passed in request will be modified in place.
|
||||
func (s Signer) SignHTTP(ctx context.Context, credentials aws.Credentials, r *http.Request, payloadHash string, service string, region string, signingTime time.Time, optFns ...func(options *SignerOptions)) error {
|
||||
options := s.options
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
signer := &httpSigner{
|
||||
Request: r,
|
||||
PayloadHash: payloadHash,
|
||||
ServiceName: service,
|
||||
Region: region,
|
||||
Credentials: credentials,
|
||||
Time: v4Internal.NewSigningTime(signingTime.UTC()),
|
||||
DisableHeaderHoisting: options.DisableHeaderHoisting,
|
||||
DisableURIPathEscaping: options.DisableURIPathEscaping,
|
||||
KeyDerivator: s.keyDerivator,
|
||||
}
|
||||
|
||||
signedRequest, err := signer.Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logSigningInfo(ctx, options, &signedRequest, false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PresignHTTP signs AWS v4 requests with the payload hash, service name, region
|
||||
// the request is made to, and time the request is signed at. The signTime
|
||||
// allows you to specify that a request is signed for the future, and cannot
|
||||
// be used until then.
|
||||
//
|
||||
// Returns the signed URL and the map of HTTP headers that were included in the
|
||||
// signature or an error if signing the request failed. For presigned requests
|
||||
// these headers and their values must be included on the HTTP request when it
|
||||
// is made. This is helpful to know what header values need to be shared with
|
||||
// the party the presigned request will be distributed to.
|
||||
//
|
||||
// The payloadHash is the hex encoded SHA-256 hash of the request payload, and
|
||||
// must be provided. Even if the request has no payload (aka body). If the
|
||||
// request has no payload you should use the hex encoded SHA-256 of an empty
|
||||
// string as the payloadHash value.
|
||||
//
|
||||
// "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
//
|
||||
// Some services such as Amazon S3 accept alternative values for the payload
|
||||
// hash, such as "UNSIGNED-PAYLOAD" for requests where the body will not be
|
||||
// included in the request signature.
|
||||
//
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
|
||||
//
|
||||
// PresignHTTP differs from SignHTTP in that it will sign the request using
|
||||
// query string instead of header values. This allows you to share the
|
||||
// Presigned Request's URL with third parties, or distribute it throughout your
|
||||
// system with minimal dependencies.
|
||||
//
|
||||
// PresignHTTP will not set the expires time of the presigned request
|
||||
// automatically. To specify the expire duration for a request add the
|
||||
// "X-Amz-Expires" query parameter on the request with the value as the
|
||||
// duration in seconds the presigned URL should be considered valid for. This
|
||||
// parameter is not used by all AWS services, and is most notable used by
|
||||
// Amazon S3 APIs.
|
||||
//
|
||||
// expires := 20 * time.Minute
|
||||
// query := req.URL.Query()
|
||||
// query.Set("X-Amz-Expires", strconv.FormatInt(int64(expires/time.Second), 10)
|
||||
// req.URL.RawQuery = query.Encode()
|
||||
//
|
||||
// This method does not modify the provided request.
|
||||
func (s *Signer) PresignHTTP(
|
||||
ctx context.Context, credentials aws.Credentials, r *http.Request,
|
||||
payloadHash string, service string, region string, signingTime time.Time,
|
||||
optFns ...func(*SignerOptions),
|
||||
) (signedURI string, signedHeaders http.Header, err error) {
|
||||
options := s.options
|
||||
|
||||
for _, fn := range optFns {
|
||||
fn(&options)
|
||||
}
|
||||
|
||||
signer := &httpSigner{
|
||||
Request: r.Clone(r.Context()),
|
||||
PayloadHash: payloadHash,
|
||||
ServiceName: service,
|
||||
Region: region,
|
||||
Credentials: credentials,
|
||||
Time: v4Internal.NewSigningTime(signingTime.UTC()),
|
||||
IsPreSign: true,
|
||||
DisableHeaderHoisting: options.DisableHeaderHoisting,
|
||||
DisableURIPathEscaping: options.DisableURIPathEscaping,
|
||||
KeyDerivator: s.keyDerivator,
|
||||
}
|
||||
|
||||
signedRequest, err := signer.Build()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
logSigningInfo(ctx, options, &signedRequest, true)
|
||||
|
||||
signedHeaders = make(http.Header)
|
||||
|
||||
// For the signed headers we canonicalize the header keys in the returned map.
|
||||
// This avoids situations where can standard library double headers like host header. For example the standard
|
||||
// library will set the Host header, even if it is present in lower-case form.
|
||||
for k, v := range signedRequest.SignedHeaders {
|
||||
key := textproto.CanonicalMIMEHeaderKey(k)
|
||||
signedHeaders[key] = append(signedHeaders[key], v...)
|
||||
}
|
||||
|
||||
return signedRequest.Request.URL.String(), signedHeaders, nil
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCredentialScope() string {
|
||||
return v4Internal.BuildCredentialScope(s.Time, s.Region, s.ServiceName)
|
||||
}
|
||||
|
||||
func buildQuery(r v4Internal.Rule, header http.Header) (url.Values, http.Header) {
|
||||
query := url.Values{}
|
||||
unsignedHeaders := http.Header{}
|
||||
for k, h := range header {
|
||||
if r.IsValid(k) {
|
||||
query[k] = h
|
||||
} else {
|
||||
unsignedHeaders[k] = h
|
||||
}
|
||||
}
|
||||
|
||||
return query, unsignedHeaders
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, header http.Header, length int64) (signed http.Header, signedHeaders, canonicalHeadersStr string) {
|
||||
signed = make(http.Header)
|
||||
|
||||
var headers []string
|
||||
const hostHeader = "host"
|
||||
headers = append(headers, hostHeader)
|
||||
signed[hostHeader] = append(signed[hostHeader], host)
|
||||
|
||||
if length > 0 {
|
||||
const contentLengthHeader = "content-length"
|
||||
headers = append(headers, contentLengthHeader)
|
||||
signed[contentLengthHeader] = append(signed[contentLengthHeader], strconv.FormatInt(length, 10))
|
||||
}
|
||||
|
||||
for k, v := range header {
|
||||
if !rule.IsValid(k) {
|
||||
continue // ignored header
|
||||
}
|
||||
|
||||
lowerCaseKey := strings.ToLower(k)
|
||||
if _, ok := signed[lowerCaseKey]; ok {
|
||||
// include additional values
|
||||
signed[lowerCaseKey] = append(signed[lowerCaseKey], v...)
|
||||
continue
|
||||
}
|
||||
|
||||
headers = append(headers, lowerCaseKey)
|
||||
signed[lowerCaseKey] = v
|
||||
}
|
||||
sort.Strings(headers)
|
||||
|
||||
signedHeaders = strings.Join(headers, ";")
|
||||
|
||||
var canonicalHeaders strings.Builder
|
||||
n := len(headers)
|
||||
const colon = ':'
|
||||
for i := 0; i < n; i++ {
|
||||
if headers[i] == hostHeader {
|
||||
canonicalHeaders.WriteString(hostHeader)
|
||||
canonicalHeaders.WriteRune(colon)
|
||||
canonicalHeaders.WriteString(v4Internal.StripExcessSpaces(host))
|
||||
} else {
|
||||
canonicalHeaders.WriteString(headers[i])
|
||||
canonicalHeaders.WriteRune(colon)
|
||||
// Trim out leading, trailing, and dedup inner spaces from signed header values.
|
||||
values := signed[headers[i]]
|
||||
for j, v := range values {
|
||||
cleanedValue := strings.TrimSpace(v4Internal.StripExcessSpaces(v))
|
||||
canonicalHeaders.WriteString(cleanedValue)
|
||||
if j < len(values)-1 {
|
||||
canonicalHeaders.WriteRune(',')
|
||||
}
|
||||
}
|
||||
}
|
||||
canonicalHeaders.WriteRune('\n')
|
||||
}
|
||||
canonicalHeadersStr = canonicalHeaders.String()
|
||||
|
||||
return signed, signedHeaders, canonicalHeadersStr
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildCanonicalString(method, uri, query, signedHeaders, canonicalHeaders string) string {
|
||||
return strings.Join([]string{
|
||||
method,
|
||||
uri,
|
||||
query,
|
||||
canonicalHeaders,
|
||||
signedHeaders,
|
||||
s.PayloadHash,
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildStringToSign(credentialScope, canonicalRequestString string) string {
|
||||
return strings.Join([]string{
|
||||
signingAlgorithm,
|
||||
s.Time.TimeFormat(),
|
||||
credentialScope,
|
||||
hex.EncodeToString(makeHash(sha256.New(), []byte(canonicalRequestString))),
|
||||
}, "\n")
|
||||
}
|
||||
|
||||
func makeHash(hash hash.Hash, b []byte) []byte {
|
||||
hash.Reset()
|
||||
hash.Write(b)
|
||||
return hash.Sum(nil)
|
||||
}
|
||||
|
||||
func (s *httpSigner) buildSignature(strToSign string) (string, error) {
|
||||
key := s.KeyDerivator.DeriveKey(s.Credentials, s.ServiceName, s.Region, s.Time)
|
||||
return hex.EncodeToString(v4Internal.HMACSHA256(key, []byte(strToSign))), nil
|
||||
}
|
||||
|
||||
func (s *httpSigner) setRequiredSigningFields(headers http.Header, query url.Values) {
|
||||
amzDate := s.Time.TimeFormat()
|
||||
|
||||
if s.IsPreSign {
|
||||
query.Set(v4Internal.AmzAlgorithmKey, signingAlgorithm)
|
||||
if sessionToken := s.Credentials.SessionToken; len(sessionToken) > 0 {
|
||||
query.Set("X-Amz-Security-Token", sessionToken)
|
||||
}
|
||||
|
||||
query.Set(v4Internal.AmzDateKey, amzDate)
|
||||
return
|
||||
}
|
||||
|
||||
headers[v4Internal.AmzDateKey] = append(headers[v4Internal.AmzDateKey][:0], amzDate)
|
||||
|
||||
if len(s.Credentials.SessionToken) > 0 {
|
||||
headers[v4Internal.AmzSecurityTokenKey] = append(headers[v4Internal.AmzSecurityTokenKey][:0], s.Credentials.SessionToken)
|
||||
}
|
||||
}
|
||||
|
||||
func logSigningInfo(ctx context.Context, options SignerOptions, request *signedRequest, isPresign bool) {
|
||||
if !options.LogSigning {
|
||||
return
|
||||
}
|
||||
signedURLMsg := ""
|
||||
if isPresign {
|
||||
signedURLMsg = fmt.Sprintf(logSignedURLMsg, request.Request.URL.String())
|
||||
}
|
||||
logger := logging.WithContext(ctx, options.Logger)
|
||||
logger.Logf(logging.Debug, logSignInfoMsg, request.CanonicalString, request.StringToSign, signedURLMsg)
|
||||
}
|
||||
|
||||
type signedRequest struct {
|
||||
Request *http.Request
|
||||
SignedHeaders http.Header
|
||||
CanonicalString string
|
||||
StringToSign string
|
||||
PreSigned bool
|
||||
}
|
||||
|
||||
const logSignInfoMsg = `Request Signature:
|
||||
---[ CANONICAL STRING ]-----------------------------
|
||||
%s
|
||||
---[ STRING TO SIGN ]--------------------------------
|
||||
%s%s
|
||||
-----------------------------------------------------`
|
||||
const logSignedURLMsg = `
|
||||
---[ SIGNED URL ]------------------------------------
|
||||
%s`
|
|
@ -0,0 +1,297 @@
|
|||
// Code generated by aws/generate.go DO NOT EDIT.
|
||||
|
||||
package aws
|
||||
|
||||
import (
|
||||
"github.com/aws/smithy-go/ptr"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bool returns a pointer value for the bool value passed in.
|
||||
func Bool(v bool) *bool {
|
||||
return ptr.Bool(v)
|
||||
}
|
||||
|
||||
// BoolSlice returns a slice of bool pointers from the values
|
||||
// passed in.
|
||||
func BoolSlice(vs []bool) []*bool {
|
||||
return ptr.BoolSlice(vs)
|
||||
}
|
||||
|
||||
// BoolMap returns a map of bool pointers from the values
|
||||
// passed in.
|
||||
func BoolMap(vs map[string]bool) map[string]*bool {
|
||||
return ptr.BoolMap(vs)
|
||||
}
|
||||
|
||||
// Byte returns a pointer value for the byte value passed in.
|
||||
func Byte(v byte) *byte {
|
||||
return ptr.Byte(v)
|
||||
}
|
||||
|
||||
// ByteSlice returns a slice of byte pointers from the values
|
||||
// passed in.
|
||||
func ByteSlice(vs []byte) []*byte {
|
||||
return ptr.ByteSlice(vs)
|
||||
}
|
||||
|
||||
// ByteMap returns a map of byte pointers from the values
|
||||
// passed in.
|
||||
func ByteMap(vs map[string]byte) map[string]*byte {
|
||||
return ptr.ByteMap(vs)
|
||||
}
|
||||
|
||||
// String returns a pointer value for the string value passed in.
|
||||
func String(v string) *string {
|
||||
return ptr.String(v)
|
||||
}
|
||||
|
||||
// StringSlice returns a slice of string pointers from the values
|
||||
// passed in.
|
||||
func StringSlice(vs []string) []*string {
|
||||
return ptr.StringSlice(vs)
|
||||
}
|
||||
|
||||
// StringMap returns a map of string pointers from the values
|
||||
// passed in.
|
||||
func StringMap(vs map[string]string) map[string]*string {
|
||||
return ptr.StringMap(vs)
|
||||
}
|
||||
|
||||
// Int returns a pointer value for the int value passed in.
|
||||
func Int(v int) *int {
|
||||
return ptr.Int(v)
|
||||
}
|
||||
|
||||
// IntSlice returns a slice of int pointers from the values
|
||||
// passed in.
|
||||
func IntSlice(vs []int) []*int {
|
||||
return ptr.IntSlice(vs)
|
||||
}
|
||||
|
||||
// IntMap returns a map of int pointers from the values
|
||||
// passed in.
|
||||
func IntMap(vs map[string]int) map[string]*int {
|
||||
return ptr.IntMap(vs)
|
||||
}
|
||||
|
||||
// Int8 returns a pointer value for the int8 value passed in.
|
||||
func Int8(v int8) *int8 {
|
||||
return ptr.Int8(v)
|
||||
}
|
||||
|
||||
// Int8Slice returns a slice of int8 pointers from the values
|
||||
// passed in.
|
||||
func Int8Slice(vs []int8) []*int8 {
|
||||
return ptr.Int8Slice(vs)
|
||||
}
|
||||
|
||||
// Int8Map returns a map of int8 pointers from the values
|
||||
// passed in.
|
||||
func Int8Map(vs map[string]int8) map[string]*int8 {
|
||||
return ptr.Int8Map(vs)
|
||||
}
|
||||
|
||||
// Int16 returns a pointer value for the int16 value passed in.
|
||||
func Int16(v int16) *int16 {
|
||||
return ptr.Int16(v)
|
||||
}
|
||||
|
||||
// Int16Slice returns a slice of int16 pointers from the values
|
||||
// passed in.
|
||||
func Int16Slice(vs []int16) []*int16 {
|
||||
return ptr.Int16Slice(vs)
|
||||
}
|
||||
|
||||
// Int16Map returns a map of int16 pointers from the values
|
||||
// passed in.
|
||||
func Int16Map(vs map[string]int16) map[string]*int16 {
|
||||
return ptr.Int16Map(vs)
|
||||
}
|
||||
|
||||
// Int32 returns a pointer value for the int32 value passed in.
|
||||
func Int32(v int32) *int32 {
|
||||
return ptr.Int32(v)
|
||||
}
|
||||
|
||||
// Int32Slice returns a slice of int32 pointers from the values
|
||||
// passed in.
|
||||
func Int32Slice(vs []int32) []*int32 {
|
||||
return ptr.Int32Slice(vs)
|
||||
}
|
||||
|
||||
// Int32Map returns a map of int32 pointers from the values
|
||||
// passed in.
|
||||
func Int32Map(vs map[string]int32) map[string]*int32 {
|
||||
return ptr.Int32Map(vs)
|
||||
}
|
||||
|
||||
// Int64 returns a pointer value for the int64 value passed in.
|
||||
func Int64(v int64) *int64 {
|
||||
return ptr.Int64(v)
|
||||
}
|
||||
|
||||
// Int64Slice returns a slice of int64 pointers from the values
|
||||
// passed in.
|
||||
func Int64Slice(vs []int64) []*int64 {
|
||||
return ptr.Int64Slice(vs)
|
||||
}
|
||||
|
||||
// Int64Map returns a map of int64 pointers from the values
|
||||
// passed in.
|
||||
func Int64Map(vs map[string]int64) map[string]*int64 {
|
||||
return ptr.Int64Map(vs)
|
||||
}
|
||||
|
||||
// Uint returns a pointer value for the uint value passed in.
|
||||
func Uint(v uint) *uint {
|
||||
return ptr.Uint(v)
|
||||
}
|
||||
|
||||
// UintSlice returns a slice of uint pointers from the values
|
||||
// passed in.
|
||||
func UintSlice(vs []uint) []*uint {
|
||||
return ptr.UintSlice(vs)
|
||||
}
|
||||
|
||||
// UintMap returns a map of uint pointers from the values
|
||||
// passed in.
|
||||
func UintMap(vs map[string]uint) map[string]*uint {
|
||||
return ptr.UintMap(vs)
|
||||
}
|
||||
|
||||
// Uint8 returns a pointer value for the uint8 value passed in.
|
||||
func Uint8(v uint8) *uint8 {
|
||||
return ptr.Uint8(v)
|
||||
}
|
||||
|
||||
// Uint8Slice returns a slice of uint8 pointers from the values
|
||||
// passed in.
|
||||
func Uint8Slice(vs []uint8) []*uint8 {
|
||||
return ptr.Uint8Slice(vs)
|
||||
}
|
||||
|
||||
// Uint8Map returns a map of uint8 pointers from the values
|
||||
// passed in.
|
||||
func Uint8Map(vs map[string]uint8) map[string]*uint8 {
|
||||
return ptr.Uint8Map(vs)
|
||||
}
|
||||
|
||||
// Uint16 returns a pointer value for the uint16 value passed in.
|
||||
func Uint16(v uint16) *uint16 {
|
||||
return ptr.Uint16(v)
|
||||
}
|
||||
|
||||
// Uint16Slice returns a slice of uint16 pointers from the values
|
||||
// passed in.
|
||||
func Uint16Slice(vs []uint16) []*uint16 {
|
||||
return ptr.Uint16Slice(vs)
|
||||
}
|
||||
|
||||
// Uint16Map returns a map of uint16 pointers from the values
|
||||
// passed in.
|
||||
func Uint16Map(vs map[string]uint16) map[string]*uint16 {
|
||||
return ptr.Uint16Map(vs)
|
||||
}
|
||||
|
||||
// Uint32 returns a pointer value for the uint32 value passed in.
|
||||
func Uint32(v uint32) *uint32 {
|
||||
return ptr.Uint32(v)
|
||||
}
|
||||
|
||||
// Uint32Slice returns a slice of uint32 pointers from the values
|
||||
// passed in.
|
||||
func Uint32Slice(vs []uint32) []*uint32 {
|
||||
return ptr.Uint32Slice(vs)
|
||||
}
|
||||
|
||||
// Uint32Map returns a map of uint32 pointers from the values
|
||||
// passed in.
|
||||
func Uint32Map(vs map[string]uint32) map[string]*uint32 {
|
||||
return ptr.Uint32Map(vs)
|
||||
}
|
||||
|
||||
// Uint64 returns a pointer value for the uint64 value passed in.
|
||||
func Uint64(v uint64) *uint64 {
|
||||
return ptr.Uint64(v)
|
||||
}
|
||||
|
||||
// Uint64Slice returns a slice of uint64 pointers from the values
|
||||
// passed in.
|
||||
func Uint64Slice(vs []uint64) []*uint64 {
|
||||
return ptr.Uint64Slice(vs)
|
||||
}
|
||||
|
||||
// Uint64Map returns a map of uint64 pointers from the values
|
||||
// passed in.
|
||||
func Uint64Map(vs map[string]uint64) map[string]*uint64 {
|
||||
return ptr.Uint64Map(vs)
|
||||
}
|
||||
|
||||
// Float32 returns a pointer value for the float32 value passed in.
|
||||
func Float32(v float32) *float32 {
|
||||
return ptr.Float32(v)
|
||||
}
|
||||
|
||||
// Float32Slice returns a slice of float32 pointers from the values
|
||||
// passed in.
|
||||
func Float32Slice(vs []float32) []*float32 {
|
||||
return ptr.Float32Slice(vs)
|
||||
}
|
||||
|
||||
// Float32Map returns a map of float32 pointers from the values
|
||||
// passed in.
|
||||
func Float32Map(vs map[string]float32) map[string]*float32 {
|
||||
return ptr.Float32Map(vs)
|
||||
}
|
||||
|
||||
// Float64 returns a pointer value for the float64 value passed in.
|
||||
func Float64(v float64) *float64 {
|
||||
return ptr.Float64(v)
|
||||
}
|
||||
|
||||
// Float64Slice returns a slice of float64 pointers from the values
|
||||
// passed in.
|
||||
func Float64Slice(vs []float64) []*float64 {
|
||||
return ptr.Float64Slice(vs)
|
||||
}
|
||||
|
||||
// Float64Map returns a map of float64 pointers from the values
|
||||
// passed in.
|
||||
func Float64Map(vs map[string]float64) map[string]*float64 {
|
||||
return ptr.Float64Map(vs)
|
||||
}
|
||||
|
||||
// Time returns a pointer value for the time.Time value passed in.
|
||||
func Time(v time.Time) *time.Time {
|
||||
return ptr.Time(v)
|
||||
}
|
||||
|
||||
// TimeSlice returns a slice of time.Time pointers from the values
|
||||
// passed in.
|
||||
func TimeSlice(vs []time.Time) []*time.Time {
|
||||
return ptr.TimeSlice(vs)
|
||||
}
|
||||
|
||||
// TimeMap returns a map of time.Time pointers from the values
|
||||
// passed in.
|
||||
func TimeMap(vs map[string]time.Time) map[string]*time.Time {
|
||||
return ptr.TimeMap(vs)
|
||||
}
|
||||
|
||||
// Duration returns a pointer value for the time.Duration value passed in.
|
||||
func Duration(v time.Duration) *time.Duration {
|
||||
return ptr.Duration(v)
|
||||
}
|
||||
|
||||
// DurationSlice returns a slice of time.Duration pointers from the values
|
||||
// passed in.
|
||||
func DurationSlice(vs []time.Duration) []*time.Duration {
|
||||
return ptr.DurationSlice(vs)
|
||||
}
|
||||
|
||||
// DurationMap returns a map of time.Duration pointers from the values
|
||||
// passed in.
|
||||
func DurationMap(vs map[string]time.Duration) map[string]*time.Duration {
|
||||
return ptr.DurationMap(vs)
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"net"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Defaults for the HTTPTransportBuilder.
|
||||
var (
|
||||
// Default connection pool options
|
||||
DefaultHTTPTransportMaxIdleConns = 100
|
||||
DefaultHTTPTransportMaxIdleConnsPerHost = 10
|
||||
|
||||
// Default connection timeouts
|
||||
DefaultHTTPTransportIdleConnTimeout = 90 * time.Second
|
||||
DefaultHTTPTransportTLSHandleshakeTimeout = 10 * time.Second
|
||||
DefaultHTTPTransportExpectContinueTimeout = 1 * time.Second
|
||||
|
||||
// Default to TLS 1.2 for all HTTPS requests.
|
||||
DefaultHTTPTransportTLSMinVersion uint16 = tls.VersionTLS12
|
||||
)
|
||||
|
||||
// Timeouts for net.Dialer's network connection.
|
||||
var (
|
||||
DefaultDialConnectTimeout = 30 * time.Second
|
||||
DefaultDialKeepAliveTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// BuildableClient provides a HTTPClient implementation with options to
|
||||
// create copies of the HTTPClient when additional configuration is provided.
|
||||
//
|
||||
// The client's methods will not share the http.Transport value between copies
|
||||
// of the BuildableClient. Only exported member values of the Transport and
|
||||
// optional Dialer will be copied between copies of BuildableClient.
|
||||
type BuildableClient struct {
|
||||
transport *http.Transport
|
||||
dialer *net.Dialer
|
||||
|
||||
initOnce sync.Once
|
||||
|
||||
clientTimeout time.Duration
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewBuildableClient returns an initialized client for invoking HTTP
|
||||
// requests.
|
||||
func NewBuildableClient() *BuildableClient {
|
||||
return &BuildableClient{}
|
||||
}
|
||||
|
||||
// Do implements the HTTPClient interface's Do method to invoke a HTTP request,
|
||||
// and receive the response. Uses the BuildableClient's current
|
||||
// configuration to invoke the http.Request.
|
||||
//
|
||||
// If connection pooling is enabled (aka HTTP KeepAlive) the client will only
|
||||
// share pooled connections with its own instance. Copies of the
|
||||
// BuildableClient will have their own connection pools.
|
||||
//
|
||||
// Redirect (3xx) responses will not be followed, the HTTP response received
|
||||
// will returned instead.
|
||||
func (b *BuildableClient) Do(req *http.Request) (*http.Response, error) {
|
||||
b.initOnce.Do(b.build)
|
||||
|
||||
return b.client.Do(req)
|
||||
}
|
||||
|
||||
// Freeze returns a frozen aws.HTTPClient implementation that is no longer a BuildableClient.
|
||||
// Use this to prevent the SDK from applying DefaultMode configuration values to a buildable client.
|
||||
func (b *BuildableClient) Freeze() aws.HTTPClient {
|
||||
cpy := b.clone()
|
||||
cpy.build()
|
||||
return cpy.client
|
||||
}
|
||||
|
||||
func (b *BuildableClient) build() {
|
||||
b.client = wrapWithLimitedRedirect(&http.Client{
|
||||
Timeout: b.clientTimeout,
|
||||
Transport: b.GetTransport(),
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BuildableClient) clone() *BuildableClient {
|
||||
cpy := NewBuildableClient()
|
||||
cpy.transport = b.GetTransport()
|
||||
cpy.dialer = b.GetDialer()
|
||||
cpy.clientTimeout = b.clientTimeout
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// WithTransportOptions copies the BuildableClient and returns it with the
|
||||
// http.Transport options applied.
|
||||
//
|
||||
// If a non (*http.Transport) was set as the round tripper, the round tripper
|
||||
// will be replaced with a default Transport value before invoking the option
|
||||
// functions.
|
||||
func (b *BuildableClient) WithTransportOptions(opts ...func(*http.Transport)) *BuildableClient {
|
||||
cpy := b.clone()
|
||||
|
||||
tr := cpy.GetTransport()
|
||||
for _, opt := range opts {
|
||||
opt(tr)
|
||||
}
|
||||
cpy.transport = tr
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// WithDialerOptions copies the BuildableClient and returns it with the
|
||||
// net.Dialer options applied. Will set the client's http.Transport DialContext
|
||||
// member.
|
||||
func (b *BuildableClient) WithDialerOptions(opts ...func(*net.Dialer)) *BuildableClient {
|
||||
cpy := b.clone()
|
||||
|
||||
dialer := cpy.GetDialer()
|
||||
for _, opt := range opts {
|
||||
opt(dialer)
|
||||
}
|
||||
cpy.dialer = dialer
|
||||
|
||||
tr := cpy.GetTransport()
|
||||
tr.DialContext = cpy.dialer.DialContext
|
||||
cpy.transport = tr
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
// WithTimeout Sets the timeout used by the client for all requests.
|
||||
func (b *BuildableClient) WithTimeout(timeout time.Duration) *BuildableClient {
|
||||
cpy := b.clone()
|
||||
cpy.clientTimeout = timeout
|
||||
return cpy
|
||||
}
|
||||
|
||||
// GetTransport returns a copy of the client's HTTP Transport.
|
||||
func (b *BuildableClient) GetTransport() *http.Transport {
|
||||
var tr *http.Transport
|
||||
if b.transport != nil {
|
||||
tr = b.transport.Clone()
|
||||
} else {
|
||||
tr = defaultHTTPTransport()
|
||||
}
|
||||
|
||||
return tr
|
||||
}
|
||||
|
||||
// GetDialer returns a copy of the client's network dialer.
|
||||
func (b *BuildableClient) GetDialer() *net.Dialer {
|
||||
var dialer *net.Dialer
|
||||
if b.dialer != nil {
|
||||
dialer = shallowCopyStruct(b.dialer).(*net.Dialer)
|
||||
} else {
|
||||
dialer = defaultDialer()
|
||||
}
|
||||
|
||||
return dialer
|
||||
}
|
||||
|
||||
// GetTimeout returns a copy of the client's timeout to cancel requests with.
|
||||
func (b *BuildableClient) GetTimeout() time.Duration {
|
||||
return b.clientTimeout
|
||||
}
|
||||
|
||||
func defaultDialer() *net.Dialer {
|
||||
return &net.Dialer{
|
||||
Timeout: DefaultDialConnectTimeout,
|
||||
KeepAlive: DefaultDialKeepAliveTimeout,
|
||||
DualStack: true,
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHTTPTransport() *http.Transport {
|
||||
dialer := defaultDialer()
|
||||
|
||||
tr := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: dialer.DialContext,
|
||||
TLSHandshakeTimeout: DefaultHTTPTransportTLSHandleshakeTimeout,
|
||||
MaxIdleConns: DefaultHTTPTransportMaxIdleConns,
|
||||
MaxIdleConnsPerHost: DefaultHTTPTransportMaxIdleConnsPerHost,
|
||||
IdleConnTimeout: DefaultHTTPTransportIdleConnTimeout,
|
||||
ExpectContinueTimeout: DefaultHTTPTransportExpectContinueTimeout,
|
||||
ForceAttemptHTTP2: true,
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: DefaultHTTPTransportTLSMinVersion,
|
||||
},
|
||||
}
|
||||
|
||||
return tr
|
||||
}
|
||||
|
||||
// shallowCopyStruct creates a shallow copy of the passed in source struct, and
|
||||
// returns that copy of the same struct type.
|
||||
func shallowCopyStruct(src interface{}) interface{} {
|
||||
srcVal := reflect.ValueOf(src)
|
||||
srcValType := srcVal.Type()
|
||||
|
||||
var returnAsPtr bool
|
||||
if srcValType.Kind() == reflect.Ptr {
|
||||
srcVal = srcVal.Elem()
|
||||
srcValType = srcValType.Elem()
|
||||
returnAsPtr = true
|
||||
}
|
||||
dstVal := reflect.New(srcValType).Elem()
|
||||
|
||||
for i := 0; i < srcValType.NumField(); i++ {
|
||||
ft := srcValType.Field(i)
|
||||
if len(ft.PkgPath) != 0 {
|
||||
// unexported fields have a PkgPath
|
||||
continue
|
||||
}
|
||||
|
||||
dstVal.Field(i).Set(srcVal.Field(i))
|
||||
}
|
||||
|
||||
if returnAsPtr {
|
||||
dstVal = dstVal.Addr()
|
||||
}
|
||||
|
||||
return dstVal.Interface()
|
||||
}
|
||||
|
||||
// wrapWithLimitedRedirect updates the Client's Transport and CheckRedirect to
|
||||
// not follow any redirect other than 307 and 308. No other redirect will be
|
||||
// followed.
|
||||
//
|
||||
// If the client does not have a Transport defined will use a new SDK default
|
||||
// http.Transport configuration.
|
||||
func wrapWithLimitedRedirect(c *http.Client) *http.Client {
|
||||
tr := c.Transport
|
||||
if tr == nil {
|
||||
tr = defaultHTTPTransport()
|
||||
}
|
||||
|
||||
cc := *c
|
||||
cc.CheckRedirect = limitedRedirect
|
||||
cc.Transport = suppressBadHTTPRedirectTransport{
|
||||
tr: tr,
|
||||
}
|
||||
|
||||
return &cc
|
||||
}
|
||||
|
||||
// limitedRedirect is a CheckRedirect that prevents the client from following
|
||||
// any non 307/308 HTTP status code redirects.
|
||||
//
|
||||
// The 307 and 308 redirects are allowed because the client must use the
|
||||
// original HTTP method for the redirected to location. Whereas 301 and 302
|
||||
// allow the client to switch to GET for the redirect.
|
||||
//
|
||||
// Suppresses all redirect requests with a URL of badHTTPRedirectLocation.
|
||||
func limitedRedirect(r *http.Request, via []*http.Request) error {
|
||||
// Request.Response, in CheckRedirect is the response that is triggering
|
||||
// the redirect.
|
||||
resp := r.Response
|
||||
if r.URL.String() == badHTTPRedirectLocation {
|
||||
resp.Header.Del(badHTTPRedirectLocation)
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
switch resp.StatusCode {
|
||||
case 307, 308:
|
||||
// Only allow 307 and 308 redirects as they preserve the method.
|
||||
return nil
|
||||
}
|
||||
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
// suppressBadHTTPRedirectTransport provides an http.RoundTripper
|
||||
// implementation that wraps another http.RoundTripper to prevent HTTP client
|
||||
// receiving 301 and 302 HTTP responses redirects without the required location
|
||||
// header.
|
||||
//
|
||||
// Clients using this utility must have a CheckRedirect, e.g. limitedRedirect,
|
||||
// that check for responses with having a URL of baseHTTPRedirectLocation, and
|
||||
// suppress the redirect.
|
||||
type suppressBadHTTPRedirectTransport struct {
|
||||
tr http.RoundTripper
|
||||
}
|
||||
|
||||
const badHTTPRedirectLocation = `https://amazonaws.com/badhttpredirectlocation`
|
||||
|
||||
// RoundTrip backfills a stub location when a 301/302 response is received
|
||||
// without a location. This stub location is used by limitedRedirect to prevent
|
||||
// the HTTP client from failing attempting to use follow a redirect without a
|
||||
// location value.
|
||||
func (t suppressBadHTTPRedirectTransport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
resp, err := t.tr.RoundTrip(r)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// S3 is the only known service to return 301 without location header.
|
||||
// The Go standard library HTTP client will return an opaque error if it
|
||||
// tries to follow a 301/302 response missing the location header.
|
||||
switch resp.StatusCode {
|
||||
case 301, 302:
|
||||
if v := resp.Header.Get("Location"); len(v) == 0 {
|
||||
resp.Header.Set("Location", badHTTPRedirectLocation)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, err
|
||||
}
|
42
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/content_type.go
generated
vendored
Normal file
42
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/content_type.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// removeContentTypeHeader is a build middleware that removes
|
||||
// content type header if content-length header is unset or
|
||||
// is set to zero,
|
||||
type removeContentTypeHeader struct {
|
||||
}
|
||||
|
||||
// ID the name of the middleware.
|
||||
func (m *removeContentTypeHeader) ID() string {
|
||||
return "RemoveContentTypeHeader"
|
||||
}
|
||||
|
||||
// HandleBuild adds or appends the constructed user agent to the request.
|
||||
func (m *removeContentTypeHeader) HandleBuild(ctx context.Context, in middleware.BuildInput, next middleware.BuildHandler) (
|
||||
out middleware.BuildOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
req, ok := in.Request.(*smithyhttp.Request)
|
||||
if !ok {
|
||||
return out, metadata, fmt.Errorf("unknown transport type %T", in)
|
||||
}
|
||||
|
||||
// remove contentTypeHeader when content-length is zero
|
||||
if req.ContentLength == 0 {
|
||||
req.Header.Del("content-type")
|
||||
}
|
||||
|
||||
return next.HandleBuild(ctx, in)
|
||||
}
|
||||
|
||||
// RemoveContentTypeHeader removes content-type header if
|
||||
// content length is unset or equal to zero.
|
||||
func RemoveContentTypeHeader(stack *middleware.Stack) error {
|
||||
return stack.Build.Add(&removeContentTypeHeader{}, middleware.After)
|
||||
}
|
33
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/response_error.go
generated
vendored
Normal file
33
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/response_error.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// ResponseError provides the HTTP centric error type wrapping the underlying error
|
||||
// with the HTTP response value and the deserialized RequestID.
|
||||
type ResponseError struct {
|
||||
*smithyhttp.ResponseError
|
||||
|
||||
// RequestID associated with response error
|
||||
RequestID string
|
||||
}
|
||||
|
||||
// ServiceRequestID returns the request id associated with Response Error
|
||||
func (e *ResponseError) ServiceRequestID() string { return e.RequestID }
|
||||
|
||||
// Error returns the formatted error
|
||||
func (e *ResponseError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"https response error StatusCode: %d, RequestID: %s, %v",
|
||||
e.Response.StatusCode, e.RequestID, e.Err)
|
||||
}
|
||||
|
||||
// As populates target and returns true if the type of target is a error type that
|
||||
// the ResponseError embeds, (e.g.AWS HTTP ResponseError)
|
||||
func (e *ResponseError) As(target interface{}) bool {
|
||||
return errors.As(e.ResponseError, target)
|
||||
}
|
54
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/response_error_middleware.go
generated
vendored
Normal file
54
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/response_error_middleware.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
// AddResponseErrorMiddleware adds response error wrapper middleware
|
||||
func AddResponseErrorMiddleware(stack *middleware.Stack) error {
|
||||
// add error wrapper middleware before request id retriever middleware so that it can wrap the error response
|
||||
// returned by operation deserializers
|
||||
return stack.Deserialize.Insert(&responseErrorWrapper{}, "RequestIDRetriever", middleware.Before)
|
||||
}
|
||||
|
||||
type responseErrorWrapper struct {
|
||||
}
|
||||
|
||||
// ID returns the middleware identifier
|
||||
func (m *responseErrorWrapper) ID() string {
|
||||
return "ResponseErrorWrapper"
|
||||
}
|
||||
|
||||
func (m *responseErrorWrapper) HandleDeserialize(ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
if err == nil {
|
||||
// Nothing to do when there is no error.
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
resp, ok := out.RawResponse.(*smithyhttp.Response)
|
||||
if !ok {
|
||||
// No raw response to wrap with.
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
// look for request id in metadata
|
||||
reqID, _ := awsmiddleware.GetRequestIDMetadata(metadata)
|
||||
|
||||
// Wrap the returned smithy error with the request id retrieved from the metadata
|
||||
err = &ResponseError{
|
||||
ResponseError: &smithyhttp.ResponseError{
|
||||
Response: resp,
|
||||
Err: err,
|
||||
},
|
||||
RequestID: reqID,
|
||||
}
|
||||
|
||||
return out, metadata, err
|
||||
}
|
104
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/timeout_read_closer.go
generated
vendored
Normal file
104
vendor/github.com/aws/aws-sdk-go-v2/aws/transport/http/timeout_read_closer.go
generated
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/aws/smithy-go"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
smithyhttp "github.com/aws/smithy-go/transport/http"
|
||||
)
|
||||
|
||||
type readResult struct {
|
||||
n int
|
||||
err error
|
||||
}
|
||||
|
||||
// ResponseTimeoutError is an error when the reads from the response are
|
||||
// delayed longer than the timeout the read was configured for.
|
||||
type ResponseTimeoutError struct {
|
||||
TimeoutDur time.Duration
|
||||
}
|
||||
|
||||
// Timeout returns that the error is was caused by a timeout, and can be
|
||||
// retried.
|
||||
func (*ResponseTimeoutError) Timeout() bool { return true }
|
||||
|
||||
func (e *ResponseTimeoutError) Error() string {
|
||||
return fmt.Sprintf("read on body reach timeout limit, %v", e.TimeoutDur)
|
||||
}
|
||||
|
||||
// timeoutReadCloser will handle body reads that take too long.
|
||||
// We will return a ErrReadTimeout error if a timeout occurs.
|
||||
type timeoutReadCloser struct {
|
||||
reader io.ReadCloser
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
// Read will spin off a goroutine to call the reader's Read method. We will
|
||||
// select on the timer's channel or the read's channel. Whoever completes first
|
||||
// will be returned.
|
||||
func (r *timeoutReadCloser) Read(b []byte) (int, error) {
|
||||
timer := time.NewTimer(r.duration)
|
||||
c := make(chan readResult, 1)
|
||||
|
||||
go func() {
|
||||
n, err := r.reader.Read(b)
|
||||
timer.Stop()
|
||||
c <- readResult{n: n, err: err}
|
||||
}()
|
||||
|
||||
select {
|
||||
case data := <-c:
|
||||
return data.n, data.err
|
||||
case <-timer.C:
|
||||
return 0, &ResponseTimeoutError{TimeoutDur: r.duration}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *timeoutReadCloser) Close() error {
|
||||
return r.reader.Close()
|
||||
}
|
||||
|
||||
// AddResponseReadTimeoutMiddleware adds a middleware to the stack that wraps the
|
||||
// response body so that a read that takes too long will return an error.
|
||||
func AddResponseReadTimeoutMiddleware(stack *middleware.Stack, duration time.Duration) error {
|
||||
return stack.Deserialize.Add(&readTimeout{duration: duration}, middleware.After)
|
||||
}
|
||||
|
||||
// readTimeout wraps the response body with a timeoutReadCloser
|
||||
type readTimeout struct {
|
||||
duration time.Duration
|
||||
}
|
||||
|
||||
// ID returns the id of the middleware
|
||||
func (*readTimeout) ID() string {
|
||||
return "ReadResponseTimeout"
|
||||
}
|
||||
|
||||
// HandleDeserialize implements the DeserializeMiddleware interface
|
||||
func (m *readTimeout) HandleDeserialize(
|
||||
ctx context.Context, in middleware.DeserializeInput, next middleware.DeserializeHandler,
|
||||
) (
|
||||
out middleware.DeserializeOutput, metadata middleware.Metadata, err error,
|
||||
) {
|
||||
out, metadata, err = next.HandleDeserialize(ctx, in)
|
||||
if err != nil {
|
||||
return out, metadata, err
|
||||
}
|
||||
|
||||
response, ok := out.RawResponse.(*smithyhttp.Response)
|
||||
if !ok {
|
||||
return out, metadata, &smithy.DeserializationError{Err: fmt.Errorf("unknown transport type %T", out.RawResponse)}
|
||||
}
|
||||
|
||||
response.Body = &timeoutReadCloser{
|
||||
reader: response.Body,
|
||||
duration: m.duration,
|
||||
}
|
||||
out.RawResponse = response
|
||||
|
||||
return out, metadata, err
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package aws
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Ternary is an enum allowing an unknown or none state in addition to a bool's
|
||||
// true and false.
|
||||
type Ternary int
|
||||
|
||||
func (t Ternary) String() string {
|
||||
switch t {
|
||||
case UnknownTernary:
|
||||
return "unknown"
|
||||
case FalseTernary:
|
||||
return "false"
|
||||
case TrueTernary:
|
||||
return "true"
|
||||
default:
|
||||
return fmt.Sprintf("unknown value, %d", int(t))
|
||||
}
|
||||
}
|
||||
|
||||
// Bool returns true if the value is TrueTernary, false otherwise.
|
||||
func (t Ternary) Bool() bool {
|
||||
return t == TrueTernary
|
||||
}
|
||||
|
||||
// Enumerations for the values of the Ternary type.
|
||||
const (
|
||||
UnknownTernary Ternary = iota
|
||||
FalseTernary
|
||||
TrueTernary
|
||||
)
|
||||
|
||||
// BoolTernary returns a true or false Ternary value for the bool provided.
|
||||
func BoolTernary(v bool) Ternary {
|
||||
if v {
|
||||
return TrueTernary
|
||||
}
|
||||
return FalseTernary
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
// Package aws provides core functionality for making requests to AWS services.
|
||||
package aws
|
||||
|
||||
// SDKName is the name of this AWS SDK
|
||||
const SDKName = "aws-sdk-go-v2"
|
||||
|
||||
// SDKVersion is the version of this SDK
|
||||
const SDKVersion = goModuleVersion
|
|
@ -0,0 +1,12 @@
|
|||
version: 0.2
|
||||
|
||||
phases:
|
||||
build:
|
||||
commands:
|
||||
- echo Build started on `date`
|
||||
- export GOPATH=/go
|
||||
- export SDK_CODEBUILD_ROOT=`pwd`
|
||||
- make ci-test-no-generate
|
||||
post_build:
|
||||
commands:
|
||||
- echo Build completed on `date`
|
|
@ -0,0 +1,140 @@
|
|||
# v1.15.5 (2022-05-09)
|
||||
|
||||
* **Bug Fix**: Fixes a bug in LoadDefaultConfig to correctly assign ConfigSources so all config resolvers have access to the config sources. This fixes the feature/ec2/imds client not having configuration applied via config.LoadOptions such as EC2IMDSClientEnableState. PR [#1682](https://github.com/aws/aws-sdk-go-v2/pull/1682)
|
||||
|
||||
# v1.15.4 (2022-04-25)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.3 (2022-03-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.2 (2022-03-24)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.1 (2022-03-23)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.15.0 (2022-03-08)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.14.0 (2022-02-24)
|
||||
|
||||
* **Feature**: Adds support for loading RetryMaxAttempts and RetryMod from the environment and shared configuration files. These parameters drive how the SDK's API client will initialize its default retryer, if custome retryer has not been specified. See [config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config) module and [aws.Config](https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/aws#Config) for more information about and how to use these new options.
|
||||
* **Feature**: Adds support for the `ca_bundle` parameter in shared config and credentials files. The usage of the file is the same as environment variable, `AWS_CA_BUNDLE`, but sourced from shared config. Fixes [#1589](https://github.com/aws/aws-sdk-go-v2/issues/1589)
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.1 (2022-01-28)
|
||||
|
||||
* **Bug Fix**: Fixes LoadDefaultConfig handling of errors returned by passed in functional options. Previously errors returned from the LoadOptions passed into LoadDefaultConfig were incorrectly ignored. [#1562](https://github.com/aws/aws-sdk-go-v2/pull/1562). Thanks to [Pinglei Guo](https://github.com/pingleig) for submitting this PR.
|
||||
* **Bug Fix**: Fixes the SDK's handling of `duration_sections` in the shared credentials file or specified in multiple shared config and shared credentials files under the same profile. [#1568](https://github.com/aws/aws-sdk-go-v2/pull/1568). Thanks to [Amir Szekely](https://github.com/kichik) for help reproduce this bug.
|
||||
* **Bug Fix**: Updates `config` module to use os.UserHomeDir instead of hard coded environment variable for OS. [#1563](https://github.com/aws/aws-sdk-go-v2/pull/1563)
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.13.0 (2022-01-14)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.12.0 (2022-01-07)
|
||||
|
||||
* **Feature**: Add load option for CredentialCache. Adds a new member to the LoadOptions struct, CredentialsCacheOptions. This member allows specifying a function that will be used to configure the CredentialsCache. The CredentialsCacheOptions will only be used if the configuration loader will wrap the underlying credential provider in the CredentialsCache.
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.11.1 (2021-12-21)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.11.0 (2021-12-02)
|
||||
|
||||
* **Feature**: Add support for specifying `EndpointResolverWithOptions` on `LoadOptions`, and associated `WithEndpointResolverWithOptions`.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.10.3 (2021-11-30)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.10.2 (2021-11-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.10.1 (2021-11-12)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.10.0 (2021-11-06)
|
||||
|
||||
* **Feature**: The SDK now supports configuration of FIPS and DualStack endpoints using environment variables, shared configuration, or programmatically.
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.9.0 (2021-10-21)
|
||||
|
||||
* **Feature**: Updated to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.8.3 (2021-10-11)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.8.2 (2021-09-17)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.8.1 (2021-09-10)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.8.0 (2021-09-02)
|
||||
|
||||
* **Feature**: Add support for S3 Multi-Region Access Point ARNs.
|
||||
|
||||
# v1.7.0 (2021-08-27)
|
||||
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.6.1 (2021-08-19)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.6.0 (2021-08-04)
|
||||
|
||||
* **Feature**: adds error handling for defered close calls
|
||||
* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.5.0 (2021-07-15)
|
||||
|
||||
* **Feature**: Support has been added for EC2 IPv6-enabled Instance Metadata Service Endpoints.
|
||||
* **Dependency Update**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.1 (2021-07-01)
|
||||
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.4.0 (2021-06-25)
|
||||
|
||||
* **Feature**: Adds configuration setting for enabling endpoint discovery.
|
||||
* **Feature**: Updated `github.com/aws/smithy-go` to latest version
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.3.0 (2021-05-20)
|
||||
|
||||
* **Feature**: SSO credentials can now be defined alongside other credential providers within the same configuration profile.
|
||||
* **Bug Fix**: Profile names were incorrectly normalized to lower-case, which could result in unexpected profile configurations.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
||||
# v1.2.0 (2021-05-14)
|
||||
|
||||
* **Feature**: Constant has been added to modules to enable runtime version inspection for reporting.
|
||||
* **Dependency Update**: Updated to the latest SDK module versions
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,198 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
)
|
||||
|
||||
// defaultLoaders are a slice of functions that will read external configuration
|
||||
// sources for configuration values. These values are read by the AWSConfigResolvers
|
||||
// using interfaces to extract specific information from the external configuration.
|
||||
var defaultLoaders = []loader{
|
||||
loadEnvConfig,
|
||||
loadSharedConfigIgnoreNotExist,
|
||||
}
|
||||
|
||||
// defaultAWSConfigResolvers are a slice of functions that will resolve external
|
||||
// configuration values into AWS configuration values.
|
||||
//
|
||||
// This will setup the AWS configuration's Region,
|
||||
var defaultAWSConfigResolvers = []awsConfigResolver{
|
||||
// Resolves the default configuration the SDK's aws.Config will be
|
||||
// initialized with.
|
||||
resolveDefaultAWSConfig,
|
||||
|
||||
// Sets the logger to be used. Could be user provided logger, and client
|
||||
// logging mode.
|
||||
resolveLogger,
|
||||
resolveClientLogMode,
|
||||
|
||||
// Sets the HTTP client and configuration to use for making requests using
|
||||
// the HTTP transport.
|
||||
resolveHTTPClient,
|
||||
resolveCustomCABundle,
|
||||
|
||||
// Sets the endpoint resolving behavior the API Clients will use for making
|
||||
// requests to. Clients default to their own clients this allows overrides
|
||||
// to be specified. The resolveEndpointResolver option is deprecated, but
|
||||
// we still need to set it for backwards compatibility on config
|
||||
// construction.
|
||||
resolveEndpointResolver,
|
||||
resolveEndpointResolverWithOptions,
|
||||
|
||||
// Sets the retry behavior API clients will use within their retry attempt
|
||||
// middleware. Defaults to unset, allowing API clients to define their own
|
||||
// retry behavior.
|
||||
resolveRetryer,
|
||||
|
||||
// Sets the region the API Clients should use for making requests to.
|
||||
resolveRegion,
|
||||
resolveEC2IMDSRegion,
|
||||
resolveDefaultRegion,
|
||||
|
||||
// Sets the additional set of middleware stack mutators that will custom
|
||||
// API client request pipeline middleware.
|
||||
resolveAPIOptions,
|
||||
|
||||
// Resolves the DefaultsMode that should be used by SDK clients. If this
|
||||
// mode is set to DefaultsModeAuto.
|
||||
//
|
||||
// Comes after HTTPClient and CustomCABundle to ensure the HTTP client is
|
||||
// configured if provided before invoking IMDS if mode is auto. Comes
|
||||
// before resolving credentials so that those subsequent clients use the
|
||||
// configured auto mode.
|
||||
resolveDefaultsModeOptions,
|
||||
|
||||
// Sets the resolved credentials the API clients will use for
|
||||
// authentication. Provides the SDK's default credential chain.
|
||||
//
|
||||
// Should probably be the last step in the resolve chain to ensure that all
|
||||
// other configurations are resolved first in case downstream credentials
|
||||
// implementations depend on or can be configured with earlier resolved
|
||||
// configuration options.
|
||||
resolveCredentials,
|
||||
}
|
||||
|
||||
// A Config represents a generic configuration value or set of values. This type
|
||||
// will be used by the AWSConfigResolvers to extract
|
||||
//
|
||||
// General the Config type will use type assertion against the Provider interfaces
|
||||
// to extract specific data from the Config.
|
||||
type Config interface{}
|
||||
|
||||
// A loader is used to load external configuration data and returns it as
|
||||
// a generic Config type.
|
||||
//
|
||||
// The loader should return an error if it fails to load the external configuration
|
||||
// or the configuration data is malformed, or required components missing.
|
||||
type loader func(context.Context, configs) (Config, error)
|
||||
|
||||
// An awsConfigResolver will extract configuration data from the configs slice
|
||||
// using the provider interfaces to extract specific functionality. The extracted
|
||||
// configuration values will be written to the AWS Config value.
|
||||
//
|
||||
// The resolver should return an error if it it fails to extract the data, the
|
||||
// data is malformed, or incomplete.
|
||||
type awsConfigResolver func(ctx context.Context, cfg *aws.Config, configs configs) error
|
||||
|
||||
// configs is a slice of Config values. These values will be used by the
|
||||
// AWSConfigResolvers to extract external configuration values to populate the
|
||||
// AWS Config type.
|
||||
//
|
||||
// Use AppendFromLoaders to add additional external Config values that are
|
||||
// loaded from external sources.
|
||||
//
|
||||
// Use ResolveAWSConfig after external Config values have been added or loaded
|
||||
// to extract the loaded configuration values into the AWS Config.
|
||||
type configs []Config
|
||||
|
||||
// AppendFromLoaders iterates over the slice of loaders passed in calling each
|
||||
// loader function in order. The external config value returned by the loader
|
||||
// will be added to the returned configs slice.
|
||||
//
|
||||
// If a loader returns an error this method will stop iterating and return
|
||||
// that error.
|
||||
func (cs configs) AppendFromLoaders(ctx context.Context, loaders []loader) (configs, error) {
|
||||
for _, fn := range loaders {
|
||||
cfg, err := fn(ctx, cs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cs = append(cs, cfg)
|
||||
}
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// ResolveAWSConfig returns a AWS configuration populated with values by calling
|
||||
// the resolvers slice passed in. Each resolver is called in order. Any resolver
|
||||
// may overwrite the AWS Configuration value of a previous resolver.
|
||||
//
|
||||
// If an resolver returns an error this method will return that error, and stop
|
||||
// iterating over the resolvers.
|
||||
func (cs configs) ResolveAWSConfig(ctx context.Context, resolvers []awsConfigResolver) (aws.Config, error) {
|
||||
var cfg aws.Config
|
||||
|
||||
for _, fn := range resolvers {
|
||||
if err := fn(ctx, &cfg, cs); err != nil {
|
||||
return aws.Config{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// ResolveConfig calls the provide function passing slice of configuration sources.
|
||||
// This implements the aws.ConfigResolver interface.
|
||||
func (cs configs) ResolveConfig(f func(configs []interface{}) error) error {
|
||||
var cfgs []interface{}
|
||||
for i := range cs {
|
||||
cfgs = append(cfgs, cs[i])
|
||||
}
|
||||
return f(cfgs)
|
||||
}
|
||||
|
||||
// LoadDefaultConfig reads the SDK's default external configurations, and
|
||||
// populates an AWS Config with the values from the external configurations.
|
||||
//
|
||||
// An optional variadic set of additional Config values can be provided as input
|
||||
// that will be prepended to the configs slice. Use this to add custom configuration.
|
||||
// The custom configurations must satisfy the respective providers for their data
|
||||
// or the custom data will be ignored by the resolvers and config loaders.
|
||||
//
|
||||
// cfg, err := config.LoadDefaultConfig( context.TODO(),
|
||||
// WithSharedConfigProfile("test-profile"),
|
||||
// )
|
||||
// if err != nil {
|
||||
// panic(fmt.Sprintf("failed loading config, %v", err))
|
||||
// }
|
||||
//
|
||||
//
|
||||
// The default configuration sources are:
|
||||
// * Environment Variables
|
||||
// * Shared Configuration and Shared Credentials files.
|
||||
func LoadDefaultConfig(ctx context.Context, optFns ...func(*LoadOptions) error) (cfg aws.Config, err error) {
|
||||
var options LoadOptions
|
||||
for _, optFn := range optFns {
|
||||
if err := optFn(&options); err != nil {
|
||||
return aws.Config{}, err
|
||||
}
|
||||
}
|
||||
|
||||
// assign Load Options to configs
|
||||
var cfgCpy = configs{options}
|
||||
|
||||
cfgCpy, err = cfgCpy.AppendFromLoaders(ctx, defaultLoaders)
|
||||
if err != nil {
|
||||
return aws.Config{}, err
|
||||
}
|
||||
|
||||
cfg, err = cfgCpy.ResolveAWSConfig(ctx, defaultAWSConfigResolvers)
|
||||
if err != nil {
|
||||
return aws.Config{}, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
)
|
||||
|
||||
const execEnvVar = "AWS_EXECUTION_ENV"
|
||||
|
||||
// DefaultsModeOptions is the set of options that are used to configure
|
||||
type DefaultsModeOptions struct {
|
||||
// The SDK configuration defaults mode. Defaults to legacy if not specified.
|
||||
//
|
||||
// Supported modes are: auto, cross-region, in-region, legacy, mobile, standard
|
||||
Mode aws.DefaultsMode
|
||||
|
||||
// The EC2 Instance Metadata Client that should be used when performing environment
|
||||
// discovery when aws.DefaultsModeAuto is set.
|
||||
//
|
||||
// If not specified the SDK will construct a client if the instance metadata service has not been disabled by
|
||||
// the AWS_EC2_METADATA_DISABLED environment variable.
|
||||
IMDSClient *imds.Client
|
||||
}
|
||||
|
||||
func resolveDefaultsModeRuntimeEnvironment(ctx context.Context, envConfig *EnvConfig, client *imds.Client) (aws.RuntimeEnvironment, error) {
|
||||
getRegionOutput, err := client.GetRegion(ctx, &imds.GetRegionInput{})
|
||||
// honor context timeouts, but if we couldn't talk to IMDS don't fail runtime environment introspection.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return aws.RuntimeEnvironment{}, err
|
||||
default:
|
||||
}
|
||||
|
||||
var imdsRegion string
|
||||
if err == nil {
|
||||
imdsRegion = getRegionOutput.Region
|
||||
}
|
||||
|
||||
return aws.RuntimeEnvironment{
|
||||
EnvironmentIdentifier: aws.ExecutionEnvironmentID(os.Getenv(execEnvVar)),
|
||||
Region: envConfig.Region,
|
||||
EC2InstanceMetadataRegion: imdsRegion,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// Package config provides utilities for loading configuration from multiple
|
||||
// sources that can be used to configure the SDK's API clients, and utilities.
|
||||
//
|
||||
// The config package will load configuration from environment variables, AWS
|
||||
// shared configuration file (~/.aws/config), and AWS shared credentials file
|
||||
// (~/.aws/credentials).
|
||||
//
|
||||
// Use the LoadDefaultConfig to load configuration from all the SDK's supported
|
||||
// sources, and resolve credentials using the SDK's default credential chain.
|
||||
//
|
||||
// LoadDefaultConfig allows for a variadic list of additional Config sources that can
|
||||
// provide one or more configuration values which can be used to programmatically control the resolution
|
||||
// of a specific value, or allow for broader range of additional configuration sources not supported by the SDK.
|
||||
// A Config source implements one or more provider interfaces defined in this package. Config sources passed in will
|
||||
// take precedence over the default environment and shared config sources used by the SDK. If one or more Config sources
|
||||
// implement the same provider interface, priority will be handled by the order in which the sources were passed in.
|
||||
//
|
||||
// A number of helpers (prefixed by ``With``) are provided in this package that implement their respective provider
|
||||
// interface. These helpers should be used for overriding configuration programmatically at runtime.
|
||||
package config
|
|
@ -0,0 +1,665 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
)
|
||||
|
||||
// CredentialsSourceName provides a name of the provider when config is
|
||||
// loaded from environment.
|
||||
const CredentialsSourceName = "EnvConfigCredentials"
|
||||
|
||||
// Environment variables that will be read for configuration values.
|
||||
const (
|
||||
awsAccessKeyIDEnvVar = "AWS_ACCESS_KEY_ID"
|
||||
awsAccessKeyEnvVar = "AWS_ACCESS_KEY"
|
||||
|
||||
awsSecretAccessKeyEnvVar = "AWS_SECRET_ACCESS_KEY"
|
||||
awsSecretKeyEnvVar = "AWS_SECRET_KEY"
|
||||
|
||||
awsSessionTokenEnvVar = "AWS_SESSION_TOKEN"
|
||||
|
||||
awsContainerCredentialsEndpointEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
|
||||
awsContainerCredentialsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
|
||||
awsContainerPProviderAuthorizationEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
|
||||
|
||||
awsRegionEnvVar = "AWS_REGION"
|
||||
awsDefaultRegionEnvVar = "AWS_DEFAULT_REGION"
|
||||
|
||||
awsProfileEnvVar = "AWS_PROFILE"
|
||||
awsDefaultProfileEnvVar = "AWS_DEFAULT_PROFILE"
|
||||
|
||||
awsSharedCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE"
|
||||
|
||||
awsConfigFileEnvVar = "AWS_CONFIG_FILE"
|
||||
|
||||
awsCustomCABundleEnvVar = "AWS_CA_BUNDLE"
|
||||
|
||||
awsWebIdentityTokenFilePathEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE"
|
||||
|
||||
awsRoleARNEnvVar = "AWS_ROLE_ARN"
|
||||
awsRoleSessionNameEnvVar = "AWS_ROLE_SESSION_NAME"
|
||||
|
||||
awsEnableEndpointDiscoveryEnvVar = "AWS_ENABLE_ENDPOINT_DISCOVERY"
|
||||
|
||||
awsS3UseARNRegionEnvVar = "AWS_S3_USE_ARN_REGION"
|
||||
|
||||
awsEc2MetadataServiceEndpointModeEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE"
|
||||
|
||||
awsEc2MetadataServiceEndpointEnvVar = "AWS_EC2_METADATA_SERVICE_ENDPOINT"
|
||||
|
||||
awsEc2MetadataDisabled = "AWS_EC2_METADATA_DISABLED"
|
||||
|
||||
awsS3DisableMultiRegionAccessPointEnvVar = "AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS"
|
||||
|
||||
awsUseDualStackEndpoint = "AWS_USE_DUALSTACK_ENDPOINT"
|
||||
|
||||
awsUseFIPSEndpoint = "AWS_USE_FIPS_ENDPOINT"
|
||||
|
||||
awsDefaultMode = "AWS_DEFAULTS_MODE"
|
||||
|
||||
awsRetryMaxAttempts = "AWS_MAX_ATTEMPTS"
|
||||
awsRetryMode = "AWS_RETRY_MODE"
|
||||
)
|
||||
|
||||
var (
|
||||
credAccessEnvKeys = []string{
|
||||
awsAccessKeyIDEnvVar,
|
||||
awsAccessKeyEnvVar,
|
||||
}
|
||||
credSecretEnvKeys = []string{
|
||||
awsSecretAccessKeyEnvVar,
|
||||
awsSecretKeyEnvVar,
|
||||
}
|
||||
regionEnvKeys = []string{
|
||||
awsRegionEnvVar,
|
||||
awsDefaultRegionEnvVar,
|
||||
}
|
||||
profileEnvKeys = []string{
|
||||
awsProfileEnvVar,
|
||||
awsDefaultProfileEnvVar,
|
||||
}
|
||||
)
|
||||
|
||||
// EnvConfig is a collection of environment values the SDK will read
|
||||
// setup config from. All environment values are optional. But some values
|
||||
// such as credentials require multiple values to be complete or the values
|
||||
// will be ignored.
|
||||
type EnvConfig struct {
|
||||
// Environment configuration values. If set both Access Key ID and Secret Access
|
||||
// Key must be provided. Session Token and optionally also be provided, but is
|
||||
// not required.
|
||||
//
|
||||
// # Access Key ID
|
||||
// AWS_ACCESS_KEY_ID=AKID
|
||||
// AWS_ACCESS_KEY=AKID # only read if AWS_ACCESS_KEY_ID is not set.
|
||||
//
|
||||
// # Secret Access Key
|
||||
// AWS_SECRET_ACCESS_KEY=SECRET
|
||||
// AWS_SECRET_KEY=SECRET # only read if AWS_SECRET_ACCESS_KEY is not set.
|
||||
//
|
||||
// # Session Token
|
||||
// AWS_SESSION_TOKEN=TOKEN
|
||||
Credentials aws.Credentials
|
||||
|
||||
// ContainerCredentialsEndpoint value is the HTTP enabled endpoint to retrieve credentials
|
||||
// using the endpointcreds.Provider
|
||||
ContainerCredentialsEndpoint string
|
||||
|
||||
// ContainerCredentialsRelativePath is the relative URI path that will be used when attempting to retrieve
|
||||
// credentials from the container endpoint.
|
||||
ContainerCredentialsRelativePath string
|
||||
|
||||
// ContainerAuthorizationToken is the authorization token that will be included in the HTTP Authorization
|
||||
// header when attempting to retrieve credentials from the container credentials endpoint.
|
||||
ContainerAuthorizationToken string
|
||||
|
||||
// Region value will instruct the SDK where to make service API requests to. If is
|
||||
// not provided in the environment the region must be provided before a service
|
||||
// client request is made.
|
||||
//
|
||||
// AWS_REGION=us-west-2
|
||||
// AWS_DEFAULT_REGION=us-west-2
|
||||
Region string
|
||||
|
||||
// Profile name the SDK should load use when loading shared configuration from the
|
||||
// shared configuration files. If not provided "default" will be used as the
|
||||
// profile name.
|
||||
//
|
||||
// AWS_PROFILE=my_profile
|
||||
// AWS_DEFAULT_PROFILE=my_profile
|
||||
SharedConfigProfile string
|
||||
|
||||
// Shared credentials file path can be set to instruct the SDK to use an alternate
|
||||
// file for the shared credentials. If not set the file will be loaded from
|
||||
// $HOME/.aws/credentials on Linux/Unix based systems, and
|
||||
// %USERPROFILE%\.aws\credentials on Windows.
|
||||
//
|
||||
// AWS_SHARED_CREDENTIALS_FILE=$HOME/my_shared_credentials
|
||||
SharedCredentialsFile string
|
||||
|
||||
// Shared config file path can be set to instruct the SDK to use an alternate
|
||||
// file for the shared config. If not set the file will be loaded from
|
||||
// $HOME/.aws/config on Linux/Unix based systems, and
|
||||
// %USERPROFILE%\.aws\config on Windows.
|
||||
//
|
||||
// AWS_CONFIG_FILE=$HOME/my_shared_config
|
||||
SharedConfigFile string
|
||||
|
||||
// Sets the path to a custom Credentials Authority (CA) Bundle PEM file
|
||||
// that the SDK will use instead of the system's root CA bundle.
|
||||
// Only use this if you want to configure the SDK to use a custom set
|
||||
// of CAs.
|
||||
//
|
||||
// Enabling this option will attempt to merge the Transport
|
||||
// into the SDK's HTTP client. If the client's Transport is
|
||||
// not a http.Transport an error will be returned. If the
|
||||
// Transport's TLS config is set this option will cause the
|
||||
// SDK to overwrite the Transport's TLS config's RootCAs value.
|
||||
//
|
||||
// Setting a custom HTTPClient in the aws.Config options will override this setting.
|
||||
// To use this option and custom HTTP client, the HTTP client needs to be provided
|
||||
// when creating the config. Not the service client.
|
||||
//
|
||||
// AWS_CA_BUNDLE=$HOME/my_custom_ca_bundle
|
||||
CustomCABundle string
|
||||
|
||||
// Enables endpoint discovery via environment variables.
|
||||
//
|
||||
// AWS_ENABLE_ENDPOINT_DISCOVERY=true
|
||||
EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
|
||||
|
||||
// Specifies the WebIdentity token the SDK should use to assume a role
|
||||
// with.
|
||||
//
|
||||
// AWS_WEB_IDENTITY_TOKEN_FILE=file_path
|
||||
WebIdentityTokenFilePath string
|
||||
|
||||
// Specifies the IAM role arn to use when assuming an role.
|
||||
//
|
||||
// AWS_ROLE_ARN=role_arn
|
||||
RoleARN string
|
||||
|
||||
// Specifies the IAM role session name to use when assuming a role.
|
||||
//
|
||||
// AWS_ROLE_SESSION_NAME=session_name
|
||||
RoleSessionName string
|
||||
|
||||
// Specifies if the S3 service should allow ARNs to direct the region
|
||||
// the client's requests are sent to.
|
||||
//
|
||||
// AWS_S3_USE_ARN_REGION=true
|
||||
S3UseARNRegion *bool
|
||||
|
||||
// Specifies if the EC2 IMDS service client is enabled.
|
||||
//
|
||||
// AWS_EC2_METADATA_DISABLED=true
|
||||
EC2IMDSClientEnableState imds.ClientEnableState
|
||||
|
||||
// Specifies the EC2 Instance Metadata Service default endpoint selection mode (IPv4 or IPv6)
|
||||
//
|
||||
// AWS_EC2_METADATA_SERVICE_ENDPOINT_MODE=IPv6
|
||||
EC2IMDSEndpointMode imds.EndpointModeState
|
||||
|
||||
// Specifies the EC2 Instance Metadata Service endpoint to use. If specified it overrides EC2IMDSEndpointMode.
|
||||
//
|
||||
// AWS_EC2_METADATA_SERVICE_ENDPOINT=http://fd00:ec2::254
|
||||
EC2IMDSEndpoint string
|
||||
|
||||
// Specifies if the S3 service should disable multi-region access points
|
||||
// support.
|
||||
//
|
||||
// AWS_S3_DISABLE_MULTIREGION_ACCESS_POINTS=true
|
||||
S3DisableMultiRegionAccessPoints *bool
|
||||
|
||||
// Specifies that SDK clients must resolve a dual-stack endpoint for
|
||||
// services.
|
||||
//
|
||||
// AWS_USE_DUALSTACK_ENDPOINT=true
|
||||
UseDualStackEndpoint aws.DualStackEndpointState
|
||||
|
||||
// Specifies that SDK clients must resolve a FIPS endpoint for
|
||||
// services.
|
||||
//
|
||||
// AWS_USE_FIPS_ENDPOINT=true
|
||||
UseFIPSEndpoint aws.FIPSEndpointState
|
||||
|
||||
// Specifies the SDK Defaults Mode used by services.
|
||||
//
|
||||
// AWS_DEFAULTS_MODE=standard
|
||||
DefaultsMode aws.DefaultsMode
|
||||
|
||||
// Specifies the maximum number attempts an API client will call an
|
||||
// operation that fails with a retryable error.
|
||||
//
|
||||
// AWS_MAX_ATTEMPTS=3
|
||||
RetryMaxAttempts int
|
||||
|
||||
// Specifies the retry model the API client will be created with.
|
||||
//
|
||||
// aws_retry_mode=standard
|
||||
RetryMode aws.RetryMode
|
||||
}
|
||||
|
||||
// loadEnvConfig reads configuration values from the OS's environment variables.
|
||||
// Returning the a Config typed EnvConfig to satisfy the ConfigLoader func type.
|
||||
func loadEnvConfig(ctx context.Context, cfgs configs) (Config, error) {
|
||||
return NewEnvConfig()
|
||||
}
|
||||
|
||||
// NewEnvConfig retrieves the SDK's environment configuration.
|
||||
// See `EnvConfig` for the values that will be retrieved.
|
||||
func NewEnvConfig() (EnvConfig, error) {
|
||||
var cfg EnvConfig
|
||||
|
||||
creds := aws.Credentials{
|
||||
Source: CredentialsSourceName,
|
||||
}
|
||||
setStringFromEnvVal(&creds.AccessKeyID, credAccessEnvKeys)
|
||||
setStringFromEnvVal(&creds.SecretAccessKey, credSecretEnvKeys)
|
||||
if creds.HasKeys() {
|
||||
creds.SessionToken = os.Getenv(awsSessionTokenEnvVar)
|
||||
cfg.Credentials = creds
|
||||
}
|
||||
|
||||
cfg.ContainerCredentialsEndpoint = os.Getenv(awsContainerCredentialsEndpointEnvVar)
|
||||
cfg.ContainerCredentialsRelativePath = os.Getenv(awsContainerCredentialsRelativePathEnvVar)
|
||||
cfg.ContainerAuthorizationToken = os.Getenv(awsContainerPProviderAuthorizationEnvVar)
|
||||
|
||||
setStringFromEnvVal(&cfg.Region, regionEnvKeys)
|
||||
setStringFromEnvVal(&cfg.SharedConfigProfile, profileEnvKeys)
|
||||
|
||||
cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnvVar)
|
||||
cfg.SharedConfigFile = os.Getenv(awsConfigFileEnvVar)
|
||||
|
||||
cfg.CustomCABundle = os.Getenv(awsCustomCABundleEnvVar)
|
||||
|
||||
cfg.WebIdentityTokenFilePath = os.Getenv(awsWebIdentityTokenFilePathEnvVar)
|
||||
|
||||
cfg.RoleARN = os.Getenv(awsRoleARNEnvVar)
|
||||
cfg.RoleSessionName = os.Getenv(awsRoleSessionNameEnvVar)
|
||||
|
||||
if err := setEndpointDiscoveryTypeFromEnvVal(&cfg.EnableEndpointDiscovery, []string{awsEnableEndpointDiscoveryEnvVar}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3UseARNRegion, []string{awsS3UseARNRegionEnvVar}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
setEC2IMDSClientEnableState(&cfg.EC2IMDSClientEnableState, []string{awsEc2MetadataDisabled})
|
||||
if err := setEC2IMDSEndpointMode(&cfg.EC2IMDSEndpointMode, []string{awsEc2MetadataServiceEndpointModeEnvVar}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
cfg.EC2IMDSEndpoint = os.Getenv(awsEc2MetadataServiceEndpointEnvVar)
|
||||
|
||||
if err := setBoolPtrFromEnvVal(&cfg.S3DisableMultiRegionAccessPoints, []string{awsS3DisableMultiRegionAccessPointEnvVar}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setUseDualStackEndpointFromEnvVal(&cfg.UseDualStackEndpoint, []string{awsUseDualStackEndpoint}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setUseFIPSEndpointFromEnvVal(&cfg.UseFIPSEndpoint, []string{awsUseFIPSEndpoint}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setDefaultsModeFromEnvVal(&cfg.DefaultsMode, []string{awsDefaultMode}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if err := setIntFromEnvVal(&cfg.RetryMaxAttempts, []string{awsRetryMaxAttempts}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
if err := setRetryModeFromEnvVal(&cfg.RetryMode, []string{awsRetryMode}); err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (c EnvConfig) getDefaultsMode(ctx context.Context) (aws.DefaultsMode, bool, error) {
|
||||
if len(c.DefaultsMode) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return c.DefaultsMode, true, nil
|
||||
}
|
||||
|
||||
// GetRetryMaxAttempts returns the value of AWS_MAX_ATTEMPTS if was specified,
|
||||
// and not 0.
|
||||
func (c EnvConfig) GetRetryMaxAttempts(ctx context.Context) (int, bool, error) {
|
||||
if c.RetryMaxAttempts == 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
return c.RetryMaxAttempts, true, nil
|
||||
}
|
||||
|
||||
// GetRetryMode returns the RetryMode of AWS_RETRY_MODE if was specified, and a
|
||||
// valid value.
|
||||
func (c EnvConfig) GetRetryMode(ctx context.Context) (aws.RetryMode, bool, error) {
|
||||
if len(c.RetryMode) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return c.RetryMode, true, nil
|
||||
}
|
||||
|
||||
func setEC2IMDSClientEnableState(state *imds.ClientEnableState, keys []string) {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case strings.EqualFold(value, "true"):
|
||||
*state = imds.ClientDisabled
|
||||
case strings.EqualFold(value, "false"):
|
||||
*state = imds.ClientEnabled
|
||||
default:
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func setDefaultsModeFromEnvVal(mode *aws.DefaultsMode, keys []string) error {
|
||||
for _, k := range keys {
|
||||
if value := os.Getenv(k); len(value) > 0 {
|
||||
if ok := mode.SetFromString(value); !ok {
|
||||
return fmt.Errorf("invalid %s value: %s", k, value)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setRetryModeFromEnvVal(mode *aws.RetryMode, keys []string) (err error) {
|
||||
for _, k := range keys {
|
||||
if value := os.Getenv(k); len(value) > 0 {
|
||||
*mode, err = aws.ParseRetryMode(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid %s value, %w", k, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setEC2IMDSEndpointMode(mode *imds.EndpointModeState, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
if err := mode.SetFromString(value); err != nil {
|
||||
return fmt.Errorf("invalid value for environment variable, %s=%s, %v", k, value, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRegion returns the AWS Region if set in the environment. Returns an empty
|
||||
// string if not set.
|
||||
func (c EnvConfig) getRegion(ctx context.Context) (string, bool, error) {
|
||||
if len(c.Region) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return c.Region, true, nil
|
||||
}
|
||||
|
||||
// GetSharedConfigProfile returns the shared config profile if set in the
|
||||
// environment. Returns an empty string if not set.
|
||||
func (c EnvConfig) getSharedConfigProfile(ctx context.Context) (string, bool, error) {
|
||||
if len(c.SharedConfigProfile) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return c.SharedConfigProfile, true, nil
|
||||
}
|
||||
|
||||
// getSharedConfigFiles returns a slice of filenames set in the environment.
|
||||
//
|
||||
// Will return the filenames in the order of:
|
||||
// * Shared Config
|
||||
func (c EnvConfig) getSharedConfigFiles(context.Context) ([]string, bool, error) {
|
||||
var files []string
|
||||
if v := c.SharedConfigFile; len(v) > 0 {
|
||||
files = append(files, v)
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return files, true, nil
|
||||
}
|
||||
|
||||
// getSharedCredentialsFiles returns a slice of filenames set in the environment.
|
||||
//
|
||||
// Will return the filenames in the order of:
|
||||
// * Shared Credentials
|
||||
func (c EnvConfig) getSharedCredentialsFiles(context.Context) ([]string, bool, error) {
|
||||
var files []string
|
||||
if v := c.SharedCredentialsFile; len(v) > 0 {
|
||||
files = append(files, v)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
return files, true, nil
|
||||
}
|
||||
|
||||
// GetCustomCABundle returns the custom CA bundle's PEM bytes if the file was
|
||||
func (c EnvConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
|
||||
if len(c.CustomCABundle) == 0 {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(c.CustomCABundle)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return bytes.NewReader(b), true, nil
|
||||
}
|
||||
|
||||
// GetS3UseARNRegion returns whether to allow ARNs to direct the region
|
||||
// the S3 client's requests are sent to.
|
||||
func (c EnvConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
|
||||
if c.S3UseARNRegion == nil {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
return *c.S3UseARNRegion, true, nil
|
||||
}
|
||||
|
||||
// GetS3DisableMultRegionAccessPoints returns whether to disable multi-region access point
|
||||
// support for the S3 client.
|
||||
func (c EnvConfig) GetS3DisableMultRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
|
||||
if c.S3DisableMultiRegionAccessPoints == nil {
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
return *c.S3DisableMultiRegionAccessPoints, true, nil
|
||||
}
|
||||
|
||||
// GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be
|
||||
// used for requests.
|
||||
func (c EnvConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
|
||||
if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
|
||||
return aws.DualStackEndpointStateUnset, false, nil
|
||||
}
|
||||
|
||||
return c.UseDualStackEndpoint, true, nil
|
||||
}
|
||||
|
||||
// GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be
|
||||
// used for requests.
|
||||
func (c EnvConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
|
||||
if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
|
||||
return aws.FIPSEndpointStateUnset, false, nil
|
||||
}
|
||||
|
||||
return c.UseFIPSEndpoint, true, nil
|
||||
}
|
||||
|
||||
func setStringFromEnvVal(dst *string, keys []string) {
|
||||
for _, k := range keys {
|
||||
if v := os.Getenv(k); len(v) > 0 {
|
||||
*dst = v
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setIntFromEnvVal(dst *int, keys []string) error {
|
||||
for _, k := range keys {
|
||||
if v := os.Getenv(k); len(v) > 0 {
|
||||
i, err := strconv.ParseInt(v, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid value %s=%s, %w", k, v, err)
|
||||
}
|
||||
*dst = int(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setBoolPtrFromEnvVal(dst **bool, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if *dst == nil {
|
||||
*dst = new(bool)
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.EqualFold(value, "false"):
|
||||
**dst = false
|
||||
case strings.EqualFold(value, "true"):
|
||||
**dst = true
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"invalid value for environment variable, %s=%s, need true or false",
|
||||
k, value)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setEndpointDiscoveryTypeFromEnvVal(dst *aws.EndpointDiscoveryEnableState, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue // skip if empty
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.EqualFold(value, endpointDiscoveryDisabled):
|
||||
*dst = aws.EndpointDiscoveryDisabled
|
||||
case strings.EqualFold(value, endpointDiscoveryEnabled):
|
||||
*dst = aws.EndpointDiscoveryEnabled
|
||||
case strings.EqualFold(value, endpointDiscoveryAuto):
|
||||
*dst = aws.EndpointDiscoveryAuto
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"invalid value for environment variable, %s=%s, need true, false or auto",
|
||||
k, value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUseDualStackEndpointFromEnvVal(dst *aws.DualStackEndpointState, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue // skip if empty
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.EqualFold(value, "true"):
|
||||
*dst = aws.DualStackEndpointStateEnabled
|
||||
case strings.EqualFold(value, "false"):
|
||||
*dst = aws.DualStackEndpointStateDisabled
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"invalid value for environment variable, %s=%s, need true, false",
|
||||
k, value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setUseFIPSEndpointFromEnvVal(dst *aws.FIPSEndpointState, keys []string) error {
|
||||
for _, k := range keys {
|
||||
value := os.Getenv(k)
|
||||
if len(value) == 0 {
|
||||
continue // skip if empty
|
||||
}
|
||||
|
||||
switch {
|
||||
case strings.EqualFold(value, "true"):
|
||||
*dst = aws.FIPSEndpointStateEnabled
|
||||
case strings.EqualFold(value, "false"):
|
||||
*dst = aws.FIPSEndpointStateDisabled
|
||||
default:
|
||||
return fmt.Errorf(
|
||||
"invalid value for environment variable, %s=%s, need true, false",
|
||||
k, value)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetEnableEndpointDiscovery returns resolved value for EnableEndpointDiscovery env variable setting.
|
||||
func (c EnvConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, found bool, err error) {
|
||||
if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
|
||||
return aws.EndpointDiscoveryUnset, false, nil
|
||||
}
|
||||
|
||||
return c.EnableEndpointDiscovery, true, nil
|
||||
}
|
||||
|
||||
// GetEC2IMDSClientEnableState implements a EC2IMDSClientEnableState options resolver interface.
|
||||
func (c EnvConfig) GetEC2IMDSClientEnableState() (imds.ClientEnableState, bool, error) {
|
||||
if c.EC2IMDSClientEnableState == imds.ClientDefaultEnableState {
|
||||
return imds.ClientDefaultEnableState, false, nil
|
||||
}
|
||||
|
||||
return c.EC2IMDSClientEnableState, true, nil
|
||||
}
|
||||
|
||||
// GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface.
|
||||
func (c EnvConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
|
||||
if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
|
||||
return imds.EndpointModeStateUnset, false, nil
|
||||
}
|
||||
|
||||
return c.EC2IMDSEndpointMode, true, nil
|
||||
}
|
||||
|
||||
// GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface.
|
||||
func (c EnvConfig) GetEC2IMDSEndpoint() (string, bool, error) {
|
||||
if len(c.EC2IMDSEndpoint) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return c.EC2IMDSEndpoint, true, nil
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package config
|
||||
|
||||
//go:generate go run -tags codegen ./codegen -output=provider_assert_test.go
|
||||
//go:generate gofmt -s -w ./
|
|
@ -0,0 +1,6 @@
|
|||
// Code generated by internal/repotools/cmd/updatemodulemeta DO NOT EDIT.
|
||||
|
||||
package config
|
||||
|
||||
// goModuleVersion is the tagged release for this module
|
||||
const goModuleVersion = "1.15.5"
|
|
@ -0,0 +1,926 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/processcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ssocreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// LoadOptionsFunc is a type alias for LoadOptions functional option
|
||||
type LoadOptionsFunc func(*LoadOptions) error
|
||||
|
||||
// LoadOptions are discrete set of options that are valid for loading the
|
||||
// configuration
|
||||
type LoadOptions struct {
|
||||
|
||||
// Region is the region to send requests to.
|
||||
Region string
|
||||
|
||||
// Credentials object to use when signing requests.
|
||||
Credentials aws.CredentialsProvider
|
||||
|
||||
// HTTPClient the SDK's API clients will use to invoke HTTP requests.
|
||||
HTTPClient HTTPClient
|
||||
|
||||
// EndpointResolver that can be used to provide or override an endpoint for
|
||||
// the given service and region.
|
||||
//
|
||||
// See the `aws.EndpointResolver` documentation on usage.
|
||||
//
|
||||
// Deprecated: See EndpointResolverWithOptions
|
||||
EndpointResolver aws.EndpointResolver
|
||||
|
||||
// EndpointResolverWithOptions that can be used to provide or override an
|
||||
// endpoint for the given service and region.
|
||||
//
|
||||
// See the `aws.EndpointResolverWithOptions` documentation on usage.
|
||||
EndpointResolverWithOptions aws.EndpointResolverWithOptions
|
||||
|
||||
// RetryMaxAttempts specifies the maximum number attempts an API client
|
||||
// will call an operation that fails with a retryable error.
|
||||
//
|
||||
// This value will only be used if Retryer option is nil.
|
||||
RetryMaxAttempts int
|
||||
|
||||
// RetryMode specifies the retry model the API client will be created with.
|
||||
//
|
||||
// This value will only be used if Retryer option is nil.
|
||||
RetryMode aws.RetryMode
|
||||
|
||||
// Retryer is a function that provides a Retryer implementation. A Retryer
|
||||
// guides how HTTP requests should be retried in case of recoverable
|
||||
// failures.
|
||||
//
|
||||
// If not nil, RetryMaxAttempts, and RetryMode will be ignored.
|
||||
Retryer func() aws.Retryer
|
||||
|
||||
// APIOptions provides the set of middleware mutations modify how the API
|
||||
// client requests will be handled. This is useful for adding additional
|
||||
// tracing data to a request, or changing behavior of the SDK's client.
|
||||
APIOptions []func(*middleware.Stack) error
|
||||
|
||||
// Logger writer interface to write logging messages to.
|
||||
Logger logging.Logger
|
||||
|
||||
// ClientLogMode is used to configure the events that will be sent to the
|
||||
// configured logger. This can be used to configure the logging of signing,
|
||||
// retries, request, and responses of the SDK clients.
|
||||
//
|
||||
// See the ClientLogMode type documentation for the complete set of logging
|
||||
// modes and available configuration.
|
||||
ClientLogMode *aws.ClientLogMode
|
||||
|
||||
// SharedConfigProfile is the profile to be used when loading the SharedConfig
|
||||
SharedConfigProfile string
|
||||
|
||||
// SharedConfigFiles is the slice of custom shared config files to use when
|
||||
// loading the SharedConfig. A non-default profile used within config file
|
||||
// must have name defined with prefix 'profile '. eg [profile xyz]
|
||||
// indicates a profile with name 'xyz'. To read more on the format of the
|
||||
// config file, please refer the documentation at
|
||||
// https://docs.aws.amazon.com/credref/latest/refdocs/file-format.html#file-format-config
|
||||
//
|
||||
// If duplicate profiles are provided within the same, or across multiple
|
||||
// shared config files, the next parsed profile will override only the
|
||||
// properties that conflict with the previously defined profile. Note that
|
||||
// if duplicate profiles are provided within the SharedCredentialsFiles and
|
||||
// SharedConfigFiles, the properties defined in shared credentials file
|
||||
// take precedence.
|
||||
SharedConfigFiles []string
|
||||
|
||||
// SharedCredentialsFile is the slice of custom shared credentials files to
|
||||
// use when loading the SharedConfig. The profile name used within
|
||||
// credentials file must not prefix 'profile '. eg [xyz] indicates a
|
||||
// profile with name 'xyz'. Profile declared as [profile xyz] will be
|
||||
// ignored. To read more on the format of the credentials file, please
|
||||
// refer the documentation at
|
||||
// https://docs.aws.amazon.com/credref/latest/refdocs/file-format.html#file-format-creds
|
||||
//
|
||||
// If duplicate profiles are provided with a same, or across multiple
|
||||
// shared credentials files, the next parsed profile will override only
|
||||
// properties that conflict with the previously defined profile. Note that
|
||||
// if duplicate profiles are provided within the SharedCredentialsFiles and
|
||||
// SharedConfigFiles, the properties defined in shared credentials file
|
||||
// take precedence.
|
||||
SharedCredentialsFiles []string
|
||||
|
||||
// CustomCABundle is CA bundle PEM bytes reader
|
||||
CustomCABundle io.Reader
|
||||
|
||||
// DefaultRegion is the fall back region, used if a region was not resolved
|
||||
// from other sources
|
||||
DefaultRegion string
|
||||
|
||||
// UseEC2IMDSRegion indicates if SDK should retrieve the region
|
||||
// from the EC2 Metadata service
|
||||
UseEC2IMDSRegion *UseEC2IMDSRegion
|
||||
|
||||
// CredentialsCacheOptions is a function for setting the
|
||||
// aws.CredentialsCacheOptions
|
||||
CredentialsCacheOptions func(*aws.CredentialsCacheOptions)
|
||||
|
||||
// ProcessCredentialOptions is a function for setting
|
||||
// the processcreds.Options
|
||||
ProcessCredentialOptions func(*processcreds.Options)
|
||||
|
||||
// EC2RoleCredentialOptions is a function for setting
|
||||
// the ec2rolecreds.Options
|
||||
EC2RoleCredentialOptions func(*ec2rolecreds.Options)
|
||||
|
||||
// EndpointCredentialOptions is a function for setting
|
||||
// the endpointcreds.Options
|
||||
EndpointCredentialOptions func(*endpointcreds.Options)
|
||||
|
||||
// WebIdentityRoleCredentialOptions is a function for setting
|
||||
// the stscreds.WebIdentityRoleOptions
|
||||
WebIdentityRoleCredentialOptions func(*stscreds.WebIdentityRoleOptions)
|
||||
|
||||
// AssumeRoleCredentialOptions is a function for setting the
|
||||
// stscreds.AssumeRoleOptions
|
||||
AssumeRoleCredentialOptions func(*stscreds.AssumeRoleOptions)
|
||||
|
||||
// SSOProviderOptions is a function for setting
|
||||
// the ssocreds.Options
|
||||
SSOProviderOptions func(options *ssocreds.Options)
|
||||
|
||||
// LogConfigurationWarnings when set to true, enables logging
|
||||
// configuration warnings
|
||||
LogConfigurationWarnings *bool
|
||||
|
||||
// S3UseARNRegion specifies if the S3 service should allow ARNs to direct
|
||||
// the region, the client's requests are sent to.
|
||||
S3UseARNRegion *bool
|
||||
|
||||
// EnableEndpointDiscovery specifies if endpoint discovery is enable for
|
||||
// the client.
|
||||
EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
|
||||
|
||||
// Specifies if the EC2 IMDS service client is enabled.
|
||||
//
|
||||
// AWS_EC2_METADATA_DISABLED=true
|
||||
EC2IMDSClientEnableState imds.ClientEnableState
|
||||
|
||||
// Specifies the EC2 Instance Metadata Service default endpoint selection
|
||||
// mode (IPv4 or IPv6)
|
||||
EC2IMDSEndpointMode imds.EndpointModeState
|
||||
|
||||
// Specifies the EC2 Instance Metadata Service endpoint to use. If
|
||||
// specified it overrides EC2IMDSEndpointMode.
|
||||
EC2IMDSEndpoint string
|
||||
|
||||
// Specifies that SDK clients must resolve a dual-stack endpoint for
|
||||
// services.
|
||||
UseDualStackEndpoint aws.DualStackEndpointState
|
||||
|
||||
// Specifies that SDK clients must resolve a FIPS endpoint for
|
||||
// services.
|
||||
UseFIPSEndpoint aws.FIPSEndpointState
|
||||
|
||||
// Specifies the SDK configuration mode for defaults.
|
||||
DefaultsModeOptions DefaultsModeOptions
|
||||
}
|
||||
|
||||
func (o LoadOptions) getDefaultsMode(ctx context.Context) (aws.DefaultsMode, bool, error) {
|
||||
if len(o.DefaultsModeOptions.Mode) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return o.DefaultsModeOptions.Mode, true, nil
|
||||
}
|
||||
|
||||
// GetRetryMaxAttempts returns the RetryMaxAttempts if specified in the
|
||||
// LoadOptions and not 0.
|
||||
func (o LoadOptions) GetRetryMaxAttempts(ctx context.Context) (int, bool, error) {
|
||||
if o.RetryMaxAttempts == 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
return o.RetryMaxAttempts, true, nil
|
||||
}
|
||||
|
||||
// GetRetryMode returns the RetryMode specified in the LoadOptions.
|
||||
func (o LoadOptions) GetRetryMode(ctx context.Context) (aws.RetryMode, bool, error) {
|
||||
if len(o.RetryMode) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
return o.RetryMode, true, nil
|
||||
}
|
||||
|
||||
func (o LoadOptions) getDefaultsModeIMDSClient(ctx context.Context) (*imds.Client, bool, error) {
|
||||
if o.DefaultsModeOptions.IMDSClient == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
return o.DefaultsModeOptions.IMDSClient, true, nil
|
||||
}
|
||||
|
||||
// getRegion returns Region from config's LoadOptions
|
||||
func (o LoadOptions) getRegion(ctx context.Context) (string, bool, error) {
|
||||
if len(o.Region) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.Region, true, nil
|
||||
}
|
||||
|
||||
// WithRegion is a helper function to construct functional options
|
||||
// that sets Region on config's LoadOptions. Setting the region to
|
||||
// an empty string, will result in the region value being ignored.
|
||||
// If multiple WithRegion calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithRegion(v string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.Region = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getDefaultRegion returns DefaultRegion from config's LoadOptions
|
||||
func (o LoadOptions) getDefaultRegion(ctx context.Context) (string, bool, error) {
|
||||
if len(o.DefaultRegion) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.DefaultRegion, true, nil
|
||||
}
|
||||
|
||||
// WithDefaultRegion is a helper function to construct functional options
|
||||
// that sets a DefaultRegion on config's LoadOptions. Setting the default
|
||||
// region to an empty string, will result in the default region value
|
||||
// being ignored. If multiple WithDefaultRegion calls are made, the last
|
||||
// call overrides the previous call values. Note that both WithRegion and
|
||||
// WithEC2IMDSRegion call takes precedence over WithDefaultRegion call
|
||||
// when resolving region.
|
||||
func WithDefaultRegion(v string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.DefaultRegion = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getSharedConfigProfile returns SharedConfigProfile from config's LoadOptions
|
||||
func (o LoadOptions) getSharedConfigProfile(ctx context.Context) (string, bool, error) {
|
||||
if len(o.SharedConfigProfile) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.SharedConfigProfile, true, nil
|
||||
}
|
||||
|
||||
// WithSharedConfigProfile is a helper function to construct functional options
|
||||
// that sets SharedConfigProfile on config's LoadOptions. Setting the shared
|
||||
// config profile to an empty string, will result in the shared config profile
|
||||
// value being ignored.
|
||||
// If multiple WithSharedConfigProfile calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithSharedConfigProfile(v string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.SharedConfigProfile = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getSharedConfigFiles returns SharedConfigFiles set on config's LoadOptions
|
||||
func (o LoadOptions) getSharedConfigFiles(ctx context.Context) ([]string, bool, error) {
|
||||
if o.SharedConfigFiles == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.SharedConfigFiles, true, nil
|
||||
}
|
||||
|
||||
// WithSharedConfigFiles is a helper function to construct functional options
|
||||
// that sets slice of SharedConfigFiles on config's LoadOptions.
|
||||
// Setting the shared config files to an nil string slice, will result in the
|
||||
// shared config files value being ignored.
|
||||
// If multiple WithSharedConfigFiles calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithSharedConfigFiles(v []string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.SharedConfigFiles = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getSharedCredentialsFiles returns SharedCredentialsFiles set on config's LoadOptions
|
||||
func (o LoadOptions) getSharedCredentialsFiles(ctx context.Context) ([]string, bool, error) {
|
||||
if o.SharedCredentialsFiles == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.SharedCredentialsFiles, true, nil
|
||||
}
|
||||
|
||||
// WithSharedCredentialsFiles is a helper function to construct functional options
|
||||
// that sets slice of SharedCredentialsFiles on config's LoadOptions.
|
||||
// Setting the shared credentials files to an nil string slice, will result in the
|
||||
// shared credentials files value being ignored.
|
||||
// If multiple WithSharedCredentialsFiles calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithSharedCredentialsFiles(v []string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.SharedCredentialsFiles = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getCustomCABundle returns CustomCABundle from LoadOptions
|
||||
func (o LoadOptions) getCustomCABundle(ctx context.Context) (io.Reader, bool, error) {
|
||||
if o.CustomCABundle == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.CustomCABundle, true, nil
|
||||
}
|
||||
|
||||
// WithCustomCABundle is a helper function to construct functional options
|
||||
// that sets CustomCABundle on config's LoadOptions. Setting the custom CA Bundle
|
||||
// to nil will result in custom CA Bundle value being ignored.
|
||||
// If multiple WithCustomCABundle calls are made, the last call overrides the
|
||||
// previous call values.
|
||||
func WithCustomCABundle(v io.Reader) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.CustomCABundle = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// UseEC2IMDSRegion provides a regionProvider that retrieves the region
|
||||
// from the EC2 Metadata service.
|
||||
type UseEC2IMDSRegion struct {
|
||||
// If unset will default to generic EC2 IMDS client.
|
||||
Client *imds.Client
|
||||
}
|
||||
|
||||
// getRegion attempts to retrieve the region from EC2 Metadata service.
|
||||
func (p *UseEC2IMDSRegion) getRegion(ctx context.Context) (string, bool, error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
client := p.Client
|
||||
if client == nil {
|
||||
client = imds.New(imds.Options{})
|
||||
}
|
||||
|
||||
result, err := client.GetRegion(ctx, nil)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if len(result.Region) != 0 {
|
||||
return result.Region, true, nil
|
||||
}
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
// getEC2IMDSRegion returns the value of EC2 IMDS region.
|
||||
func (o LoadOptions) getEC2IMDSRegion(ctx context.Context) (string, bool, error) {
|
||||
if o.UseEC2IMDSRegion == nil {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.UseEC2IMDSRegion.getRegion(ctx)
|
||||
}
|
||||
|
||||
// WithEC2IMDSRegion is a helper function to construct functional options
|
||||
// that enables resolving EC2IMDS region. The function takes
|
||||
// in a UseEC2IMDSRegion functional option, and can be used to set the
|
||||
// EC2IMDS client which will be used to resolve EC2IMDSRegion.
|
||||
// If no functional option is provided, an EC2IMDS client is built and used
|
||||
// by the resolver. If multiple WithEC2IMDSRegion calls are made, the last
|
||||
// call overrides the previous call values. Note that the WithRegion calls takes
|
||||
// precedence over WithEC2IMDSRegion when resolving region.
|
||||
func WithEC2IMDSRegion(fnOpts ...func(o *UseEC2IMDSRegion)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.UseEC2IMDSRegion = &UseEC2IMDSRegion{}
|
||||
|
||||
for _, fn := range fnOpts {
|
||||
fn(o.UseEC2IMDSRegion)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getCredentialsProvider returns the credentials value
|
||||
func (o LoadOptions) getCredentialsProvider(ctx context.Context) (aws.CredentialsProvider, bool, error) {
|
||||
if o.Credentials == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.Credentials, true, nil
|
||||
}
|
||||
|
||||
// WithCredentialsProvider is a helper function to construct functional options
|
||||
// that sets Credential provider value on config's LoadOptions. If credentials
|
||||
// provider is set to nil, the credentials provider value will be ignored.
|
||||
// If multiple WithCredentialsProvider calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithCredentialsProvider(v aws.CredentialsProvider) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.Credentials = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getCredentialsCacheOptionsProvider returns the wrapped function to set aws.CredentialsCacheOptions
|
||||
func (o LoadOptions) getCredentialsCacheOptions(ctx context.Context) (func(*aws.CredentialsCacheOptions), bool, error) {
|
||||
if o.CredentialsCacheOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.CredentialsCacheOptions, true, nil
|
||||
}
|
||||
|
||||
// WithCredentialsCacheOptions is a helper function to construct functional
|
||||
// options that sets a function to modify the aws.CredentialsCacheOptions the
|
||||
// aws.CredentialsCache will be configured with, if the CredentialsCache is used
|
||||
// by the configuration loader.
|
||||
//
|
||||
// If multiple WithCredentialsCacheOptions calls are made, the last call
|
||||
// overrides the previous call values.
|
||||
func WithCredentialsCacheOptions(v func(*aws.CredentialsCacheOptions)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.CredentialsCacheOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getProcessCredentialOptions returns the wrapped function to set processcreds.Options
|
||||
func (o LoadOptions) getProcessCredentialOptions(ctx context.Context) (func(*processcreds.Options), bool, error) {
|
||||
if o.ProcessCredentialOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.ProcessCredentialOptions, true, nil
|
||||
}
|
||||
|
||||
// WithProcessCredentialOptions is a helper function to construct functional options
|
||||
// that sets a function to use processcreds.Options on config's LoadOptions.
|
||||
// If process credential options is set to nil, the process credential value will
|
||||
// be ignored. If multiple WithProcessCredentialOptions calls are made, the last call
|
||||
// overrides the previous call values.
|
||||
func WithProcessCredentialOptions(v func(*processcreds.Options)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.ProcessCredentialOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getEC2RoleCredentialOptions returns the wrapped function to set the ec2rolecreds.Options
|
||||
func (o LoadOptions) getEC2RoleCredentialOptions(ctx context.Context) (func(*ec2rolecreds.Options), bool, error) {
|
||||
if o.EC2RoleCredentialOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.EC2RoleCredentialOptions, true, nil
|
||||
}
|
||||
|
||||
// WithEC2RoleCredentialOptions is a helper function to construct functional options
|
||||
// that sets a function to use ec2rolecreds.Options on config's LoadOptions. If
|
||||
// EC2 role credential options is set to nil, the EC2 role credential options value
|
||||
// will be ignored. If multiple WithEC2RoleCredentialOptions calls are made,
|
||||
// the last call overrides the previous call values.
|
||||
func WithEC2RoleCredentialOptions(v func(*ec2rolecreds.Options)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EC2RoleCredentialOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getEndpointCredentialOptions returns the wrapped function to set endpointcreds.Options
|
||||
func (o LoadOptions) getEndpointCredentialOptions(context.Context) (func(*endpointcreds.Options), bool, error) {
|
||||
if o.EndpointCredentialOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.EndpointCredentialOptions, true, nil
|
||||
}
|
||||
|
||||
// WithEndpointCredentialOptions is a helper function to construct functional options
|
||||
// that sets a function to use endpointcreds.Options on config's LoadOptions. If
|
||||
// endpoint credential options is set to nil, the endpoint credential options
|
||||
// value will be ignored. If multiple WithEndpointCredentialOptions calls are made,
|
||||
// the last call overrides the previous call values.
|
||||
func WithEndpointCredentialOptions(v func(*endpointcreds.Options)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EndpointCredentialOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getWebIdentityRoleCredentialOptions returns the wrapped function
|
||||
func (o LoadOptions) getWebIdentityRoleCredentialOptions(context.Context) (func(*stscreds.WebIdentityRoleOptions), bool, error) {
|
||||
if o.WebIdentityRoleCredentialOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.WebIdentityRoleCredentialOptions, true, nil
|
||||
}
|
||||
|
||||
// WithWebIdentityRoleCredentialOptions is a helper function to construct
|
||||
// functional options that sets a function to use stscreds.WebIdentityRoleOptions
|
||||
// on config's LoadOptions. If web identity role credentials options is set to nil,
|
||||
// the web identity role credentials value will be ignored. If multiple
|
||||
// WithWebIdentityRoleCredentialOptions calls are made, the last call
|
||||
// overrides the previous call values.
|
||||
func WithWebIdentityRoleCredentialOptions(v func(*stscreds.WebIdentityRoleOptions)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.WebIdentityRoleCredentialOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getAssumeRoleCredentialOptions returns AssumeRoleCredentialOptions from LoadOptions
|
||||
func (o LoadOptions) getAssumeRoleCredentialOptions(context.Context) (func(options *stscreds.AssumeRoleOptions), bool, error) {
|
||||
if o.AssumeRoleCredentialOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.AssumeRoleCredentialOptions, true, nil
|
||||
}
|
||||
|
||||
// WithAssumeRoleCredentialOptions is a helper function to construct
|
||||
// functional options that sets a function to use stscreds.AssumeRoleOptions
|
||||
// on config's LoadOptions. If assume role credentials options is set to nil,
|
||||
// the assume role credentials value will be ignored. If multiple
|
||||
// WithAssumeRoleCredentialOptions calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithAssumeRoleCredentialOptions(v func(*stscreds.AssumeRoleOptions)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.AssumeRoleCredentialOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getHTTPClient(ctx context.Context) (HTTPClient, bool, error) {
|
||||
if o.HTTPClient == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.HTTPClient, true, nil
|
||||
}
|
||||
|
||||
// WithHTTPClient is a helper function to construct functional options
|
||||
// that sets HTTPClient on LoadOptions. If HTTPClient is set to nil,
|
||||
// the HTTPClient value will be ignored.
|
||||
// If multiple WithHTTPClient calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithHTTPClient(v HTTPClient) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.HTTPClient = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getAPIOptions(ctx context.Context) ([]func(*middleware.Stack) error, bool, error) {
|
||||
if o.APIOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.APIOptions, true, nil
|
||||
}
|
||||
|
||||
// WithAPIOptions is a helper function to construct functional options
|
||||
// that sets APIOptions on LoadOptions. If APIOptions is set to nil, the
|
||||
// APIOptions value is ignored. If multiple WithAPIOptions calls are
|
||||
// made, the last call overrides the previous call values.
|
||||
func WithAPIOptions(v []func(*middleware.Stack) error) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
o.APIOptions = append(o.APIOptions, v...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getRetryMaxAttempts(ctx context.Context) (int, bool, error) {
|
||||
if o.RetryMaxAttempts == 0 {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
return o.RetryMaxAttempts, true, nil
|
||||
}
|
||||
|
||||
// WithRetryMaxAttempts is a helper function to construct functional options that sets
|
||||
// RetryMaxAttempts on LoadOptions. If RetryMaxAttempts is unset, the RetryMaxAttempts value is
|
||||
// ignored. If multiple WithRetryMaxAttempts calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
//
|
||||
// Will be ignored of LoadOptions.Retryer or WithRetryer are used.
|
||||
func WithRetryMaxAttempts(v int) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.RetryMaxAttempts = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getRetryMode(ctx context.Context) (aws.RetryMode, bool, error) {
|
||||
if o.RetryMode == "" {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.RetryMode, true, nil
|
||||
}
|
||||
|
||||
// WithRetryMode is a helper function to construct functional options that sets
|
||||
// RetryMode on LoadOptions. If RetryMode is unset, the RetryMode value is
|
||||
// ignored. If multiple WithRetryMode calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
//
|
||||
// Will be ignored of LoadOptions.Retryer or WithRetryer are used.
|
||||
func WithRetryMode(v aws.RetryMode) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.RetryMode = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getRetryer(ctx context.Context) (func() aws.Retryer, bool, error) {
|
||||
if o.Retryer == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.Retryer, true, nil
|
||||
}
|
||||
|
||||
// WithRetryer is a helper function to construct functional options
|
||||
// that sets Retryer on LoadOptions. If Retryer is set to nil, the
|
||||
// Retryer value is ignored. If multiple WithRetryer calls are
|
||||
// made, the last call overrides the previous call values.
|
||||
func WithRetryer(v func() aws.Retryer) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.Retryer = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getEndpointResolver(ctx context.Context) (aws.EndpointResolver, bool, error) {
|
||||
if o.EndpointResolver == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.EndpointResolver, true, nil
|
||||
}
|
||||
|
||||
// WithEndpointResolver is a helper function to construct functional options
|
||||
// that sets the EndpointResolver on LoadOptions. If the EndpointResolver is set to nil,
|
||||
// the EndpointResolver value is ignored. If multiple WithEndpointResolver calls
|
||||
// are made, the last call overrides the previous call values.
|
||||
//
|
||||
// Deprecated: See WithEndpointResolverWithOptions
|
||||
func WithEndpointResolver(v aws.EndpointResolver) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EndpointResolver = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getEndpointResolverWithOptions(ctx context.Context) (aws.EndpointResolverWithOptions, bool, error) {
|
||||
if o.EndpointResolverWithOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.EndpointResolverWithOptions, true, nil
|
||||
}
|
||||
|
||||
// WithEndpointResolverWithOptions is a helper function to construct functional options
|
||||
// that sets the EndpointResolverWithOptions on LoadOptions. If the EndpointResolverWithOptions is set to nil,
|
||||
// the EndpointResolver value is ignored. If multiple WithEndpointResolver calls
|
||||
// are made, the last call overrides the previous call values.
|
||||
func WithEndpointResolverWithOptions(v aws.EndpointResolverWithOptions) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EndpointResolverWithOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getLogger(ctx context.Context) (logging.Logger, bool, error) {
|
||||
if o.Logger == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.Logger, true, nil
|
||||
}
|
||||
|
||||
// WithLogger is a helper function to construct functional options
|
||||
// that sets Logger on LoadOptions. If Logger is set to nil, the
|
||||
// Logger value will be ignored. If multiple WithLogger calls are made,
|
||||
// the last call overrides the previous call values.
|
||||
func WithLogger(v logging.Logger) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.Logger = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getClientLogMode(ctx context.Context) (aws.ClientLogMode, bool, error) {
|
||||
if o.ClientLogMode == nil {
|
||||
return 0, false, nil
|
||||
}
|
||||
|
||||
return *o.ClientLogMode, true, nil
|
||||
}
|
||||
|
||||
// WithClientLogMode is a helper function to construct functional options
|
||||
// that sets client log mode on LoadOptions. If client log mode is set to nil,
|
||||
// the client log mode value will be ignored. If multiple WithClientLogMode calls are made,
|
||||
// the last call overrides the previous call values.
|
||||
func WithClientLogMode(v aws.ClientLogMode) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.ClientLogMode = &v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (o LoadOptions) getLogConfigurationWarnings(ctx context.Context) (v bool, found bool, err error) {
|
||||
if o.LogConfigurationWarnings == nil {
|
||||
return false, false, nil
|
||||
}
|
||||
return *o.LogConfigurationWarnings, true, nil
|
||||
}
|
||||
|
||||
// WithLogConfigurationWarnings is a helper function to construct
|
||||
// functional options that can be used to set LogConfigurationWarnings
|
||||
// on LoadOptions.
|
||||
//
|
||||
// If multiple WithLogConfigurationWarnings calls are made, the last call
|
||||
// overrides the previous call values.
|
||||
func WithLogConfigurationWarnings(v bool) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.LogConfigurationWarnings = &v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetS3UseARNRegion returns whether to allow ARNs to direct the region
|
||||
// the S3 client's requests are sent to.
|
||||
func (o LoadOptions) GetS3UseARNRegion(ctx context.Context) (v bool, found bool, err error) {
|
||||
if o.S3UseARNRegion == nil {
|
||||
return false, false, nil
|
||||
}
|
||||
return *o.S3UseARNRegion, true, nil
|
||||
}
|
||||
|
||||
// WithS3UseARNRegion is a helper function to construct functional options
|
||||
// that can be used to set S3UseARNRegion on LoadOptions.
|
||||
// If multiple WithS3UseARNRegion calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithS3UseARNRegion(v bool) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.S3UseARNRegion = &v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetEnableEndpointDiscovery returns if the EnableEndpointDiscovery flag is set.
|
||||
func (o LoadOptions) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, ok bool, err error) {
|
||||
if o.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
|
||||
return aws.EndpointDiscoveryUnset, false, nil
|
||||
}
|
||||
return o.EnableEndpointDiscovery, true, nil
|
||||
}
|
||||
|
||||
// WithEndpointDiscovery is a helper function to construct functional options
|
||||
// that can be used to enable endpoint discovery on LoadOptions for supported clients.
|
||||
// If multiple WithEndpointDiscovery calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithEndpointDiscovery(v aws.EndpointDiscoveryEnableState) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EnableEndpointDiscovery = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// getSSOProviderOptions returns AssumeRoleCredentialOptions from LoadOptions
|
||||
func (o LoadOptions) getSSOProviderOptions(context.Context) (func(options *ssocreds.Options), bool, error) {
|
||||
if o.SSOProviderOptions == nil {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
return o.SSOProviderOptions, true, nil
|
||||
}
|
||||
|
||||
// WithSSOProviderOptions is a helper function to construct
|
||||
// functional options that sets a function to use ssocreds.Options
|
||||
// on config's LoadOptions. If the SSO credential provider options is set to nil,
|
||||
// the sso provider options value will be ignored. If multiple
|
||||
// WithSSOProviderOptions calls are made, the last call overrides
|
||||
// the previous call values.
|
||||
func WithSSOProviderOptions(v func(*ssocreds.Options)) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.SSOProviderOptions = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetEC2IMDSClientEnableState implements a EC2IMDSClientEnableState options resolver interface.
|
||||
func (o LoadOptions) GetEC2IMDSClientEnableState() (imds.ClientEnableState, bool, error) {
|
||||
if o.EC2IMDSClientEnableState == imds.ClientDefaultEnableState {
|
||||
return imds.ClientDefaultEnableState, false, nil
|
||||
}
|
||||
|
||||
return o.EC2IMDSClientEnableState, true, nil
|
||||
}
|
||||
|
||||
// GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface.
|
||||
func (o LoadOptions) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
|
||||
if o.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
|
||||
return imds.EndpointModeStateUnset, false, nil
|
||||
}
|
||||
|
||||
return o.EC2IMDSEndpointMode, true, nil
|
||||
}
|
||||
|
||||
// GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface.
|
||||
func (o LoadOptions) GetEC2IMDSEndpoint() (string, bool, error) {
|
||||
if len(o.EC2IMDSEndpoint) == 0 {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
return o.EC2IMDSEndpoint, true, nil
|
||||
}
|
||||
|
||||
// WithEC2IMDSClientEnableState is a helper function to construct functional options that sets the EC2IMDSClientEnableState.
|
||||
func WithEC2IMDSClientEnableState(v imds.ClientEnableState) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EC2IMDSClientEnableState = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEC2IMDSEndpointMode is a helper function to construct functional options that sets the EC2IMDSEndpointMode.
|
||||
func WithEC2IMDSEndpointMode(v imds.EndpointModeState) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EC2IMDSEndpointMode = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithEC2IMDSEndpoint is a helper function to construct functional options that sets the EC2IMDSEndpoint.
|
||||
func WithEC2IMDSEndpoint(v string) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.EC2IMDSEndpoint = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithUseDualStackEndpoint is a helper function to construct
|
||||
// functional options that can be used to set UseDualStackEndpoint on LoadOptions.
|
||||
func WithUseDualStackEndpoint(v aws.DualStackEndpointState) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.UseDualStackEndpoint = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be
|
||||
// used for requests.
|
||||
func (o LoadOptions) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
|
||||
if o.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
|
||||
return aws.DualStackEndpointStateUnset, false, nil
|
||||
}
|
||||
return o.UseDualStackEndpoint, true, nil
|
||||
}
|
||||
|
||||
// WithUseFIPSEndpoint is a helper function to construct
|
||||
// functional options that can be used to set UseFIPSEndpoint on LoadOptions.
|
||||
func WithUseFIPSEndpoint(v aws.FIPSEndpointState) LoadOptionsFunc {
|
||||
return func(o *LoadOptions) error {
|
||||
o.UseFIPSEndpoint = v
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be
|
||||
// used for requests.
|
||||
func (o LoadOptions) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
|
||||
if o.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
|
||||
return aws.FIPSEndpointStateUnset, false, nil
|
||||
}
|
||||
return o.UseFIPSEndpoint, true, nil
|
||||
}
|
||||
|
||||
// WithDefaultsMode sets the SDK defaults configuration mode to the value provided.
|
||||
//
|
||||
// Zero or more functional options can be provided to provide configuration options for performing
|
||||
// environment discovery when using aws.DefaultsModeAuto.
|
||||
func WithDefaultsMode(mode aws.DefaultsMode, optFns ...func(options *DefaultsModeOptions)) LoadOptionsFunc {
|
||||
do := DefaultsModeOptions{
|
||||
Mode: mode,
|
||||
}
|
||||
for _, fn := range optFns {
|
||||
fn(&do)
|
||||
}
|
||||
return func(options *LoadOptions) error {
|
||||
options.DefaultsModeOptions = do
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var lookupHostFn = net.LookupHost
|
||||
|
||||
func isLoopbackHost(host string) (bool, error) {
|
||||
ip := net.ParseIP(host)
|
||||
if ip != nil {
|
||||
return ip.IsLoopback(), nil
|
||||
}
|
||||
|
||||
// Host is not an ip, perform lookup
|
||||
addrs, err := lookupHostFn(host)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(addrs) == 0 {
|
||||
return false, fmt.Errorf("no addrs found for host, %s", host)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if !net.ParseIP(addr).IsLoopback() {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func validateLocalURL(v string) error {
|
||||
u, err := url.Parse(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
host := u.Hostname()
|
||||
if len(host) == 0 {
|
||||
return fmt.Errorf("unable to parse host from local HTTP cred provider URL")
|
||||
} else if isLoopback, err := isLoopbackHost(host); err != nil {
|
||||
return fmt.Errorf("failed to resolve host %q, %v", host, err)
|
||||
} else if !isLoopback {
|
||||
return fmt.Errorf("invalid endpoint host, %q, only host resolving to loopback addresses are allowed", host)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,533 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/processcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ssocreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
"github.com/aws/smithy-go/middleware"
|
||||
)
|
||||
|
||||
// sharedConfigProfileProvider provides access to the shared config profile
|
||||
// name external configuration value.
|
||||
type sharedConfigProfileProvider interface {
|
||||
getSharedConfigProfile(ctx context.Context) (string, bool, error)
|
||||
}
|
||||
|
||||
// getSharedConfigProfile searches the configs for a sharedConfigProfileProvider
|
||||
// and returns the value if found. Returns an error if a provider fails before a
|
||||
// value is found.
|
||||
func getSharedConfigProfile(ctx context.Context, configs configs) (value string, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(sharedConfigProfileProvider); ok {
|
||||
value, found, err = p.getSharedConfigProfile(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// sharedConfigFilesProvider provides access to the shared config filesnames
|
||||
// external configuration value.
|
||||
type sharedConfigFilesProvider interface {
|
||||
getSharedConfigFiles(ctx context.Context) ([]string, bool, error)
|
||||
}
|
||||
|
||||
// getSharedConfigFiles searches the configs for a sharedConfigFilesProvider
|
||||
// and returns the value if found. Returns an error if a provider fails before a
|
||||
// value is found.
|
||||
func getSharedConfigFiles(ctx context.Context, configs configs) (value []string, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(sharedConfigFilesProvider); ok {
|
||||
value, found, err = p.getSharedConfigFiles(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// sharedCredentialsFilesProvider provides access to the shared credentials filesnames
|
||||
// external configuration value.
|
||||
type sharedCredentialsFilesProvider interface {
|
||||
getSharedCredentialsFiles(ctx context.Context) ([]string, bool, error)
|
||||
}
|
||||
|
||||
// getSharedCredentialsFiles searches the configs for a sharedCredentialsFilesProvider
|
||||
// and returns the value if found. Returns an error if a provider fails before a
|
||||
// value is found.
|
||||
func getSharedCredentialsFiles(ctx context.Context, configs configs) (value []string, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(sharedCredentialsFilesProvider); ok {
|
||||
value, found, err = p.getSharedCredentialsFiles(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// customCABundleProvider provides access to the custom CA bundle PEM bytes.
|
||||
type customCABundleProvider interface {
|
||||
getCustomCABundle(ctx context.Context) (io.Reader, bool, error)
|
||||
}
|
||||
|
||||
// getCustomCABundle searches the configs for a customCABundleProvider
|
||||
// and returns the value if found. Returns an error if a provider fails before a
|
||||
// value is found.
|
||||
func getCustomCABundle(ctx context.Context, configs configs) (value io.Reader, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(customCABundleProvider); ok {
|
||||
value, found, err = p.getCustomCABundle(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// regionProvider provides access to the region external configuration value.
|
||||
type regionProvider interface {
|
||||
getRegion(ctx context.Context) (string, bool, error)
|
||||
}
|
||||
|
||||
// getRegion searches the configs for a regionProvider and returns the value
|
||||
// if found. Returns an error if a provider fails before a value is found.
|
||||
func getRegion(ctx context.Context, configs configs) (value string, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if p, ok := cfg.(regionProvider); ok {
|
||||
value, found, err = p.getRegion(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ec2IMDSRegionProvider provides access to the ec2 imds region
|
||||
// configuration value
|
||||
type ec2IMDSRegionProvider interface {
|
||||
getEC2IMDSRegion(ctx context.Context) (string, bool, error)
|
||||
}
|
||||
|
||||
// getEC2IMDSRegion searches the configs for a ec2IMDSRegionProvider and
|
||||
// returns the value if found. Returns an error if a provider fails before
|
||||
// a value is found.
|
||||
func getEC2IMDSRegion(ctx context.Context, configs configs) (region string, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if provider, ok := cfg.(ec2IMDSRegionProvider); ok {
|
||||
region, found, err = provider.getEC2IMDSRegion(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// credentialsProviderProvider provides access to the credentials external
|
||||
// configuration value.
|
||||
type credentialsProviderProvider interface {
|
||||
getCredentialsProvider(ctx context.Context) (aws.CredentialsProvider, bool, error)
|
||||
}
|
||||
|
||||
// getCredentialsProvider searches the configs for a credentialsProviderProvider
|
||||
// and returns the value if found. Returns an error if a provider fails before a
|
||||
// value is found.
|
||||
func getCredentialsProvider(ctx context.Context, configs configs) (p aws.CredentialsProvider, found bool, err error) {
|
||||
for _, cfg := range configs {
|
||||
if provider, ok := cfg.(credentialsProviderProvider); ok {
|
||||
p, found, err = provider.getCredentialsProvider(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// credentialsCacheOptionsProvider is an interface for retrieving a function for setting
|
||||
// the aws.CredentialsCacheOptions.
|
||||
type credentialsCacheOptionsProvider interface {
|
||||
getCredentialsCacheOptions(ctx context.Context) (func(*aws.CredentialsCacheOptions), bool, error)
|
||||
}
|
||||
|
||||
// getCredentialsCacheOptionsProvider is an interface for retrieving a function for setting
|
||||
// the aws.CredentialsCacheOptions.
|
||||
func getCredentialsCacheOptionsProvider(ctx context.Context, configs configs) (
|
||||
f func(*aws.CredentialsCacheOptions), found bool, err error,
|
||||
) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(credentialsCacheOptionsProvider); ok {
|
||||
f, found, err = p.getCredentialsCacheOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// processCredentialOptions is an interface for retrieving a function for setting
|
||||
// the processcreds.Options.
|
||||
type processCredentialOptions interface {
|
||||
getProcessCredentialOptions(ctx context.Context) (func(*processcreds.Options), bool, error)
|
||||
}
|
||||
|
||||
// getProcessCredentialOptions searches the slice of configs and returns the first function found
|
||||
func getProcessCredentialOptions(ctx context.Context, configs configs) (f func(*processcreds.Options), found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(processCredentialOptions); ok {
|
||||
f, found, err = p.getProcessCredentialOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ec2RoleCredentialOptionsProvider is an interface for retrieving a function
|
||||
// for setting the ec2rolecreds.Provider options.
|
||||
type ec2RoleCredentialOptionsProvider interface {
|
||||
getEC2RoleCredentialOptions(ctx context.Context) (func(*ec2rolecreds.Options), bool, error)
|
||||
}
|
||||
|
||||
// getEC2RoleCredentialProviderOptions searches the slice of configs and returns the first function found
|
||||
func getEC2RoleCredentialProviderOptions(ctx context.Context, configs configs) (f func(*ec2rolecreds.Options), found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(ec2RoleCredentialOptionsProvider); ok {
|
||||
f, found, err = p.getEC2RoleCredentialOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// defaultRegionProvider is an interface for retrieving a default region if a region was not resolved from other sources
|
||||
type defaultRegionProvider interface {
|
||||
getDefaultRegion(ctx context.Context) (string, bool, error)
|
||||
}
|
||||
|
||||
// getDefaultRegion searches the slice of configs and returns the first fallback region found
|
||||
func getDefaultRegion(ctx context.Context, configs configs) (value string, found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(defaultRegionProvider); ok {
|
||||
value, found, err = p.getDefaultRegion(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// endpointCredentialOptionsProvider is an interface for retrieving a function for setting
|
||||
// the endpointcreds.ProviderOptions.
|
||||
type endpointCredentialOptionsProvider interface {
|
||||
getEndpointCredentialOptions(ctx context.Context) (func(*endpointcreds.Options), bool, error)
|
||||
}
|
||||
|
||||
// getEndpointCredentialProviderOptions searches the slice of configs and returns the first function found
|
||||
func getEndpointCredentialProviderOptions(ctx context.Context, configs configs) (f func(*endpointcreds.Options), found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(endpointCredentialOptionsProvider); ok {
|
||||
f, found, err = p.getEndpointCredentialOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// webIdentityRoleCredentialOptionsProvider is an interface for retrieving a function for setting
|
||||
// the stscreds.WebIdentityRoleProvider.
|
||||
type webIdentityRoleCredentialOptionsProvider interface {
|
||||
getWebIdentityRoleCredentialOptions(ctx context.Context) (func(*stscreds.WebIdentityRoleOptions), bool, error)
|
||||
}
|
||||
|
||||
// getWebIdentityCredentialProviderOptions searches the slice of configs and returns the first function found
|
||||
func getWebIdentityCredentialProviderOptions(ctx context.Context, configs configs) (f func(*stscreds.WebIdentityRoleOptions), found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(webIdentityRoleCredentialOptionsProvider); ok {
|
||||
f, found, err = p.getWebIdentityRoleCredentialOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// assumeRoleCredentialOptionsProvider is an interface for retrieving a function for setting
|
||||
// the stscreds.AssumeRoleOptions.
|
||||
type assumeRoleCredentialOptionsProvider interface {
|
||||
getAssumeRoleCredentialOptions(ctx context.Context) (func(*stscreds.AssumeRoleOptions), bool, error)
|
||||
}
|
||||
|
||||
// getAssumeRoleCredentialProviderOptions searches the slice of configs and returns the first function found
|
||||
func getAssumeRoleCredentialProviderOptions(ctx context.Context, configs configs) (f func(*stscreds.AssumeRoleOptions), found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(assumeRoleCredentialOptionsProvider); ok {
|
||||
f, found, err = p.getAssumeRoleCredentialOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HTTPClient is an HTTP client implementation
|
||||
type HTTPClient interface {
|
||||
Do(*http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// httpClientProvider is an interface for retrieving HTTPClient
|
||||
type httpClientProvider interface {
|
||||
getHTTPClient(ctx context.Context) (HTTPClient, bool, error)
|
||||
}
|
||||
|
||||
// getHTTPClient searches the slice of configs and returns the HTTPClient set on configs
|
||||
func getHTTPClient(ctx context.Context, configs configs) (client HTTPClient, found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(httpClientProvider); ok {
|
||||
client, found, err = p.getHTTPClient(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// apiOptionsProvider is an interface for retrieving APIOptions
|
||||
type apiOptionsProvider interface {
|
||||
getAPIOptions(ctx context.Context) ([]func(*middleware.Stack) error, bool, error)
|
||||
}
|
||||
|
||||
// getAPIOptions searches the slice of configs and returns the APIOptions set on configs
|
||||
func getAPIOptions(ctx context.Context, configs configs) (apiOptions []func(*middleware.Stack) error, found bool, err error) {
|
||||
for _, config := range configs {
|
||||
if p, ok := config.(apiOptionsProvider); ok {
|
||||
// retrieve APIOptions from configs and set it on cfg
|
||||
apiOptions, found, err = p.getAPIOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// endpointResolverProvider is an interface for retrieving an aws.EndpointResolver from a configuration source
|
||||
type endpointResolverProvider interface {
|
||||
getEndpointResolver(ctx context.Context) (aws.EndpointResolver, bool, error)
|
||||
}
|
||||
|
||||
// getEndpointResolver searches the provided config sources for a EndpointResolverFunc that can be used
|
||||
// to configure the aws.Config.EndpointResolver value.
|
||||
func getEndpointResolver(ctx context.Context, configs configs) (f aws.EndpointResolver, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(endpointResolverProvider); ok {
|
||||
f, found, err = p.getEndpointResolver(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// endpointResolverWithOptionsProvider is an interface for retrieving an aws.EndpointResolverWithOptions from a configuration source
|
||||
type endpointResolverWithOptionsProvider interface {
|
||||
getEndpointResolverWithOptions(ctx context.Context) (aws.EndpointResolverWithOptions, bool, error)
|
||||
}
|
||||
|
||||
// getEndpointResolver searches the provided config sources for a EndpointResolverFunc that can be used
|
||||
// to configure the aws.Config.EndpointResolver value.
|
||||
func getEndpointResolverWithOptions(ctx context.Context, configs configs) (f aws.EndpointResolverWithOptions, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(endpointResolverWithOptionsProvider); ok {
|
||||
f, found, err = p.getEndpointResolverWithOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// loggerProvider is an interface for retrieving a logging.Logger from a configuration source.
|
||||
type loggerProvider interface {
|
||||
getLogger(ctx context.Context) (logging.Logger, bool, error)
|
||||
}
|
||||
|
||||
// getLogger searches the provided config sources for a logging.Logger that can be used
|
||||
// to configure the aws.Config.Logger value.
|
||||
func getLogger(ctx context.Context, configs configs) (l logging.Logger, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(loggerProvider); ok {
|
||||
l, found, err = p.getLogger(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// clientLogModeProvider is an interface for retrieving the aws.ClientLogMode from a configuration source.
|
||||
type clientLogModeProvider interface {
|
||||
getClientLogMode(ctx context.Context) (aws.ClientLogMode, bool, error)
|
||||
}
|
||||
|
||||
func getClientLogMode(ctx context.Context, configs configs) (m aws.ClientLogMode, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(clientLogModeProvider); ok {
|
||||
m, found, err = p.getClientLogMode(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// retryProvider is an configuration provider for custom Retryer.
|
||||
type retryProvider interface {
|
||||
getRetryer(ctx context.Context) (func() aws.Retryer, bool, error)
|
||||
}
|
||||
|
||||
func getRetryer(ctx context.Context, configs configs) (v func() aws.Retryer, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(retryProvider); ok {
|
||||
v, found, err = p.getRetryer(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// logConfigurationWarningsProvider is an configuration provider for
|
||||
// retrieving a boolean indicating whether configuration issues should
|
||||
// be logged when loading from config sources
|
||||
type logConfigurationWarningsProvider interface {
|
||||
getLogConfigurationWarnings(ctx context.Context) (bool, bool, error)
|
||||
}
|
||||
|
||||
func getLogConfigurationWarnings(ctx context.Context, configs configs) (v bool, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(logConfigurationWarningsProvider); ok {
|
||||
v, found, err = p.getLogConfigurationWarnings(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ssoCredentialOptionsProvider is an interface for retrieving a function for setting
|
||||
// the ssocreds.Options.
|
||||
type ssoCredentialOptionsProvider interface {
|
||||
getSSOProviderOptions(context.Context) (func(*ssocreds.Options), bool, error)
|
||||
}
|
||||
|
||||
func getSSOProviderOptions(ctx context.Context, configs configs) (v func(options *ssocreds.Options), found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(ssoCredentialOptionsProvider); ok {
|
||||
v, found, err = p.getSSOProviderOptions(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, found, err
|
||||
}
|
||||
|
||||
type defaultsModeIMDSClientProvider interface {
|
||||
getDefaultsModeIMDSClient(context.Context) (*imds.Client, bool, error)
|
||||
}
|
||||
|
||||
func getDefaultsModeIMDSClient(ctx context.Context, configs configs) (v *imds.Client, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(defaultsModeIMDSClientProvider); ok {
|
||||
v, found, err = p.getDefaultsModeIMDSClient(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, found, err
|
||||
}
|
||||
|
||||
type defaultsModeProvider interface {
|
||||
getDefaultsMode(context.Context) (aws.DefaultsMode, bool, error)
|
||||
}
|
||||
|
||||
func getDefaultsMode(ctx context.Context, configs configs) (v aws.DefaultsMode, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(defaultsModeProvider); ok {
|
||||
v, found, err = p.getDefaultsMode(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, found, err
|
||||
}
|
||||
|
||||
type retryMaxAttemptsProvider interface {
|
||||
GetRetryMaxAttempts(context.Context) (int, bool, error)
|
||||
}
|
||||
|
||||
func getRetryMaxAttempts(ctx context.Context, configs configs) (v int, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(retryMaxAttemptsProvider); ok {
|
||||
v, found, err = p.GetRetryMaxAttempts(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, found, err
|
||||
}
|
||||
|
||||
type retryModeProvider interface {
|
||||
GetRetryMode(context.Context) (aws.RetryMode, bool, error)
|
||||
}
|
||||
|
||||
func getRetryMode(ctx context.Context, configs configs) (v aws.RetryMode, found bool, err error) {
|
||||
for _, c := range configs {
|
||||
if p, ok := c.(retryModeProvider); ok {
|
||||
v, found, err = p.GetRetryMode(ctx)
|
||||
if err != nil || found {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return v, found, err
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/smithy-go/logging"
|
||||
)
|
||||
|
||||
// resolveDefaultAWSConfig will write default configuration values into the cfg
|
||||
// value. It will write the default values, overwriting any previous value.
|
||||
//
|
||||
// This should be used as the first resolver in the slice of resolvers when
|
||||
// resolving external configuration.
|
||||
func resolveDefaultAWSConfig(ctx context.Context, cfg *aws.Config, cfgs configs) error {
|
||||
var sources []interface{}
|
||||
for _, s := range cfgs {
|
||||
sources = append(sources, s)
|
||||
}
|
||||
|
||||
*cfg = aws.Config{
|
||||
Credentials: aws.AnonymousCredentials{},
|
||||
Logger: logging.NewStandardLogger(os.Stderr),
|
||||
ConfigSources: sources,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveCustomCABundle extracts the first instance of a custom CA bundle filename
|
||||
// from the external configurations. It will update the HTTP Client's builder
|
||||
// to be configured with the custom CA bundle.
|
||||
//
|
||||
// Config provider used:
|
||||
// * customCABundleProvider
|
||||
func resolveCustomCABundle(ctx context.Context, cfg *aws.Config, cfgs configs) error {
|
||||
pemCerts, found, err := getCustomCABundle(ctx, cfgs)
|
||||
if err != nil {
|
||||
// TODO error handling, What is the best way to handle this?
|
||||
// capture previous errors continue. error out if all errors
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cfg.HTTPClient == nil {
|
||||
cfg.HTTPClient = awshttp.NewBuildableClient()
|
||||
}
|
||||
|
||||
trOpts, ok := cfg.HTTPClient.(*awshttp.BuildableClient)
|
||||
if !ok {
|
||||
return fmt.Errorf("unable to add custom RootCAs HTTPClient, "+
|
||||
"has no WithTransportOptions, %T", cfg.HTTPClient)
|
||||
}
|
||||
|
||||
var appendErr error
|
||||
client := trOpts.WithTransportOptions(func(tr *http.Transport) {
|
||||
if tr.TLSClientConfig == nil {
|
||||
tr.TLSClientConfig = &tls.Config{}
|
||||
}
|
||||
if tr.TLSClientConfig.RootCAs == nil {
|
||||
tr.TLSClientConfig.RootCAs = x509.NewCertPool()
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(pemCerts)
|
||||
if err != nil {
|
||||
appendErr = fmt.Errorf("failed to read custom CA bundle PEM file")
|
||||
}
|
||||
|
||||
if !tr.TLSClientConfig.RootCAs.AppendCertsFromPEM(b) {
|
||||
appendErr = fmt.Errorf("failed to load custom CA bundle PEM file")
|
||||
}
|
||||
})
|
||||
if appendErr != nil {
|
||||
return appendErr
|
||||
}
|
||||
|
||||
cfg.HTTPClient = client
|
||||
return err
|
||||
}
|
||||
|
||||
// resolveRegion extracts the first instance of a Region from the configs slice.
|
||||
//
|
||||
// Config providers used:
|
||||
// * regionProvider
|
||||
func resolveRegion(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
v, found, err := getRegion(ctx, configs)
|
||||
if err != nil {
|
||||
// TODO error handling, What is the best way to handle this?
|
||||
// capture previous errors continue. error out if all errors
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.Region = v
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveDefaultRegion extracts the first instance of a default region and sets `aws.Config.Region` to the default
|
||||
// region if region had not been resolved from other sources.
|
||||
func resolveDefaultRegion(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
if len(cfg.Region) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, found, err := getDefaultRegion(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.Region = v
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveHTTPClient extracts the first instance of a HTTPClient and sets `aws.Config.HTTPClient` to the HTTPClient instance
|
||||
// if one has not been resolved from other sources.
|
||||
func resolveHTTPClient(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
c, found, err := getHTTPClient(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.HTTPClient = c
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveAPIOptions extracts the first instance of APIOptions and sets `aws.Config.APIOptions` to the resolved API options
|
||||
// if one has not been resolved from other sources.
|
||||
func resolveAPIOptions(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
o, found, err := getAPIOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.APIOptions = o
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveEndpointResolver extracts the first instance of a EndpointResolverFunc from the config slice
|
||||
// and sets the functions result on the aws.Config.EndpointResolver
|
||||
func resolveEndpointResolver(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
endpointResolver, found, err := getEndpointResolver(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.EndpointResolver = endpointResolver
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveEndpointResolver extracts the first instance of a EndpointResolverFunc from the config slice
|
||||
// and sets the functions result on the aws.Config.EndpointResolver
|
||||
func resolveEndpointResolverWithOptions(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
endpointResolver, found, err := getEndpointResolverWithOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.EndpointResolverWithOptions = endpointResolver
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveLogger(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
logger, found, err := getLogger(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.Logger = logger
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveClientLogMode(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
mode, found, err := getClientLogMode(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.ClientLogMode = mode
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveRetryer(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
retryer, found, err := getRetryer(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if found {
|
||||
cfg.Retryer = retryer
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only load the retry options if a custom retryer has not be specified.
|
||||
if err = resolveRetryMaxAttempts(ctx, cfg, configs); err != nil {
|
||||
return err
|
||||
}
|
||||
return resolveRetryMode(ctx, cfg, configs)
|
||||
}
|
||||
|
||||
func resolveEC2IMDSRegion(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
if len(cfg.Region) > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
region, found, err := getEC2IMDSRegion(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg.Region = region
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveDefaultsModeOptions(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
defaultsMode, found, err := getDefaultsMode(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
defaultsMode = aws.DefaultsModeLegacy
|
||||
}
|
||||
|
||||
var environment aws.RuntimeEnvironment
|
||||
if defaultsMode == aws.DefaultsModeAuto {
|
||||
envConfig, _, _ := getAWSConfigSources(configs)
|
||||
|
||||
client, found, err := getDefaultsModeIMDSClient(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !found {
|
||||
client = imds.NewFromConfig(*cfg)
|
||||
}
|
||||
|
||||
environment, err = resolveDefaultsModeRuntimeEnvironment(ctx, envConfig, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cfg.DefaultsMode = defaultsMode
|
||||
cfg.RuntimeEnvironment = environment
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveRetryMaxAttempts(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
maxAttempts, found, err := getRetryMaxAttempts(ctx, configs)
|
||||
if err != nil || !found {
|
||||
return err
|
||||
}
|
||||
cfg.RetryMaxAttempts = maxAttempts
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveRetryMode(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
retryMode, found, err := getRetryMode(ctx, configs)
|
||||
if err != nil || !found {
|
||||
return err
|
||||
}
|
||||
cfg.RetryMode = retryMode
|
||||
|
||||
return nil
|
||||
}
|
470
vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go
generated
vendored
Normal file
470
vendor/github.com/aws/aws-sdk-go-v2/config/resolve_credentials.go
generated
vendored
Normal file
|
@ -0,0 +1,470 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/processcreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/ssocreds"
|
||||
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
|
||||
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sso"
|
||||
"github.com/aws/aws-sdk-go-v2/service/sts"
|
||||
)
|
||||
|
||||
const (
|
||||
// valid credential source values
|
||||
credSourceEc2Metadata = "Ec2InstanceMetadata"
|
||||
credSourceEnvironment = "Environment"
|
||||
credSourceECSContainer = "EcsContainer"
|
||||
)
|
||||
|
||||
var (
|
||||
ecsContainerEndpoint = "http://169.254.170.2" // not constant to allow for swapping during unit-testing
|
||||
)
|
||||
|
||||
// resolveCredentials extracts a credential provider from slice of config sources.
|
||||
//
|
||||
// If an explict credential provider is not found the resolver will fallback to resolving
|
||||
// credentials by extracting a credential provider from EnvConfig and SharedConfig.
|
||||
func resolveCredentials(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
found, err := resolveCredentialProvider(ctx, cfg, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = resolveCredentialChain(ctx, cfg, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolveCredentialProvider extracts the first instance of Credentials from the
|
||||
// config slices.
|
||||
//
|
||||
// The resolved CredentialProvider will be wrapped in a cache to ensure the
|
||||
// credentials are only refreshed when needed. This also protects the
|
||||
// credential provider to be used concurrently.
|
||||
//
|
||||
// Config providers used:
|
||||
// * credentialsProviderProvider
|
||||
func resolveCredentialProvider(ctx context.Context, cfg *aws.Config, configs configs) (bool, error) {
|
||||
credProvider, found, err := getCredentialsProvider(ctx, configs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !found {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, credProvider)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// resolveCredentialChain resolves a credential provider chain using EnvConfig
|
||||
// and SharedConfig if present in the slice of provided configs.
|
||||
//
|
||||
// The resolved CredentialProvider will be wrapped in a cache to ensure the
|
||||
// credentials are only refreshed when needed. This also protects the
|
||||
// credential provider to be used concurrently.
|
||||
func resolveCredentialChain(ctx context.Context, cfg *aws.Config, configs configs) (err error) {
|
||||
envConfig, sharedConfig, other := getAWSConfigSources(configs)
|
||||
|
||||
// When checking if a profile was specified programmatically we should only consider the "other"
|
||||
// configuration sources that have been provided. This ensures we correctly honor the expected credential
|
||||
// hierarchy.
|
||||
_, sharedProfileSet, err := getSharedConfigProfile(ctx, other)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case sharedProfileSet:
|
||||
err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig, other)
|
||||
case envConfig.Credentials.HasKeys():
|
||||
cfg.Credentials = credentials.StaticCredentialsProvider{Value: envConfig.Credentials}
|
||||
case len(envConfig.WebIdentityTokenFilePath) > 0:
|
||||
err = assumeWebIdentity(ctx, cfg, envConfig.WebIdentityTokenFilePath, envConfig.RoleARN, envConfig.RoleSessionName, configs)
|
||||
default:
|
||||
err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig, other)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wrap the resolved provider in a cache so the SDK will cache credentials.
|
||||
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, cfg.Credentials)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveCredsFromProfile(ctx context.Context, cfg *aws.Config, envConfig *EnvConfig, sharedConfig *SharedConfig, configs configs) (err error) {
|
||||
|
||||
switch {
|
||||
case sharedConfig.Source != nil:
|
||||
// Assume IAM role with credentials source from a different profile.
|
||||
err = resolveCredsFromProfile(ctx, cfg, envConfig, sharedConfig.Source, configs)
|
||||
|
||||
case sharedConfig.Credentials.HasKeys():
|
||||
// Static Credentials from Shared Config/Credentials file.
|
||||
cfg.Credentials = credentials.StaticCredentialsProvider{
|
||||
Value: sharedConfig.Credentials,
|
||||
}
|
||||
|
||||
case len(sharedConfig.CredentialSource) != 0:
|
||||
err = resolveCredsFromSource(ctx, cfg, envConfig, sharedConfig, configs)
|
||||
|
||||
case len(sharedConfig.WebIdentityTokenFile) != 0:
|
||||
// Credentials from Assume Web Identity token require an IAM Role, and
|
||||
// that roll will be assumed. May be wrapped with another assume role
|
||||
// via SourceProfile.
|
||||
return assumeWebIdentity(ctx, cfg, sharedConfig.WebIdentityTokenFile, sharedConfig.RoleARN, sharedConfig.RoleSessionName, configs)
|
||||
|
||||
case sharedConfig.hasSSOConfiguration():
|
||||
err = resolveSSOCredentials(ctx, cfg, sharedConfig, configs)
|
||||
|
||||
case len(sharedConfig.CredentialProcess) != 0:
|
||||
// Get credentials from CredentialProcess
|
||||
err = processCredentials(ctx, cfg, sharedConfig, configs)
|
||||
|
||||
case len(envConfig.ContainerCredentialsEndpoint) != 0:
|
||||
err = resolveLocalHTTPCredProvider(ctx, cfg, envConfig.ContainerCredentialsEndpoint, envConfig.ContainerAuthorizationToken, configs)
|
||||
|
||||
case len(envConfig.ContainerCredentialsRelativePath) != 0:
|
||||
err = resolveHTTPCredProvider(ctx, cfg, ecsContainerURI(envConfig.ContainerCredentialsRelativePath), envConfig.ContainerAuthorizationToken, configs)
|
||||
|
||||
default:
|
||||
err = resolveEC2RoleCredentials(ctx, cfg, configs)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(sharedConfig.RoleARN) > 0 {
|
||||
return credsFromAssumeRole(ctx, cfg, sharedConfig, configs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveSSOCredentials(ctx context.Context, cfg *aws.Config, sharedConfig *SharedConfig, configs configs) error {
|
||||
if err := sharedConfig.validateSSOConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var options []func(*ssocreds.Options)
|
||||
v, found, err := getSSOProviderOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
options = append(options, v)
|
||||
}
|
||||
|
||||
cfgCopy := cfg.Copy()
|
||||
cfgCopy.Region = sharedConfig.SSORegion
|
||||
|
||||
cfg.Credentials = ssocreds.New(sso.NewFromConfig(cfgCopy), sharedConfig.SSOAccountID, sharedConfig.SSORoleName, sharedConfig.SSOStartURL, options...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ecsContainerURI(path string) string {
|
||||
return fmt.Sprintf("%s%s", ecsContainerEndpoint, path)
|
||||
}
|
||||
|
||||
func processCredentials(ctx context.Context, cfg *aws.Config, sharedConfig *SharedConfig, configs configs) error {
|
||||
var opts []func(*processcreds.Options)
|
||||
|
||||
options, found, err := getProcessCredentialOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
opts = append(opts, options)
|
||||
}
|
||||
|
||||
cfg.Credentials = processcreds.NewProvider(sharedConfig.CredentialProcess, opts...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveLocalHTTPCredProvider(ctx context.Context, cfg *aws.Config, endpointURL, authToken string, configs configs) error {
|
||||
var resolveErr error
|
||||
|
||||
parsed, err := url.Parse(endpointURL)
|
||||
if err != nil {
|
||||
resolveErr = fmt.Errorf("invalid URL, %w", err)
|
||||
} else {
|
||||
host := parsed.Hostname()
|
||||
if len(host) == 0 {
|
||||
resolveErr = fmt.Errorf("unable to parse host from local HTTP cred provider URL")
|
||||
} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
|
||||
resolveErr = fmt.Errorf("failed to resolve host %q, %v", host, loopbackErr)
|
||||
} else if !isLoopback {
|
||||
resolveErr = fmt.Errorf("invalid endpoint host, %q, only loopback hosts are allowed", host)
|
||||
}
|
||||
}
|
||||
|
||||
if resolveErr != nil {
|
||||
return resolveErr
|
||||
}
|
||||
|
||||
return resolveHTTPCredProvider(ctx, cfg, endpointURL, authToken, configs)
|
||||
}
|
||||
|
||||
func resolveHTTPCredProvider(ctx context.Context, cfg *aws.Config, url, authToken string, configs configs) error {
|
||||
optFns := []func(*endpointcreds.Options){
|
||||
func(options *endpointcreds.Options) {
|
||||
if len(authToken) != 0 {
|
||||
options.AuthorizationToken = authToken
|
||||
}
|
||||
options.APIOptions = cfg.APIOptions
|
||||
if cfg.Retryer != nil {
|
||||
options.Retryer = cfg.Retryer()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
optFn, found, err := getEndpointCredentialProviderOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
optFns = append(optFns, optFn)
|
||||
}
|
||||
|
||||
provider := endpointcreds.New(url, optFns...)
|
||||
|
||||
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider, func(options *aws.CredentialsCacheOptions) {
|
||||
options.ExpiryWindow = 5 * time.Minute
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveCredsFromSource(ctx context.Context, cfg *aws.Config, envConfig *EnvConfig, sharedCfg *SharedConfig, configs configs) (err error) {
|
||||
switch sharedCfg.CredentialSource {
|
||||
case credSourceEc2Metadata:
|
||||
return resolveEC2RoleCredentials(ctx, cfg, configs)
|
||||
|
||||
case credSourceEnvironment:
|
||||
cfg.Credentials = credentials.StaticCredentialsProvider{Value: envConfig.Credentials}
|
||||
|
||||
case credSourceECSContainer:
|
||||
if len(envConfig.ContainerCredentialsRelativePath) == 0 {
|
||||
return fmt.Errorf("EcsContainer was specified as the credential_source, but 'AWS_CONTAINER_CREDENTIALS_RELATIVE_URI' was not set")
|
||||
}
|
||||
return resolveHTTPCredProvider(ctx, cfg, ecsContainerURI(envConfig.ContainerCredentialsRelativePath), envConfig.ContainerAuthorizationToken, configs)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("credential_source values must be EcsContainer, Ec2InstanceMetadata, or Environment")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveEC2RoleCredentials(ctx context.Context, cfg *aws.Config, configs configs) error {
|
||||
optFns := make([]func(*ec2rolecreds.Options), 0, 2)
|
||||
|
||||
optFn, found, err := getEC2RoleCredentialProviderOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
optFns = append(optFns, optFn)
|
||||
}
|
||||
|
||||
optFns = append(optFns, func(o *ec2rolecreds.Options) {
|
||||
// Only define a client from config if not already defined.
|
||||
if o.Client == nil {
|
||||
o.Client = imds.NewFromConfig(*cfg)
|
||||
}
|
||||
})
|
||||
|
||||
provider := ec2rolecreds.New(optFns...)
|
||||
|
||||
cfg.Credentials, err = wrapWithCredentialsCache(ctx, configs, provider)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAWSConfigSources(cfgs configs) (*EnvConfig, *SharedConfig, configs) {
|
||||
var (
|
||||
envConfig *EnvConfig
|
||||
sharedConfig *SharedConfig
|
||||
other configs
|
||||
)
|
||||
|
||||
for i := range cfgs {
|
||||
switch c := cfgs[i].(type) {
|
||||
case EnvConfig:
|
||||
if envConfig == nil {
|
||||
envConfig = &c
|
||||
}
|
||||
case *EnvConfig:
|
||||
if envConfig == nil {
|
||||
envConfig = c
|
||||
}
|
||||
case SharedConfig:
|
||||
if sharedConfig == nil {
|
||||
sharedConfig = &c
|
||||
}
|
||||
case *SharedConfig:
|
||||
if envConfig == nil {
|
||||
sharedConfig = c
|
||||
}
|
||||
default:
|
||||
other = append(other, c)
|
||||
}
|
||||
}
|
||||
|
||||
if envConfig == nil {
|
||||
envConfig = &EnvConfig{}
|
||||
}
|
||||
|
||||
if sharedConfig == nil {
|
||||
sharedConfig = &SharedConfig{}
|
||||
}
|
||||
|
||||
return envConfig, sharedConfig, other
|
||||
}
|
||||
|
||||
// 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.
|
||||
type AssumeRoleTokenProviderNotSetError struct{}
|
||||
|
||||
// Error is the error message
|
||||
func (e AssumeRoleTokenProviderNotSetError) Error() string {
|
||||
return fmt.Sprintf("assume role with MFA enabled, but AssumeRoleTokenProvider session option not set.")
|
||||
}
|
||||
|
||||
func assumeWebIdentity(ctx context.Context, cfg *aws.Config, filepath string, roleARN, sessionName string, configs configs) error {
|
||||
if len(filepath) == 0 {
|
||||
return fmt.Errorf("token file path is not set")
|
||||
}
|
||||
|
||||
if len(roleARN) == 0 {
|
||||
return fmt.Errorf("role ARN is not set")
|
||||
}
|
||||
|
||||
optFns := []func(*stscreds.WebIdentityRoleOptions){
|
||||
func(options *stscreds.WebIdentityRoleOptions) {
|
||||
options.RoleSessionName = sessionName
|
||||
},
|
||||
}
|
||||
|
||||
optFn, found, err := getWebIdentityCredentialProviderOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
optFns = append(optFns, optFn)
|
||||
}
|
||||
|
||||
provider := stscreds.NewWebIdentityRoleProvider(sts.NewFromConfig(*cfg), roleARN, stscreds.IdentityTokenFile(filepath), optFns...)
|
||||
|
||||
cfg.Credentials = provider
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func credsFromAssumeRole(ctx context.Context, cfg *aws.Config, sharedCfg *SharedConfig, configs configs) (err error) {
|
||||
optFns := []func(*stscreds.AssumeRoleOptions){
|
||||
func(options *stscreds.AssumeRoleOptions) {
|
||||
options.RoleSessionName = sharedCfg.RoleSessionName
|
||||
if sharedCfg.RoleDurationSeconds != nil {
|
||||
if *sharedCfg.RoleDurationSeconds/time.Minute > 15 {
|
||||
options.Duration = *sharedCfg.RoleDurationSeconds
|
||||
}
|
||||
}
|
||||
// Assume role with external ID
|
||||
if len(sharedCfg.ExternalID) > 0 {
|
||||
options.ExternalID = aws.String(sharedCfg.ExternalID)
|
||||
}
|
||||
|
||||
// Assume role with MFA
|
||||
if len(sharedCfg.MFASerial) != 0 {
|
||||
options.SerialNumber = aws.String(sharedCfg.MFASerial)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
optFn, found, err := getAssumeRoleCredentialProviderOptions(ctx, configs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if found {
|
||||
optFns = append(optFns, optFn)
|
||||
}
|
||||
|
||||
{
|
||||
// Synthesize options early to validate configuration errors sooner to ensure a token provider
|
||||
// is present if the SerialNumber was set.
|
||||
var o stscreds.AssumeRoleOptions
|
||||
for _, fn := range optFns {
|
||||
fn(&o)
|
||||
}
|
||||
if o.TokenProvider == nil && o.SerialNumber != nil {
|
||||
return AssumeRoleTokenProviderNotSetError{}
|
||||
}
|
||||
}
|
||||
|
||||
cfg.Credentials = stscreds.NewAssumeRoleProvider(sts.NewFromConfig(*cfg), sharedCfg.RoleARN, optFns...)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// wrapWithCredentialsCache will wrap provider with an aws.CredentialsCache
|
||||
// with the provided options if the provider is not already a
|
||||
// aws.CredentialsCache.
|
||||
func wrapWithCredentialsCache(
|
||||
ctx context.Context,
|
||||
cfgs configs,
|
||||
provider aws.CredentialsProvider,
|
||||
optFns ...func(options *aws.CredentialsCacheOptions),
|
||||
) (aws.CredentialsProvider, error) {
|
||||
_, ok := provider.(*aws.CredentialsCache)
|
||||
if ok {
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
credCacheOptions, found, err := getCredentialsCacheOptionsProvider(ctx, cfgs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// force allocation of a new slice if the additional options are
|
||||
// needed, to prevent overwriting the passed in slice of options.
|
||||
optFns = optFns[:len(optFns):len(optFns)]
|
||||
if found {
|
||||
optFns = append(optFns, credCacheOptions)
|
||||
}
|
||||
|
||||
return aws.NewCredentialsCache(provider, optFns...), nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue