Update vendored deps, including AWS SDK, openpgp, ftp, ...

This commit is contained in:
Andrey Smirnov
2018-04-05 17:46:45 +03:00
parent cef4fefc40
commit 0e6ee35942
1497 changed files with 450721 additions and 68034 deletions
Generated
+14 -10
View File
@@ -44,6 +44,8 @@
"aws/request", "aws/request",
"aws/session", "aws/session",
"aws/signer/v4", "aws/signer/v4",
"internal/sdkio",
"internal/sdkrand",
"internal/shareddefaults", "internal/shareddefaults",
"private/protocol", "private/protocol",
"private/protocol/query", "private/protocol/query",
@@ -54,8 +56,8 @@
"service/s3", "service/s3",
"service/sts" "service/sts"
] ]
revision = "c652f9369083515c3ddf1fbaf6df68da2c101545" revision = "7f68df2a5baf19398d17def23a514a6e617e5937"
version = "v1.12.1" version = "v1.13.28"
[[projects]] [[projects]]
name = "github.com/cheggaaa/pb" name = "github.com/cheggaaa/pb"
@@ -98,16 +100,16 @@
revision = "553a641470496b2327abcac10b36396bd98e45c9" revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]] [[projects]]
branch = "master"
name = "github.com/h2non/filetype" name = "github.com/h2non/filetype"
packages = ["matchers"] packages = ["matchers"]
revision = "0df83c38d14ff5f653d419d480eaac286ccbc823" revision = "cc14fdc9ca0e4c2bafad7458f6ff79fd3947cfbb"
version = "v1.0.5"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "github.com/jlaffaye/ftp" name = "github.com/jlaffaye/ftp"
packages = ["."] packages = ["."]
revision = "7b85eb4638a2c0473acefcfb929a98f879c15c86" revision = "2403248fa8cc9f7909862627aa7337f13f8e0bf1"
[[projects]] [[projects]]
name = "github.com/jmespath/go-jmespath" name = "github.com/jmespath/go-jmespath"
@@ -218,7 +220,7 @@
"leveldb/table", "leveldb/table",
"leveldb/util" "leveldb/util"
] ]
revision = "549b6d6b1c0419617182954dd77770f2e2685ed5" revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]] [[projects]]
name = "github.com/ugorji/go" name = "github.com/ugorji/go"
@@ -245,13 +247,16 @@
"openpgp/s2k", "openpgp/s2k",
"ssh/terminal" "ssh/terminal"
] ]
revision = "459e26527287adbc2adcc5d0d49abff9a5f315a7" revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
[[projects]] [[projects]]
branch = "master" branch = "master"
name = "golang.org/x/sys" name = "golang.org/x/sys"
packages = ["unix"] packages = [
revision = "99f16d856c9836c42d24e7ab64ea72916925fa97" "unix",
"windows"
]
revision = "1d206c9fa8975fb4cf00df1dc8bf3283dc24ba0e"
[[projects]] [[projects]]
branch = "v1" branch = "v1"
@@ -283,4 +288,3 @@
inputs-digest = "5ab2b384766e62be84d3941971a1d8e99c637f80a2cb1482b3d9704c668b549f" inputs-digest = "5ab2b384766e62be84d3941971a1d8e99c637f80a2cb1482b3d9704c668b549f"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1
 
+2 -1
View File
@@ -3,6 +3,7 @@ package s3
import ( import (
"bytes" "bytes"
"crypto/md5" "crypto/md5"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
@@ -634,7 +635,7 @@ func (objr objectResource) put(a *action) interface{} {
var expectHash []byte var expectHash []byte
if c := a.req.Header.Get("Content-MD5"); c != "" { if c := a.req.Header.Get("Content-MD5"); c != "" {
var err error var err error
expectHash, err = hex.DecodeString(c) expectHash, err = base64.StdEncoding.DecodeString(c)
if err != nil || len(expectHash) != md5.Size { if err != nil || len(expectHash) != md5.Size {
fatalError(400, "InvalidDigest", "The Content-MD5 you specified was invalid") fatalError(400, "InvalidDigest", "The Content-MD5 you specified was invalid")
} }
+10 -5
View File
@@ -3,11 +3,12 @@ language: go
sudo: required sudo: required
go: go:
- 1.5 - 1.5.x
- 1.6 - 1.6.x
- 1.7 - 1.7.x
- 1.8 - 1.8.x
- 1.9 - 1.9.x
- 1.10.x
- tip - tip
# Use Go 1.5's vendoring experiment for 1.5 tests. # Use Go 1.5's vendoring experiment for 1.5 tests.
@@ -23,3 +24,7 @@ script:
matrix: matrix:
allow_failures: allow_failures:
- go: tip - go: tip
branches:
only:
- master
+1199
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -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.
+1 -1
View File
@@ -67,7 +67,7 @@ Please be aware of the following notes prior to opening a pull request:
5. The JSON files under the SDK's `models` folder are sourced from outside the SDK. 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 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 directly on these models. If you discover an issue with the models please
create a Github [issue](issues) describing the issue. create a [GitHub issue][issues] describing the issue.
### Testing ### Testing
+4 -2
View File
@@ -6,6 +6,8 @@ aws-sdk-go is the official AWS SDK for the Go programming language.
Checkout our [release notes](https://github.com/aws/aws-sdk-go/releases) for information about the latest bug fixes, updates, and features added to the SDK. Checkout our [release notes](https://github.com/aws/aws-sdk-go/releases) for information about the latest bug fixes, updates, and features added to the SDK.
We [announced](https://aws.amazon.com/blogs/developer/aws-sdk-for-go-2-0-developer-preview/) the Developer Preview for the [v2 AWS SDK for Go](https://github.com/aws/aws-sdk-go-v2). The v2 SDK is available at https://github.com/aws/aws-sdk-go-v2, and `go get github.com/aws/aws-sdk-go-v2` via `go get`. Check out the v2 SDK's [changes and updates](https://github.com/aws/aws-sdk-go-v2/blob/master/CHANGELOG.md), and let us know what you think. We want your feedback.
## Installing ## Installing
If you are using Go 1.5 with the `GO15VENDOREXPERIMENT=1` vendoring flag, or 1.6 and higher you can use the following command to retrieve the SDK. The SDK's non-testing dependencies will be included and are vendored in the `vendor` folder. If you are using Go 1.5 with the `GO15VENDOREXPERIMENT=1` vendoring flag, or 1.6 and higher you can use the following command to retrieve the SDK. The SDK's non-testing dependencies will be included and are vendored in the `vendor` folder.
@@ -185,7 +187,7 @@ Option's SharedConfigState parameter.
})) }))
``` ```
[credentials_pkg]: ttps://docs.aws.amazon.com/sdk-for-go/api/aws/credentials [credentials_pkg]: https://docs.aws.amazon.com/sdk-for-go/api/aws/credentials
### Configuring AWS Region ### Configuring AWS Region
@@ -305,7 +307,7 @@ documentation for the errors that could be returned.
// will leak connections. // will leak connections.
defer result.Body.Close() defer result.Body.Close()
fmt.Println("Object Size:", aws.StringValue(result.ContentLength)) fmt.Println("Object Size:", aws.Int64Value(result.ContentLength))
``` ```
### API Request Pagination and Resource Waiters ### API Request Pagination and Resource Waiters
+133 -45
View File
@@ -5,11 +5,11 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"reflect"
"testing" "testing"
"time" "time"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/stretchr/testify/assert"
) )
func ExampleCopy() { func ExampleCopy() {
@@ -81,12 +81,24 @@ func TestCopy1(t *testing.T) {
awsutil.Copy(&f2, f1) awsutil.Copy(&f2, f1)
// Values are equal // Values are equal
assert.Equal(t, f2.A, f1.A) if v1, v2 := f2.A, f1.A; v1 != v2 {
assert.Equal(t, f2.B, f1.B) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.Equal(t, f2.C, f1.C) }
assert.Equal(t, f2.D, f1.D) if v1, v2 := f2.B, f1.B; !reflect.DeepEqual(v1, v2) {
assert.Equal(t, f2.E.B, f1.E.B) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.Equal(t, f2.E.D, f1.E.D) }
if v1, v2 := f2.C, f1.C; !reflect.DeepEqual(v1, v2) {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := f2.D, f1.D; !v1.Equal(*v2) {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := f2.E.B, f1.E.B; !reflect.DeepEqual(v1, v2) {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := f2.E.D, f1.E.D; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
// But pointers are not! // But pointers are not!
str3 := "nothello" str3 := "nothello"
@@ -99,14 +111,30 @@ func TestCopy1(t *testing.T) {
*f2.E.B = int3 *f2.E.B = int3
f2.E.c = 5 f2.E.c = 5
f2.E.D = 5 f2.E.D = 5
assert.NotEqual(t, f2.A, f1.A) if v1, v2 := f2.A, f1.A; v1 == v2 {
assert.NotEqual(t, f2.B, f1.B) t.Errorf("expected values to be not equivalent, but received %v", v1)
assert.NotEqual(t, f2.C, f1.C) }
assert.NotEqual(t, f2.D, f1.D) if v1, v2 := f2.B, f1.B; reflect.DeepEqual(v1, v2) {
assert.NotEqual(t, f2.E.a, f1.E.a) t.Errorf("expected values to be not equivalent, but received %v", v1)
assert.NotEqual(t, f2.E.B, f1.E.B) }
assert.NotEqual(t, f2.E.c, f1.E.c) if v1, v2 := f2.C, f1.C; reflect.DeepEqual(v1, v2) {
assert.NotEqual(t, f2.E.D, f1.E.D) t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.D, f1.D; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.E.a, f1.E.a; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.E.B, f1.E.B; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.E.c, f1.E.c; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.E.D, f1.E.D; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
} }
func TestCopyNestedWithUnexported(t *testing.T) { func TestCopyNestedWithUnexported(t *testing.T) {
@@ -125,10 +153,18 @@ func TestCopyNestedWithUnexported(t *testing.T) {
awsutil.Copy(&f2, f1) awsutil.Copy(&f2, f1)
// Values match // Values match
assert.Equal(t, f2.A, f1.A) if v1, v2 := f2.A, f1.A; v1 != v2 {
assert.NotEqual(t, f2.B, f1.B) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.NotEqual(t, f2.B.a, f1.B.a) }
assert.Equal(t, f2.B.B, f2.B.B) if v1, v2 := f2.B, f1.B; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.B.a, f1.B.a; v1 == v2 {
t.Errorf("expected values to be not equivalent, but received %v", v1)
}
if v1, v2 := f2.B.B, f2.B.B; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
} }
func TestCopyIgnoreNilMembers(t *testing.T) { func TestCopyIgnoreNilMembers(t *testing.T) {
@@ -139,34 +175,56 @@ func TestCopyIgnoreNilMembers(t *testing.T) {
} }
f := &Foo{} f := &Foo{}
assert.Nil(t, f.A) if v1 := f.A; v1 != nil {
assert.Nil(t, f.B) t.Errorf("expected nil, but received %v", v1)
assert.Nil(t, f.C) }
if v1 := f.B; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
if v1 := f.C; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
var f2 Foo var f2 Foo
awsutil.Copy(&f2, f) awsutil.Copy(&f2, f)
assert.Nil(t, f2.A) if v1 := f2.A; v1 != nil {
assert.Nil(t, f2.B) t.Errorf("expected nil, but received %v", v1)
assert.Nil(t, f2.C) }
if v1 := f2.B; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
if v1 := f2.C; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
fcopy := awsutil.CopyOf(f) fcopy := awsutil.CopyOf(f)
f3 := fcopy.(*Foo) f3 := fcopy.(*Foo)
assert.Nil(t, f3.A) if v1 := f3.A; v1 != nil {
assert.Nil(t, f3.B) t.Errorf("expected nil, but received %v", v1)
assert.Nil(t, f3.C) }
if v1 := f3.B; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
if v1 := f3.C; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
} }
func TestCopyPrimitive(t *testing.T) { func TestCopyPrimitive(t *testing.T) {
str := "hello" str := "hello"
var s string var s string
awsutil.Copy(&s, &str) awsutil.Copy(&s, &str)
assert.Equal(t, "hello", s) if v1, v2 := "hello", s; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
} }
func TestCopyNil(t *testing.T) { func TestCopyNil(t *testing.T) {
var s string var s string
awsutil.Copy(&s, nil) awsutil.Copy(&s, nil)
assert.Equal(t, "", s) if v1, v2 := "", s; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
} }
func TestCopyReader(t *testing.T) { func TestCopyReader(t *testing.T) {
@@ -174,13 +232,21 @@ func TestCopyReader(t *testing.T) {
var r io.Reader var r io.Reader
awsutil.Copy(&r, buf) awsutil.Copy(&r, buf)
b, err := ioutil.ReadAll(r) b, err := ioutil.ReadAll(r)
assert.NoError(t, err) if err != nil {
assert.Equal(t, []byte("hello world"), b) t.Errorf("expected no error, but received %v", err)
}
if v1, v2 := []byte("hello world"), b; !bytes.Equal(v1, v2) {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
// empty bytes because this is not a deep copy // empty bytes because this is not a deep copy
b, err = ioutil.ReadAll(buf) b, err = ioutil.ReadAll(buf)
assert.NoError(t, err) if err != nil {
assert.Equal(t, []byte(""), b) t.Errorf("expected no error, but received %v", err)
}
if v1, v2 := []byte(""), b; !bytes.Equal(v1, v2) {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
} }
func TestCopyDifferentStructs(t *testing.T) { func TestCopyDifferentStructs(t *testing.T) {
@@ -226,17 +292,39 @@ func TestCopyDifferentStructs(t *testing.T) {
awsutil.Copy(&f2, f1) awsutil.Copy(&f2, f1)
// Values are equal // Values are equal
assert.Equal(t, f2.A, f1.A) if v1, v2 := f2.A, f1.A; v1 != v2 {
assert.Equal(t, f2.B, f1.B) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.Equal(t, f2.C, f1.C) }
assert.Equal(t, "unique", f1.SrcUnique) if v1, v2 := f2.B, f1.B; !reflect.DeepEqual(v1, v2) {
assert.Equal(t, 1, f1.SameNameDiffType) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.Equal(t, 0, f2.DstUnique) }
assert.Equal(t, "", f2.SameNameDiffType) if v1, v2 := f2.C, f1.C; !reflect.DeepEqual(v1, v2) {
assert.Equal(t, int1, *f1.unexportedPtr) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
assert.Nil(t, f2.unexportedPtr) }
assert.Equal(t, int2, *f1.ExportedPtr) if v1, v2 := "unique", f1.SrcUnique; v1 != v2 {
assert.Equal(t, int2, *f2.ExportedPtr) t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := 1, f1.SameNameDiffType; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := 0, f2.DstUnique; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := "", f2.SameNameDiffType; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := int1, *f1.unexportedPtr; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1 := f2.unexportedPtr; v1 != nil {
t.Errorf("expected nil, but received %v", v1)
}
if v1, v2 := int2, *f1.ExportedPtr; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
if v1, v2 := int2, *f2.ExportedPtr; v1 != v2 {
t.Errorf("expected values to be equivalent but received %v and %v", v1, v2)
}
} }
func ExampleCopyOf() { func ExampleCopyOf() {
+3 -2
View File
@@ -5,7 +5,6 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/stretchr/testify/assert"
) )
func TestDeepEqual(t *testing.T) { func TestDeepEqual(t *testing.T) {
@@ -24,6 +23,8 @@ func TestDeepEqual(t *testing.T) {
} }
for i, c := range cases { for i, c := range cases {
assert.Equal(t, c.equal, awsutil.DeepEqual(c.a, c.b), "%d, a:%v b:%v, %t", i, c.a, c.b, c.equal) if awsutil.DeepEqual(c.a, c.b) != c.equal {
t.Errorf("%d, a:%v b:%v, %t", i, c.a, c.b, c.equal)
}
} }
} }
+61 -21
View File
@@ -1,10 +1,10 @@
package awsutil_test package awsutil_test
import ( import (
"strings"
"testing" "testing"
"github.com/aws/aws-sdk-go/aws/awsutil" "github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/stretchr/testify/assert"
) )
type Struct struct { type Struct struct {
@@ -50,8 +50,12 @@ func TestValueAtPathSuccess(t *testing.T) {
} }
for i, c := range testCases { for i, c := range testCases {
v, err := awsutil.ValuesAtPath(c.data, c.path) v, err := awsutil.ValuesAtPath(c.data, c.path)
assert.NoError(t, err, "case %d, expected no error, %s", i, c.path) if err != nil {
assert.Equal(t, c.expect, v, "case %d, %s", i, c.path) t.Errorf("case %v, expected no error, %v", i, c.path)
}
if e, a := c.expect, v; !awsutil.DeepEqual(e, a) {
t.Errorf("case %v, %v", i, c.path)
}
} }
} }
@@ -78,12 +82,18 @@ func TestValueAtPathFailure(t *testing.T) {
for i, c := range testCases { for i, c := range testCases {
v, err := awsutil.ValuesAtPath(c.data, c.path) v, err := awsutil.ValuesAtPath(c.data, c.path)
if c.errContains != "" { if c.errContains != "" {
assert.Contains(t, err.Error(), c.errContains, "case %d, expected error, %s", i, c.path) if !strings.Contains(err.Error(), c.errContains) {
t.Errorf("case %v, expected error, %v", i, c.path)
}
continue continue
} else { } else {
assert.NoError(t, err, "case %d, expected no error, %s", i, c.path) if err != nil {
t.Errorf("case %v, expected no error, %v", i, c.path)
}
}
if e, a := c.expect, v; !awsutil.DeepEqual(e, a) {
t.Errorf("case %v, %v", i, c.path)
} }
assert.Equal(t, c.expect, v, "case %d, %s", i, c.path)
} }
} }
@@ -92,51 +102,81 @@ func TestSetValueAtPathSuccess(t *testing.T) {
awsutil.SetValueAtPath(&s, "C", "test1") awsutil.SetValueAtPath(&s, "C", "test1")
awsutil.SetValueAtPath(&s, "B.B.C", "test2") awsutil.SetValueAtPath(&s, "B.B.C", "test2")
awsutil.SetValueAtPath(&s, "B.D.C", "test3") awsutil.SetValueAtPath(&s, "B.D.C", "test3")
assert.Equal(t, "test1", s.C) if e, a := "test1", s.C; e != a {
assert.Equal(t, "test2", s.B.B.C) t.Errorf("expected %v, but received %v", e, a)
assert.Equal(t, "test3", s.B.D.C) }
if e, a := "test2", s.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
if e, a := "test3", s.B.D.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
awsutil.SetValueAtPath(&s, "B.*.C", "test0") awsutil.SetValueAtPath(&s, "B.*.C", "test0")
assert.Equal(t, "test0", s.B.B.C) if e, a := "test0", s.B.B.C; e != a {
assert.Equal(t, "test0", s.B.D.C) t.Errorf("expected %v, but received %v", e, a)
}
if e, a := "test0", s.B.D.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
var s2 Struct var s2 Struct
awsutil.SetValueAtPath(&s2, "b.b.c", "test0") awsutil.SetValueAtPath(&s2, "b.b.c", "test0")
assert.Equal(t, "test0", s2.B.B.C) if e, a := "test0", s2.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
awsutil.SetValueAtPath(&s2, "A", []Struct{{}}) awsutil.SetValueAtPath(&s2, "A", []Struct{{}})
assert.Equal(t, []Struct{{}}, s2.A) if e, a := []Struct{{}}, s2.A; !awsutil.DeepEqual(e, a) {
t.Errorf("expected %v, but received %v", e, a)
}
str := "foo" str := "foo"
s3 := Struct{} s3 := Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", str) awsutil.SetValueAtPath(&s3, "b.b.c", str)
assert.Equal(t, "foo", s3.B.B.C) if e, a := "foo", s3.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s3 = Struct{B: &Struct{B: &Struct{C: str}}} s3 = Struct{B: &Struct{B: &Struct{C: str}}}
awsutil.SetValueAtPath(&s3, "b.b.c", nil) awsutil.SetValueAtPath(&s3, "b.b.c", nil)
assert.Equal(t, "", s3.B.B.C) if e, a := "", s3.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s3 = Struct{} s3 = Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", nil) awsutil.SetValueAtPath(&s3, "b.b.c", nil)
assert.Equal(t, "", s3.B.B.C) if e, a := "", s3.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s3 = Struct{} s3 = Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", &str) awsutil.SetValueAtPath(&s3, "b.b.c", &str)
assert.Equal(t, "foo", s3.B.B.C) if e, a := "foo", s3.B.B.C; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
var s4 struct{ Name *string } var s4 struct{ Name *string }
awsutil.SetValueAtPath(&s4, "Name", str) awsutil.SetValueAtPath(&s4, "Name", str)
assert.Equal(t, str, *s4.Name) if e, a := str, *s4.Name; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s4 = struct{ Name *string }{} s4 = struct{ Name *string }{}
awsutil.SetValueAtPath(&s4, "Name", nil) awsutil.SetValueAtPath(&s4, "Name", nil)
assert.Equal(t, (*string)(nil), s4.Name) if e, a := (*string)(nil), s4.Name; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s4 = struct{ Name *string }{Name: &str} s4 = struct{ Name *string }{Name: &str}
awsutil.SetValueAtPath(&s4, "Name", nil) awsutil.SetValueAtPath(&s4, "Name", nil)
assert.Equal(t, (*string)(nil), s4.Name) if e, a := (*string)(nil), s4.Name; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
s4 = struct{ Name *string }{} s4 = struct{ Name *string }{}
awsutil.SetValueAtPath(&s4, "Name", &str) awsutil.SetValueAtPath(&s4, "Name", &str)
assert.Equal(t, str, *s4.Name) if e, a := str, *s4.Name; e != a {
t.Errorf("expected %v, but received %v", e, a)
}
} }
+6
View File
@@ -15,6 +15,12 @@ type Config struct {
Endpoint string Endpoint string
SigningRegion string SigningRegion string
SigningName string SigningName string
// States that the signing name did not come from a modeled source but
// was derived based on other data. Used by service client constructors
// to determine if the signin name can be overriden based on metadata the
// service has.
SigningNameDerived bool
} }
// ConfigProvider provides a generic way for a service client to receive // ConfigProvider provides a generic way for a service client to receive
+48 -28
View File
@@ -1,11 +1,11 @@
package client package client
import ( import (
"math/rand" "strconv"
"sync"
"time" "time"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/internal/sdkrand"
) )
// DefaultRetryer implements basic retry logic using exponential backoff for // DefaultRetryer implements basic retry logic using exponential backoff for
@@ -30,25 +30,27 @@ func (d DefaultRetryer) MaxRetries() int {
return d.NumMaxRetries return d.NumMaxRetries
} }
var seededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
// RetryRules returns the delay duration before retrying this request again // RetryRules returns the delay duration before retrying this request again
func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration {
// Set the upper limit of delay in retrying at ~five minutes // Set the upper limit of delay in retrying at ~five minutes
minTime := 30 minTime := 30
throttle := d.shouldThrottle(r) throttle := d.shouldThrottle(r)
if throttle { if throttle {
if delay, ok := getRetryDelay(r); ok {
return delay
}
minTime = 500 minTime = 500
} }
retryCount := r.RetryCount retryCount := r.RetryCount
if retryCount > 13 { if throttle && retryCount > 8 {
retryCount = 13
} else if throttle && retryCount > 8 {
retryCount = 8 retryCount = 8
} else if retryCount > 13 {
retryCount = 13
} }
delay := (1 << uint(retryCount)) * (seededRand.Intn(minTime) + minTime) delay := (1 << uint(retryCount)) * (sdkrand.SeededRand.Intn(minTime) + minTime)
return time.Duration(delay) * time.Millisecond return time.Duration(delay) * time.Millisecond
} }
@@ -60,7 +62,7 @@ func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
return *r.Retryable return *r.Retryable
} }
if r.HTTPResponse.StatusCode >= 500 { if r.HTTPResponse.StatusCode >= 500 && r.HTTPResponse.StatusCode != 501 {
return true return true
} }
return r.IsErrorRetryable() || d.shouldThrottle(r) return r.IsErrorRetryable() || d.shouldThrottle(r)
@@ -68,29 +70,47 @@ func (d DefaultRetryer) ShouldRetry(r *request.Request) bool {
// ShouldThrottle returns true if the request should be throttled. // ShouldThrottle returns true if the request should be throttled.
func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { func (d DefaultRetryer) shouldThrottle(r *request.Request) bool {
if r.HTTPResponse.StatusCode == 502 || switch r.HTTPResponse.StatusCode {
r.HTTPResponse.StatusCode == 503 || case 429:
r.HTTPResponse.StatusCode == 504 { case 502:
return true case 503:
case 504:
default:
return r.IsErrorThrottle()
} }
return r.IsErrorThrottle()
return true
} }
// lockedSource is a thread-safe implementation of rand.Source // This will look in the Retry-After header, RFC 7231, for how long
type lockedSource struct { // it will wait before attempting another request
lk sync.Mutex func getRetryDelay(r *request.Request) (time.Duration, bool) {
src rand.Source if !canUseRetryAfterHeader(r) {
return 0, false
}
delayStr := r.HTTPResponse.Header.Get("Retry-After")
if len(delayStr) == 0 {
return 0, false
}
delay, err := strconv.Atoi(delayStr)
if err != nil {
return 0, false
}
return time.Duration(delay) * time.Second, true
} }
func (r *lockedSource) Int63() (n int64) { // Will look at the status code to see if the retry header pertains to
r.lk.Lock() // the status code.
n = r.src.Int63() func canUseRetryAfterHeader(r *request.Request) bool {
r.lk.Unlock() switch r.HTTPResponse.StatusCode {
return case 429:
} case 503:
default:
return false
}
func (r *lockedSource) Seed(seed int64) { return true
r.lk.Lock()
r.src.Seed(seed)
r.lk.Unlock()
} }
+189
View File
@@ -0,0 +1,189 @@
package client
import (
"net/http"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws/request"
)
func TestRetryThrottleStatusCodes(t *testing.T) {
cases := []struct {
expectThrottle bool
expectRetry bool
r request.Request
}{
{
false,
false,
request.Request{
HTTPResponse: &http.Response{StatusCode: 200},
},
},
{
true,
true,
request.Request{
HTTPResponse: &http.Response{StatusCode: 429},
},
},
{
true,
true,
request.Request{
HTTPResponse: &http.Response{StatusCode: 502},
},
},
{
true,
true,
request.Request{
HTTPResponse: &http.Response{StatusCode: 503},
},
},
{
true,
true,
request.Request{
HTTPResponse: &http.Response{StatusCode: 504},
},
},
{
false,
true,
request.Request{
HTTPResponse: &http.Response{StatusCode: 500},
},
},
}
d := DefaultRetryer{NumMaxRetries: 10}
for i, c := range cases {
throttle := d.shouldThrottle(&c.r)
retry := d.ShouldRetry(&c.r)
if e, a := c.expectThrottle, throttle; e != a {
t.Errorf("%d: expected %v, but received %v", i, e, a)
}
if e, a := c.expectRetry, retry; e != a {
t.Errorf("%d: expected %v, but received %v", i, e, a)
}
}
}
func TestCanUseRetryAfter(t *testing.T) {
cases := []struct {
r request.Request
e bool
}{
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 200},
},
false,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 500},
},
false,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 429},
},
true,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 503},
},
true,
},
}
for i, c := range cases {
a := canUseRetryAfterHeader(&c.r)
if c.e != a {
t.Errorf("%d: expected %v, but received %v", i, c.e, a)
}
}
}
func TestGetRetryDelay(t *testing.T) {
cases := []struct {
r request.Request
e time.Duration
equal bool
ok bool
}{
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 429, Header: http.Header{"Retry-After": []string{"3600"}}},
},
3600 * time.Second,
true,
true,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 503, Header: http.Header{"Retry-After": []string{"120"}}},
},
120 * time.Second,
true,
true,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 503, Header: http.Header{"Retry-After": []string{"120"}}},
},
1 * time.Second,
false,
true,
},
{
request.Request{
HTTPResponse: &http.Response{StatusCode: 503, Header: http.Header{"Retry-After": []string{""}}},
},
0 * time.Second,
true,
false,
},
}
for i, c := range cases {
a, ok := getRetryDelay(&c.r)
if c.ok != ok {
t.Errorf("%d: expected %v, but received %v", i, c.ok, ok)
}
if (c.e != a) == c.equal {
t.Errorf("%d: expected %v, but received %v", i, c.e, a)
}
}
}
func TestRetryDelay(t *testing.T) {
r := request.Request{}
for i := 0; i < 100; i++ {
rTemp := r
rTemp.HTTPResponse = &http.Response{StatusCode: 500, Header: http.Header{"Retry-After": []string{""}}}
rTemp.RetryCount = i
a, _ := getRetryDelay(&rTemp)
if a > 5*time.Minute {
t.Errorf("retry delay should never be greater than five minutes, received %d", a)
}
}
for i := 0; i < 100; i++ {
rTemp := r
rTemp.RetryCount = i
rTemp.HTTPResponse = &http.Response{StatusCode: 503, Header: http.Header{"Retry-After": []string{""}}}
a, _ := getRetryDelay(&rTemp)
if a > 5*time.Minute {
t.Errorf("retry delay should never be greater than five minutes, received %d", a)
}
}
}
+4
View File
@@ -46,6 +46,7 @@ func (reader *teeReaderCloser) Close() error {
func logRequest(r *request.Request) { func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody) logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
bodySeekable := aws.IsReaderSeekable(r.Body)
dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody) dumpedBody, err := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if err != nil { if err != nil {
r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err)) r.Config.Logger.Log(fmt.Sprintf(logReqErrMsg, r.ClientInfo.ServiceName, r.Operation.Name, err))
@@ -53,6 +54,9 @@ func logRequest(r *request.Request) {
} }
if logBody { if logBody {
if !bodySeekable {
r.SetReaderBody(aws.ReadSeekCloser(r.HTTPRequest.Body))
}
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's // Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
// Body as a NoOpCloser and will not be reset after read by the HTTP // Body as a NoOpCloser and will not be reset after read by the HTTP
// client reader. // client reader.
+87
View File
@@ -2,8 +2,17 @@ package client
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"io/ioutil"
"reflect"
"testing" "testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
) )
type mockCloser struct { type mockCloser struct {
@@ -55,3 +64,81 @@ func TestLogWriter(t *testing.T) {
t.Errorf("Expected %q, but received %q", expected, lw.buf.String()) t.Errorf("Expected %q, but received %q", expected, lw.buf.String())
} }
} }
func TestLogRequest(t *testing.T) {
cases := []struct {
Body io.ReadSeeker
ExpectBody []byte
LogLevel aws.LogLevelType
}{
{
Body: aws.ReadSeekCloser(bytes.NewBuffer([]byte("body content"))),
ExpectBody: []byte("body content"),
},
{
Body: aws.ReadSeekCloser(bytes.NewBuffer([]byte("body content"))),
LogLevel: aws.LogDebugWithHTTPBody,
ExpectBody: []byte("body content"),
},
{
Body: bytes.NewReader([]byte("body content")),
ExpectBody: []byte("body content"),
},
{
Body: bytes.NewReader([]byte("body content")),
LogLevel: aws.LogDebugWithHTTPBody,
ExpectBody: []byte("body content"),
},
}
for i, c := range cases {
logW := bytes.NewBuffer(nil)
req := request.New(
aws.Config{
Credentials: credentials.AnonymousCredentials,
Logger: &bufLogger{w: logW},
LogLevel: aws.LogLevel(c.LogLevel),
},
metadata.ClientInfo{
Endpoint: "https://mock-service.mock-region.amazonaws.com",
},
testHandlers(),
nil,
&request.Operation{
Name: "APIName",
HTTPMethod: "POST",
HTTPPath: "/",
},
struct{}{}, nil,
)
req.SetReaderBody(c.Body)
req.Build()
logRequest(req)
b, err := ioutil.ReadAll(req.HTTPRequest.Body)
if err != nil {
t.Fatalf("%d, expect to read SDK request Body", i)
}
if e, a := c.ExpectBody, b; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %v body, got %v", i, e, a)
}
}
}
type bufLogger struct {
w *bytes.Buffer
}
func (l *bufLogger) Log(args ...interface{}) {
fmt.Fprintln(l.w, args...)
}
func testHandlers() request.Handlers {
var handlers request.Handlers
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
return handlers
}
+23 -1
View File
@@ -151,6 +151,15 @@ type Config struct {
// with accelerate. // with accelerate.
S3UseAccelerate *bool S3UseAccelerate *bool
// S3DisableContentMD5Validation config option is temporarily disabled,
// For S3 GetObject API calls, #1837.
//
// Set this to `true` to disable the S3 service client from automatically
// adding the ContentMD5 to S3 Object Put and Upload API calls. This option
// will also disable the SDK from performing object ContentMD5 validation
// on GetObject API calls.
S3DisableContentMD5Validation *bool
// Set this to `true` to disable the EC2Metadata client from overriding the // Set this to `true` to disable the EC2Metadata client from overriding the
// default http.Client's Timeout. This is helpful if you do not want the // default http.Client's Timeout. This is helpful if you do not want the
// EC2Metadata client to create a new http.Client. This options is only // EC2Metadata client to create a new http.Client. This options is only
@@ -168,7 +177,7 @@ type Config struct {
// //
EC2MetadataDisableTimeoutOverride *bool EC2MetadataDisableTimeoutOverride *bool
// Instructs the endpiont to be generated for a service client to // Instructs the endpoint to be generated for a service client to
// be the dual stack endpoint. The dual stack endpoint will support // be the dual stack endpoint. The dual stack endpoint will support
// both IPv4 and IPv6 addressing. // both IPv4 and IPv6 addressing.
// //
@@ -336,6 +345,15 @@ func (c *Config) WithS3Disable100Continue(disable bool) *Config {
func (c *Config) WithS3UseAccelerate(enable bool) *Config { func (c *Config) WithS3UseAccelerate(enable bool) *Config {
c.S3UseAccelerate = &enable c.S3UseAccelerate = &enable
return c return c
}
// WithS3DisableContentMD5Validation sets a config
// S3DisableContentMD5Validation value returning a Config pointer for chaining.
func (c *Config) WithS3DisableContentMD5Validation(enable bool) *Config {
c.S3DisableContentMD5Validation = &enable
return c
} }
// WithUseDualStack sets a config UseDualStack value returning a Config // WithUseDualStack sets a config UseDualStack value returning a Config
@@ -435,6 +453,10 @@ func mergeInConfig(dst *Config, other *Config) {
dst.S3UseAccelerate = other.S3UseAccelerate dst.S3UseAccelerate = other.S3UseAccelerate
} }
if other.S3DisableContentMD5Validation != nil {
dst.S3DisableContentMD5Validation = other.S3DisableContentMD5Validation
}
if other.UseDualStack != nil { if other.UseDualStack != nil {
dst.UseDualStack = other.UseDualStack dst.UseDualStack = other.UseDualStack
} }
+259 -88
View File
@@ -1,10 +1,9 @@
package aws package aws
import ( import (
"reflect"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
) )
var testCasesStringSlice = [][]string{ var testCasesStringSlice = [][]string{
@@ -18,14 +17,22 @@ func TestStringSlice(t *testing.T) {
continue continue
} }
out := StringSlice(in) out := StringSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := StringValueSlice(out) out2 := StringValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -39,22 +46,34 @@ func TestStringValueSlice(t *testing.T) {
continue continue
} }
out := StringValueSlice(in) out := StringValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if out[i] != "" {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := StringSlice(out) out2 := StringSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if *(out2[i]) != "" {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := *in[i], *out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -70,14 +89,22 @@ func TestStringMap(t *testing.T) {
continue continue
} }
out := StringMap(in) out := StringMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := StringValueMap(out) out2 := StringValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -91,14 +118,22 @@ func TestBoolSlice(t *testing.T) {
continue continue
} }
out := BoolSlice(in) out := BoolSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := BoolValueSlice(out) out2 := BoolValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -110,22 +145,34 @@ func TestBoolValueSlice(t *testing.T) {
continue continue
} }
out := BoolValueSlice(in) out := BoolValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if out[i] {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := BoolSlice(out) out2 := BoolSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if *(out2[i]) {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := in[i], out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -141,14 +188,22 @@ func TestBoolMap(t *testing.T) {
continue continue
} }
out := BoolMap(in) out := BoolMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := BoolValueMap(out) out2 := BoolValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -162,14 +217,22 @@ func TestIntSlice(t *testing.T) {
continue continue
} }
out := IntSlice(in) out := IntSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := IntValueSlice(out) out2 := IntValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -181,22 +244,34 @@ func TestIntValueSlice(t *testing.T) {
continue continue
} }
out := IntValueSlice(in) out := IntValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if out[i] != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := IntSlice(out) out2 := IntSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if *(out2[i]) != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := in[i], out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -212,14 +287,22 @@ func TestIntMap(t *testing.T) {
continue continue
} }
out := IntMap(in) out := IntMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := IntValueMap(out) out2 := IntValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -233,14 +316,22 @@ func TestInt64Slice(t *testing.T) {
continue continue
} }
out := Int64Slice(in) out := Int64Slice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := Int64ValueSlice(out) out2 := Int64ValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -252,22 +343,34 @@ func TestInt64ValueSlice(t *testing.T) {
continue continue
} }
out := Int64ValueSlice(in) out := Int64ValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if out[i] != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := Int64Slice(out) out2 := Int64Slice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if *(out2[i]) != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := in[i], out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -283,14 +386,22 @@ func TestInt64Map(t *testing.T) {
continue continue
} }
out := Int64Map(in) out := Int64Map(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := Int64ValueMap(out) out2 := Int64ValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -304,14 +415,22 @@ func TestFloat64Slice(t *testing.T) {
continue continue
} }
out := Float64Slice(in) out := Float64Slice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := Float64ValueSlice(out) out2 := Float64ValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -323,22 +442,34 @@ func TestFloat64ValueSlice(t *testing.T) {
continue continue
} }
out := Float64ValueSlice(in) out := Float64ValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if out[i] != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := Float64Slice(out) out2 := Float64Slice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if *(out2[i]) != 0 {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := in[i], out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -354,14 +485,22 @@ func TestFloat64Map(t *testing.T) {
continue continue
} }
out := Float64Map(in) out := Float64Map(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := Float64ValueMap(out) out2 := Float64ValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -375,14 +514,22 @@ func TestTimeSlice(t *testing.T) {
continue continue
} }
out := TimeSlice(in) out := TimeSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := TimeValueSlice(out) out2 := TimeValueSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -394,22 +541,34 @@ func TestTimeValueSlice(t *testing.T) {
continue continue
} }
out := TimeValueSlice(in) out := TimeValueSlice(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, out[i], "Unexpected value at idx %d", idx) if !out[i].IsZero() {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx) if e, a := *(in[i]), out[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
out2 := TimeSlice(out) out2 := TimeSlice(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out2 { for i := range out2 {
if in[i] == nil { if in[i] == nil {
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx) if !(*(out2[i])).IsZero() {
t.Errorf("Unexpected value at idx %d", idx)
}
} else { } else {
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx) if e, a := in[i], out2[i]; e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
} }
@@ -425,14 +584,22 @@ func TestTimeMap(t *testing.T) {
continue continue
} }
out := TimeMap(in) out := TimeMap(in)
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx) if e, a := len(out), len(in); e != a {
t.Errorf("Unexpected len at idx %d", idx)
}
for i := range out { for i := range out {
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx) if e, a := in[i], *(out[i]); e != a {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
out2 := TimeValueMap(out) out2 := TimeValueMap(out)
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx) if e, a := len(out2), len(in); e != a {
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx) t.Errorf("Unexpected len at idx %d", idx)
}
if e, a := in, out2; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected value at idx %d", idx)
}
} }
} }
@@ -458,13 +625,17 @@ var testCasesTimeValue = []TimeValueTestCase{
func TestSecondsTimeValue(t *testing.T) { func TestSecondsTimeValue(t *testing.T) {
for idx, testCase := range testCasesTimeValue { for idx, testCase := range testCasesTimeValue {
out := SecondsTimeValue(&testCase.in) out := SecondsTimeValue(&testCase.in)
assert.Equal(t, testCase.outSecs, out, "Unexpected value for time value at %d", idx) if e, a := testCase.outSecs, out; e != a {
t.Errorf("Unexpected value for time value at %d", idx)
}
} }
} }
func TestMillisecondsTimeValue(t *testing.T) { func TestMillisecondsTimeValue(t *testing.T) {
for idx, testCase := range testCasesTimeValue { for idx, testCase := range testCasesTimeValue {
out := MillisecondsTimeValue(&testCase.in) out := MillisecondsTimeValue(&testCase.in)
assert.Equal(t, testCase.outMillis, out, "Unexpected value for time value at %d", idx) if e, a := testCase.outMillis, out; e != a {
t.Errorf("Unexpected value for time value at %d", idx)
}
} }
} }
+7 -21
View File
@@ -3,12 +3,10 @@ package corehandlers
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"time" "time"
@@ -36,18 +34,13 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" { if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
length, _ = strconv.ParseInt(slength, 10, 64) length, _ = strconv.ParseInt(slength, 10, 64)
} else { } else {
switch body := r.Body.(type) { if r.Body != nil {
case nil: var err error
length = 0 length, err = aws.SeekerLen(r.Body)
case lener: if err != nil {
length = int64(body.Len()) r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err)
case io.Seeker: return
r.BodyStart, _ = body.Seek(0, 1) }
end, _ := body.Seek(0, 2)
body.Seek(r.BodyStart, 0) // make sure to seek back to original location
length = end - r.BodyStart
default:
panic("Cannot get length of body, must provide `ContentLength`")
} }
} }
@@ -60,13 +53,6 @@ var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLen
} }
}} }}
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
var SDKVersionUserAgentHandler = request.NamedHandler{
Name: "core.SDKVersionUserAgentHandler",
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
runtime.Version(), runtime.GOOS, runtime.GOARCH),
}
var reStatusCode = regexp.MustCompile(`^(\d{3})`) var reStatusCode = regexp.MustCompile(`^(\d{3})`)
// ValidateReqSigHandler is a request handler to ensure that the request's // ValidateReqSigHandler is a request handler to ensure that the request's
+67 -24
View File
@@ -7,11 +7,10 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os" "os"
"strings"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/corehandlers" "github.com/aws/aws-sdk-go/aws/corehandlers"
@@ -32,7 +31,9 @@ func TestValidateEndpointHandler(t *testing.T) {
req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil)
err := req.Build() err := req.Build()
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
} }
func TestValidateEndpointHandlerErrorRegion(t *testing.T) { func TestValidateEndpointHandlerErrorRegion(t *testing.T) {
@@ -45,8 +46,12 @@ func TestValidateEndpointHandlerErrorRegion(t *testing.T) {
req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil)
err := req.Build() err := req.Build()
assert.Error(t, err) if err == nil {
assert.Equal(t, aws.ErrMissingRegion, err) t.Errorf("expect error, got none")
}
if e, a := aws.ErrMissingRegion, err; e != a {
t.Errorf("expect %v to be %v", e, a)
}
} }
type mockCredsProvider struct { type mockCredsProvider struct {
@@ -82,18 +87,30 @@ func TestAfterRetryRefreshCreds(t *testing.T) {
}) })
svc.Handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler) svc.Handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
assert.True(t, svc.Config.Credentials.IsExpired(), "Expect to start out expired") if !svc.Config.Credentials.IsExpired() {
assert.False(t, credProvider.retrieveCalled) t.Errorf("Expect to start out expired")
}
if credProvider.retrieveCalled {
t.Errorf("expect not called")
}
req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil) req := svc.NewRequest(&request.Operation{Name: "Operation"}, nil, nil)
req.Send() req.Send()
assert.True(t, svc.Config.Credentials.IsExpired()) if !svc.Config.Credentials.IsExpired() {
assert.False(t, credProvider.retrieveCalled) t.Errorf("Expect to start out expired")
}
if credProvider.retrieveCalled {
t.Errorf("expect not called")
}
_, err := svc.Config.Credentials.Get() _, err := svc.Config.Credentials.Get()
assert.NoError(t, err) if err != nil {
assert.True(t, credProvider.retrieveCalled) t.Errorf("expect no error, got %v", err)
}
if !credProvider.retrieveCalled {
t.Errorf("expect not called")
}
} }
func TestAfterRetryWithContextCanceled(t *testing.T) { func TestAfterRetryWithContextCanceled(t *testing.T) {
@@ -202,8 +219,12 @@ func TestSendHandlerError(t *testing.T) {
r.Send() r.Send()
assert.Error(t, r.Error) if r.Error == nil {
assert.NotNil(t, r.HTTPResponse) t.Errorf("expect error, got none")
}
if r.HTTPResponse == nil {
t.Errorf("expect response, got none")
}
} }
func TestSendWithoutFollowRedirects(t *testing.T) { func TestSendWithoutFollowRedirects(t *testing.T) {
@@ -273,31 +294,47 @@ func TestValidateReqSigHandler(t *testing.T) {
corehandlers.ValidateReqSigHandler.Fn(c.Req) corehandlers.ValidateReqSigHandler.Fn(c.Req)
assert.NoError(t, c.Req.Error, "%d, expect no error", i) if c.Req.Error != nil {
assert.Equal(t, c.Resign, resigned, "%d, expected resigning to match", i) t.Errorf("expect no error, got %v", c.Req.Error)
}
if e, a := c.Resign, resigned; e != a {
t.Errorf("%d, expect %v to be %v", i, e, a)
}
} }
} }
func setupContentLengthTestServer(t *testing.T, hasContentLength bool, contentLength int64) *httptest.Server { func setupContentLengthTestServer(t *testing.T, hasContentLength bool, contentLength int64) *httptest.Server {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, ok := r.Header["Content-Length"] _, ok := r.Header["Content-Length"]
assert.Equal(t, hasContentLength, ok, "expect content length to be set, %t", hasContentLength) if e, a := hasContentLength, ok; e != a {
t.Errorf("expect %v to be %v", e, a)
}
if hasContentLength { if hasContentLength {
assert.Equal(t, contentLength, r.ContentLength) if e, a := contentLength, r.ContentLength; e != a {
t.Errorf("expect %v to be %v", e, a)
}
} }
b, err := ioutil.ReadAll(r.Body) b, err := ioutil.ReadAll(r.Body)
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
r.Body.Close() r.Body.Close()
authHeader := r.Header.Get("Authorization") authHeader := r.Header.Get("Authorization")
if hasContentLength { if hasContentLength {
assert.Contains(t, authHeader, "content-length") if e, a := "content-length", authHeader; !strings.Contains(a, e) {
t.Errorf("expect %v to be in %v", e, a)
}
} else { } else {
assert.NotContains(t, authHeader, "content-length") if e, a := "content-length", authHeader; strings.Contains(a, e) {
t.Errorf("expect %v to not be in %v", e, a)
}
} }
assert.Equal(t, contentLength, int64(len(b))) if e, a := contentLength, int64(len(b)); e != a {
t.Errorf("expect %v to be %v", e, a)
}
})) }))
return server return server
@@ -316,7 +353,9 @@ func TestBuildContentLength_ZeroBody(t *testing.T) {
Key: aws.String("keyname"), Key: aws.String("keyname"),
}) })
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
} }
func TestBuildContentLength_NegativeBody(t *testing.T) { func TestBuildContentLength_NegativeBody(t *testing.T) {
@@ -334,7 +373,9 @@ func TestBuildContentLength_NegativeBody(t *testing.T) {
req.HTTPRequest.Header.Set("Content-Length", "-1") req.HTTPRequest.Header.Set("Content-Length", "-1")
assert.NoError(t, req.Send()) if req.Error != nil {
t.Errorf("expect no error, got %v", req.Error)
}
} }
func TestBuildContentLength_WithBody(t *testing.T) { func TestBuildContentLength_WithBody(t *testing.T) {
@@ -351,5 +392,7 @@ func TestBuildContentLength_WithBody(t *testing.T) {
Body: bytes.NewReader(make([]byte, 1024)), Body: bytes.NewReader(make([]byte, 1024)),
}) })
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
} }
+52 -20
View File
@@ -3,8 +3,7 @@ package corehandlers_test
import ( import (
"fmt" "fmt"
"testing" "testing"
"reflect"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
@@ -14,7 +13,6 @@ import (
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit" "github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/service/kinesis" "github.com/aws/aws-sdk-go/service/kinesis"
"github.com/stretchr/testify/require"
) )
var testSvc = func() *client.Client { var testSvc = func() *client.Client {
@@ -113,7 +111,9 @@ func TestNoErrors(t *testing.T) {
req := testSvc.NewRequest(&request.Operation{}, input, nil) req := testSvc.NewRequest(&request.Operation{}, input, nil)
corehandlers.ValidateParametersHandler.Fn(req) corehandlers.ValidateParametersHandler.Fn(req)
require.NoError(t, req.Error) if req.Error != nil {
t.Fatalf("expect no error, got %v", req.Error)
}
} }
func TestMissingRequiredParameters(t *testing.T) { func TestMissingRequiredParameters(t *testing.T) {
@@ -121,17 +121,33 @@ func TestMissingRequiredParameters(t *testing.T) {
req := testSvc.NewRequest(&request.Operation{}, input, nil) req := testSvc.NewRequest(&request.Operation{}, input, nil)
corehandlers.ValidateParametersHandler.Fn(req) corehandlers.ValidateParametersHandler.Fn(req)
require.Error(t, req.Error) if req.Error == nil {
assert.Equal(t, "InvalidParameter", req.Error.(awserr.Error).Code()) t.Fatalf("expect error")
assert.Equal(t, "3 validation error(s) found.", req.Error.(awserr.Error).Message()) }
if e, a := "InvalidParameter", req.Error.(awserr.Error).Code(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "3 validation error(s) found.", req.Error.(awserr.Error).Message(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
errs := req.Error.(awserr.BatchedErrors).OrigErrs() errs := req.Error.(awserr.BatchedErrors).OrigErrs()
assert.Len(t, errs, 3) if e, a := 3, len(errs); e != a {
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.RequiredList.", errs[0].Error()) t.Errorf("expect %v, got %v", e, a)
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.RequiredMap.", errs[1].Error()) }
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.RequiredBool.", errs[2].Error()) if e, a := "ParamRequiredError: missing required field, StructShape.RequiredList.", errs[0].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "ParamRequiredError: missing required field, StructShape.RequiredMap.", errs[1].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "ParamRequiredError: missing required field, StructShape.RequiredBool.", errs[2].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
assert.Equal(t, "InvalidParameter: 3 validation error(s) found.\n- missing required field, StructShape.RequiredList.\n- missing required field, StructShape.RequiredMap.\n- missing required field, StructShape.RequiredBool.\n", req.Error.Error()) if e, a := "InvalidParameter: 3 validation error(s) found.\n- missing required field, StructShape.RequiredList.\n- missing required field, StructShape.RequiredMap.\n- missing required field, StructShape.RequiredBool.\n", req.Error.Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestNestedMissingRequiredParameters(t *testing.T) { func TestNestedMissingRequiredParameters(t *testing.T) {
@@ -148,15 +164,29 @@ func TestNestedMissingRequiredParameters(t *testing.T) {
req := testSvc.NewRequest(&request.Operation{}, input, nil) req := testSvc.NewRequest(&request.Operation{}, input, nil)
corehandlers.ValidateParametersHandler.Fn(req) corehandlers.ValidateParametersHandler.Fn(req)
require.Error(t, req.Error) if req.Error == nil {
assert.Equal(t, "InvalidParameter", req.Error.(awserr.Error).Code()) t.Fatalf("expect error")
assert.Equal(t, "3 validation error(s) found.", req.Error.(awserr.Error).Message()) }
if e, a := "InvalidParameter", req.Error.(awserr.Error).Code(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "3 validation error(s) found.", req.Error.(awserr.Error).Message(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
errs := req.Error.(awserr.BatchedErrors).OrigErrs() errs := req.Error.(awserr.BatchedErrors).OrigErrs()
assert.Len(t, errs, 3) if e, a := 3, len(errs); e != a {
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.RequiredList[0].Name.", errs[0].Error()) t.Errorf("expect %v, got %v", e, a)
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.RequiredMap[key2].Name.", errs[1].Error()) }
assert.Equal(t, "ParamRequiredError: missing required field, StructShape.OptionalStruct.Name.", errs[2].Error()) if e, a := "ParamRequiredError: missing required field, StructShape.RequiredList[0].Name.", errs[0].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "ParamRequiredError: missing required field, StructShape.RequiredMap[key2].Name.", errs[1].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "ParamRequiredError: missing required field, StructShape.OptionalStruct.Name.", errs[2].Error(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
type testInput struct { type testInput struct {
@@ -226,7 +256,9 @@ func TestValidateFieldMinParameter(t *testing.T) {
req := testSvc.NewRequest(&request.Operation{}, &c.in, nil) req := testSvc.NewRequest(&request.Operation{}, &c.in, nil)
corehandlers.ValidateParametersHandler.Fn(req) corehandlers.ValidateParametersHandler.Fn(req)
assert.Equal(t, c.err, req.Error, "%d case failed", i) if e, a := c.err, req.Error; !reflect.DeepEqual(e,a) {
t.Errorf("%d, expect %v, got %v", i, e, a)
}
} }
} }
+37
View File
@@ -0,0 +1,37 @@
package corehandlers
import (
"os"
"runtime"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
)
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version
// to the user agent.
var SDKVersionUserAgentHandler = request.NamedHandler{
Name: "core.SDKVersionUserAgentHandler",
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
runtime.Version(), runtime.GOOS, runtime.GOARCH),
}
const execEnvVar = `AWS_EXECUTION_ENV`
const execEnvUAKey = `exec_env`
// AddHostExecEnvUserAgentHander is a request handler appending the SDK's
// execution environment to the user agent.
//
// If the environment variable AWS_EXECUTION_ENV is set, its value will be
// appended to the user agent string.
var AddHostExecEnvUserAgentHander = request.NamedHandler{
Name: "core.AddHostExecEnvUserAgentHander",
Fn: func(r *request.Request) {
v := os.Getenv(execEnvVar)
if len(v) == 0 {
return
}
request.AddToUserAgent(r, execEnvUAKey+"/"+v)
},
}
+40
View File
@@ -0,0 +1,40 @@
package corehandlers
import (
"net/http"
"os"
"testing"
"github.com/aws/aws-sdk-go/aws/request"
)
func TestAddHostExecEnvUserAgentHander(t *testing.T) {
cases := []struct {
ExecEnv string
Expect string
}{
{ExecEnv: "Lambda", Expect: "exec_env/Lambda"},
{ExecEnv: "", Expect: ""},
{ExecEnv: "someThingCool", Expect: "exec_env/someThingCool"},
}
for i, c := range cases {
os.Clearenv()
os.Setenv(execEnvVar, c.ExecEnv)
req := &request.Request{
HTTPRequest: &http.Request{
Header: http.Header{},
},
}
AddHostExecEnvUserAgentHander.Fn(req)
if err := req.Error; err != nil {
t.Fatalf("%d, expect no error, got %v", i, err)
}
if e, a := c.Expect, req.HTTPRequest.Header.Get("User-Agent"); e != a {
t.Errorf("%d, expect %v user agent, got %v", i, e, a)
}
}
}
+33 -2
View File
@@ -9,6 +9,7 @@ package defaults
import ( import (
"fmt" "fmt"
"net"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@@ -72,6 +73,7 @@ func Handlers() request.Handlers {
handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler) handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
handlers.Validate.AfterEachFn = request.HandlerListStopOnError handlers.Validate.AfterEachFn = request.HandlerListStopOnError
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler) handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
handlers.Build.PushBackNamed(corehandlers.AddHostExecEnvUserAgentHander)
handlers.Build.AfterEachFn = request.HandlerListStopOnError handlers.Build.AfterEachFn = request.HandlerListStopOnError
handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler) handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler) handlers.Send.PushBackNamed(corehandlers.ValidateReqSigHandler)
@@ -118,14 +120,43 @@ func RemoteCredProvider(cfg aws.Config, handlers request.Handlers) credentials.P
return ec2RoleProvider(cfg, handlers) return ec2RoleProvider(cfg, handlers)
} }
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
}
for _, addr := range addrs {
if !net.ParseIP(addr).IsLoopback() {
return false, nil
}
}
return true, nil
}
func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider { func localHTTPCredProvider(cfg aws.Config, handlers request.Handlers, u string) credentials.Provider {
var errMsg string var errMsg string
parsed, err := url.Parse(u) parsed, err := url.Parse(u)
if err != nil { if err != nil {
errMsg = fmt.Sprintf("invalid URL, %v", err) errMsg = fmt.Sprintf("invalid URL, %v", err)
} else if host := aws.URLHostname(parsed); !(host == "localhost" || host == "127.0.0.1") { } else {
errMsg = fmt.Sprintf("invalid host address, %q, only localhost and 127.0.0.1 are valid.", host) host := aws.URLHostname(parsed)
if len(host) == 0 {
errMsg = "unable to parse host from local HTTP cred provider URL"
} else if isLoopback, loopbackErr := isLoopbackHost(host); loopbackErr != nil {
errMsg = fmt.Sprintf("failed to resolve host %q, %v", host, loopbackErr)
} else if !isLoopback {
errMsg = fmt.Sprintf("invalid endpoint host, %q, only loopback hosts are allowed.", host)
}
} }
if len(errMsg) > 0 { if len(errMsg) > 0 {
+30 -2
View File
@@ -13,12 +13,40 @@ import (
) )
func TestHTTPCredProvider(t *testing.T) { func TestHTTPCredProvider(t *testing.T) {
origFn := lookupHostFn
defer func() { lookupHostFn = origFn }()
lookupHostFn = func(host string) ([]string, error) {
m := map[string]struct {
Addrs []string
Err error
}{
"localhost": {Addrs: []string{"::1", "127.0.0.1"}},
"actuallylocal": {Addrs: []string{"127.0.0.2"}},
"notlocal": {Addrs: []string{"::1", "127.0.0.1", "192.168.1.10"}},
"www.example.com": {Addrs: []string{"10.10.10.10"}},
}
h, ok := m[host]
if !ok {
t.Fatalf("unknown host in test, %v", host)
return nil, fmt.Errorf("unknown host")
}
return h.Addrs, h.Err
}
cases := []struct { cases := []struct {
Host string Host string
Fail bool Fail bool
}{ }{
{"localhost", false}, {"127.0.0.1", false}, {"localhost", false},
{"www.example.com", true}, {"169.254.170.2", true}, {"actuallylocal", false},
{"127.0.0.1", false},
{"127.1.1.1", false},
{"[::1]", false},
{"www.example.com", true},
{"169.254.170.2", true},
} }
defer os.Clearenv() defer os.Clearenv()
+82 -35
View File
@@ -11,8 +11,6 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/ec2metadata"
@@ -71,8 +69,12 @@ func TestEndpoint(t *testing.T) {
} }
req := c.NewRequest(op, nil, nil) req := c.NewRequest(op, nil, nil)
assert.Equal(t, "http://169.254.169.254/latest", req.ClientInfo.Endpoint) if e, a := "http://169.254.169.254/latest", req.ClientInfo.Endpoint; e != a {
assert.Equal(t, "http://169.254.169.254/latest/meta-data/testpath", req.HTTPRequest.URL.String()) t.Errorf("expect %v, got %v", e, a)
}
if e, a := "http://169.254.169.254/latest/meta-data/testpath", req.HTTPRequest.URL.String(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestGetMetadata(t *testing.T) { func TestGetMetadata(t *testing.T) {
@@ -85,8 +87,12 @@ func TestGetMetadata(t *testing.T) {
resp, err := c.GetMetadata("some/path") resp, err := c.GetMetadata("some/path")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "success", resp) t.Errorf("expect no error, got %v", err)
}
if e, a := "success", resp; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestGetUserData(t *testing.T) { func TestGetUserData(t *testing.T) {
@@ -99,8 +105,12 @@ func TestGetUserData(t *testing.T) {
resp, err := c.GetUserData() resp, err := c.GetUserData()
assert.NoError(t, err) if err != nil {
assert.Equal(t, "success", resp) t.Errorf("expect no error, got %v", err)
}
if e, a := "success", resp; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestGetUserData_Error(t *testing.T) { func TestGetUserData_Error(t *testing.T) {
@@ -126,12 +136,17 @@ func TestGetUserData_Error(t *testing.T) {
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")}) c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
resp, err := c.GetUserData() resp, err := c.GetUserData()
assert.Error(t, err) if err == nil {
assert.Empty(t, resp) t.Errorf("expect error")
}
if len(resp) != 0 {
t.Errorf("expect empty, got %v", resp)
}
aerr, ok := err.(awserr.Error) aerr := err.(awserr.Error)
assert.True(t, ok) if e, a := "NotFoundError", aerr.Code(); e != a {
assert.Equal(t, "NotFoundError", aerr.Code()) t.Errorf("expect %v, got %v", e, a)
}
} }
func TestGetRegion(t *testing.T) { func TestGetRegion(t *testing.T) {
@@ -144,8 +159,12 @@ func TestGetRegion(t *testing.T) {
region, err := c.Region() region, err := c.Region()
assert.NoError(t, err) if err != nil {
assert.Equal(t, "us-west-2", region) t.Errorf("expect no error, got %v", err)
}
if e, a := "us-west-2", region; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestMetadataAvailable(t *testing.T) { func TestMetadataAvailable(t *testing.T) {
@@ -156,9 +175,9 @@ func TestMetadataAvailable(t *testing.T) {
defer server.Close() defer server.Close()
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")}) c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
available := c.Available() if !c.Available() {
t.Errorf("expect available")
assert.True(t, available) }
} }
func TestMetadataIAMInfo_success(t *testing.T) { func TestMetadataIAMInfo_success(t *testing.T) {
@@ -170,10 +189,18 @@ func TestMetadataIAMInfo_success(t *testing.T) {
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")}) c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
iamInfo, err := c.IAMInfo() iamInfo, err := c.IAMInfo()
assert.NoError(t, err) if err != nil {
assert.Equal(t, "Success", iamInfo.Code) t.Errorf("expect no error, got %v", err)
assert.Equal(t, "arn:aws:iam::123456789012:instance-profile/my-instance-profile", iamInfo.InstanceProfileArn) }
assert.Equal(t, "AIPAABCDEFGHIJKLMN123", iamInfo.InstanceProfileID) if e, a := "Success", iamInfo.Code; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "arn:aws:iam::123456789012:instance-profile/my-instance-profile", iamInfo.InstanceProfileArn; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "AIPAABCDEFGHIJKLMN123", iamInfo.InstanceProfileID; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestMetadataIAMInfo_failure(t *testing.T) { func TestMetadataIAMInfo_failure(t *testing.T) {
@@ -185,10 +212,18 @@ func TestMetadataIAMInfo_failure(t *testing.T) {
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")}) c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
iamInfo, err := c.IAMInfo() iamInfo, err := c.IAMInfo()
assert.NotNil(t, err) if err == nil {
assert.Equal(t, "", iamInfo.Code) t.Errorf("expect error")
assert.Equal(t, "", iamInfo.InstanceProfileArn) }
assert.Equal(t, "", iamInfo.InstanceProfileID) if e, a := "", iamInfo.Code; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "", iamInfo.InstanceProfileArn; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "", iamInfo.InstanceProfileID; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestMetadataNotAvailable(t *testing.T) { func TestMetadataNotAvailable(t *testing.T) {
@@ -204,9 +239,9 @@ func TestMetadataNotAvailable(t *testing.T) {
r.Retryable = aws.Bool(true) // network errors are retryable r.Retryable = aws.Bool(true) // network errors are retryable
}) })
available := c.Available() if c.Available() {
t.Errorf("expect not available")
assert.False(t, available) }
} }
func TestMetadataErrorResponse(t *testing.T) { func TestMetadataErrorResponse(t *testing.T) {
@@ -222,8 +257,12 @@ func TestMetadataErrorResponse(t *testing.T) {
}) })
data, err := c.GetMetadata("uri/path") data, err := c.GetMetadata("uri/path")
assert.Empty(t, data) if len(data) != 0 {
assert.Contains(t, err.Error(), "error message text") t.Errorf("expect empty, got %v", data)
}
if e, a := "error message text", err.Error(); !strings.Contains(a, e) {
t.Errorf("expect %v to be in %v", e, a)
}
} }
func TestEC2RoleProviderInstanceIdentity(t *testing.T) { func TestEC2RoleProviderInstanceIdentity(t *testing.T) {
@@ -235,8 +274,16 @@ func TestEC2RoleProviderInstanceIdentity(t *testing.T) {
c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")}) c := ec2metadata.New(unit.Session, &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
doc, err := c.GetInstanceIdentityDocument() doc, err := c.GetInstanceIdentityDocument()
assert.Nil(t, err, "Expect no error, %v", err) if err != nil {
assert.Equal(t, doc.AccountID, "123456789012") t.Errorf("expect no error, got %v", err)
assert.Equal(t, doc.AvailabilityZone, "us-east-1d") }
assert.Equal(t, doc.Region, "us-east-1") if e, a := doc.AccountID, "123456789012"; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := doc.AvailabilityZone, "us-east-1d"; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := doc.Region, "us-east-1"; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
+24
View File
@@ -1,5 +1,10 @@
// Package ec2metadata provides the client for making API calls to the // Package ec2metadata provides the client for making API calls to the
// EC2 Metadata service. // EC2 Metadata service.
//
// This package's client can be disabled completely by setting the environment
// variable "AWS_EC2_METADATA_DISABLED=true". This environment variable set to
// true instructs the SDK to disable the EC2 Metadata client. The client cannot
// be used while the environemnt variable is set to true, (case insensitive).
package ec2metadata package ec2metadata
import ( import (
@@ -7,17 +12,21 @@ import (
"errors" "errors"
"io" "io"
"net/http" "net/http"
"os"
"strings"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
) )
// ServiceName is the name of the service. // ServiceName is the name of the service.
const ServiceName = "ec2metadata" const ServiceName = "ec2metadata"
const disableServiceEnvVar = "AWS_EC2_METADATA_DISABLED"
// A EC2Metadata is an EC2 Metadata service Client. // A EC2Metadata is an EC2 Metadata service Client.
type EC2Metadata struct { type EC2Metadata struct {
@@ -75,6 +84,21 @@ func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegio
svc.Handlers.Validate.Clear() svc.Handlers.Validate.Clear()
svc.Handlers.Validate.PushBack(validateEndpointHandler) svc.Handlers.Validate.PushBack(validateEndpointHandler)
// Disable the EC2 Metadata service if the environment variable is set.
// This shortcirctes the service's functionality to always fail to send
// requests.
if strings.ToLower(os.Getenv(disableServiceEnvVar)) == "true" {
svc.Handlers.Send.SwapNamed(request.NamedHandler{
Name: corehandlers.SendHandler.Name,
Fn: func(r *request.Request) {
r.Error = awserr.New(
request.CanceledErrorCode,
"EC2 IMDS access disabled via "+disableServiceEnvVar+" env var",
nil)
},
})
}
// Add additional options to the service config // Add additional options to the service config
for _, option := range opts { for _, option := range opts {
option(svc.Client) option(svc.Client)
+52 -10
View File
@@ -3,21 +3,30 @@ package ec2metadata_test
import ( import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"strings"
"sync" "sync"
"testing" "testing"
"time" "time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/awstesting/unit" "github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/stretchr/testify/assert"
) )
func TestClientOverrideDefaultHTTPClientTimeout(t *testing.T) { func TestClientOverrideDefaultHTTPClientTimeout(t *testing.T) {
svc := ec2metadata.New(unit.Session) svc := ec2metadata.New(unit.Session)
assert.NotEqual(t, http.DefaultClient, svc.Config.HTTPClient) if e, a := http.DefaultClient, svc.Config.HTTPClient; e == a {
assert.Equal(t, 5*time.Second, svc.Config.HTTPClient.Timeout) t.Errorf("expect %v, not to equal %v", e, a)
}
if e, a := 5*time.Second, svc.Config.HTTPClient.Timeout; e != a {
t.Errorf("expect %v to be %v", e, a)
}
} }
func TestClientNotOverrideDefaultHTTPClientTimeout(t *testing.T) { func TestClientNotOverrideDefaultHTTPClientTimeout(t *testing.T) {
@@ -28,18 +37,25 @@ func TestClientNotOverrideDefaultHTTPClientTimeout(t *testing.T) {
svc := ec2metadata.New(unit.Session) svc := ec2metadata.New(unit.Session)
assert.Equal(t, http.DefaultClient, svc.Config.HTTPClient) if e, a := http.DefaultClient, svc.Config.HTTPClient; e != a {
t.Errorf("expect %v, got %v", e, a)
}
tr, ok := svc.Config.HTTPClient.Transport.(*http.Transport) tr := svc.Config.HTTPClient.Transport.(*http.Transport)
assert.True(t, ok) if tr == nil {
assert.NotNil(t, tr) t.Fatalf("expect transport not to be nil")
assert.Nil(t, tr.Dial) }
if tr.Dial != nil {
t.Errorf("expect dial to be nil, was not")
}
} }
func TestClientDisableOverrideDefaultHTTPClientTimeout(t *testing.T) { func TestClientDisableOverrideDefaultHTTPClientTimeout(t *testing.T) {
svc := ec2metadata.New(unit.Session, aws.NewConfig().WithEC2MetadataDisableTimeoutOverride(true)) svc := ec2metadata.New(unit.Session, aws.NewConfig().WithEC2MetadataDisableTimeoutOverride(true))
assert.Equal(t, http.DefaultClient, svc.Config.HTTPClient) if e, a := http.DefaultClient, svc.Config.HTTPClient; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestClientOverrideDefaultHTTPClientTimeoutRace(t *testing.T) { func TestClientOverrideDefaultHTTPClientTimeoutRace(t *testing.T) {
@@ -63,6 +79,30 @@ func TestClientOverrideDefaultHTTPClientTimeoutRaceWithTransport(t *testing.T) {
runEC2MetadataClients(t, cfg, 100) runEC2MetadataClients(t, cfg, 100)
} }
func TestClientDisableIMDS(t *testing.T) {
env := awstesting.StashEnv()
defer awstesting.PopEnv(env)
os.Setenv("AWS_EC2_METADATA_DISABLED", "true")
svc := ec2metadata.New(unit.Session)
resp, err := svc.Region()
if err == nil {
t.Fatalf("expect error, got none")
}
if len(resp) != 0 {
t.Errorf("expect no response, got %v", resp)
}
aerr := err.(awserr.Error)
if e, a := request.CanceledErrorCode, aerr.Code(); e != a {
t.Errorf("expect %v error code, got %v", e, a)
}
if e, a := "AWS_EC2_METADATA_DISABLED", aerr.Message(); !strings.Contains(a, e) {
t.Errorf("expect %v in error message, got %v", e, a)
}
}
func runEC2MetadataClients(t *testing.T, cfg *aws.Config, atOnce int) { func runEC2MetadataClients(t *testing.T, cfg *aws.Config, atOnce int) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(atOnce) wg.Add(atOnce)
@@ -70,7 +110,9 @@ func runEC2MetadataClients(t *testing.T, cfg *aws.Config, atOnce int) {
go func() { go func() {
svc := ec2metadata.New(unit.Session, cfg) svc := ec2metadata.New(unit.Session, cfg)
_, err := svc.Region() _, err := svc.Region()
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
wg.Done() wg.Done()
}() }()
} }
File diff suppressed because it is too large Load Diff
+4
View File
@@ -347,6 +347,10 @@ type ResolvedEndpoint struct {
// The service name that should be used for signing requests. // The service name that should be used for signing requests.
SigningName string SigningName string
// States that the signing name for this endpoint was derived from metadata
// passed in, but was not explicitly modeled.
SigningNameDerived bool
// The signing method that should be used for signing requests. // The signing method that should be used for signing requests.
SigningMethod string SigningMethod string
} }
+8 -4
View File
@@ -226,16 +226,20 @@ func (e endpoint) resolve(service, region, dnsSuffix string, defs []endpoint, op
if len(signingRegion) == 0 { if len(signingRegion) == 0 {
signingRegion = region signingRegion = region
} }
signingName := e.CredentialScope.Service signingName := e.CredentialScope.Service
var signingNameDerived bool
if len(signingName) == 0 { if len(signingName) == 0 {
signingName = service signingName = service
signingNameDerived = true
} }
return ResolvedEndpoint{ return ResolvedEndpoint{
URL: u, URL: u,
SigningRegion: signingRegion, SigningRegion: signingRegion,
SigningName: signingName, SigningName: signingName,
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner), SigningNameDerived: signingNameDerived,
SigningMethod: getByPriority(e.SignatureVersions, signerPriority, defaultSigner),
} }
} }
+265 -78
View File
@@ -2,10 +2,9 @@ package endpoints
import ( import (
"encoding/json" "encoding/json"
"reflect"
"regexp" "regexp"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestUnmarshalRegionRegex(t *testing.T) { func TestUnmarshalRegionRegex(t *testing.T) {
@@ -16,12 +15,18 @@ func TestUnmarshalRegionRegex(t *testing.T) {
p := partition{} p := partition{}
err := json.Unmarshal(input, &p) err := json.Unmarshal(input, &p)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
expectRegexp, err := regexp.Compile(`^(us|eu|ap|sa|ca)\-\w+\-\d+$`) expectRegexp, err := regexp.Compile(`^(us|eu|ap|sa|ca)\-\w+\-\d+$`)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
assert.Equal(t, expectRegexp.String(), p.RegionRegex.Regexp.String()) if e, a := expectRegexp.String(), p.RegionRegex.Regexp.String(); e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestUnmarshalRegion(t *testing.T) { func TestUnmarshalRegion(t *testing.T) {
@@ -37,16 +42,28 @@ func TestUnmarshalRegion(t *testing.T) {
rs := regions{} rs := regions{}
err := json.Unmarshal(input, &rs) err := json.Unmarshal(input, &rs)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
assert.Len(t, rs, 2) if e, a := 2, len(rs); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
r, ok := rs["aws-global"] r, ok := rs["aws-global"]
assert.True(t, ok) if !ok {
assert.Equal(t, "AWS partition-global endpoint", r.Description) t.Errorf("expect found, was not")
}
if e, a := "AWS partition-global endpoint", r.Description; e != a {
t.Errorf("expect %v, got %v", e, a)
}
r, ok = rs["us-east-1"] r, ok = rs["us-east-1"]
assert.True(t, ok) if !ok {
assert.Equal(t, "US East (N. Virginia)", r.Description) t.Errorf("expect found, was not")
}
if e, a := "US East (N. Virginia)", r.Description; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestUnmarshalServices(t *testing.T) { func TestUnmarshalServices(t *testing.T) {
@@ -75,23 +92,45 @@ func TestUnmarshalServices(t *testing.T) {
ss := services{} ss := services{}
err := json.Unmarshal(input, &ss) err := json.Unmarshal(input, &ss)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
assert.Len(t, ss, 3) if e, a := 3, len(ss); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
s, ok := ss["acm"] s, ok := ss["acm"]
assert.True(t, ok) if !ok {
assert.Len(t, s.Endpoints, 1) t.Errorf("expect found, was not")
assert.Equal(t, boxedBoolUnset, s.IsRegionalized) }
if e, a := 1, len(s.Endpoints); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
if e, a := boxedBoolUnset, s.IsRegionalized; e != a {
t.Errorf("expect %v, got %v", e, a)
}
s, ok = ss["apigateway"] s, ok = ss["apigateway"]
assert.True(t, ok) if !ok {
assert.Len(t, s.Endpoints, 2) t.Errorf("expect found, was not")
assert.Equal(t, boxedTrue, s.IsRegionalized) }
if e, a := 2, len(s.Endpoints); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
if e, a := boxedTrue, s.IsRegionalized; e != a {
t.Errorf("expect %v, got %v", e, a)
}
s, ok = ss["notRegionalized"] s, ok = ss["notRegionalized"]
assert.True(t, ok) if !ok {
assert.Len(t, s.Endpoints, 2) t.Errorf("expect found, was not")
assert.Equal(t, boxedFalse, s.IsRegionalized) }
if e, a := 2, len(s.Endpoints); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
if e, a := boxedFalse, s.IsRegionalized; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestUnmarshalEndpoints(t *testing.T) { func TestUnmarshalEndpoints(t *testing.T) {
@@ -115,16 +154,32 @@ func TestUnmarshalEndpoints(t *testing.T) {
es := endpoints{} es := endpoints{}
err := json.Unmarshal(inputs, &es) err := json.Unmarshal(inputs, &es)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
assert.Len(t, es, 2) if e, a := 2, len(es); e != a {
t.Errorf("expect %v len, got %v", e, a)
}
s, ok := es["aws-global"] s, ok := es["aws-global"]
assert.True(t, ok) if !ok {
assert.Equal(t, "cloudfront.amazonaws.com", s.Hostname) t.Errorf("expect found, was not")
assert.Equal(t, []string{"http", "https"}, s.Protocols) }
assert.Equal(t, []string{"v4"}, s.SignatureVersions) if e, a := "cloudfront.amazonaws.com", s.Hostname; e != a {
assert.Equal(t, credentialScope{"us-east-1", "serviceName"}, s.CredentialScope) t.Errorf("expect %v, got %v", e, a)
assert.Equal(t, "commonName", s.SSLCommonName) }
if e, a := []string{"http", "https"}, s.Protocols; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := []string{"v4"}, s.SignatureVersions; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := (credentialScope{"us-east-1", "serviceName"}), s.CredentialScope; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "commonName", s.SSLCommonName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestEndpointResolve(t *testing.T) { func TestEndpointResolve(t *testing.T) {
@@ -155,10 +210,18 @@ func TestEndpointResolve(t *testing.T) {
defs, Options{}, defs, Options{},
) )
assert.Equal(t, "https://service.region.dnsSuffix", resolved.URL) if e, a := "https://service.region.dnsSuffix", resolved.URL; e != a {
assert.Equal(t, "signing_service", resolved.SigningName) t.Errorf("expect %v, got %v", e, a)
assert.Equal(t, "signing_region", resolved.SigningRegion) }
assert.Equal(t, "v4", resolved.SigningMethod) if e, a := "signing_service", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "signing_region", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "v4", resolved.SigningMethod; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestEndpointMergeIn(t *testing.T) { func TestEndpointMergeIn(t *testing.T) {
@@ -185,7 +248,9 @@ func TestEndpointMergeIn(t *testing.T) {
}, },
}) })
assert.Equal(t, expected, actual) if e, a := expected, actual; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
} }
var testPartitions = partitions{ var testPartitions = partitions{
@@ -213,6 +278,11 @@ var testPartitions = partitions{
Services: services{ Services: services{
"s3": service{}, "s3": service{},
"service1": service{ "service1": service{
Defaults: endpoint{
CredentialScope: credentialScope{
Service: "service1",
},
},
Endpoints: endpoints{ Endpoints: endpoints{
"us-east-1": {}, "us-east-1": {},
"us-west-2": { "us-west-2": {
@@ -221,7 +291,13 @@ var testPartitions = partitions{
}, },
}, },
}, },
"service2": service{}, "service2": service{
Defaults: endpoint{
CredentialScope: credentialScope{
Service: "service2",
},
},
},
"httpService": service{ "httpService": service{
Defaults: endpoint{ Defaults: endpoint{
Protocols: []string{"http"}, Protocols: []string{"http"},
@@ -246,109 +322,220 @@ var testPartitions = partitions{
func TestResolveEndpoint(t *testing.T) { func TestResolveEndpoint(t *testing.T) {
resolved, err := testPartitions.EndpointFor("service2", "us-west-2") resolved, err := testPartitions.EndpointFor("service2", "us-west-2")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://service2.us-west-2.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-west-2", resolved.SigningRegion) }
assert.Equal(t, "service2", resolved.SigningName) if e, a := "https://service2.us-west-2.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-west-2", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "service2", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if resolved.SigningNameDerived {
t.Errorf("expect the signing name not to be derived, but was")
}
} }
func TestResolveEndpoint_DisableSSL(t *testing.T) { func TestResolveEndpoint_DisableSSL(t *testing.T) {
resolved, err := testPartitions.EndpointFor("service2", "us-west-2", DisableSSLOption) resolved, err := testPartitions.EndpointFor("service2", "us-west-2", DisableSSLOption)
assert.NoError(t, err) if err != nil {
assert.Equal(t, "http://service2.us-west-2.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-west-2", resolved.SigningRegion) }
assert.Equal(t, "service2", resolved.SigningName) if e, a := "http://service2.us-west-2.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-west-2", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "service2", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if resolved.SigningNameDerived {
t.Errorf("expect the signing name not to be derived, but was")
}
} }
func TestResolveEndpoint_UseDualStack(t *testing.T) { func TestResolveEndpoint_UseDualStack(t *testing.T) {
resolved, err := testPartitions.EndpointFor("service1", "us-west-2", UseDualStackOption) resolved, err := testPartitions.EndpointFor("service1", "us-west-2", UseDualStackOption)
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://service1.dualstack.us-west-2.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-west-2", resolved.SigningRegion) }
assert.Equal(t, "service1", resolved.SigningName) if e, a := "https://service1.dualstack.us-west-2.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-west-2", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "service1", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if resolved.SigningNameDerived {
t.Errorf("expect the signing name not to be derived, but was")
}
} }
func TestResolveEndpoint_HTTPProtocol(t *testing.T) { func TestResolveEndpoint_HTTPProtocol(t *testing.T) {
resolved, err := testPartitions.EndpointFor("httpService", "us-west-2") resolved, err := testPartitions.EndpointFor("httpService", "us-west-2")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "http://httpService.us-west-2.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-west-2", resolved.SigningRegion) }
assert.Equal(t, "httpService", resolved.SigningName) if e, a := "http://httpService.us-west-2.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-west-2", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "httpService", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if !resolved.SigningNameDerived {
t.Errorf("expect the signing name to be derived")
}
} }
func TestResolveEndpoint_UnknownService(t *testing.T) { func TestResolveEndpoint_UnknownService(t *testing.T) {
_, err := testPartitions.EndpointFor("unknownservice", "us-west-2") _, err := testPartitions.EndpointFor("unknownservice", "us-west-2")
assert.Error(t, err) if err == nil {
t.Errorf("expect error, got none")
}
_, ok := err.(UnknownServiceError) _, ok := err.(UnknownServiceError)
assert.True(t, ok, "expect error to be UnknownServiceError") if !ok {
t.Errorf("expect error to be UnknownServiceError")
}
} }
func TestResolveEndpoint_ResolveUnknownService(t *testing.T) { func TestResolveEndpoint_ResolveUnknownService(t *testing.T) {
resolved, err := testPartitions.EndpointFor("unknown-service", "us-region-1", resolved, err := testPartitions.EndpointFor("unknown-service", "us-region-1",
ResolveUnknownServiceOption) ResolveUnknownServiceOption)
assert.NoError(t, err) if err != nil {
t.Fatalf("expect no error, got %v", err)
}
assert.Equal(t, "https://unknown-service.us-region-1.amazonaws.com", resolved.URL) if e, a := "https://unknown-service.us-region-1.amazonaws.com", resolved.URL; e != a {
assert.Equal(t, "us-region-1", resolved.SigningRegion) t.Errorf("expect %v, got %v", e, a)
assert.Equal(t, "unknown-service", resolved.SigningName) }
if e, a := "us-region-1", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "unknown-service", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if !resolved.SigningNameDerived {
t.Errorf("expect the signing name to be derived")
}
} }
func TestResolveEndpoint_UnknownMatchedRegion(t *testing.T) { func TestResolveEndpoint_UnknownMatchedRegion(t *testing.T) {
resolved, err := testPartitions.EndpointFor("service2", "us-region-1") resolved, err := testPartitions.EndpointFor("service2", "us-region-1")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://service2.us-region-1.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-region-1", resolved.SigningRegion) }
assert.Equal(t, "service2", resolved.SigningName) if e, a := "https://service2.us-region-1.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-region-1", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "service2", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if resolved.SigningNameDerived {
t.Errorf("expect the signing name not to be derived, but was")
}
} }
func TestResolveEndpoint_UnknownRegion(t *testing.T) { func TestResolveEndpoint_UnknownRegion(t *testing.T) {
resolved, err := testPartitions.EndpointFor("service2", "unknownregion") resolved, err := testPartitions.EndpointFor("service2", "unknownregion")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://service2.unknownregion.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "unknownregion", resolved.SigningRegion) }
assert.Equal(t, "service2", resolved.SigningName) if e, a := "https://service2.unknownregion.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "unknownregion", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "service2", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if resolved.SigningNameDerived {
t.Errorf("expect the signing name not to be derived, but was")
}
} }
func TestResolveEndpoint_StrictPartitionUnknownEndpoint(t *testing.T) { func TestResolveEndpoint_StrictPartitionUnknownEndpoint(t *testing.T) {
_, err := testPartitions[0].EndpointFor("service2", "unknownregion", StrictMatchingOption) _, err := testPartitions[0].EndpointFor("service2", "unknownregion", StrictMatchingOption)
assert.Error(t, err) if err == nil {
t.Errorf("expect error, got none")
}
_, ok := err.(UnknownEndpointError) _, ok := err.(UnknownEndpointError)
assert.True(t, ok, "expect error to be UnknownEndpointError") if !ok {
t.Errorf("expect error to be UnknownEndpointError")
}
} }
func TestResolveEndpoint_StrictPartitionsUnknownEndpoint(t *testing.T) { func TestResolveEndpoint_StrictPartitionsUnknownEndpoint(t *testing.T) {
_, err := testPartitions.EndpointFor("service2", "us-region-1", StrictMatchingOption) _, err := testPartitions.EndpointFor("service2", "us-region-1", StrictMatchingOption)
assert.Error(t, err) if err == nil {
t.Errorf("expect error, got none")
}
_, ok := err.(UnknownEndpointError) _, ok := err.(UnknownEndpointError)
assert.True(t, ok, "expect error to be UnknownEndpointError") if !ok {
t.Errorf("expect error to be UnknownEndpointError")
}
} }
func TestResolveEndpoint_NotRegionalized(t *testing.T) { func TestResolveEndpoint_NotRegionalized(t *testing.T) {
resolved, err := testPartitions.EndpointFor("globalService", "us-west-2") resolved, err := testPartitions.EndpointFor("globalService", "us-west-2")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://globalService.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-east-1", resolved.SigningRegion) }
assert.Equal(t, "globalService", resolved.SigningName) if e, a := "https://globalService.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-east-1", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "globalService", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if !resolved.SigningNameDerived {
t.Errorf("expect the signing name to be derived")
}
} }
func TestResolveEndpoint_AwsGlobal(t *testing.T) { func TestResolveEndpoint_AwsGlobal(t *testing.T) {
resolved, err := testPartitions.EndpointFor("globalService", "aws-global") resolved, err := testPartitions.EndpointFor("globalService", "aws-global")
assert.NoError(t, err) if err != nil {
assert.Equal(t, "https://globalService.amazonaws.com", resolved.URL) t.Fatalf("expect no error, got %v", err)
assert.Equal(t, "us-east-1", resolved.SigningRegion) }
assert.Equal(t, "globalService", resolved.SigningName) if e, a := "https://globalService.amazonaws.com", resolved.URL; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "us-east-1", resolved.SigningRegion; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if e, a := "globalService", resolved.SigningName; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if !resolved.SigningNameDerived {
t.Errorf("expect the signing name to be derived")
}
} }
+3 -1
View File
@@ -3,6 +3,8 @@ package request
import ( import (
"io" "io"
"sync" "sync"
"github.com/aws/aws-sdk-go/internal/sdkio"
) )
// offsetReader is a thread-safe io.ReadCloser to prevent racing // offsetReader is a thread-safe io.ReadCloser to prevent racing
@@ -15,7 +17,7 @@ type offsetReader struct {
func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader { func newOffsetReader(buf io.ReadSeeker, offset int64) *offsetReader {
reader := &offsetReader{} reader := &offsetReader{}
buf.Seek(offset, 0) buf.Seek(offset, sdkio.SeekStart)
reader.buf = buf reader.buf = buf
return reader return reader
+4 -3
View File
@@ -8,6 +8,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/aws/aws-sdk-go/internal/sdkio"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -28,15 +29,15 @@ func TestOffsetReaderSeek(t *testing.T) {
buf := []byte("testData") buf := []byte("testData")
reader := newOffsetReader(bytes.NewReader(buf), 0) reader := newOffsetReader(bytes.NewReader(buf), 0)
orig, err := reader.Seek(0, 1) orig, err := reader.Seek(0, sdkio.SeekCurrent)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(0), orig) assert.Equal(t, int64(0), orig)
n, err := reader.Seek(0, 2) n, err := reader.Seek(0, sdkio.SeekEnd)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(len(buf)), n) assert.Equal(t, int64(len(buf)), n)
n, err = reader.Seek(orig, 0) n, err = reader.Seek(orig, sdkio.SeekStart)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, int64(0), n) assert.Equal(t, int64(0), n)
} }
+133 -60
View File
@@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/internal/sdkio"
) )
const ( const (
@@ -28,6 +29,10 @@ const (
// during body reads. // during body reads.
ErrCodeResponseTimeout = "ResponseTimeout" ErrCodeResponseTimeout = "ResponseTimeout"
// ErrCodeInvalidPresignExpire is returned when the expire time provided to
// presign is invalid
ErrCodeInvalidPresignExpire = "InvalidPresignExpireError"
// CanceledErrorCode is the error code that will be returned by an // CanceledErrorCode is the error code that will be returned by an
// API request that was canceled. Requests given a aws.Context may // API request that was canceled. Requests given a aws.Context may
// return this error when canceled. // return this error when canceled.
@@ -42,7 +47,6 @@ type Request struct {
Retryer Retryer
Time time.Time Time time.Time
ExpireTime time.Duration
Operation *Operation Operation *Operation
HTTPRequest *http.Request HTTPRequest *http.Request
HTTPResponse *http.Response HTTPResponse *http.Response
@@ -60,6 +64,11 @@ type Request struct {
LastSignedAt time.Time LastSignedAt time.Time
DisableFollowRedirects bool DisableFollowRedirects bool
// A value greater than 0 instructs the request to be signed as Presigned URL
// You should not set this field directly. Instead use Request's
// Presign or PresignRequest methods.
ExpireTime time.Duration
context aws.Context context aws.Context
built bool built bool
@@ -104,6 +113,8 @@ func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err) err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
} }
SanitizeHostForHeader(httpReq)
r := &Request{ r := &Request{
Config: cfg, Config: cfg,
ClientInfo: clientInfo, ClientInfo: clientInfo,
@@ -214,6 +225,9 @@ func (r *Request) SetContext(ctx aws.Context) {
// WillRetry returns if the request's can be retried. // WillRetry returns if the request's can be retried.
func (r *Request) WillRetry() bool { func (r *Request) WillRetry() bool {
if !aws.IsReaderSeekable(r.Body) && r.HTTPRequest.Body != NoBody {
return false
}
return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries() return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
} }
@@ -245,45 +259,70 @@ func (r *Request) SetStringBody(s string) {
// SetReaderBody will set the request's body reader. // SetReaderBody will set the request's body reader.
func (r *Request) SetReaderBody(reader io.ReadSeeker) { func (r *Request) SetReaderBody(reader io.ReadSeeker) {
r.Body = reader r.Body = reader
r.BodyStart, _ = reader.Seek(0, sdkio.SeekCurrent) // Get the Bodies current offset.
r.ResetBody() r.ResetBody()
} }
// Presign returns the request's signed URL. Error will be returned // Presign returns the request's signed URL. Error will be returned
// if the signing fails. // if the signing fails.
func (r *Request) Presign(expireTime time.Duration) (string, error) { //
r.ExpireTime = expireTime // It is invalid to create a presigned URL with a expire duration 0 or less. An
// error is returned if expire duration is 0 or less.
func (r *Request) Presign(expire time.Duration) (string, error) {
r = r.copy()
// Presign requires all headers be hoisted. There is no way to retrieve
// the signed headers not hoisted without this. Making the presigned URL
// useless.
r.NotHoist = false r.NotHoist = false
if r.Operation.BeforePresignFn != nil { u, _, err := getPresignedURL(r, expire)
r = r.copy() return u, err
err := r.Operation.BeforePresignFn(r)
if err != nil {
return "", err
}
}
r.Sign()
if r.Error != nil {
return "", r.Error
}
return r.HTTPRequest.URL.String(), nil
} }
// PresignRequest behaves just like presign, with the addition of returning a // PresignRequest behaves just like presign, with the addition of returning a
// set of headers that were signed. // set of headers that were signed.
// //
// It is invalid to create a presigned URL with a expire duration 0 or less. An
// error is returned if expire duration is 0 or less.
//
// Returns the URL string for the API operation with signature in the query string, // Returns the URL string for the API operation with signature in the query string,
// and the HTTP headers that were included in the signature. These headers must // and the HTTP headers that were included in the signature. These headers must
// be included in any HTTP request made with the presigned URL. // be included in any HTTP request made with the presigned URL.
// //
// To prevent hoisting any headers to the query string set NotHoist to true on // To prevent hoisting any headers to the query string set NotHoist to true on
// this Request value prior to calling PresignRequest. // this Request value prior to calling PresignRequest.
func (r *Request) PresignRequest(expireTime time.Duration) (string, http.Header, error) { func (r *Request) PresignRequest(expire time.Duration) (string, http.Header, error) {
r.ExpireTime = expireTime r = r.copy()
r.Sign() return getPresignedURL(r, expire)
if r.Error != nil { }
return "", nil, r.Error
// IsPresigned returns true if the request represents a presigned API url.
func (r *Request) IsPresigned() bool {
return r.ExpireTime != 0
}
func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, error) {
if expire <= 0 {
return "", nil, awserr.New(
ErrCodeInvalidPresignExpire,
"presigned URL requires an expire duration greater than 0",
nil,
)
} }
r.ExpireTime = expire
if r.Operation.BeforePresignFn != nil {
if err := r.Operation.BeforePresignFn(r); err != nil {
return "", nil, err
}
}
if err := r.Sign(); err != nil {
return "", nil, err
}
return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
} }
@@ -303,7 +342,7 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) {
// Build will build the request's object so it can be signed and sent // Build will build the request's object so it can be signed and sent
// to the service. Build will also validate all the request's parameters. // to the service. Build will also validate all the request's parameters.
// Anny additional build Handlers set on this request will be run // Any additional build Handlers set on this request will be run
// in the order they were set. // in the order they were set.
// //
// The request will only be built once. Multiple calls to build will have // The request will only be built once. Multiple calls to build will have
@@ -364,7 +403,7 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
// of the SDK if they used that field. // of the SDK if they used that field.
// //
// Related golang/go#18257 // Related golang/go#18257
l, err := computeBodyLength(r.Body) l, err := aws.SeekerLen(r.Body)
if err != nil { if err != nil {
return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err) return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err)
} }
@@ -382,7 +421,8 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
// Transfer-Encoding: chunked bodies for these methods. // Transfer-Encoding: chunked bodies for these methods.
// //
// This would only happen if a aws.ReaderSeekerCloser was used with // This would only happen if a aws.ReaderSeekerCloser was used with
// a io.Reader that was not also an io.Seeker. // a io.Reader that was not also an io.Seeker, or did not implement
// Len() method.
switch r.Operation.HTTPMethod { switch r.Operation.HTTPMethod {
case "GET", "HEAD", "DELETE": case "GET", "HEAD", "DELETE":
body = NoBody body = NoBody
@@ -394,42 +434,6 @@ func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
return body, nil return body, nil
} }
// Attempts to compute the length of the body of the reader using the
// io.Seeker interface. If the value is not seekable because of being
// a ReaderSeekerCloser without an unerlying Seeker -1 will be returned.
// If no error occurs the length of the body will be returned.
func computeBodyLength(r io.ReadSeeker) (int64, error) {
seekable := true
// Determine if the seeker is actually seekable. ReaderSeekerCloser
// hides the fact that a io.Readers might not actually be seekable.
switch v := r.(type) {
case aws.ReaderSeekerCloser:
seekable = v.IsSeeker()
case *aws.ReaderSeekerCloser:
seekable = v.IsSeeker()
}
if !seekable {
return -1, nil
}
curOffset, err := r.Seek(0, 1)
if err != nil {
return 0, err
}
endOffset, err := r.Seek(0, 2)
if err != nil {
return 0, err
}
_, err = r.Seek(curOffset, 0)
if err != nil {
return 0, err
}
return endOffset - curOffset, nil
}
// GetBody will return an io.ReadSeeker of the Request's underlying // GetBody will return an io.ReadSeeker of the Request's underlying
// input body with a concurrency safe wrapper. // input body with a concurrency safe wrapper.
func (r *Request) GetBody() io.ReadSeeker { func (r *Request) GetBody() io.ReadSeeker {
@@ -579,3 +583,72 @@ func shouldRetryCancel(r *Request) bool {
errStr != "net/http: request canceled while waiting for connection") errStr != "net/http: request canceled while waiting for connection")
} }
// 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
}
+20 -5
View File
@@ -142,13 +142,28 @@ func (r *Request) nextPageTokens() []interface{} {
tokens := []interface{}{} tokens := []interface{}{}
tokenAdded := false tokenAdded := false
for _, outToken := range r.Operation.OutputTokens { for _, outToken := range r.Operation.OutputTokens {
v, _ := awsutil.ValuesAtPath(r.Data, outToken) vs, _ := awsutil.ValuesAtPath(r.Data, outToken)
if len(v) > 0 { if len(vs) == 0 {
tokens = append(tokens, v[0])
tokenAdded = true
} else {
tokens = append(tokens, nil) tokens = append(tokens, nil)
continue
} }
v := vs[0]
switch tv := v.(type) {
case *string:
if len(aws.StringValue(tv)) == 0 {
tokens = append(tokens, nil)
continue
}
case string:
if len(tv) == 0 {
tokens = append(tokens, nil)
continue
}
}
tokenAdded = true
tokens = append(tokens, v)
} }
if !tokenAdded { if !tokenAdded {
return nil return nil
+78 -63
View File
@@ -454,78 +454,93 @@ func TestPaginationWithContextNilInput(t *testing.T) {
} }
} }
type testPageInput struct {
NextToken string
}
type testPageOutput struct {
Value string
NextToken *string
}
func TestPagination_Standalone(t *testing.T) { func TestPagination_Standalone(t *testing.T) {
expect := []struct { type testPageInput struct {
Value, PrevToken, NextToken string NextToken *string
}{
{"FirstValue", "InitalToken", "FirstToken"},
{"SecondValue", "FirstToken", "SecondToken"},
{"ThirdValue", "SecondToken", ""},
} }
input := testPageInput{ type testPageOutput struct {
NextToken: expect[0].PrevToken, Value *string
NextToken *string
}
type testCase struct {
Value, PrevToken, NextToken *string
} }
c := awstesting.NewClient() cases := [][]testCase{
i := 0 {
p := request.Pagination{ testCase{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
NewRequest: func() (*request.Request, error) { testCase{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
r := c.NewRequest( testCase{aws.String("ThirdValue"), aws.String("SecondToken"), nil},
&request.Operation{ },
Name: "Operation", {
Paginator: &request.Paginator{ testCase{aws.String("FirstValue"), aws.String("InitalToken"), aws.String("FirstToken")},
InputTokens: []string{"NextToken"}, testCase{aws.String("SecondValue"), aws.String("FirstToken"), aws.String("SecondToken")},
OutputTokens: []string{"NextToken"}, testCase{aws.String("ThirdValue"), aws.String("SecondToken"), aws.String("")},
},
},
&input, &testPageOutput{},
)
// Setup handlers for testing
r.Handlers.Clear()
r.Handlers.Build.PushBack(func(req *request.Request) {
in := req.Params.(*testPageInput)
if e, a := expect[i].PrevToken, in.NextToken; e != a {
t.Errorf("%d, expect NextToken input %q, got %q", i, e, a)
}
})
r.Handlers.Unmarshal.PushBack(func(req *request.Request) {
out := &testPageOutput{
Value: expect[i].Value,
}
if len(expect[i].NextToken) > 0 {
out.NextToken = aws.String(expect[i].NextToken)
}
req.Data = out
})
return r, nil
}, },
} }
for p.Next() { for _, c := range cases {
data := p.Page().(*testPageOutput) input := testPageInput{
NextToken: c[0].PrevToken,
if e, a := expect[i].Value, data.Value; e != a {
t.Errorf("%d, expect Value to be %q, got %q", i, e, a)
}
if e, a := expect[i].NextToken, aws.StringValue(data.NextToken); e != a {
t.Errorf("%d, expect NextToken to be %q, got %q", i, e, a)
} }
i++ svc := awstesting.NewClient()
} i := 0
if e, a := len(expect), i; e != a { p := request.Pagination{
t.Errorf("expected to process %d pages, did %d", e, a) NewRequest: func() (*request.Request, error) {
} r := svc.NewRequest(
if err := p.Err(); err != nil { &request.Operation{
t.Fatalf("%d, expected no error, got %v", i, err) Name: "Operation",
Paginator: &request.Paginator{
InputTokens: []string{"NextToken"},
OutputTokens: []string{"NextToken"},
},
},
&input, &testPageOutput{},
)
// Setup handlers for testing
r.Handlers.Clear()
r.Handlers.Build.PushBack(func(req *request.Request) {
if e, a := len(c), i+1; a > e {
t.Fatalf("expect no more than %d requests, got %d", e, a)
}
in := req.Params.(*testPageInput)
if e, a := aws.StringValue(c[i].PrevToken), aws.StringValue(in.NextToken); e != a {
t.Errorf("%d, expect NextToken input %q, got %q", i, e, a)
}
})
r.Handlers.Unmarshal.PushBack(func(req *request.Request) {
out := &testPageOutput{
Value: c[i].Value,
}
if c[i].NextToken != nil {
next := *c[i].NextToken
out.NextToken = aws.String(next)
}
req.Data = out
})
return r, nil
},
}
for p.Next() {
data := p.Page().(*testPageOutput)
if e, a := aws.StringValue(c[i].Value), aws.StringValue(data.Value); e != a {
t.Errorf("%d, expect Value to be %q, got %q", i, e, a)
}
if e, a := aws.StringValue(c[i].NextToken), aws.StringValue(data.NextToken); e != a {
t.Errorf("%d, expect NextToken to be %q, got %q", i, e, a)
}
i++
}
if e, a := len(c), i; e != a {
t.Errorf("expected to process %d pages, did %d", e, a)
}
if err := p.Err(); err != nil {
t.Fatalf("%d, expected no error, got %v", i, err)
}
} }
} }
+60 -11
View File
@@ -2,6 +2,7 @@ package request
import ( import (
"bytes" "bytes"
"io"
"net/http" "net/http"
"strings" "strings"
"testing" "testing"
@@ -25,21 +26,70 @@ func TestResetBody_WithBodyContents(t *testing.T) {
} }
} }
func TestResetBody_ExcludeUnseekableBodyByMethod(t *testing.T) { type mockReader struct{}
func (mockReader) Read([]byte) (int, error) {
return 0, io.EOF
}
func TestResetBody_ExcludeEmptyUnseekableBodyByMethod(t *testing.T) {
cases := []struct { cases := []struct {
Method string Method string
Body io.ReadSeeker
IsNoBody bool IsNoBody bool
}{ }{
{"GET", true}, {
{"HEAD", true}, Method: "GET",
{"DELETE", true}, IsNoBody: true,
{"PUT", false}, Body: aws.ReadSeekCloser(mockReader{}),
{"PATCH", false}, },
{"POST", false}, {
Method: "HEAD",
IsNoBody: true,
Body: aws.ReadSeekCloser(mockReader{}),
},
{
Method: "DELETE",
IsNoBody: true,
Body: aws.ReadSeekCloser(mockReader{}),
},
{
Method: "PUT",
IsNoBody: false,
Body: aws.ReadSeekCloser(mockReader{}),
},
{
Method: "PATCH",
IsNoBody: false,
Body: aws.ReadSeekCloser(mockReader{}),
},
{
Method: "POST",
IsNoBody: false,
Body: aws.ReadSeekCloser(mockReader{}),
},
{
Method: "GET",
IsNoBody: false,
Body: aws.ReadSeekCloser(bytes.NewBuffer([]byte("abc"))),
},
{
Method: "GET",
IsNoBody: true,
Body: aws.ReadSeekCloser(bytes.NewBuffer(nil)),
},
{
Method: "POST",
IsNoBody: false,
Body: aws.ReadSeekCloser(bytes.NewBuffer([]byte("abc"))),
},
{
Method: "POST",
IsNoBody: true,
Body: aws.ReadSeekCloser(bytes.NewBuffer(nil)),
},
} }
reader := aws.ReadSeekCloser(bytes.NewBuffer([]byte("abc")))
for i, c := range cases { for i, c := range cases {
r := Request{ r := Request{
HTTPRequest: &http.Request{}, HTTPRequest: &http.Request{},
@@ -47,8 +97,7 @@ func TestResetBody_ExcludeUnseekableBodyByMethod(t *testing.T) {
HTTPMethod: c.Method, HTTPMethod: c.Method,
}, },
} }
r.SetReaderBody(c.Body)
r.SetReaderBody(reader)
if a, e := r.HTTPRequest.Body == NoBody, c.IsNoBody; a != e { if a, e := r.HTTPRequest.Body == NoBody, c.IsNoBody; a != e {
t.Errorf("%d, expect body to be set to noBody(%t), but was %t", i, e, a) t.Errorf("%d, expect body to be set to noBody(%t), but was %t", i, e, a)
+280 -3
View File
@@ -8,9 +8,11 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url"
"reflect" "reflect"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"testing" "testing"
"time" "time"
@@ -20,6 +22,7 @@ import (
"github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/corehandlers" "github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/awstesting" "github.com/aws/aws-sdk-go/awstesting"
@@ -80,7 +83,7 @@ func TestRequestRecoverRetry5xx(t *testing.T) {
reqNum := 0 reqNum := 0
reqs := []http.Response{ reqs := []http.Response{
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
{StatusCode: 501, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)}, {StatusCode: 502, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
{StatusCode: 200, Body: body(`{"data":"valid"}`)}, {StatusCode: 200, Body: body(`{"data":"valid"}`)},
} }
@@ -112,7 +115,8 @@ func TestRequestRecoverRetry4xxRetryable(t *testing.T) {
reqNum := 0 reqNum := 0
reqs := []http.Response{ reqs := []http.Response{
{StatusCode: 400, Body: body(`{"__type":"Throttling","message":"Rate exceeded."}`)}, {StatusCode: 400, Body: body(`{"__type":"Throttling","message":"Rate exceeded."}`)},
{StatusCode: 429, Body: body(`{"__type":"ProvisionedThroughputExceededException","message":"Rate exceeded."}`)}, {StatusCode: 400, Body: body(`{"__type":"ProvisionedThroughputExceededException","message":"Rate exceeded."}`)},
{StatusCode: 429, Body: body(`{"__type":"FooException","message":"Rate exceeded."}`)},
{StatusCode: 200, Body: body(`{"data":"valid"}`)}, {StatusCode: 200, Body: body(`{"data":"valid"}`)},
} }
@@ -131,7 +135,7 @@ func TestRequestRecoverRetry4xxRetryable(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("expect no error, but got %v", err) t.Fatalf("expect no error, but got %v", err)
} }
if e, a := 2, int(r.RetryCount); e != a { if e, a := 3, int(r.RetryCount); e != a {
t.Errorf("expect %d retry count, got %d", e, a) t.Errorf("expect %d retry count, got %d", e, a)
} }
if e, a := "valid", out.Data; e != a { if e, a := "valid", out.Data; e != a {
@@ -842,3 +846,276 @@ func TestRequest_TemporaryRetry(t *testing.T) {
t.Errorf("expect temporary error, was not") t.Errorf("expect temporary error, was not")
} }
} }
func TestRequest_Presign(t *testing.T) {
presign := func(r *request.Request, expire time.Duration) (string, http.Header, error) {
u, err := r.Presign(expire)
return u, nil, err
}
presignRequest := func(r *request.Request, expire time.Duration) (string, http.Header, error) {
return r.PresignRequest(expire)
}
mustParseURL := func(v string) *url.URL {
u, err := url.Parse(v)
if err != nil {
panic(err)
}
return u
}
cases := []struct {
Expire time.Duration
PresignFn func(*request.Request, time.Duration) (string, http.Header, error)
SignerFn func(*request.Request)
URL string
Header http.Header
Err string
}{
{
PresignFn: presign,
Err: request.ErrCodeInvalidPresignExpire,
},
{
PresignFn: presignRequest,
Err: request.ErrCodeInvalidPresignExpire,
},
{
Expire: -1,
PresignFn: presign,
Err: request.ErrCodeInvalidPresignExpire,
},
{
// Presign clear NotHoist
Expire: 1 * time.Minute,
PresignFn: func(r *request.Request, dur time.Duration) (string, http.Header, error) {
r.NotHoist = true
return presign(r, dur)
},
SignerFn: func(r *request.Request) {
r.HTTPRequest.URL = mustParseURL("https://endpoint/presignedURL")
if r.NotHoist {
r.Error = fmt.Errorf("expect NotHoist to be cleared")
}
},
URL: "https://endpoint/presignedURL",
},
{
// PresignRequest does not clear NotHoist
Expire: 1 * time.Minute,
PresignFn: func(r *request.Request, dur time.Duration) (string, http.Header, error) {
r.NotHoist = true
return presignRequest(r, dur)
},
SignerFn: func(r *request.Request) {
r.HTTPRequest.URL = mustParseURL("https://endpoint/presignedURL")
if !r.NotHoist {
r.Error = fmt.Errorf("expect NotHoist not to be cleared")
}
},
URL: "https://endpoint/presignedURL",
},
{
// PresignRequest returns signed headers
Expire: 1 * time.Minute,
PresignFn: presignRequest,
SignerFn: func(r *request.Request) {
r.HTTPRequest.URL = mustParseURL("https://endpoint/presignedURL")
r.HTTPRequest.Header.Set("UnsigndHeader", "abc")
r.SignedHeaderVals = http.Header{
"X-Amzn-Header": []string{"abc", "123"},
"X-Amzn-Header2": []string{"efg", "456"},
}
},
URL: "https://endpoint/presignedURL",
Header: http.Header{
"X-Amzn-Header": []string{"abc", "123"},
"X-Amzn-Header2": []string{"efg", "456"},
},
},
}
svc := awstesting.NewClient()
svc.Handlers.Clear()
for i, c := range cases {
req := svc.NewRequest(&request.Operation{
Name: "name", HTTPMethod: "GET", HTTPPath: "/path",
}, &struct{}{}, &struct{}{})
req.Handlers.Sign.PushBack(c.SignerFn)
u, h, err := c.PresignFn(req, c.Expire)
if len(c.Err) != 0 {
if e, a := c.Err, err.Error(); !strings.Contains(a, e) {
t.Errorf("%d, expect %v to be in %v", i, e, a)
}
continue
} else if err != nil {
t.Errorf("%d, expect no error, got %v", i, err)
continue
}
if e, a := c.URL, u; e != a {
t.Errorf("%d, expect %v URL, got %v", i, e, a)
}
if e, a := c.Header, h; !reflect.DeepEqual(e, a) {
t.Errorf("%d, expect %v header got %v", i, e, a)
}
}
}
func TestNew_EndpointWithDefaultPort(t *testing.T) {
endpoint := "https://estest.us-east-1.es.amazonaws.com:443"
expectedRequestHost := "estest.us-east-1.es.amazonaws.com"
r := request.New(
aws.Config{},
metadata.ClientInfo{Endpoint: endpoint},
defaults.Handlers(),
client.DefaultRetryer{},
&request.Operation{},
nil,
nil,
)
if h := r.HTTPRequest.Host; h != expectedRequestHost {
t.Errorf("expect %v host, got %q", expectedRequestHost, h)
}
}
func TestSanitizeHostForHeader(t *testing.T) {
cases := []struct {
url string
expectedRequestHost string
}{
{"https://estest.us-east-1.es.amazonaws.com:443", "estest.us-east-1.es.amazonaws.com"},
{"https://estest.us-east-1.es.amazonaws.com", "estest.us-east-1.es.amazonaws.com"},
{"https://localhost:9200", "localhost:9200"},
{"http://localhost:80", "localhost"},
{"http://localhost:8080", "localhost:8080"},
}
for _, c := range cases {
r, _ := http.NewRequest("GET", c.url, nil)
request.SanitizeHostForHeader(r)
if h := r.Host; h != c.expectedRequestHost {
t.Errorf("expect %v host, got %q", c.expectedRequestHost, h)
}
}
}
func TestRequestWillRetry_ByBody(t *testing.T) {
svc := awstesting.NewClient()
cases := []struct {
WillRetry bool
HTTPMethod string
Body io.ReadSeeker
IsReqNoBody bool
}{
{
WillRetry: true,
HTTPMethod: "GET",
Body: bytes.NewReader([]byte{}),
IsReqNoBody: true,
},
{
WillRetry: true,
HTTPMethod: "GET",
Body: bytes.NewReader(nil),
IsReqNoBody: true,
},
{
WillRetry: true,
HTTPMethod: "POST",
Body: bytes.NewReader([]byte("abc123")),
},
{
WillRetry: true,
HTTPMethod: "POST",
Body: aws.ReadSeekCloser(bytes.NewReader([]byte("abc123"))),
},
{
WillRetry: true,
HTTPMethod: "GET",
Body: aws.ReadSeekCloser(bytes.NewBuffer(nil)),
IsReqNoBody: true,
},
{
WillRetry: true,
HTTPMethod: "POST",
Body: aws.ReadSeekCloser(bytes.NewBuffer(nil)),
IsReqNoBody: true,
},
{
WillRetry: false,
HTTPMethod: "POST",
Body: aws.ReadSeekCloser(bytes.NewBuffer([]byte("abc123"))),
},
}
for i, c := range cases {
req := svc.NewRequest(&request.Operation{
Name: "Operation",
HTTPMethod: c.HTTPMethod,
HTTPPath: "/",
}, nil, nil)
req.SetReaderBody(c.Body)
req.Build()
req.Error = fmt.Errorf("some error")
req.Retryable = aws.Bool(true)
req.HTTPResponse = &http.Response{
StatusCode: 500,
}
if e, a := c.IsReqNoBody, request.NoBody == req.HTTPRequest.Body; e != a {
t.Errorf("%d, expect request to be no body, %t, got %t, %T", i, e, a, req.HTTPRequest.Body)
}
if e, a := c.WillRetry, req.WillRetry(); e != a {
t.Errorf("%d, expect %t willRetry, got %t", i, e, a)
}
if req.Error == nil {
t.Fatalf("%d, expect error, got none", i)
}
if e, a := "some error", req.Error.Error(); !strings.Contains(a, e) {
t.Errorf("%d, expect %q error in %q", i, e, a)
}
if e, a := 0, req.RetryCount; e != a {
t.Errorf("%d, expect retry count to be %d, got %d", i, e, a)
}
}
}
func Test501NotRetrying(t *testing.T) {
reqNum := 0
reqs := []http.Response{
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
{StatusCode: 501, Body: body(`{"__type":"NotImplemented","message":"An error occurred."}`)},
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
}
s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10))
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
s.Handlers.Send.Clear() // mock sending
s.Handlers.Send.PushBack(func(r *request.Request) {
r.HTTPResponse = &reqs[reqNum]
reqNum++
})
out := &testData{}
r := s.NewRequest(&request.Operation{Name: "Operation"}, nil, out)
err := r.Send()
if err == nil {
t.Fatal("expect error, but got none")
}
aerr := err.(awserr.Error)
if e, a := "NotImplemented", aerr.Code(); e != a {
t.Errorf("expected error code %q, but received %q", e, a)
}
if e, a := 1, int(r.RetryCount); e != a {
t.Errorf("expect %d retry count, got %d", e, a)
}
}
+8
View File
@@ -5,6 +5,7 @@ import (
"strconv" "strconv"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
) )
// EnvProviderName provides a name of the provider when config is loaded from environment. // EnvProviderName provides a name of the provider when config is loaded from environment.
@@ -176,6 +177,13 @@ func envConfigLoad(enableSharedConfig bool) envConfig {
setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey) setFromEnvVal(&cfg.SharedCredentialsFile, sharedCredsFileEnvKey)
setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey) setFromEnvVal(&cfg.SharedConfigFile, sharedConfigFileEnvKey)
if len(cfg.SharedCredentialsFile) == 0 {
cfg.SharedCredentialsFile = defaults.SharedCredentialsFilename()
}
if len(cfg.SharedConfigFile) == 0 {
cfg.SharedConfigFile = defaults.SharedConfigFilename()
}
cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE") cfg.CustomCABundle = os.Getenv("AWS_CA_BUNDLE")
return cfg return cfg
+37 -10
View File
@@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/awstesting" "github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/internal/shareddefaults"
) )
func TestLoadEnvConfig_Creds(t *testing.T) { func TestLoadEnvConfig_Creds(t *testing.T) {
@@ -105,6 +106,8 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
}, },
{ {
@@ -116,6 +119,8 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
}, },
{ {
@@ -128,7 +133,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
}, },
{ {
@@ -136,6 +143,10 @@ func TestLoadEnvConfig(t *testing.T) {
"AWS_DEFAULT_REGION": "default_region", "AWS_DEFAULT_REGION": "default_region",
"AWS_DEFAULT_PROFILE": "default_profile", "AWS_DEFAULT_PROFILE": "default_profile",
}, },
Config: envConfig{
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
},
}, },
{ {
Env: map[string]string{ Env: map[string]string{
@@ -145,7 +156,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "default_region", Profile: "default_profile", Region: "default_region", Profile: "default_profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
}, },
{ {
@@ -155,7 +168,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
@@ -168,7 +183,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
@@ -182,7 +199,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "region", Profile: "profile", Region: "region", Profile: "profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
@@ -193,7 +212,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "default_region", Profile: "default_profile", Region: "default_region", Profile: "default_profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
@@ -205,7 +226,9 @@ func TestLoadEnvConfig(t *testing.T) {
}, },
Config: envConfig{ Config: envConfig{
Region: "default_region", Profile: "default_profile", Region: "default_region", Profile: "default_profile",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
@@ -214,7 +237,9 @@ func TestLoadEnvConfig(t *testing.T) {
"AWS_CA_BUNDLE": "custom_ca_bundle", "AWS_CA_BUNDLE": "custom_ca_bundle",
}, },
Config: envConfig{ Config: envConfig{
CustomCABundle: "custom_ca_bundle", CustomCABundle: "custom_ca_bundle",
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
}, },
{ {
@@ -222,8 +247,10 @@ func TestLoadEnvConfig(t *testing.T) {
"AWS_CA_BUNDLE": "custom_ca_bundle", "AWS_CA_BUNDLE": "custom_ca_bundle",
}, },
Config: envConfig{ Config: envConfig{
CustomCABundle: "custom_ca_bundle", CustomCABundle: "custom_ca_bundle",
EnableSharedConfig: true, EnableSharedConfig: true,
SharedCredentialsFile: shareddefaults.SharedCredentialsFilename(),
SharedConfigFile: shareddefaults.SharedConfigFilename(),
}, },
UseSharedConfigCall: true, UseSharedConfigCall: true,
}, },
+19 -19
View File
@@ -26,7 +26,7 @@ import (
// Sessions are safe to create service clients concurrently, but it is not safe // Sessions are safe to create service clients concurrently, but it is not safe
// to mutate the Session concurrently. // to mutate the Session concurrently.
// //
// The Session satisfies the service client's client.ClientConfigProvider. // The Session satisfies the service client's client.ConfigProvider.
type Session struct { type Session struct {
Config *aws.Config Config *aws.Config
Handlers request.Handlers Handlers request.Handlers
@@ -58,7 +58,12 @@ func New(cfgs ...*aws.Config) *Session {
envCfg := loadEnvConfig() envCfg := loadEnvConfig()
if envCfg.EnableSharedConfig { if envCfg.EnableSharedConfig {
s, err := newSession(Options{}, envCfg, cfgs...) var cfg aws.Config
cfg.MergeIn(cfgs...)
s, err := NewSessionWithOptions(Options{
Config: cfg,
SharedConfigState: SharedConfigEnable,
})
if err != nil { if err != nil {
// Old session.New expected all errors to be discovered when // Old session.New expected all errors to be discovered when
// a request is made, and would report the errors then. This // a request is made, and would report the errors then. This
@@ -243,13 +248,6 @@ func NewSessionWithOptions(opts Options) (*Session, error) {
envCfg.EnableSharedConfig = true envCfg.EnableSharedConfig = true
} }
if len(envCfg.SharedCredentialsFile) == 0 {
envCfg.SharedCredentialsFile = defaults.SharedCredentialsFilename()
}
if len(envCfg.SharedConfigFile) == 0 {
envCfg.SharedConfigFile = defaults.SharedConfigFilename()
}
// Only use AWS_CA_BUNDLE if session option is not provided. // Only use AWS_CA_BUNDLE if session option is not provided.
if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil { if len(envCfg.CustomCABundle) != 0 && opts.CustomCABundle == nil {
f, err := os.Open(envCfg.CustomCABundle) f, err := os.Open(envCfg.CustomCABundle)
@@ -573,11 +571,12 @@ func (s *Session) clientConfigWithErr(serviceName string, cfgs ...*aws.Config) (
} }
return client.Config{ return client.Config{
Config: s.Config, Config: s.Config,
Handlers: s.Handlers, Handlers: s.Handlers,
Endpoint: resolved.URL, Endpoint: resolved.URL,
SigningRegion: resolved.SigningRegion, SigningRegion: resolved.SigningRegion,
SigningName: resolved.SigningName, SigningNameDerived: resolved.SigningNameDerived,
SigningName: resolved.SigningName,
}, err }, err
} }
@@ -597,10 +596,11 @@ func (s *Session) ClientConfigNoResolveEndpoint(cfgs ...*aws.Config) client.Conf
} }
return client.Config{ return client.Config{
Config: s.Config, Config: s.Config,
Handlers: s.Handlers, Handlers: s.Handlers,
Endpoint: resolved.URL, Endpoint: resolved.URL,
SigningRegion: resolved.SigningRegion, SigningRegion: resolved.SigningRegion,
SigningName: resolved.SigningName, SigningNameDerived: resolved.SigningNameDerived,
SigningName: resolved.SigningName,
} }
} }
+23 -5
View File
@@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults" "github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/awstesting" "github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
) )
@@ -89,14 +90,31 @@ func TestSessionCopy(t *testing.T) {
} }
func TestSessionClientConfig(t *testing.T) { func TestSessionClientConfig(t *testing.T) {
s, err := NewSession(&aws.Config{Region: aws.String("orig_region")}) s, err := NewSession(&aws.Config{
Credentials: credentials.AnonymousCredentials,
Region: aws.String("orig_region"),
EndpointResolver: endpoints.ResolverFunc(
func(service, region string, opts ...func(*endpoints.Options)) (endpoints.ResolvedEndpoint, error) {
if e, a := "mock-service", service; e != a {
t.Errorf("expect %q service, got %q", e, a)
}
if e, a := "other-region", region; e != a {
t.Errorf("expect %q region, got %q", e, a)
}
return endpoints.ResolvedEndpoint{
URL: "https://" + service + "." + region + ".amazonaws.com",
SigningRegion: region,
}, nil
},
),
})
assert.NoError(t, err) assert.NoError(t, err)
cfg := s.ClientConfig("s3", &aws.Config{Region: aws.String("us-west-2")}) cfg := s.ClientConfig("mock-service", &aws.Config{Region: aws.String("other-region")})
assert.Equal(t, "https://s3-us-west-2.amazonaws.com", cfg.Endpoint) assert.Equal(t, "https://mock-service.other-region.amazonaws.com", cfg.Endpoint)
assert.Equal(t, "us-west-2", cfg.SigningRegion) assert.Equal(t, "other-region", cfg.SigningRegion)
assert.Equal(t, "us-west-2", *cfg.Config.Region) assert.Equal(t, "other-region", *cfg.Config.Region)
} }
func TestNewSession_NoCredentials(t *testing.T) { func TestNewSession_NoCredentials(t *testing.T) {
+30 -11
View File
@@ -10,7 +10,6 @@ import (
"github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/awstesting/unit" "github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/stretchr/testify/assert"
) )
func TestStandaloneSign(t *testing.T) { func TestStandaloneSign(t *testing.T) {
@@ -22,7 +21,9 @@ func TestStandaloneSign(t *testing.T) {
c.SubDomain, c.Region, c.Service) c.SubDomain, c.Region, c.Service)
req, err := http.NewRequest("GET", host, nil) req, err := http.NewRequest("GET", host, nil)
assert.NoError(t, err) if err != nil {
t.Errorf("expected no error, but received %v", err)
}
// URL.EscapedPath() will be used by the signer to get the // URL.EscapedPath() will be used by the signer to get the
// escaped form of the request's URI path. // escaped form of the request's URI path.
@@ -30,12 +31,20 @@ func TestStandaloneSign(t *testing.T) {
req.URL.RawQuery = c.OrigQuery req.URL.RawQuery = c.OrigQuery
_, err = signer.Sign(req, nil, c.Service, c.Region, time.Unix(0, 0)) _, err = signer.Sign(req, nil, c.Service, c.Region, time.Unix(0, 0))
assert.NoError(t, err) if err != nil {
t.Errorf("expected no error, but received %v", err)
}
actual := req.Header.Get("Authorization") actual := req.Header.Get("Authorization")
assert.Equal(t, c.ExpSig, actual) if e, a := c.ExpSig, actual; e != a {
assert.Equal(t, c.OrigURI, req.URL.Path) t.Errorf("expected %v, but recieved %v", e, a)
assert.Equal(t, c.EscapedURI, req.URL.EscapedPath()) }
if e, a := c.OrigURI, req.URL.Path; e != a {
t.Errorf("expected %v, but recieved %v", e, a)
}
if e, a := c.EscapedURI, req.URL.EscapedPath(); e != a {
t.Errorf("expected %v, but recieved %v", e, a)
}
} }
} }
@@ -48,7 +57,9 @@ func TestStandaloneSign_RawPath(t *testing.T) {
c.SubDomain, c.Region, c.Service) c.SubDomain, c.Region, c.Service)
req, err := http.NewRequest("GET", host, nil) req, err := http.NewRequest("GET", host, nil)
assert.NoError(t, err) if err != nil {
t.Errorf("expected no error, but received %v", err)
}
// URL.EscapedPath() will be used by the signer to get the // URL.EscapedPath() will be used by the signer to get the
// escaped form of the request's URI path. // escaped form of the request's URI path.
@@ -57,11 +68,19 @@ func TestStandaloneSign_RawPath(t *testing.T) {
req.URL.RawQuery = c.OrigQuery req.URL.RawQuery = c.OrigQuery
_, err = signer.Sign(req, nil, c.Service, c.Region, time.Unix(0, 0)) _, err = signer.Sign(req, nil, c.Service, c.Region, time.Unix(0, 0))
assert.NoError(t, err) if err != nil {
t.Errorf("expected no error, but received %v", err)
}
actual := req.Header.Get("Authorization") actual := req.Header.Get("Authorization")
assert.Equal(t, c.ExpSig, actual) if e, a := c.ExpSig, actual; e != a {
assert.Equal(t, c.OrigURI, req.URL.Path) t.Errorf("expected %v, but recieved %v", e, a)
assert.Equal(t, c.EscapedURI, req.URL.EscapedPath()) }
if e, a := c.OrigURI, req.URL.Path; e != a {
t.Errorf("expected %v, but recieved %v", e, a)
}
if e, a := c.EscapedURI, req.URL.EscapedPath(); e != a {
t.Errorf("expected %v, but recieved %v", e, a)
}
} }
} }
+88
View File
@@ -164,3 +164,91 @@ func TestStandaloneSign_CustomURIEscape(t *testing.T) {
t.Errorf("expect %v, got %v", e, a) t.Errorf("expect %v, got %v", e, a)
} }
} }
func TestStandaloneSign_WithPort(t *testing.T) {
cases := []struct {
description string
url string
expectedSig string
}{
{
"default HTTPS port",
"https://estest.us-east-1.es.amazonaws.com:443/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=e573fc9aa3a156b720976419319be98fb2824a3abc2ddd895ecb1d1611c6a82d",
},
{
"default HTTP port",
"http://example.com:80/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=54ebe60c4ae03a40948b849e13c333523235f38002e2807059c64a9a8c7cb951",
},
{
"non-standard HTTP port",
"http://example.com:9200/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd9d926a460f8d3b58b57beadbd87666dc667e014c0afaa4cea37b2867f51b4f",
},
{
"non-standard HTTPS port",
"https://example.com:9200/_search",
"AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/es/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=cd9d926a460f8d3b58b57beadbd87666dc667e014c0afaa4cea37b2867f51b4f",
},
}
for _, c := range cases {
signer := v4.NewSigner(unit.Session.Config.Credentials)
req, _ := http.NewRequest("GET", c.url, nil)
_, err := signer.Sign(req, nil, "es", "us-east-1", time.Unix(0, 0))
if err != nil {
t.Fatalf("expect no error, got %v", err)
}
actual := req.Header.Get("Authorization")
if e, a := c.expectedSig, actual; e != a {
t.Errorf("%s, expect %v, got %v", c.description, e, a)
}
}
}
func TestStandalonePresign_WithPort(t *testing.T) {
cases := []struct {
description string
url string
expectedSig string
}{
{
"default HTTPS port",
"https://estest.us-east-1.es.amazonaws.com:443/_search",
"0abcf61a351063441296febf4b485734d780634fba8cf1e7d9769315c35255d6",
},
{
"default HTTP port",
"http://example.com:80/_search",
"fce9976dd6c849c21adfa6d3f3e9eefc651d0e4a2ccd740d43efddcccfdc8179",
},
{
"non-standard HTTP port",
"http://example.com:9200/_search",
"f33c25a81c735e42bef35ed5e9f720c43940562e3e616ff0777bf6dde75249b0",
},
{
"non-standard HTTPS port",
"https://example.com:9200/_search",
"f33c25a81c735e42bef35ed5e9f720c43940562e3e616ff0777bf6dde75249b0",
},
}
for _, c := range cases {
signer := v4.NewSigner(unit.Session.Config.Credentials)
req, _ := http.NewRequest("GET", c.url, nil)
_, err := signer.Presign(req, nil, "es", "us-east-1", 5 * time.Minute, time.Unix(0, 0))
if err != nil {
t.Fatalf("expect no error, got %v", err)
}
actual := req.URL.Query().Get("X-Amz-Signature")
if e, a := c.expectedSig, actual; e != a {
t.Errorf("%s, expect %v, got %v", c.description, e, a)
}
}
}
+33 -13
View File
@@ -2,8 +2,6 @@ package v4
import ( import (
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestRuleCheckWhitelist(t *testing.T) { func TestRuleCheckWhitelist(t *testing.T) {
@@ -13,8 +11,12 @@ func TestRuleCheckWhitelist(t *testing.T) {
}, },
} }
assert.True(t, w.IsValid("Cache-Control")) if !w.IsValid("Cache-Control") {
assert.False(t, w.IsValid("Cache-")) t.Error("expected true value")
}
if w.IsValid("Cache-") {
t.Error("expected false value")
}
} }
func TestRuleCheckBlacklist(t *testing.T) { func TestRuleCheckBlacklist(t *testing.T) {
@@ -24,16 +26,26 @@ func TestRuleCheckBlacklist(t *testing.T) {
}, },
} }
assert.False(t, b.IsValid("Cache-Control")) if b.IsValid("Cache-Control") {
assert.True(t, b.IsValid("Cache-")) t.Error("expected false value")
}
if !b.IsValid("Cache-") {
t.Error("expected true value")
}
} }
func TestRuleCheckPattern(t *testing.T) { func TestRuleCheckPattern(t *testing.T) {
p := patterns{"X-Amz-Meta-"} p := patterns{"X-Amz-Meta-"}
assert.True(t, p.IsValid("X-Amz-Meta-")) if !p.IsValid("X-Amz-Meta-") {
assert.True(t, p.IsValid("X-Amz-Meta-Star")) t.Error("expected true value")
assert.False(t, p.IsValid("Cache-")) }
if !p.IsValid("X-Amz-Meta-Star") {
t.Error("expected true value")
}
if p.IsValid("Cache-") {
t.Error("expected false value")
}
} }
func TestRuleComplexWhitelist(t *testing.T) { func TestRuleComplexWhitelist(t *testing.T) {
@@ -50,8 +62,16 @@ func TestRuleComplexWhitelist(t *testing.T) {
inclusiveRules{patterns{"X-Amz-"}, blacklist{w}}, inclusiveRules{patterns{"X-Amz-"}, blacklist{w}},
} }
assert.True(t, r.IsValid("X-Amz-Blah")) if !r.IsValid("X-Amz-Blah") {
assert.False(t, r.IsValid("X-Amz-Meta-")) t.Error("expected true value")
assert.False(t, r.IsValid("X-Amz-Meta-Star")) }
assert.False(t, r.IsValid("Cache-Control")) if r.IsValid("X-Amz-Meta-") {
t.Error("expected false value")
}
if r.IsValid("X-Amz-Meta-Star") {
t.Error("expected false value")
}
if r.IsValid("Cache-Control") {
t.Error("expected false value")
}
} }
+28 -11
View File
@@ -71,6 +71,7 @@ import (
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/internal/sdkio"
"github.com/aws/aws-sdk-go/private/protocol/rest" "github.com/aws/aws-sdk-go/private/protocol/rest"
) )
@@ -268,7 +269,7 @@ type signingCtx struct {
// "X-Amz-Content-Sha256" header with a precomputed value. The signer will // "X-Amz-Content-Sha256" header with a precomputed value. The signer will
// only compute the hash if the request header value is empty. // only compute the hash if the request header value is empty.
func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) { func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region string, signTime time.Time) (http.Header, error) {
return v4.signWithBody(r, body, service, region, 0, signTime) return v4.signWithBody(r, body, service, region, 0, false, signTime)
} }
// Presign signs AWS v4 requests with the provided body, service name, region // Presign signs AWS v4 requests with the provided body, service name, region
@@ -302,10 +303,10 @@ func (v4 Signer) Sign(r *http.Request, body io.ReadSeeker, service, region strin
// presigned request's signature you can set the "X-Amz-Content-Sha256" // presigned request's signature you can set the "X-Amz-Content-Sha256"
// HTTP header and that will be included in the request's signature. // HTTP header and that will be included in the request's signature.
func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) { func (v4 Signer) Presign(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) {
return v4.signWithBody(r, body, service, region, exp, signTime) return v4.signWithBody(r, body, service, region, exp, true, signTime)
} }
func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, signTime time.Time) (http.Header, error) { func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, region string, exp time.Duration, isPresign bool, signTime time.Time) (http.Header, error) {
currentTimeFn := v4.currentTimeFn currentTimeFn := v4.currentTimeFn
if currentTimeFn == nil { if currentTimeFn == nil {
currentTimeFn = time.Now currentTimeFn = time.Now
@@ -317,7 +318,7 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
Query: r.URL.Query(), Query: r.URL.Query(),
Time: signTime, Time: signTime,
ExpireTime: exp, ExpireTime: exp,
isPresign: exp != 0, isPresign: isPresign,
ServiceName: service, ServiceName: service,
Region: region, Region: region,
DisableURIPathEscaping: v4.DisableURIPathEscaping, DisableURIPathEscaping: v4.DisableURIPathEscaping,
@@ -339,8 +340,11 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
return http.Header{}, err return http.Header{}, err
} }
ctx.sanitizeHostForHeader()
ctx.assignAmzQueryValues() ctx.assignAmzQueryValues()
ctx.build(v4.DisableHeaderHoisting) if err := ctx.build(v4.DisableHeaderHoisting); err != nil {
return nil, err
}
// If the request is not presigned the body should be attached to it. This // If the request is not presigned the body should be attached to it. This
// prevents the confusion of wanting to send a signed request without // prevents the confusion of wanting to send a signed request without
@@ -363,6 +367,10 @@ func (v4 Signer) signWithBody(r *http.Request, body io.ReadSeeker, service, regi
return ctx.SignedHeaderVals, nil return ctx.SignedHeaderVals, nil
} }
func (ctx *signingCtx) sanitizeHostForHeader() {
request.SanitizeHostForHeader(ctx.Request)
}
func (ctx *signingCtx) handlePresignRemoval() { func (ctx *signingCtx) handlePresignRemoval() {
if !ctx.isPresign { if !ctx.isPresign {
return return
@@ -467,7 +475,7 @@ func signSDKRequestWithCurrTime(req *request.Request, curTimeFn func() time.Time
} }
signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(), signedHeaders, err := v4.signWithBody(req.HTTPRequest, req.GetBody(),
name, region, req.ExpireTime, signingTime, name, region, req.ExpireTime, req.ExpireTime > 0, signingTime,
) )
if err != nil { if err != nil {
req.Error = err req.Error = err
@@ -498,11 +506,13 @@ func (v4 *Signer) logSigningInfo(ctx *signingCtx) {
v4.Logger.Log(msg) v4.Logger.Log(msg)
} }
func (ctx *signingCtx) build(disableHeaderHoisting bool) { func (ctx *signingCtx) build(disableHeaderHoisting bool) error {
ctx.buildTime() // no depends ctx.buildTime() // no depends
ctx.buildCredentialString() // no depends ctx.buildCredentialString() // no depends
ctx.buildBodyDigest() if err := ctx.buildBodyDigest(); err != nil {
return err
}
unsignedHeaders := ctx.Request.Header unsignedHeaders := ctx.Request.Header
if ctx.isPresign { if ctx.isPresign {
@@ -530,6 +540,8 @@ func (ctx *signingCtx) build(disableHeaderHoisting bool) {
} }
ctx.Request.Header.Set("Authorization", strings.Join(parts, ", ")) ctx.Request.Header.Set("Authorization", strings.Join(parts, ", "))
} }
return nil
} }
func (ctx *signingCtx) buildTime() { func (ctx *signingCtx) buildTime() {
@@ -656,7 +668,7 @@ func (ctx *signingCtx) buildSignature() {
ctx.signature = hex.EncodeToString(signature) ctx.signature = hex.EncodeToString(signature)
} }
func (ctx *signingCtx) buildBodyDigest() { func (ctx *signingCtx) buildBodyDigest() error {
hash := ctx.Request.Header.Get("X-Amz-Content-Sha256") hash := ctx.Request.Header.Get("X-Amz-Content-Sha256")
if hash == "" { if hash == "" {
if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") { if ctx.unsignedPayload || (ctx.isPresign && ctx.ServiceName == "s3") {
@@ -664,6 +676,9 @@ func (ctx *signingCtx) buildBodyDigest() {
} else if ctx.Body == nil { } else if ctx.Body == nil {
hash = emptyStringSHA256 hash = emptyStringSHA256
} else { } else {
if !aws.IsReaderSeekable(ctx.Body) {
return fmt.Errorf("cannot use unseekable request body %T, for signed request with body", ctx.Body)
}
hash = hex.EncodeToString(makeSha256Reader(ctx.Body)) hash = hex.EncodeToString(makeSha256Reader(ctx.Body))
} }
if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" { if ctx.unsignedPayload || ctx.ServiceName == "s3" || ctx.ServiceName == "glacier" {
@@ -671,6 +686,8 @@ func (ctx *signingCtx) buildBodyDigest() {
} }
} }
ctx.bodyDigest = hash ctx.bodyDigest = hash
return nil
} }
// isRequestSigned returns if the request is currently signed or presigned // isRequestSigned returns if the request is currently signed or presigned
@@ -710,8 +727,8 @@ func makeSha256(data []byte) []byte {
func makeSha256Reader(reader io.ReadSeeker) []byte { func makeSha256Reader(reader io.ReadSeeker) []byte {
hash := sha256.New() hash := sha256.New()
start, _ := reader.Seek(0, 1) start, _ := reader.Seek(0, sdkio.SeekCurrent)
defer reader.Seek(start, 0) defer reader.Seek(start, sdkio.SeekStart)
io.Copy(hash, reader) io.Copy(hash, reader)
return hash.Sum(nil) return hash.Sum(nil)
+85 -12
View File
@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"reflect" "reflect"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@@ -61,17 +62,42 @@ func TestStripExcessHeaders(t *testing.T) {
} }
func buildRequest(serviceName, region, body string) (*http.Request, io.ReadSeeker) { func buildRequest(serviceName, region, body string) (*http.Request, io.ReadSeeker) {
endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
reader := strings.NewReader(body) reader := strings.NewReader(body)
req, _ := http.NewRequest("POST", endpoint, reader) return buildRequestWithBodyReader(serviceName, region, reader)
}
func buildRequestWithBodyReader(serviceName, region string, body io.Reader) (*http.Request, io.ReadSeeker) {
var bodyLen int
type lenner interface {
Len() int
}
if lr, ok := body.(lenner); ok {
bodyLen = lr.Len()
}
endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
req, _ := http.NewRequest("POST", endpoint, body)
req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()" req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()"
req.Header.Add("X-Amz-Target", "prefix.Operation") req.Header.Set("X-Amz-Target", "prefix.Operation")
req.Header.Add("Content-Type", "application/x-amz-json-1.0") req.Header.Set("Content-Type", "application/x-amz-json-1.0")
req.Header.Add("Content-Length", string(len(body)))
req.Header.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)") if bodyLen > 0 {
req.Header.Set("Content-Length", strconv.Itoa(bodyLen))
}
req.Header.Set("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)")
req.Header.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") req.Header.Add("X-Amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)")
req.Header.Add("X-amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)") req.Header.Add("X-amz-Meta-Other-Header_With_Underscore", "some-value=!@#$%^&* (+)")
return req, reader
var seeker io.ReadSeeker
if sr, ok := body.(io.ReadSeeker); ok {
seeker = sr
} else {
seeker = aws.ReadSeekCloser(body)
}
return req, seeker
} }
func buildSigner() Signer { func buildSigner() Signer {
@@ -101,7 +127,7 @@ func TestPresignRequest(t *testing.T) {
expectedDate := "19700101T000000Z" expectedDate := "19700101T000000Z"
expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore" expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore"
expectedSig := "ea7856749041f727690c580569738282e99c79355fe0d8f125d3b5535d2ece83" expectedSig := "122f0b9e091e4ba84286097e2b3404a1f1f4c4aad479adda95b7dff0ccbe5581"
expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request" expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request"
expectedTarget := "prefix.Operation" expectedTarget := "prefix.Operation"
@@ -135,7 +161,7 @@ func TestPresignBodyWithArrayRequest(t *testing.T) {
expectedDate := "19700101T000000Z" expectedDate := "19700101T000000Z"
expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore" expectedHeaders := "content-length;content-type;host;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore"
expectedSig := "fef6002062400bbf526d70f1a6456abc0fb2e213fe1416012737eebd42a62924" expectedSig := "e3ac55addee8711b76c6d608d762cff285fe8b627a057f8b5ec9268cf82c08b1"
expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request" expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request"
expectedTarget := "prefix.Operation" expectedTarget := "prefix.Operation"
@@ -166,14 +192,14 @@ func TestSignRequest(t *testing.T) {
signer.Sign(req, body, "dynamodb", "us-east-1", time.Unix(0, 0)) signer.Sign(req, body, "dynamodb", "us-east-1", time.Unix(0, 0))
expectedDate := "19700101T000000Z" expectedDate := "19700101T000000Z"
expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=ea766cabd2ec977d955a3c2bae1ae54f4515d70752f2207618396f20aa85bd21" expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-amz-meta-other-header;x-amz-meta-other-header_with_underscore;x-amz-security-token;x-amz-target, Signature=a518299330494908a70222cec6899f6f32f297f8595f6df1776d998936652ad9"
q := req.Header q := req.Header
if e, a := expectedSig, q.Get("Authorization"); e != a { if e, a := expectedSig, q.Get("Authorization"); e != a {
t.Errorf("expect %v, got %v", e, a) t.Errorf("expect\n%v\nactual\n%v\n", e, a)
} }
if e, a := expectedDate, q.Get("X-Amz-Date"); e != a { if e, a := expectedDate, q.Get("X-Amz-Date"); e != a {
t.Errorf("expect %v, got %v", e, a) t.Errorf("expect\n%v\nactual\n%v\n", e, a)
} }
} }
@@ -207,6 +233,53 @@ func TestPresignEmptyBodyS3(t *testing.T) {
} }
} }
func TestSignUnseekableBody(t *testing.T) {
req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello")))
signer := buildSigner()
_, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now())
if err == nil {
t.Fatalf("expect error signing request")
}
if e, a := "unseekable request body", err.Error(); !strings.Contains(a, e) {
t.Errorf("expect %q to be in %q", e, a)
}
}
func TestSignUnsignedPayloadUnseekableBody(t *testing.T) {
req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello")))
signer := buildSigner()
signer.UnsignedPayload = true
_, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now())
if err != nil {
t.Fatalf("expect no error, got %v", err)
}
hash := req.Header.Get("X-Amz-Content-Sha256")
if e, a := "UNSIGNED-PAYLOAD", hash; e != a {
t.Errorf("expect %v, got %v", e, a)
}
}
func TestSignPreComputedHashUnseekableBody(t *testing.T) {
req, body := buildRequestWithBodyReader("mock-service", "mock-region", bytes.NewBuffer([]byte("hello")))
signer := buildSigner()
req.Header.Set("X-Amz-Content-Sha256", "some-content-sha256")
_, err := signer.Sign(req, body, "mock-service", "mock-region", time.Now())
if err != nil {
t.Fatalf("expect no error, got %v", err)
}
hash := req.Header.Get("X-Amz-Content-Sha256")
if e, a := "some-content-sha256", hash; e != a {
t.Errorf("expect %v, got %v", e, a)
}
}
func TestSignPrecomputedBodyChecksum(t *testing.T) { func TestSignPrecomputedBodyChecksum(t *testing.T) {
req, body := buildRequest("dynamodb", "us-east-1", "hello") req, body := buildRequest("dynamodb", "us-east-1", "hello")
req.Header.Set("X-Amz-Content-Sha256", "PRECOMPUTED") req.Header.Set("X-Amz-Content-Sha256", "PRECOMPUTED")
+83
View File
@@ -3,6 +3,8 @@ package aws
import ( import (
"io" "io"
"sync" "sync"
"github.com/aws/aws-sdk-go/internal/sdkio"
) )
// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Should // ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser. Should
@@ -22,6 +24,22 @@ type ReaderSeekerCloser struct {
r io.Reader r io.Reader
} }
// IsReaderSeekable returns if the underlying reader type can be seeked. A
// io.Reader might not actually be seekable if it is the ReaderSeekerCloser
// type.
func IsReaderSeekable(r io.Reader) bool {
switch v := r.(type) {
case ReaderSeekerCloser:
return v.IsSeeker()
case *ReaderSeekerCloser:
return v.IsSeeker()
case io.ReadSeeker:
return true
default:
return false
}
}
// Read reads from the reader up to size of p. The number of bytes read, and // Read reads from the reader up to size of p. The number of bytes read, and
// error if it occurred will be returned. // error if it occurred will be returned.
// //
@@ -56,6 +74,71 @@ func (r ReaderSeekerCloser) IsSeeker() bool {
return ok return ok
} }
// HasLen returns the length of the underlying reader if the value implements
// the Len() int method.
func (r ReaderSeekerCloser) HasLen() (int, bool) {
type lenner interface {
Len() int
}
if lr, ok := r.r.(lenner); ok {
return lr.Len(), true
}
return 0, false
}
// GetLen returns the length of the bytes remaining in the underlying reader.
// Checks first for Len(), then io.Seeker to determine the size of the
// underlying reader.
//
// Will return -1 if the length cannot be determined.
func (r ReaderSeekerCloser) GetLen() (int64, error) {
if l, ok := r.HasLen(); ok {
return int64(l), nil
}
if s, ok := r.r.(io.Seeker); ok {
return seekerLen(s)
}
return -1, nil
}
// SeekerLen attempts to get the number of bytes remaining at the seeker's
// current position. Returns the number of bytes remaining or error.
func SeekerLen(s io.Seeker) (int64, error) {
// Determine if the seeker is actually seekable. ReaderSeekerCloser
// hides the fact that a io.Readers might not actually be seekable.
switch v := s.(type) {
case ReaderSeekerCloser:
return v.GetLen()
case *ReaderSeekerCloser:
return v.GetLen()
}
return seekerLen(s)
}
func seekerLen(s io.Seeker) (int64, error) {
curOffset, err := s.Seek(0, sdkio.SeekCurrent)
if err != nil {
return 0, err
}
endOffset, err := s.Seek(0, sdkio.SeekEnd)
if err != nil {
return 0, err
}
_, err = s.Seek(curOffset, sdkio.SeekStart)
if err != nil {
return 0, err
}
return endOffset - curOffset, nil
}
// Close closes the ReaderSeekerCloser. // Close closes the ReaderSeekerCloser.
// //
// If the ReaderSeekerCloser is not an io.Closer nothing will be done. // If the ReaderSeekerCloser is not an io.Closer nothing will be done.
+28 -11
View File
@@ -1,32 +1,49 @@
package aws package aws
import ( import (
"bytes"
"math/rand" "math/rand"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestWriteAtBuffer(t *testing.T) { func TestWriteAtBuffer(t *testing.T) {
b := &WriteAtBuffer{} b := &WriteAtBuffer{}
n, err := b.WriteAt([]byte{1}, 0) n, err := b.WriteAt([]byte{1}, 0)
assert.NoError(t, err) if err != nil {
assert.Equal(t, 1, n) t.Errorf("expected no error, but received %v", err)
}
if e, a := 1, n; e != a {
t.Errorf("expected %d, but recieved %d", e, a)
}
n, err = b.WriteAt([]byte{1, 1, 1}, 5) n, err = b.WriteAt([]byte{1, 1, 1}, 5)
assert.NoError(t, err) if err != nil {
assert.Equal(t, 3, n) t.Errorf("expected no error, but received %v", err)
}
if e, a := 3, n; e != a {
t.Errorf("expected %d, but recieved %d", e, a)
}
n, err = b.WriteAt([]byte{2}, 1) n, err = b.WriteAt([]byte{2}, 1)
assert.NoError(t, err) if err != nil {
assert.Equal(t, 1, n) t.Errorf("expected no error, but received %v", err)
}
if e, a := 1, n; e != a {
t.Errorf("expected %d, but recieved %d", e, a)
}
n, err = b.WriteAt([]byte{3}, 2) n, err = b.WriteAt([]byte{3}, 2)
assert.NoError(t, err) if err != nil {
assert.Equal(t, 1, n) t.Errorf("expected no error, but received %v", err)
}
if e, a := 1, n; e != a {
t.Errorf("expected %d, but received %d", e, a)
}
assert.Equal(t, []byte{1, 2, 3, 0, 0, 1, 1, 1}, b.Bytes()) if !bytes.Equal([]byte{1, 2, 3, 0, 0, 1, 1, 1}, b.Bytes()) {
t.Errorf("expected %v, but received %v", []byte{1, 2, 3, 0, 0, 1, 1, 1}, b.Bytes())
}
} }
func BenchmarkWriteAtBuffer(b *testing.B) { func BenchmarkWriteAtBuffer(b *testing.B) {
+1 -1
View File
@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go" const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK // SDKVersion is the version of this SDK
const SDKVersion = "1.12.1" const SDKVersion = "1.13.31"
+16
View File
@@ -125,6 +125,22 @@ func AssertXML(t *testing.T, expect, actual string, container interface{}, msgAn
return equal(t, expectVal, actualVal, msgAndArgs...) return equal(t, expectVal, actualVal, msgAndArgs...)
} }
// DidPanic returns if the function paniced and returns true if the function paniced.
func DidPanic(fn func()) (bool, interface{}) {
var paniced bool
var msg interface{}
func() {
defer func() {
if msg = recover(); msg != nil {
paniced = true
}
}()
fn()
}()
return paniced, msg
}
// objectsAreEqual determines if two objects are considered equal. // objectsAreEqual determines if two objects are considered equal.
// //
// This function does no assertion of any kind. // This function does no assertion of any kind.
@@ -1,82 +1,42 @@
// +build integration // +build integration
// Package s3_test runs integration tests for S3 // Package s3 runs integration tests for S3
package s3_test package s3
import ( import (
"bytes" "bytes"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "reflect"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/awstesting/integration"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
) )
var bucketName *string
var svc *s3.S3
func TestMain(m *testing.M) {
setup()
defer teardown() // only called if we panic
result := m.Run()
teardown()
os.Exit(result)
}
// Create a bucket for testing
func setup() {
svc = s3.New(integration.Session)
bucketName = aws.String(
fmt.Sprintf("aws-sdk-go-integration-%d-%s", time.Now().Unix(), integration.UniqueID()))
for i := 0; i < 10; i++ {
_, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName})
if err == nil {
break
}
}
for {
_, err := svc.HeadBucket(&s3.HeadBucketInput{Bucket: bucketName})
if err == nil {
break
}
time.Sleep(1 * time.Second)
}
}
// Delete the bucket
func teardown() {
resp, _ := svc.ListObjects(&s3.ListObjectsInput{Bucket: bucketName})
for _, o := range resp.Contents {
svc.DeleteObject(&s3.DeleteObjectInput{Bucket: bucketName, Key: o.Key})
}
svc.DeleteBucket(&s3.DeleteBucketInput{Bucket: bucketName})
}
func TestWriteToObject(t *testing.T) { func TestWriteToObject(t *testing.T) {
_, err := svc.PutObject(&s3.PutObjectInput{ _, err := svc.PutObject(&s3.PutObjectInput{
Bucket: bucketName, Bucket: bucketName,
Key: aws.String("key name"), Key: aws.String("key name"),
Body: bytes.NewReader([]byte("hello world")), Body: bytes.NewReader([]byte("hello world")),
}) })
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
resp, err := svc.GetObject(&s3.GetObjectInput{ resp, err := svc.GetObject(&s3.GetObjectInput{
Bucket: bucketName, Bucket: bucketName,
Key: aws.String("key name"), Key: aws.String("key name"),
}) })
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
b, _ := ioutil.ReadAll(resp.Body) b, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("hello world"), b) if e, a := []byte("hello world"), b; !reflect.DeepEqual(e, a) {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestPresignedGetPut(t *testing.T) { func TestPresignedGetPut(t *testing.T) {
@@ -89,18 +49,26 @@ func TestPresignedGetPut(t *testing.T) {
// Presign a PUT request // Presign a PUT request
var puturl string var puturl string
puturl, err = putreq.Presign(300 * time.Second) puturl, err = putreq.Presign(300 * time.Second)
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
// PUT to the presigned URL with a body // PUT to the presigned URL with a body
var puthttpreq *http.Request var puthttpreq *http.Request
buf := bytes.NewReader([]byte("hello world")) buf := bytes.NewReader([]byte("hello world"))
puthttpreq, err = http.NewRequest("PUT", puturl, buf) puthttpreq, err = http.NewRequest("PUT", puturl, buf)
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
var putresp *http.Response var putresp *http.Response
putresp, err = http.DefaultClient.Do(puthttpreq) putresp, err = http.DefaultClient.Do(puthttpreq)
assert.NoError(t, err) if err != nil {
assert.Equal(t, 200, putresp.StatusCode) t.Errorf("expect put with presign url no error, got %v", err)
}
if e, a := 200, putresp.StatusCode; e != a {
t.Errorf("expect %v, got %v", e, a)
}
// Presign a GET on the same URL // Presign a GET on the same URL
getreq, _ := svc.GetObjectRequest(&s3.GetObjectInput{ getreq, _ := svc.GetObjectRequest(&s3.GetObjectInput{
@@ -110,15 +78,21 @@ func TestPresignedGetPut(t *testing.T) {
var geturl string var geturl string
geturl, err = getreq.Presign(300 * time.Second) geturl, err = getreq.Presign(300 * time.Second)
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
// Get the body // Get the body
var getresp *http.Response var getresp *http.Response
getresp, err = http.Get(geturl) getresp, err = http.Get(geturl)
assert.NoError(t, err) if err != nil {
t.Errorf("expect no error, got %v", err)
}
var b []byte var b []byte
defer getresp.Body.Close() defer getresp.Body.Close()
b, err = ioutil.ReadAll(getresp.Body) b, err = ioutil.ReadAll(getresp.Body)
assert.Equal(t, "hello world", string(b)) if e, a := "hello world", string(b); e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
@@ -0,0 +1,102 @@
// +build integration
package s3
import (
"bytes"
"crypto/md5"
"encoding/base64"
"fmt"
"io"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
)
func base64Sum(content []byte) string {
sum := md5.Sum(content)
return base64.StdEncoding.EncodeToString(sum[:])
}
func SkipTestContentMD5Validate(t *testing.T) {
body := []byte("really cool body content")
cases := []struct {
Name string
Body []byte
Sum64 string
RangeGet []int64
}{
{
Body: body,
Sum64: base64Sum(body),
Name: "contentMD5validation.pop",
},
{
Body: []byte{},
Sum64: base64Sum([]byte{}),
Name: "contentMD5validation.empty",
},
{
Body: body,
Sum64: base64Sum(body),
RangeGet: []int64{0, 9},
Name: "contentMD5validation.range",
},
}
for i, c := range cases {
keyName := aws.String(c.Name)
req, _ := svc.PutObjectRequest(&s3.PutObjectInput{
Bucket: bucketName,
Key: keyName,
Body: bytes.NewReader(c.Body),
})
req.Build()
if e, a := c.Sum64, req.HTTPRequest.Header.Get("Content-Md5"); e != a {
t.Errorf("%d, expect %v sum, got %v", i, e, a)
}
if err := req.Send(); err != nil {
t.Fatalf("%d, expect no error, got %v", i, err)
}
getObjIn := &s3.GetObjectInput{
Bucket: bucketName,
Key: keyName,
}
expectBody := c.Body
if c.RangeGet != nil {
getObjIn.Range = aws.String(fmt.Sprintf("bytes=%d-%d", c.RangeGet[0], c.RangeGet[1]-1))
expectBody = c.Body[c.RangeGet[0]:c.RangeGet[1]]
}
getReq, getOut := svc.GetObjectRequest(getObjIn)
getReq.Build()
if e, a := "append-md5", getReq.HTTPRequest.Header.Get("X-Amz-Te"); e != a {
t.Errorf("%d, expect %v encoding, got %v", i, e, a)
}
if err := getReq.Send(); err != nil {
t.Fatalf("%d, expect no error, got %v", i, err)
}
defer getOut.Body.Close()
if e, a := "append-md5", getReq.HTTPResponse.Header.Get("X-Amz-Transfer-Encoding"); e != a {
t.Fatalf("%d, expect response tx encoding header %v, got %v", i, e, a)
}
var readBody bytes.Buffer
_, err := io.Copy(&readBody, getOut.Body)
if err != nil {
t.Fatalf("%d, expect no error, got %v", i, err)
}
if e, a := expectBody, readBody.Bytes(); !bytes.Equal(e, a) {
t.Errorf("%d, expect %v body, got %v", i, e, a)
}
}
}
@@ -11,7 +11,6 @@ import (
"strings" "strings"
"github.com/gucumber/gucumber" "github.com/gucumber/gucumber"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
@@ -31,7 +30,9 @@ func init() {
Bucket: aws.String(bucket), Bucket: aws.String(bucket),
Prefix: aws.String(baseFolder + "/" + prefix), Prefix: aws.String(baseFolder + "/" + prefix),
}) })
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
plaintexts := make(map[string][]byte) plaintexts := make(map[string][]byte)
for _, obj := range out.Contents { for _, obj := range out.Contents {
@@ -40,10 +41,14 @@ func init() {
Bucket: aws.String(bucket), Bucket: aws.String(bucket),
Key: plaintextKey, Key: plaintextKey,
}) })
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
caseKey := strings.TrimPrefix(*plaintextKey, baseFolder+"/"+prefix) caseKey := strings.TrimPrefix(*plaintextKey, baseFolder+"/"+prefix)
plaintext, err := ioutil.ReadAll(ptObj.Body) plaintext, err := ioutil.ReadAll(ptObj.Body)
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
plaintexts[caseKey] = plaintext plaintexts[caseKey] = plaintext
} }
@@ -84,10 +89,14 @@ func init() {
Key: &cipherKey, Key: &cipherKey,
}, },
) )
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
ciphertext, err := ioutil.ReadAll(ctObj.Body) ciphertext, err := ioutil.ReadAll(ctObj.Body)
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
ciphertexts[caseKey] = ciphertext ciphertexts[caseKey] = ciphertext
} }
gucumber.World["decrypted"] = ciphertexts gucumber.World["decrypted"] = ciphertexts
@@ -97,8 +106,12 @@ func init() {
plaintexts := gucumber.World["plaintexts"].(map[string][]byte) plaintexts := gucumber.World["plaintexts"].(map[string][]byte)
ciphertexts := gucumber.World["decrypted"].(map[string][]byte) ciphertexts := gucumber.World["decrypted"].(map[string][]byte)
for caseKey, ciphertext := range ciphertexts { for caseKey, ciphertext := range ciphertexts {
assert.Equal(gucumber.T, len(plaintexts[caseKey]), len(ciphertext)) if e, a := len(plaintexts[caseKey]), len(ciphertext); e != a {
assert.True(gucumber.T, bytes.Equal(plaintexts[caseKey], ciphertext)) gucumber.T.Errorf("expect %v, got %v", e, a)
}
if e, a := plaintexts[caseKey], ciphertext; !bytes.Equal(e, a) {
gucumber.T.Errorf("expect %v, got %v", e, a)
}
} }
}) })
@@ -108,16 +121,22 @@ func init() {
switch kek { switch kek {
case "kms": case "kms":
arn, err := getAliasInformation(v1, v2) arn, err := getAliasInformation(v1, v2)
assert.Nil(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect nil, got %v", nil)
}
b64Arn := base64.StdEncoding.EncodeToString([]byte(arn)) b64Arn := base64.StdEncoding.EncodeToString([]byte(arn))
assert.Nil(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect nil, got %v", nil)
}
gucumber.World["Masterkey"] = b64Arn gucumber.World["Masterkey"] = b64Arn
handler = s3crypto.NewKMSKeyGenerator(kms.New(session.New(&aws.Config{ handler = s3crypto.NewKMSKeyGenerator(kms.New(session.New(&aws.Config{
Region: &v2, Region: &v2,
})), arn) })), arn)
assert.Nil(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect nil, got %v", nil)
}
default: default:
gucumber.T.Skip() gucumber.T.Skip()
} }
@@ -157,7 +176,9 @@ func init() {
} }
_, err := c.PutObject(input) _, err := c.PutObject(input)
assert.Nil(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect nil, got %v", nil)
}
} }
}) })
} }
@@ -12,7 +12,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"time"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
@@ -52,7 +51,7 @@ func setup() error {
// Create a bucket for testing // Create a bucket for testing
bucketName = aws.String( bucketName = aws.String(
fmt.Sprintf("aws-sdk-go-integration-%d-%s", time.Now().Unix(), integration.UniqueID())) fmt.Sprintf("aws-sdk-go-integration-%s", integration.UniqueID()))
_, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName}) _, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName})
if err != nil { if err != nil {
@@ -0,0 +1,67 @@
// +build integration
package s3
import (
"fmt"
"os"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/awstesting/integration"
"github.com/aws/aws-sdk-go/service/s3"
)
const integBucketPrefix = "aws-sdk-go-integration"
var bucketName *string
var svc *s3.S3
func TestMain(m *testing.M) {
setup()
defer teardown() // only called if we panic
result := m.Run()
teardown()
os.Exit(result)
}
// Create a bucket for testing
func setup() {
svc = s3.New(integration.Session)
bucketName = aws.String(
fmt.Sprintf("%s-%s",
integBucketPrefix, integration.UniqueID()))
_, err := svc.CreateBucket(&s3.CreateBucketInput{Bucket: bucketName})
if err != nil {
panic(fmt.Sprintf("failed to create bucket %s, %v", *bucketName, err))
}
err = svc.WaitUntilBucketExists(&s3.HeadBucketInput{Bucket: bucketName})
if err != nil {
panic(fmt.Sprintf("failed waiting for bucket %s to be created", *bucketName))
}
}
// Delete the bucket
func teardown() {
resp, err := svc.ListObjects(&s3.ListObjectsInput{Bucket: bucketName})
if err != nil {
panic(fmt.Sprintf("failed to list s3 bucket %s objects, %v", *bucketName, err))
}
errs := []error{}
for _, o := range resp.Contents {
_, err = svc.DeleteObject(&s3.DeleteObjectInput{Bucket: bucketName, Key: o.Key})
if err != nil {
errs = append(errs, err)
}
}
if len(errs) != 0 {
panic(fmt.Sprintf("failed to delete objects, %s", errs))
}
svc.DeleteBucket(&s3.DeleteBucketInput{Bucket: bucketName})
}
@@ -0,0 +1,7 @@
# language: en
@autoscalingplans @client
Feature: AWS Auto Scaling Plans
Scenario: Making a request
When I call the "DescribeScalingPlans" API
Then the request should be successful
@@ -0,0 +1,16 @@
// +build integration
//Package autoscalingplans provides gucumber integration tests support.
package autoscalingplans
import (
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/autoscalingplans"
"github.com/gucumber/gucumber"
)
func init() {
gucumber.Before("@autoscalingplans", func() {
gucumber.World["client"] = autoscalingplans.New(smoke.Session)
})
}
@@ -0,0 +1,16 @@
// +build integration
//Package cloudhsmv2 provides gucumber integration tests support.
package cloudhsmv2
import (
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/cloudhsmv2"
"github.com/gucumber/gucumber"
)
func init() {
gucumber.Before("@cloudhsmv2", func() {
gucumber.World["client"] = cloudhsmv2.New(smoke.Session)
})
}
@@ -0,0 +1,7 @@
# language: en
@cloudhsmv2 @client
Feature: Amazon CloudHSMv2
Scenario: Making a request
When I call the "DescribeBackups" API
Then the request should be successful
@@ -0,0 +1,16 @@
// +build integration
//Package mediastore provides gucumber integration tests support.
package mediastore
import (
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/mediastore"
"github.com/gucumber/gucumber"
)
func init() {
gucumber.Before("@mediastore", func() {
gucumber.World["client"] = mediastore.New(smoke.Session)
})
}
@@ -0,0 +1,7 @@
# language: en
@mediastore @client
Feature: AWS Elemental MediaStore
Scenario: Making a request
When I call the "ListContainers" API
Then the request should be successful
@@ -0,0 +1,34 @@
// +build integration
//Package mediastoredata provides gucumber integration tests support.
package mediastoredata
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/mediastore"
"github.com/aws/aws-sdk-go/service/mediastoredata"
"github.com/gucumber/gucumber"
)
func init() {
const containerName = "awsgosdkteamintegcontainer"
gucumber.Before("@mediastoredata", func() {
mediastoreSvc := mediastore.New(smoke.Session)
resp, err := mediastoreSvc.DescribeContainer(&mediastore.DescribeContainerInput{
ContainerName: aws.String(containerName),
})
if err != nil {
gucumber.World["error"] = fmt.Errorf("failed to get mediastore container endpoint for test, %v", err)
return
}
gucumber.World["client"] = mediastoredata.New(smoke.Session, &aws.Config{
Endpoint: resp.Container.Endpoint,
})
})
}
@@ -0,0 +1,7 @@
# language: en
@mediastoredata @client
Feature: AWS Elemental MediaStore Data Plane
Scenario: Making a request
When I call the "ListItems" API
Then the request should be successful
@@ -0,0 +1,16 @@
// +build integration
//Package mobile provides gucumber integration tests support.
package mobile
import (
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/mobile"
"github.com/gucumber/gucumber"
)
func init() {
gucumber.Before("@mobile", func() {
gucumber.World["client"] = mobile.New(smoke.Session)
})
}
@@ -0,0 +1,7 @@
# language: en
@mobile @client
Feature: AWS Mobile
Scenario: Making a request
When I call the "ListBundles" API
Then the request should be successful
@@ -0,0 +1,16 @@
// +build integration
//Package sagemakerruntime provides gucumber integration tests support.
package sagemakerruntime
import (
"github.com/aws/aws-sdk-go/awstesting/integration/smoke"
"github.com/aws/aws-sdk-go/service/sagemakerruntime"
"github.com/gucumber/gucumber"
)
func init() {
gucumber.Before("@sagemakerruntime", func() {
gucumber.World["client"] = sagemakerruntime.New(smoke.Session)
})
}
@@ -0,0 +1,10 @@
# language: en
@sagemakerruntime @client
Feature: Amazon SageMaker Runtime
Scenario: Making a request
When I attempt to call the "InvokeEndpoint" API with JSON:
"""
{"EndpointName": "fake-endpoint", "Body": [123, 125]}
"""
Then I expect the response error code to be "ValidationError"
+62 -24
View File
@@ -5,7 +5,6 @@ package smoke
import ( import (
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"reflect" "reflect"
"regexp" "regexp"
@@ -13,7 +12,6 @@ import (
"strings" "strings"
"github.com/gucumber/gucumber" "github.com/gucumber/gucumber"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
@@ -47,12 +45,16 @@ func init() {
gucumber.Then(`^the value at "(.+?)" should be a list$`, func(member string) { gucumber.Then(`^the value at "(.+?)" should be a list$`, func(member string) {
vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member) vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member)
assert.NotNil(gucumber.T, vals) if vals == nil {
gucumber.T.Errorf("expect not nil, was")
}
}) })
gucumber.Then(`^the response should contain a "(.+?)"$`, func(member string) { gucumber.Then(`^the response should contain a "(.+?)"$`, func(member string) {
vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member) vals, _ := awsutil.ValuesAtPath(gucumber.World["response"], member)
assert.NotEmpty(gucumber.T, vals) if len(vals) == 0 {
gucumber.T.Errorf("expect values, got none")
}
}) })
gucumber.When(`^I attempt to call the "(.+?)" API with:$`, func(op string, args [][]string) { gucumber.When(`^I attempt to call the "(.+?)" API with:$`, func(op string, args [][]string) {
@@ -61,23 +63,33 @@ func init() {
gucumber.Then(`^I expect the response error code to be "(.+?)"$`, func(code string) { gucumber.Then(`^I expect the response error code to be "(.+?)"$`, func(code string) {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
gucumber.T.Errorf("no error returned")
}
if ok { if ok {
assert.Equal(gucumber.T, code, err.Code(), "Error: %v", err) if e, a := code, err.Code(); e != a {
gucumber.T.Errorf("Error: %v", err)
}
} }
}) })
gucumber.And(`^I expect the response error message to include:$`, func(data string) { gucumber.And(`^I expect the response error message to include:$`, func(data string) {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
gucumber.T.Errorf("no error returned")
}
if ok { if ok {
assert.Contains(gucumber.T, err.Error(), data) if a := err.Error(); len(a) == 0 {
gucumber.T.Errorf("expect string length to be greater than zero")
}
} }
}) })
gucumber.And(`^I expect the response error message to include one of:$`, func(table [][]string) { gucumber.And(`^I expect the response error message to include one of:$`, func(table [][]string) {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
gucumber.T.Errorf("no error returned")
}
if ok { if ok {
found := false found := false
for _, row := range table { for _, row := range table {
@@ -87,14 +99,20 @@ func init() {
} }
} }
assert.True(gucumber.T, found, fmt.Sprintf("no error messages matched: \"%s\"", err.Error())) if !found {
gucumber.T.Errorf("no error messages matched: \"%s\"", err.Error())
}
} }
}) })
gucumber.And(`^I expect the response error message not be empty$`, func() { gucumber.And(`^I expect the response error message not be empty$`, func() {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
assert.NotEmpty(gucumber.T, err.Message()) gucumber.T.Errorf("no error returned")
}
if len(err.Message()) == 0 {
gucumber.T.Errorf("expect values, got none")
}
}) })
gucumber.When(`^I call the "(.+?)" API with JSON:$`, func(s1 string, data string) { gucumber.When(`^I call the "(.+?)" API with JSON:$`, func(s1 string, data string) {
@@ -107,26 +125,42 @@ func init() {
gucumber.Then(`^the error code should be "(.+?)"$`, func(s1 string) { gucumber.Then(`^the error code should be "(.+?)"$`, func(s1 string) {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
assert.Equal(gucumber.T, s1, err.Code()) gucumber.T.Errorf("no error returned")
}
if e, a := s1, err.Code(); e != a {
gucumber.T.Errorf("expect %v, got %v", e, a)
}
}) })
gucumber.And(`^the error message should contain:$`, func(data string) { gucumber.And(`^the error message should contain:$`, func(data string) {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
assert.Contains(gucumber.T, err.Error(), data) gucumber.T.Errorf("no error returned")
}
if a := err.Error(); len(a) == 0 {
gucumber.T.Errorf("expect string length to be greater than zero")
}
}) })
gucumber.Then(`^the request should fail$`, func() { gucumber.Then(`^the request should fail$`, func() {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.True(gucumber.T, ok, "no error returned") if !ok {
assert.Error(gucumber.T, err) gucumber.T.Errorf("no error returned")
}
if err == nil {
gucumber.T.Errorf("expect error, got none")
}
}) })
gucumber.Then(`^the request should be successful$`, func() { gucumber.Then(`^the request should be successful$`, func() {
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.False(gucumber.T, ok, "error returned") if ok {
assert.NoError(gucumber.T, err) gucumber.T.Errorf("error returned")
}
if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
}) })
} }
@@ -160,10 +194,12 @@ func call(op string, args [][]string, allowError bool) {
if !allowError { if !allowError {
err, _ := gucumber.World["error"].(error) err, _ := gucumber.World["error"].(error)
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
} }
} else { } else {
assert.Fail(gucumber.T, "failed to find operation "+op) gucumber.T.Errorf("failed to find operation " + op)
} }
} }
@@ -215,10 +251,12 @@ func callWithJSON(op, j string, allowError bool) {
if !allowError { if !allowError {
err, _ := gucumber.World["error"].(error) err, _ := gucumber.World["error"].(error)
assert.NoError(gucumber.T, err) if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
} }
} else { } else {
assert.Fail(gucumber.T, "failed to find operation "+op) gucumber.T.Errorf("failed to find operation " + op)
} }
} }
+6 -3
View File
@@ -9,7 +9,6 @@ import (
"runtime" "runtime"
"github.com/gucumber/gucumber" "github.com/gucumber/gucumber"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/awserr"
@@ -32,8 +31,12 @@ func init() {
gucumber.Then(`^I should not have leaked any resources$`, func() { gucumber.Then(`^I should not have leaked any resources$`, func() {
runtime.GC() runtime.GC()
err, ok := gucumber.World["error"].(awserr.Error) err, ok := gucumber.World["error"].(awserr.Error)
assert.False(gucumber.T, ok, "error returned") if ok {
assert.NoError(gucumber.T, err) gucumber.T.Errorf("error returned")
}
if err != nil {
gucumber.T.Errorf("expect no error, got %v", err)
}
}) })
gucumber.And(`^I have a list of services$`, func() { gucumber.And(`^I have a list of services$`, func() {
+42 -16
View File
@@ -4,8 +4,6 @@ import (
"io" "io"
"testing" "testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/awstesting" "github.com/aws/aws-sdk-go/awstesting"
) )
@@ -13,9 +11,15 @@ func TestReadCloserClose(t *testing.T) {
rc := awstesting.ReadCloser{Size: 1} rc := awstesting.ReadCloser{Size: 1}
err := rc.Close() err := rc.Close()
assert.Nil(t, err) if err != nil {
assert.True(t, rc.Closed) t.Errorf("expect nil, got %v", err)
assert.Equal(t, rc.Size, 1) }
if !rc.Closed {
t.Errorf("expect closed, was not")
}
if e, a := rc.Size, 1; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestReadCloserRead(t *testing.T) { func TestReadCloserRead(t *testing.T) {
@@ -24,16 +28,30 @@ func TestReadCloserRead(t *testing.T) {
n, err := rc.Read(b) n, err := rc.Read(b)
assert.Nil(t, err) if err != nil {
assert.Equal(t, n, 2) t.Errorf("expect nil, got %v", err)
assert.False(t, rc.Closed) }
assert.Equal(t, rc.Size, 3) if e, a := n, 2; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if rc.Closed {
t.Errorf("expect not to be closed")
}
if e, a := rc.Size, 3; e != a {
t.Errorf("expect %v, got %v", e, a)
}
err = rc.Close() err = rc.Close()
assert.Nil(t, err) if err != nil {
t.Errorf("expect nil, got %v", err)
}
n, err = rc.Read(b) n, err = rc.Read(b)
assert.Equal(t, err, io.EOF) if e, a := err, io.EOF; e != a {
assert.Equal(t, n, 0) t.Errorf("expect %v, got %v", e, a)
}
if e, a := n, 0; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
func TestReadCloserReadAll(t *testing.T) { func TestReadCloserReadAll(t *testing.T) {
@@ -42,8 +60,16 @@ func TestReadCloserReadAll(t *testing.T) {
n, err := rc.Read(b) n, err := rc.Read(b)
assert.Equal(t, err, io.EOF) if e, a := err, io.EOF; e != a {
assert.Equal(t, n, 5) t.Errorf("expect %v, got %v", e, a)
assert.False(t, rc.Closed) }
assert.Equal(t, rc.Size, 0) if e, a := n, 5; e != a {
t.Errorf("expect %v, got %v", e, a)
}
if rc.Closed {
t.Errorf("expect not to be closed")
}
if e, a := rc.Size, 0; e != a {
t.Errorf("expect %v, got %v", e, a)
}
} }
+21
View File
@@ -0,0 +1,21 @@
version: 0.2
phases:
build:
commands:
- echo Build started on `date`
- export GOPATH=/go
- export SDK_CB_ROOT=`pwd`
- export SDK_GO_ROOT=/go/src/github.com/aws/aws-sdk-go
- mkdir -p /go/src/github.com/aws
- ln -s $SDK_CB_ROOT $SDK_GO_ROOT
- cd $SDK_GO_ROOT
- make unit
- cd $SDK_CB_ROOT
- #echo Compiling the Go code...
post_build:
commands:
- echo Build completed on `date`
#artifacts:
# files:
# - hello
+1 -1
View File
@@ -16,7 +16,7 @@
<script type="text/javascript" src="/sdk-for-go/api/lib/godoc/jquery.treeview.js"></script> <script type="text/javascript" src="/sdk-for-go/api/lib/godoc/jquery.treeview.js"></script>
<script type="text/javascript" src="/sdk-for-go/api/lib/godoc/jquery.treeview.edit.js"></script> <script type="text/javascript" src="/sdk-for-go/api/lib/godoc/jquery.treeview.edit.js"></script>
<script src="https://a0.awsstatic.com/s_code/js/1.0/awshome_s_code.js%22&gt;&lt;/script&gt;"></script> <script src="https://a0.awsstatic.com/s_code/js/1.0/awshome_s_code.js"></script>
<!-- SiteCatalyst code version: H.25.1. Copyright 1996-2012 Adobe, Inc. All Rights Reserved --> <!-- SiteCatalyst code version: H.25.1. Copyright 1996-2012 Adobe, Inc. All Rights Reserved -->
<script><!-- <script><!--
/************* DO NOT ALTER ANYTHING BELOW THIS LINE ! **************/ /************* DO NOT ALTER ANYTHING BELOW THIS LINE ! **************/
+1 -1
View File
@@ -197,7 +197,7 @@
// regions different from the Session's region. // regions different from the Session's region.
// //
// svc := s3.New(sess, &aws.Config{ // svc := s3.New(sess, &aws.Config{
// Region: aws.String(ednpoints.UsWest2RegionID), // Region: aws.String(endpoints.UsWest2RegionID),
// }) // })
// //
// See the Config type in the aws package for more information and additional // See the Config type in the aws package for more information and additional
@@ -68,7 +68,7 @@ func main() {
ddbSvc.ListTables(&dynamodb.ListTablesInput{}) ddbSvc.ListTables(&dynamodb.ListTablesInput{})
// Setting Config's Endpoint will override the EndpointResolver. Forcing // Setting Config's Endpoint will override the EndpointResolver. Forcing
// the service clien to make all operation to the endpoint specified // the service client to make all operation to the endpoint specified
// the in the config. // the in the config.
ddbSvcLocal := dynamodb.New(sess, &aws.Config{ ddbSvcLocal := dynamodb.New(sess, &aws.Config{
Endpoint: aws.String("http://localhost:8088"), Endpoint: aws.String("http://localhost:8088"),
@@ -0,0 +1,11 @@
# Using Custom Retry Strategies with the SDK
This example highlights how you can define a custom retry strategy for the SDK to use. The example wraps the SDK's DefaultRetryer with a set of custom rules to not retry HTTP 5xx status codes. In all other cases the custom retry strategy falls back the SDK's DefaultRetryer's functionality.
## Usage
This example will attempt to make an Amazon CloudWatch Logs PutLogEvents DescribeLogGroups API call. This example expects to retrieve credentials from the `~/.aws/credentials` file.
```sh
go run ./custom_retryer.go
```
@@ -0,0 +1,75 @@
// +build example
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/endpoints"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
)
func main() {
sess := session.Must(
session.NewSession(&aws.Config{
// Use a custom retryer to provide custom retry rules.
Retryer: CustomRetryer{DefaultRetryer: client.DefaultRetryer{NumMaxRetries: 3}},
// Use the SDK's SharedCredentialsProvider directly instead of the
// SDK's default credential chain. This ensures that the
// application can call Config.Credentials.Expire. This is counter
// to the SDK's default credentials chain, which will never reread
// the shared credentials file.
Credentials: credentials.NewCredentials(&credentials.SharedCredentialsProvider{
Filename: defaults.SharedCredentialsFilename(),
Profile: "default",
}),
Region: aws.String(endpoints.UsWest2RegionID),
}),
)
// Add a request handler to the AfterRetry handler stack that is used by the
// SDK to be executed after the SDK has determined if it will retry.
// This handler forces the SDK's Credentials to be expired, and next call to
// Credentials.Get will attempt to refresh the credentials.
sess.Handlers.AfterRetry.PushBack(func(req *request.Request) {
if aerr, ok := req.Error.(awserr.RequestFailure); ok && aerr != nil {
if aerr.Code() == "InvalidClaimException" {
// Force the credentials to expire based on error code. Next
// call to Credentials.Get will attempt to refresh credentials.
req.Config.Credentials.Expire()
}
}
})
svc := cloudwatchlogs.New(sess)
resp, err := svc.DescribeLogGroups(&cloudwatchlogs.DescribeLogGroupsInput{})
fmt.Println(resp, err)
}
// CustomRetryer wraps the SDK's built in DefaultRetryer adding additional
// custom features. Such as, no retry for 5xx status codes, and refresh
// credentials.
type CustomRetryer struct {
client.DefaultRetryer
}
// ShouldRetry overrides the SDK's built in DefaultRetryer adding customization
// to not retry 5xx status codes.
func (r CustomRetryer) ShouldRetry(req *request.Request) bool {
if req.HTTPResponse.StatusCode >= 500 {
// Don't retry any 5xx status codes.
return false
}
// Fallback to SDK's built in retry rules
return r.DefaultRetryer.ShouldRetry(req)
}
@@ -4,7 +4,7 @@ Uploads a file to S3 given a bucket and object key. Also takes a duration
value to terminate the update if it doesn't complete within that time. value to terminate the update if it doesn't complete within that time.
The AWS Region needs to be provided in the AWS shared config or on the The AWS Region needs to be provided in the AWS shared config or on the
environment variable as `AWS_REGION`. Credentials also must be provided environment variable as `AWS_REGION`. Credentials also must be provided.
Will default to shared config file, but can load from environment if provided. Will default to shared config file, but can load from environment if provided.
## Usage: ## Usage:
@@ -8,10 +8,10 @@ This is an example using the AWS SDK for Go to list ec2 instances instance state
```sh ```sh
# To fetch the stopped instance of all region use below: # To fetch the stopped and running instances of all region use below:
./filter_ec2_by_region --state running --state stopped ./filter_ec2_by_region --state running --state stopped
# To fetch the stopped and running instance for region us-west-1 and eu-west-1 use below: # To fetch the stopped and running instances for region us-west-1 and eu-west-1 use below:
./filter_ec2_by_region --state running --state stopped --region us-west-1 --region=eu-west-1 ./filter_ec2_by_region --state running --state stopped --region us-west-1 --region=eu-west-1
``` ```
@@ -60,9 +60,9 @@ func main() {
if err != nil { if err != nil {
fmt.Println("Error", err) fmt.Println("Error", err)
} else { } else {
fmt.Printf("\n\n\nFetching instace details for region: %s with criteria: %s**\n ", region, instanceCriteria) fmt.Printf("\n\n\nFetching instance details for region: %s with criteria: %s**\n ", region, instanceCriteria)
if len(result.Reservations) == 0 { if len(result.Reservations) == 0 {
fmt.Printf("There is no instance for the for region %s with the matching Criteria:%s \n", region, instanceCriteria) fmt.Printf("There is no instance for the region: %s with the matching criteria:%s \n", region, instanceCriteria)
} }
for _, reservation := range result.Reservations { for _, reservation := range result.Reservations {
+26
View File
@@ -0,0 +1,26 @@
# Example
sync will upload a given directory to Amazon S3 using the upload iterator interface defined in the
s3manager package. This example uses a path that is specified during runtime to walk and build keys
to upload to Amazon S3. It will use the keys to upload the files/folders to Amazon S3.
# Usage
```sh
sync <params>
-region <region> // required
-bucket <bucket> // required
-path <path> // required
```
```sh
go run -tags example sync.go
-region <region> // required
-bucket <bucket> // required
-path <path> // required
```
Output:
```
success
```
+112
View File
@@ -0,0 +1,112 @@
// +build example
package main
import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
)
// SyncFolderIterator is used to upload a given folder
// to Amazon S3.
type SyncFolderIterator struct {
bucket string
fileInfos []fileInfo
err error
}
type fileInfo struct {
key string
fullpath string
}
// NewSyncFolderIterator will walk the path, and store the key and full path
// of the object to be uploaded. This will return a new SyncFolderIterator
// with the data provided from walking the path.
func NewSyncFolderIterator(path, bucket string) *SyncFolderIterator {
metadata := []fileInfo{}
filepath.Walk(path, func(p string, info os.FileInfo, err error) error {
if !info.IsDir() {
key := strings.TrimPrefix(p, path)
metadata = append(metadata, fileInfo{key, p})
}
return nil
})
return &SyncFolderIterator{
bucket,
metadata,
nil,
}
}
// Next will determine whether or not there is any remaining files to
// be uploaded.
func (iter *SyncFolderIterator) Next() bool {
return len(iter.fileInfos) > 0
}
// Err returns any error when os.Open is called.
func (iter *SyncFolderIterator) Err() error {
return iter.err
}
// UploadObject will prep the new upload object by open that file and constructing a new
// s3manager.UploadInput.
func (iter *SyncFolderIterator) UploadObject() s3manager.BatchUploadObject {
fi := iter.fileInfos[0]
iter.fileInfos = iter.fileInfos[1:]
body, err := os.Open(fi.fullpath)
if err != nil {
iter.err = err
}
input := s3manager.UploadInput{
Bucket: &iter.bucket,
Key: &fi.key,
Body: body,
}
return s3manager.BatchUploadObject{
&input,
nil,
}
}
// Upload a directory to a given bucket
//
// Usage:
// sync <params>
// -region <region> // required
// -bucket <bucket> // required
// -path <path> // required
func main() {
bucketPtr := flag.String("bucket", "", "bucket to upload to")
regionPtr := flag.String("region", "", "region to be used when making requests")
pathPtr := flag.String("path", "", "path of directory to be synced")
flag.Parse()
sess := session.New(&aws.Config{
Region: regionPtr,
})
uploader := s3manager.NewUploader(sess)
iter := NewSyncFolderIterator(*pathPtr, *bucketPtr)
if err := uploader.UploadWithIterator(aws.BackgroundContext(), iter); err != nil {
fmt.Fprintf(os.Stderr, "unexpected error has occured: %v", err)
}
if err := iter.Err(); err != nil {
fmt.Fprintf(os.Stderr, "unexpected error occured during file walking: %v", err)
}
fmt.Println("Success")
}
+10
View File
@@ -0,0 +1,10 @@
// +build !go1.7
package sdkio
// Copy of Go 1.7 io package's Seeker constants.
const (
SeekStart = 0 // seek relative to the origin of the file
SeekCurrent = 1 // seek relative to the current offset
SeekEnd = 2 // seek relative to the end
)
+12
View File
@@ -0,0 +1,12 @@
// +build go1.7
package sdkio
import "io"
// Alias for Go 1.7 io package Seeker constants
const (
SeekStart = io.SeekStart // seek relative to the origin of the file
SeekCurrent = io.SeekCurrent // seek relative to the current offset
SeekEnd = io.SeekEnd // seek relative to the end
)
+29
View File
@@ -0,0 +1,29 @@
package sdkrand
import (
"math/rand"
"sync"
"time"
)
// lockedSource is a thread-safe implementation of rand.Source
type lockedSource struct {
lk sync.Mutex
src rand.Source
}
func (r *lockedSource) Int63() (n int64) {
r.lk.Lock()
n = r.src.Int63()
r.lk.Unlock()
return
}
func (r *lockedSource) Seed(seed int64) {
r.lk.Lock()
r.src.Seed(seed)
r.lk.Unlock()
}
// SeededRand is a new RNG using a thread safe implementation of rand.Source
var SeededRand = rand.New(&lockedSource{src: rand.NewSource(time.Now().UnixNano())})
@@ -774,8 +774,7 @@
"VM_NAME", "VM_NAME",
"VM_PATH", "VM_PATH",
"BIOS_ID", "BIOS_ID",
"MOTHERBOARD_SERIAL_NUMBER", "MOTHERBOARD_SERIAL_NUMBER"
"LABEL"
] ]
}, },
"ResourceAttributeValue":{ "ResourceAttributeValue":{
@@ -1,11 +1,11 @@
{ {
"version": "2.0", "version": "2.0",
"service": "<p/>", "service": "<p>The AWS Migration Hub API methods help to obtain server and application migration status and integrate your resource-specific migration tool by providing a programmatic interface to Migration Hub. </p>",
"operations": { "operations": {
"AssociateCreatedArtifact": "<p>Associates a created artifact of an AWS cloud resource, the target receiving the migration, with the migration task performed by a migration tool. This API has the following traits:</p> <ul> <li> <p>Migration tools can call the <code>AssociateCreatedArtifact</code> operation to indicate which AWS artifact is associated with a migration task.</p> </li> <li> <p>The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: <code>arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b</code>.</p> </li> <li> <p>Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or DMS endpoint, etc.</p> </li> </ul>", "AssociateCreatedArtifact": "<p>Associates a created artifact of an AWS cloud resource, the target receiving the migration, with the migration task performed by a migration tool. This API has the following traits:</p> <ul> <li> <p>Migration tools can call the <code>AssociateCreatedArtifact</code> operation to indicate which AWS artifact is associated with a migration task.</p> </li> <li> <p>The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: <code>arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b</code>.</p> </li> <li> <p>Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or DMS endpoint, etc.</p> </li> </ul>",
"AssociateDiscoveredResource": "<p>Associates a discovered resource ID from Application Discovery Service (ADS) with a migration task.</p>", "AssociateDiscoveredResource": "<p>Associates a discovered resource ID from Application Discovery Service (ADS) with a migration task.</p>",
"CreateProgressUpdateStream": "<p>Creates a progress update stream which is an AWS resource used for access control as well as a namespace for migration task names that is implicitly linked to your AWS account. It must uniquely identify the migration tool as it is used for all updates made by the tool; however, it does not need to be unique for each AWS account because it is scoped to the AWS account.</p>", "CreateProgressUpdateStream": "<p>Creates a progress update stream which is an AWS resource used for access control as well as a namespace for migration task names that is implicitly linked to your AWS account. It must uniquely identify the migration tool as it is used for all updates made by the tool; however, it does not need to be unique for each AWS account because it is scoped to the AWS account.</p>",
"DeleteProgressUpdateStream": "<p>Deletes a progress update stream, including all of its tasks, which was previously created as an AWS resource used for access control. This API has the following traits:</p> <ul> <li> <p>The only parameter needed for <code>DeleteProgressUpdateStream</code> is the stream name (same as a <code>CreateProgressUpdateStream</code> call).</p> </li> <li> <p>The call will return, and a background process will asynchronously be doing the actual delete of the stream and all of its resources (tasks, associated resources, resource attributes, created artifacts).</p> </li> <li> <p>If the stream takes time to be deleted, it might still show up on a <code>ListProgressUpdateStreams</code> call.</p> </li> <li> <p> <code>CreateProgressUpdateStream</code>, <code>ImportMigrationTask</code>, <code>NotifyMigrationTaskState</code>, and all Associate[*] APIs realted to the tasks belonging to the stream will throw \"InvalidInputException\" if the stream of the same name is in the process of being deleted.</p> </li> <li> <p>Once the stream and all of its resources are deleted, <code>CreateProgressUpdateStream</code> for a stream of the same name will succeed, and that stream will be an entirely new logical resource (without any resources associated with the old stream).</p> </li> </ul>", "DeleteProgressUpdateStream": "<p>Deletes a progress update stream, including all of its tasks, which was previously created as an AWS resource used for access control. This API has the following traits:</p> <ul> <li> <p>The only parameter needed for <code>DeleteProgressUpdateStream</code> is the stream name (same as a <code>CreateProgressUpdateStream</code> call).</p> </li> <li> <p>The call will return, and a background process will asynchronously delete the stream and all of its resources (tasks, associated resources, resource attributes, created artifacts).</p> </li> <li> <p>If the stream takes time to be deleted, it might still show up on a <code>ListProgressUpdateStreams</code> call.</p> </li> <li> <p> <code>CreateProgressUpdateStream</code>, <code>ImportMigrationTask</code>, <code>NotifyMigrationTaskState</code>, and all Associate[*] APIs realted to the tasks belonging to the stream will throw \"InvalidInputException\" if the stream of the same name is in the process of being deleted.</p> </li> <li> <p>Once the stream and all of its resources are deleted, <code>CreateProgressUpdateStream</code> for a stream of the same name will succeed, and that stream will be an entirely new logical resource (without any resources associated with the old stream).</p> </li> </ul>",
"DescribeApplicationState": "<p>Gets the migration status of an application.</p>", "DescribeApplicationState": "<p>Gets the migration status of an application.</p>",
"DescribeMigrationTask": "<p>Retrieves a list of all attributes associated with a specific migration task.</p>", "DescribeMigrationTask": "<p>Retrieves a list of all attributes associated with a specific migration task.</p>",
"DisassociateCreatedArtifact": "<p>Disassociates a created artifact of an AWS resource with a migration task performed by a migration tool that was previously associated. This API has the following traits:</p> <ul> <li> <p>A migration user can call the <code>DisassociateCreatedArtifacts</code> operation to disassociate a created AWS Artifact from a migration task.</p> </li> <li> <p>The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: <code>arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b</code>.</p> </li> <li> <p>Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or RDS instance, etc.</p> </li> </ul>", "DisassociateCreatedArtifact": "<p>Disassociates a created artifact of an AWS resource with a migration task performed by a migration tool that was previously associated. This API has the following traits:</p> <ul> <li> <p>A migration user can call the <code>DisassociateCreatedArtifacts</code> operation to disassociate a created AWS Artifact from a migration task.</p> </li> <li> <p>The created artifact name must be provided in ARN (Amazon Resource Name) format which will contain information about type and region; for example: <code>arn:aws:ec2:us-east-1:488216288981:image/ami-6d0ba87b</code>.</p> </li> <li> <p>Examples of the AWS resource behind the created artifact are, AMI's, EC2 instance, or RDS instance, etc.</p> </li> </ul>",
@@ -17,11 +17,11 @@
"ListProgressUpdateStreams": "<p>Lists progress update streams associated with the user account making this call.</p>", "ListProgressUpdateStreams": "<p>Lists progress update streams associated with the user account making this call.</p>",
"NotifyApplicationState": "<p>Sets the migration state of an application. For a given application identified by the value passed to <code>ApplicationId</code>, its status is set or updated by passing one of three values to <code>Status</code>: <code>NOT_STARTED | IN_PROGRESS | COMPLETED</code>.</p>", "NotifyApplicationState": "<p>Sets the migration state of an application. For a given application identified by the value passed to <code>ApplicationId</code>, its status is set or updated by passing one of three values to <code>Status</code>: <code>NOT_STARTED | IN_PROGRESS | COMPLETED</code>.</p>",
"NotifyMigrationTaskState": "<p>Notifies Migration Hub of the current status, progress, or other detail regarding a migration task. This API has the following traits:</p> <ul> <li> <p>Migration tools will call the <code>NotifyMigrationTaskState</code> API to share the latest progress and status.</p> </li> <li> <p> <code>MigrationTaskName</code> is used for addressing updates to the correct target.</p> </li> <li> <p> <code>ProgressUpdateStream</code> is used for access control and to provide a namespace for each migration tool.</p> </li> </ul>", "NotifyMigrationTaskState": "<p>Notifies Migration Hub of the current status, progress, or other detail regarding a migration task. This API has the following traits:</p> <ul> <li> <p>Migration tools will call the <code>NotifyMigrationTaskState</code> API to share the latest progress and status.</p> </li> <li> <p> <code>MigrationTaskName</code> is used for addressing updates to the correct target.</p> </li> <li> <p> <code>ProgressUpdateStream</code> is used for access control and to provide a namespace for each migration tool.</p> </li> </ul>",
"PutResourceAttributes": "<p>Provides identifying details of the resource being migrated so that it can be associated in the Application Discovery Service (ADS)'s repository. This association occurs asynchronously after <code>PutResourceAttributes</code> returns.</p> <important> <p>Keep in mind that subsequent calls to PutResourceAttributes will override previously stored attributes. For example, if it is first called with a MAC address, but later, it is desired to <i>add</i> an IP address, it will then be required to call it with <i>both</i> the IP and MAC addresses to prevent overiding the MAC address.</p> </important> <note> <p>Because this is an asynchronous call, it will always return 200, whether an association occurs or not. To confirm if an association was found based on the provided details, call <code>ListAssociatedResource</code>.</p> </note>" "PutResourceAttributes": "<p>Provides identifying details of the resource being migrated so that it can be associated in the Application Discovery Service (ADS)'s repository. This association occurs asynchronously after <code>PutResourceAttributes</code> returns.</p> <important> <ul> <li> <p>Keep in mind that subsequent calls to PutResourceAttributes will override previously stored attributes. For example, if it is first called with a MAC address, but later, it is desired to <i>add</i> an IP address, it will then be required to call it with <i>both</i> the IP and MAC addresses to prevent overiding the MAC address.</p> </li> <li> <p>Note the instructions regarding the special use case of the <code>ResourceAttributeList</code> parameter when specifying any \"VM\" related value.</p> </li> </ul> </important> <note> <p>Because this is an asynchronous call, it will always return 200, whether an association occurs or not. To confirm if an association was found based on the provided details, call <code>ListDiscoveredResources</code>.</p> </note>"
}, },
"shapes": { "shapes": {
"AccessDeniedException": { "AccessDeniedException": {
"base": "<p>Exception raised when the account making the call is not whitelisted or there are other authentication errors.</p>", "base": "<p>You do not have sufficient access to perform this action.</p>",
"refs": { "refs": {
} }
}, },
@@ -351,7 +351,7 @@
} }
}, },
"PolicyErrorException": { "PolicyErrorException": {
"base": "<p>Exception raised when there are problems accessing ADS (Application Discovery Service); most likely due to a misconfigured policy or the <code>ADSCaller</code> role is missing or not configured correctly.</p>", "base": "<p>Exception raised when there are problems accessing ADS (Application Discovery Service); most likely due to a misconfigured policy or the <code>migrationhub-discovery</code> role is missing or not configured correctly.</p>",
"refs": { "refs": {
} }
}, },
@@ -405,7 +405,7 @@
} }
}, },
"ResourceAttribute": { "ResourceAttribute": {
"base": "<p>Attribute associated with a resource.</p>", "base": "<p>Attribute associated with a resource.</p> <p>Note the corresponding format required per type listed below:</p> <dl> <dt>IPV4</dt> <dd> <p> <code>x.x.x.x</code> </p> <p> <i>where x is an integer in the range [0,255]</i> </p> </dd> <dt>IPV6</dt> <dd> <p> <code>y : y : y : y : y : y : y : y</code> </p> <p> <i>where y is a hexadecimal between 0 and FFFF. [0, FFFF]</i> </p> </dd> <dt>MAC_ADDRESS</dt> <dd> <p> <code>^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$</code> </p> </dd> <dt>FQDN</dt> <dd> <p> <code>^[^&lt;&gt;{}\\\\\\\\/?,=\\\\p{Cntrl}]{1,256}$</code> </p> </dd> </dl>",
"refs": { "refs": {
"LatestResourceAttributeList$member": null, "LatestResourceAttributeList$member": null,
"ResourceAttributeList$member": null "ResourceAttributeList$member": null
@@ -414,7 +414,7 @@
"ResourceAttributeList": { "ResourceAttributeList": {
"base": null, "base": null,
"refs": { "refs": {
"PutResourceAttributesRequest$ResourceAttributeList": "<p>Information about the resource that is being migrated. This data will be used to map the task to a resource in the Application Discovery Service (ADS)'s repository.</p>" "PutResourceAttributesRequest$ResourceAttributeList": "<p>Information about the resource that is being migrated. This data will be used to map the task to a resource in the Application Discovery Service (ADS)'s repository.</p> <note> <p>In the <code>ResourceAttribute</code> object array, the <code>Type</code> field is reserved for the following values: <code>IPV4_ADDRESS | IPV6_ADDRESS | MAC_ADDRESS | FQDN | VM_MANAGER_ID | VM_MANAGED_OBJECT_REFERENCE | VM_NAME | VM_PATH | BIOS_ID | MOTHERBOARD_SERIAL_NUMBER</code>, and the identifying value can be a string up to 256 characters.</p> </note> <important> <p>If any \"VM\" related value is used for a <code>ResourceAttribute</code> object, it is required that <code>VM_MANAGER_ID</code>, as a minimum, is always used. If it is not used, the server will not be associated in the Application Discovery Service (ADS)'s repository using any of the other \"VM\" related values, and you will experience data loss. See the Example section below for a use case of specifying \"VM\" related values.</p> </important>"
} }
}, },
"ResourceAttributeType": { "ResourceAttributeType": {
@@ -441,7 +441,7 @@
} }
}, },
"ServiceUnavailableException": { "ServiceUnavailableException": {
"base": "<p>Exception raised when the service encounters throttled communication with upstream dependencies or is overloaded with requests.</p>", "base": "<p>Exception raised when there is an internal, configuration, or dependency error encountered.</p>",
"refs": { "refs": {
} }
}, },
@@ -0,0 +1,905 @@
{
"version":"2.0",
"metadata":{
"apiVersion":"2017-08-22",
"endpointPrefix":"acm-pca",
"jsonVersion":"1.1",
"protocol":"json",
"serviceAbbreviation":"ACM-PCA",
"serviceFullName":"AWS Certificate Manager Private Certificate Authority",
"serviceId":"ACM PCA",
"signatureVersion":"v4",
"targetPrefix":"ACMPrivateCA",
"uid":"acm-pca-2017-08-22"
},
"operations":{
"CreateCertificateAuthority":{
"name":"CreateCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CreateCertificateAuthorityRequest"},
"output":{"shape":"CreateCertificateAuthorityResponse"},
"errors":[
{"shape":"InvalidArgsException"},
{"shape":"InvalidPolicyException"},
{"shape":"LimitExceededException"}
],
"idempotent":true
},
"CreateCertificateAuthorityAuditReport":{
"name":"CreateCertificateAuthorityAuditReport",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"CreateCertificateAuthorityAuditReportRequest"},
"output":{"shape":"CreateCertificateAuthorityAuditReportResponse"},
"errors":[
{"shape":"RequestInProgressException"},
{"shape":"RequestFailedException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidArgsException"},
{"shape":"InvalidStateException"}
],
"idempotent":true
},
"DeleteCertificateAuthority":{
"name":"DeleteCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DeleteCertificateAuthorityRequest"},
"errors":[
{"shape":"ConcurrentModificationException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidStateException"}
]
},
"DescribeCertificateAuthority":{
"name":"DescribeCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeCertificateAuthorityRequest"},
"output":{"shape":"DescribeCertificateAuthorityResponse"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"}
]
},
"DescribeCertificateAuthorityAuditReport":{
"name":"DescribeCertificateAuthorityAuditReport",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"DescribeCertificateAuthorityAuditReportRequest"},
"output":{"shape":"DescribeCertificateAuthorityAuditReportResponse"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArgsException"}
]
},
"GetCertificate":{
"name":"GetCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetCertificateRequest"},
"output":{"shape":"GetCertificateResponse"},
"errors":[
{"shape":"RequestInProgressException"},
{"shape":"RequestFailedException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidStateException"}
]
},
"GetCertificateAuthorityCertificate":{
"name":"GetCertificateAuthorityCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetCertificateAuthorityCertificateRequest"},
"output":{"shape":"GetCertificateAuthorityCertificateResponse"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidStateException"},
{"shape":"InvalidArnException"}
]
},
"GetCertificateAuthorityCsr":{
"name":"GetCertificateAuthorityCsr",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"GetCertificateAuthorityCsrRequest"},
"output":{"shape":"GetCertificateAuthorityCsrResponse"},
"errors":[
{"shape":"RequestInProgressException"},
{"shape":"RequestFailedException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"}
]
},
"ImportCertificateAuthorityCertificate":{
"name":"ImportCertificateAuthorityCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ImportCertificateAuthorityCertificateRequest"},
"errors":[
{"shape":"ConcurrentModificationException"},
{"shape":"RequestInProgressException"},
{"shape":"RequestFailedException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"MalformedCertificateException"},
{"shape":"CertificateMismatchException"}
]
},
"IssueCertificate":{
"name":"IssueCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"IssueCertificateRequest"},
"output":{"shape":"IssueCertificateResponse"},
"errors":[
{"shape":"LimitExceededException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidStateException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidArgsException"},
{"shape":"MalformedCSRException"}
],
"idempotent":true
},
"ListCertificateAuthorities":{
"name":"ListCertificateAuthorities",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ListCertificateAuthoritiesRequest"},
"output":{"shape":"ListCertificateAuthoritiesResponse"},
"errors":[
{"shape":"InvalidNextTokenException"}
]
},
"ListTags":{
"name":"ListTags",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ListTagsRequest"},
"output":{"shape":"ListTagsResponse"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"}
]
},
"RevokeCertificate":{
"name":"RevokeCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"RevokeCertificateRequest"},
"errors":[
{"shape":"ConcurrentModificationException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidStateException"},
{"shape":"ResourceNotFoundException"},
{"shape":"RequestAlreadyProcessedException"},
{"shape":"RequestInProgressException"},
{"shape":"RequestFailedException"}
]
},
"TagCertificateAuthority":{
"name":"TagCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"TagCertificateAuthorityRequest"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidTagException"},
{"shape":"TooManyTagsException"}
]
},
"UntagCertificateAuthority":{
"name":"UntagCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"UntagCertificateAuthorityRequest"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidTagException"}
]
},
"UpdateCertificateAuthority":{
"name":"UpdateCertificateAuthority",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"UpdateCertificateAuthorityRequest"},
"errors":[
{"shape":"ConcurrentModificationException"},
{"shape":"ResourceNotFoundException"},
{"shape":"InvalidArgsException"},
{"shape":"InvalidArnException"},
{"shape":"InvalidStateException"},
{"shape":"InvalidPolicyException"}
]
}
},
"shapes":{
"ASN1Subject":{
"type":"structure",
"members":{
"Country":{"shape":"CountryCodeString"},
"Organization":{"shape":"String64"},
"OrganizationalUnit":{"shape":"String64"},
"DistinguishedNameQualifier":{"shape":"DistinguishedNameQualifierString"},
"State":{"shape":"String128"},
"CommonName":{"shape":"String64"},
"SerialNumber":{"shape":"String64"},
"Locality":{"shape":"String128"},
"Title":{"shape":"String64"},
"Surname":{"shape":"String40"},
"GivenName":{"shape":"String16"},
"Initials":{"shape":"String5"},
"Pseudonym":{"shape":"String128"},
"GenerationQualifier":{"shape":"String3"}
}
},
"Arn":{
"type":"string",
"max":200,
"min":5,
"pattern":"arn:[\\w+=/,.@-]+:[\\w+=/,.@-]+:[\\w+=/,.@-]*:[0-9]+:[\\w+=,.@-]+(/[\\w+=/,.@-]+)*"
},
"AuditReportId":{
"type":"string",
"max":36,
"min":36,
"pattern":"[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"
},
"AuditReportResponseFormat":{
"type":"string",
"enum":[
"JSON",
"CSV"
]
},
"AuditReportStatus":{
"type":"string",
"enum":[
"CREATING",
"SUCCESS",
"FAILED"
]
},
"Boolean":{"type":"boolean"},
"CertificateAuthorities":{
"type":"list",
"member":{"shape":"CertificateAuthority"}
},
"CertificateAuthority":{
"type":"structure",
"members":{
"Arn":{"shape":"Arn"},
"CreatedAt":{"shape":"TStamp"},
"LastStateChangeAt":{"shape":"TStamp"},
"Type":{"shape":"CertificateAuthorityType"},
"Serial":{"shape":"String"},
"Status":{"shape":"CertificateAuthorityStatus"},
"NotBefore":{"shape":"TStamp"},
"NotAfter":{"shape":"TStamp"},
"FailureReason":{"shape":"FailureReason"},
"CertificateAuthorityConfiguration":{"shape":"CertificateAuthorityConfiguration"},
"RevocationConfiguration":{"shape":"RevocationConfiguration"}
}
},
"CertificateAuthorityConfiguration":{
"type":"structure",
"required":[
"KeyAlgorithm",
"SigningAlgorithm",
"Subject"
],
"members":{
"KeyAlgorithm":{"shape":"KeyAlgorithm"},
"SigningAlgorithm":{"shape":"SigningAlgorithm"},
"Subject":{"shape":"ASN1Subject"}
}
},
"CertificateAuthorityStatus":{
"type":"string",
"enum":[
"CREATING",
"PENDING_CERTIFICATE",
"ACTIVE",
"DISABLED",
"EXPIRED",
"FAILED"
]
},
"CertificateAuthorityType":{
"type":"string",
"enum":["SUBORDINATE"]
},
"CertificateBody":{"type":"string"},
"CertificateBodyBlob":{
"type":"blob",
"max":32768,
"min":1
},
"CertificateChain":{"type":"string"},
"CertificateChainBlob":{
"type":"blob",
"max":2097152,
"min":0
},
"CertificateMismatchException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"ConcurrentModificationException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"CountryCodeString":{
"type":"string",
"pattern":"[A-Za-z]{2}"
},
"CreateCertificateAuthorityAuditReportRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"S3BucketName",
"AuditReportResponseFormat"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"S3BucketName":{"shape":"String"},
"AuditReportResponseFormat":{"shape":"AuditReportResponseFormat"}
}
},
"CreateCertificateAuthorityAuditReportResponse":{
"type":"structure",
"members":{
"AuditReportId":{"shape":"AuditReportId"},
"S3Key":{"shape":"String"}
}
},
"CreateCertificateAuthorityRequest":{
"type":"structure",
"required":[
"CertificateAuthorityConfiguration",
"CertificateAuthorityType"
],
"members":{
"CertificateAuthorityConfiguration":{"shape":"CertificateAuthorityConfiguration"},
"RevocationConfiguration":{"shape":"RevocationConfiguration"},
"CertificateAuthorityType":{"shape":"CertificateAuthorityType"},
"IdempotencyToken":{"shape":"IdempotencyToken"}
}
},
"CreateCertificateAuthorityResponse":{
"type":"structure",
"members":{
"CertificateAuthorityArn":{"shape":"Arn"}
}
},
"CrlConfiguration":{
"type":"structure",
"required":["Enabled"],
"members":{
"Enabled":{
"shape":"Boolean",
"box":true
},
"ExpirationInDays":{
"shape":"Integer1To5000",
"box":true
},
"CustomCname":{"shape":"String253"},
"S3BucketName":{"shape":"String3To255"}
}
},
"CsrBlob":{
"type":"blob",
"max":32768,
"min":1
},
"CsrBody":{"type":"string"},
"DeleteCertificateAuthorityRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"}
}
},
"DescribeCertificateAuthorityAuditReportRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"AuditReportId"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"AuditReportId":{"shape":"AuditReportId"}
}
},
"DescribeCertificateAuthorityAuditReportResponse":{
"type":"structure",
"members":{
"AuditReportStatus":{"shape":"AuditReportStatus"},
"S3BucketName":{"shape":"String"},
"S3Key":{"shape":"String"},
"CreatedAt":{"shape":"TStamp"}
}
},
"DescribeCertificateAuthorityRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"}
}
},
"DescribeCertificateAuthorityResponse":{
"type":"structure",
"members":{
"CertificateAuthority":{"shape":"CertificateAuthority"}
}
},
"DistinguishedNameQualifierString":{
"type":"string",
"max":64,
"min":0,
"pattern":"[a-zA-Z0-9'()+-.?:/= ]*"
},
"FailureReason":{
"type":"string",
"enum":[
"REQUEST_TIMED_OUT",
"UNSUPPORTED_ALGORITHM",
"OTHER"
]
},
"GetCertificateAuthorityCertificateRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"}
}
},
"GetCertificateAuthorityCertificateResponse":{
"type":"structure",
"members":{
"Certificate":{"shape":"CertificateBody"},
"CertificateChain":{"shape":"CertificateChain"}
}
},
"GetCertificateAuthorityCsrRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"}
}
},
"GetCertificateAuthorityCsrResponse":{
"type":"structure",
"members":{
"Csr":{"shape":"CsrBody"}
}
},
"GetCertificateRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"CertificateArn"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"CertificateArn":{"shape":"Arn"}
}
},
"GetCertificateResponse":{
"type":"structure",
"members":{
"Certificate":{"shape":"CertificateBody"},
"CertificateChain":{"shape":"CertificateChain"}
}
},
"IdempotencyToken":{
"type":"string",
"max":36,
"min":1,
"pattern":"[\\u0009\\u000A\\u000D\\u0020-\\u00FF]*"
},
"ImportCertificateAuthorityCertificateRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"Certificate",
"CertificateChain"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"Certificate":{"shape":"CertificateBodyBlob"},
"CertificateChain":{"shape":"CertificateChainBlob"}
}
},
"Integer1To5000":{
"type":"integer",
"max":5000,
"min":1
},
"InvalidArgsException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"InvalidArnException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"InvalidNextTokenException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"InvalidPolicyException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"InvalidStateException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"InvalidTagException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"IssueCertificateRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"Csr",
"SigningAlgorithm",
"Validity"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"Csr":{"shape":"CsrBlob"},
"SigningAlgorithm":{"shape":"SigningAlgorithm"},
"Validity":{"shape":"Validity"},
"IdempotencyToken":{"shape":"IdempotencyToken"}
}
},
"IssueCertificateResponse":{
"type":"structure",
"members":{
"CertificateArn":{"shape":"Arn"}
}
},
"KeyAlgorithm":{
"type":"string",
"enum":[
"RSA_2048",
"RSA_4096",
"EC_prime256v1",
"EC_secp384r1"
]
},
"LimitExceededException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"ListCertificateAuthoritiesRequest":{
"type":"structure",
"members":{
"NextToken":{"shape":"NextToken"},
"MaxResults":{"shape":"MaxResults"}
}
},
"ListCertificateAuthoritiesResponse":{
"type":"structure",
"members":{
"CertificateAuthorities":{"shape":"CertificateAuthorities"},
"NextToken":{"shape":"NextToken"}
}
},
"ListTagsRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"NextToken":{"shape":"NextToken"},
"MaxResults":{"shape":"MaxResults"}
}
},
"ListTagsResponse":{
"type":"structure",
"members":{
"Tags":{"shape":"TagList"},
"NextToken":{"shape":"NextToken"}
}
},
"MalformedCSRException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"MalformedCertificateException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"MaxResults":{
"type":"integer",
"max":1000,
"min":1
},
"NextToken":{
"type":"string",
"max":500,
"min":1
},
"PositiveLong":{
"type":"long",
"min":1
},
"RequestAlreadyProcessedException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"RequestFailedException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"RequestInProgressException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"ResourceNotFoundException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"RevocationConfiguration":{
"type":"structure",
"members":{
"CrlConfiguration":{"shape":"CrlConfiguration"}
}
},
"RevocationReason":{
"type":"string",
"enum":[
"UNSPECIFIED",
"KEY_COMPROMISE",
"CERTIFICATE_AUTHORITY_COMPROMISE",
"AFFILIATION_CHANGED",
"SUPERSEDED",
"CESSATION_OF_OPERATION",
"PRIVILEGE_WITHDRAWN",
"A_A_COMPROMISE"
]
},
"RevokeCertificateRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"CertificateSerial",
"RevocationReason"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"CertificateSerial":{"shape":"String128"},
"RevocationReason":{"shape":"RevocationReason"}
}
},
"SigningAlgorithm":{
"type":"string",
"enum":[
"SHA256WITHECDSA",
"SHA384WITHECDSA",
"SHA512WITHECDSA",
"SHA256WITHRSA",
"SHA384WITHRSA",
"SHA512WITHRSA"
]
},
"String":{"type":"string"},
"String128":{
"type":"string",
"max":128,
"min":0
},
"String16":{
"type":"string",
"max":16,
"min":0
},
"String253":{
"type":"string",
"max":253,
"min":0
},
"String3":{
"type":"string",
"max":3,
"min":0
},
"String3To255":{
"type":"string",
"max":255,
"min":3
},
"String40":{
"type":"string",
"max":40,
"min":0
},
"String5":{
"type":"string",
"max":5,
"min":0
},
"String64":{
"type":"string",
"max":64,
"min":0
},
"TStamp":{"type":"timestamp"},
"Tag":{
"type":"structure",
"required":["Key"],
"members":{
"Key":{"shape":"TagKey"},
"Value":{"shape":"TagValue"}
}
},
"TagCertificateAuthorityRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"Tags"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"Tags":{"shape":"TagList"}
}
},
"TagKey":{
"type":"string",
"max":128,
"min":1,
"pattern":"[\\p{L}\\p{Z}\\p{N}_.:\\/=+\\-@]*"
},
"TagList":{
"type":"list",
"member":{"shape":"Tag"},
"max":50,
"min":1
},
"TagValue":{
"type":"string",
"max":256,
"min":0,
"pattern":"[\\p{L}\\p{Z}\\p{N}_.:\\/=+\\-@]*"
},
"TooManyTagsException":{
"type":"structure",
"members":{
"message":{"shape":"String"}
},
"exception":true
},
"UntagCertificateAuthorityRequest":{
"type":"structure",
"required":[
"CertificateAuthorityArn",
"Tags"
],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"Tags":{"shape":"TagList"}
}
},
"UpdateCertificateAuthorityRequest":{
"type":"structure",
"required":["CertificateAuthorityArn"],
"members":{
"CertificateAuthorityArn":{"shape":"Arn"},
"RevocationConfiguration":{"shape":"RevocationConfiguration"},
"Status":{"shape":"CertificateAuthorityStatus"}
}
},
"Validity":{
"type":"structure",
"required":[
"Value",
"Type"
],
"members":{
"Value":{
"shape":"PositiveLong",
"box":true
},
"Type":{"shape":"ValidityPeriodType"}
}
},
"ValidityPeriodType":{
"type":"string",
"enum":[
"END_DATE",
"ABSOLUTE",
"DAYS",
"MONTHS",
"YEARS"
]
}
}
}
@@ -0,0 +1,575 @@
{
"version": "2.0",
"service": "<p>You can use the ACM PCA API to create a private certificate authority (CA). You must first call the <a>CreateCertificateAuthority</a> function. If successful, the function returns an Amazon Resource Name (ARN) for your private CA. Use this ARN as input to the <a>GetCertificateAuthorityCsr</a> function to retrieve the certificate signing request (CSR) for your private CA certificate. Sign the CSR using the root or an intermediate CA in your on-premises PKI hierarchy, and call the <a>ImportCertificateAuthorityCertificate</a> to import your signed private CA certificate into ACM PCA. </p> <p>Use your private CA to issue and revoke certificates. These are private certificates that identify and secure client computers, servers, applications, services, devices, and users over SSLS/TLS connections within your organization. Call the <a>IssueCertificate</a> function to issue a certificate. Call the <a>RevokeCertificate</a> function to revoke a certificate. </p> <note> <p>Certificates issued by your private CA can be trusted only within your organization, not publicly.</p> </note> <p>Your private CA can optionally create a certificate revocation list (CRL) to track the certificates you revoke. To create a CRL, you must specify a <a>RevocationConfiguration</a> object when you call the <a>CreateCertificateAuthority</a> function. ACM PCA writes the CRL to an S3 bucket that you specify. You must specify a bucket policy that grants ACM PCA write permission. </p> <p>You can also call the <a>CreateCertificateAuthorityAuditReport</a> to create an optional audit report that lists every time the CA private key is used. The private key is used for signing when the <b>IssueCertificate</b> or <b>RevokeCertificate</b> function is called. </p>",
"operations": {
"CreateCertificateAuthority": "<p>Creates a private subordinate certificate authority (CA). You must specify the CA configuration, the revocation configuration, the CA type, and an optional idempotency token. The CA configuration specifies the name of the algorithm and key size to be used to create the CA private key, the type of signing algorithm that the CA uses to sign, and X.500 subject information. The CRL (certificate revocation list) configuration specifies the CRL expiration period in days (the validity period of the CRL), the Amazon S3 bucket that will contain the CRL, and a CNAME alias for the S3 bucket that is included in certificates issued by the CA. If successful, this function returns the Amazon Resource Name (ARN) of the CA.</p>",
"CreateCertificateAuthorityAuditReport": "<p>Creates an audit report that lists every time that the your CA private key is used. The report is saved in the Amazon S3 bucket that you specify on input. The <a>IssueCertificate</a> and <a>RevokeCertificate</a> functions use the private key. You can generate a new report every 30 minutes.</p>",
"DeleteCertificateAuthority": "<p>Deletes the private certificate authority (CA) that you created or started to create by calling the <a>CreateCertificateAuthority</a> function. This action requires that you enter an ARN (Amazon Resource Name) for the private CA that you want to delete. You can find the ARN by calling the <a>ListCertificateAuthorities</a> function. You can delete the CA if you are waiting for it to be created (the <b>Status</b> field of the <a>CertificateAuthority</a> is <code>CREATING</code>) or if the CA has been created but you haven't yet imported the signed certificate (the <b>Status</b> is <code>PENDING_CERTIFICATE</code>) into ACM PCA. If you've already imported the certificate, you cannot delete the CA unless it has been disabled for more than 30 days. To disable a CA, call the <a>UpdateCertificateAuthority</a> function and set the <b>CertificateAuthorityStatus</b> argument to <code>DISABLED</code>. </p>",
"DescribeCertificateAuthority": "<p>Lists information about your private certificate authority (CA). You specify the private CA on input by its ARN (Amazon Resource Name). The output contains the status of your CA. This can be any of the following: </p> <ul> <li> <p> <b>CREATING:</b> ACM PCA is creating your private certificate authority.</p> </li> <li> <p> <b>PENDING_CERTIFICATE:</b> The certificate is pending. You must use your on-premises root or subordinate CA to sign your private CA CSR and then import it into PCA. </p> </li> <li> <p> <b>ACTIVE:</b> Your private CA is active.</p> </li> <li> <p> <b>DISABLED:</b> Your private CA has been disabled.</p> </li> <li> <p> <b>EXPIRED:</b> Your private CA certificate has expired.</p> </li> <li> <p> <b>FAILED:</b> Your private CA has failed. Your CA can fail for problems such a network outage or backend AWS failure or other errors. A failed CA can never return to the pending state. You must create a new CA. </p> </li> </ul>",
"DescribeCertificateAuthorityAuditReport": "<p>Lists information about a specific audit report created by calling the <a>CreateCertificateAuthorityAuditReport</a> function. Audit information is created every time the certificate authority (CA) private key is used. The private key is used when you call the <a>IssueCertificate</a> function or the <a>RevokeCertificate</a> function. </p>",
"GetCertificate": "<p>Retrieves a certificate from your private CA. The ARN of the certificate is returned when you call the <a>IssueCertificate</a> function. You must specify both the ARN of your private CA and the ARN of the issued certificate when calling the <b>GetCertificate</b> function. You can retrieve the certificate if it is in the <b>ISSUED</b> state. You can call the <a>CreateCertificateAuthorityAuditReport</a> function to create a report that contains information about all of the certificates issued and revoked by your private CA. </p>",
"GetCertificateAuthorityCertificate": "<p>Retrieves the certificate and certificate chain for your private certificate authority (CA). Both the certificate and the chain are base64 PEM-encoded. The chain does not include the CA certificate. Each certificate in the chain signs the one before it. </p>",
"GetCertificateAuthorityCsr": "<p>Retrieves the certificate signing request (CSR) for your private certificate authority (CA). The CSR is created when you call the <a>CreateCertificateAuthority</a> function. Take the CSR to your on-premises X.509 infrastructure and sign it by using your root or a subordinate CA. Then import the signed certificate back into ACM PCA by calling the <a>ImportCertificateAuthorityCertificate</a> function. The CSR is returned as a base64 PEM-encoded string. </p>",
"ImportCertificateAuthorityCertificate": "<p>Imports your signed private CA certificate into ACM PCA. Before you can call this function, you must create the private certificate authority by calling the <a>CreateCertificateAuthority</a> function. You must then generate a certificate signing request (CSR) by calling the <a>GetCertificateAuthorityCsr</a> function. Take the CSR to your on-premises CA and use the root certificate or a subordinate certificate to sign it. Create a certificate chain and copy the signed certificate and the certificate chain to your working directory. </p> <note> <p>Your certificate chain must not include the private CA certificate that you are importing.</p> </note> <note> <p>Your on-premises CA certificate must be the last certificate in your chain. The subordinate certificate, if any, that your root CA signed must be next to last. The subordinate certificate signed by the preceding subordinate CA must come next, and so on until your chain is built. </p> </note> <note> <p>The chain must be PEM-encoded.</p> </note>",
"IssueCertificate": "<p>Uses your private certificate authority (CA) to issue a client certificate. This function returns the Amazon Resource Name (ARN) of the certificate. You can retrieve the certificate by calling the <a>GetCertificate</a> function and specifying the ARN. </p> <note> <p>You cannot use the ACM <b>ListCertificateAuthorities</b> function to retrieve the ARNs of the certificates that you issue by using ACM PCA.</p> </note>",
"ListCertificateAuthorities": "<p>Lists the private certificate authorities that you created by using the <a>CreateCertificateAuthority</a> function.</p>",
"ListTags": "<p>Lists the tags, if any, that are associated with your private CA. Tags are labels that you can use to identify and organize your CAs. Each tag consists of a key and an optional value. Call the <a>TagCertificateAuthority</a> function to add one or more tags to your CA. Call the <a>UntagCertificateAuthority</a> function to remove tags. </p>",
"RevokeCertificate": "<p>Revokes a certificate that you issued by calling the <a>IssueCertificate</a> function. If you enable a certificate revocation list (CRL) when you create or update your private CA, information about the revoked certificates will be included in the CRL. ACM PCA writes the CRL to an S3 bucket that you specify. For more information about revocation, see the <a>CrlConfiguration</a> structure. ACM PCA also writes revocation information to the audit report. For more information, see <a>CreateCertificateAuthorityAuditReport</a>. </p>",
"TagCertificateAuthority": "<p>Adds one or more tags to your private CA. Tags are labels that you can use to identify and organize your AWS resources. Each tag consists of a key and an optional value. You specify the private CA on input by its Amazon Resource Name (ARN). You specify the tag by using a key-value pair. You can apply a tag to just one private CA if you want to identify a specific characteristic of that CA, or you can apply the same tag to multiple private CAs if you want to filter for a common relationship among those CAs. To remove one or more tags, use the <a>UntagCertificateAuthority</a> function. Call the <a>ListTags</a> function to see what tags are associated with your CA. </p>",
"UntagCertificateAuthority": "<p>Remove one or more tags from your private CA. A tag consists of a key-value pair. If you do not specify the value portion of the tag when calling this function, the tag will be removed regardless of value. If you specify a value, the tag is removed only if it is associated with the specified value. To add tags to a private CA, use the <a>TagCertificateAuthority</a>. Call the <a>ListTags</a> function to see what tags are associated with your CA. </p>",
"UpdateCertificateAuthority": "<p>Updates the status or configuration of a private certificate authority (CA). Your private CA must be in the <b> <code>ACTIVE</code> </b> or <b> <code>DISABLED</code> </b> state before you can update it. You can disable a private CA that is in the <b> <code>ACTIVE</code> </b> state or make a CA that is in the <b> <code>DISABLED</code> </b> state active again.</p>"
},
"shapes": {
"ASN1Subject": {
"base": "<p>Contains information about the certificate subject. The certificate can be one issued by your private certificate authority (CA) or it can be your private CA certificate. The <b>Subject</b> field in the certificate identifies the entity that owns or controls the public key in the certificate. The entity can be a user, computer, device, or service. The <b>Subject</b> must contain an X.500 distinguished name (DN). A DN is a sequence of relative distinguished names (RDNs). The RDNs are separated by commas in the certificate. The DN must be unique for each for each entity, but your private CA can issue more than one certificate with the same DN to the same entity. </p>",
"refs": {
"CertificateAuthorityConfiguration$Subject": "<p>Structure that contains X.500 distinguished name information for your private CA.</p>"
}
},
"Arn": {
"base": null,
"refs": {
"CertificateAuthority$Arn": "<p>Amazon Resource Name (ARN) for your private certificate authority (CA). The format is <code> <i>12345678-1234-1234-1234-123456789012</i> </code>.</p>",
"CreateCertificateAuthorityAuditReportRequest$CertificateAuthorityArn": "<p>Amazon Resource Name (ARN) of the CA to be audited. This is of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>.</p>",
"CreateCertificateAuthorityResponse$CertificateAuthorityArn": "<p>If successful, the Amazon Resource Name (ARN) of the certificate authority (CA). This is of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"DeleteCertificateAuthorityRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"DescribeCertificateAuthorityAuditReportRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) of the private CA. This must be of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"DescribeCertificateAuthorityRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"GetCertificateAuthorityCertificateRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) of your private CA. This is of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"GetCertificateAuthorityCsrRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called the <a>CreateCertificateAuthority</a> function. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"GetCertificateRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code>. </p>",
"GetCertificateRequest$CertificateArn": "<p>The ARN of the issued certificate. The ARN contains the certificate serial number and must be in the following form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i>/certificate/<i>286535153982981100925020015808220737245</i> </code> </p>",
"ImportCertificateAuthorityCertificateRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"IssueCertificateRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"IssueCertificateResponse$CertificateArn": "<p>The Amazon Resource Name (ARN) of the issued certificate and the certificate serial number. This is of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i>/certificate/<i>286535153982981100925020015808220737245</i> </code> </p>",
"ListTagsRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called the <a>CreateCertificateAuthority</a> function. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"RevokeCertificateRequest$CertificateAuthorityArn": "<p>Amazon Resource Name (ARN) of the private CA that issued the certificate to be revoked. This must be of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"TagCertificateAuthorityRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"UntagCertificateAuthorityRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) that was returned when you called <a>CreateCertificateAuthority</a>. This must be of the form: </p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>",
"UpdateCertificateAuthorityRequest$CertificateAuthorityArn": "<p>Amazon Resource Name (ARN) of the private CA that issued the certificate to be revoked. This must be of the form:</p> <p> <code>arn:aws:acm:<i>region</i>:<i>account</i>:certificate-authority/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>"
}
},
"AuditReportId": {
"base": null,
"refs": {
"CreateCertificateAuthorityAuditReportResponse$AuditReportId": "<p>An alphanumeric string that contains a report identifier.</p>",
"DescribeCertificateAuthorityAuditReportRequest$AuditReportId": "<p>The report ID returned by calling the <a>CreateCertificateAuthorityAuditReport</a> function.</p>"
}
},
"AuditReportResponseFormat": {
"base": null,
"refs": {
"CreateCertificateAuthorityAuditReportRequest$AuditReportResponseFormat": "<p>Format in which to create the report. This can be either <b>JSON</b> or <b>CSV</b>.</p>"
}
},
"AuditReportStatus": {
"base": null,
"refs": {
"DescribeCertificateAuthorityAuditReportResponse$AuditReportStatus": "<p>Specifies whether report creation is in progress, has succeeded, or has failed.</p>"
}
},
"Boolean": {
"base": null,
"refs": {
"CrlConfiguration$Enabled": "<p>Boolean value that specifies whether certificate revocation lists (CRLs) are enabled. You can use this value to enable certificate revocation for a new CA when you call the <a>CreateCertificateAuthority</a> function or for an existing CA when you call the <a>UpdateCertificateAuthority</a> function. </p>"
}
},
"CertificateAuthorities": {
"base": null,
"refs": {
"ListCertificateAuthoritiesResponse$CertificateAuthorities": "<p>Summary information about each certificate authority you have created.</p>"
}
},
"CertificateAuthority": {
"base": "<p>Contains information about your private certificate authority (CA). Your private CA can issue and revoke X.509 digital certificates. Digital certificates verify that the entity named in the certificate <b>Subject</b> field owns or controls the public key contained in the <b>Subject Public Key Info</b> field. Call the <a>CreateCertificateAuthority</a> function to create your private CA. You must then call the <a>GetCertificateAuthorityCertificate</a> function to retrieve a private CA certificate signing request (CSR). Take the CSR to your on-premises CA and sign it with the root CA certificate or a subordinate certificate. Call the <a>ImportCertificateAuthorityCertificate</a> function to import the signed certificate into AWS Certificate Manager (ACM). </p>",
"refs": {
"CertificateAuthorities$member": null,
"DescribeCertificateAuthorityResponse$CertificateAuthority": "<p>A <a>CertificateAuthority</a> structure that contains information about your private CA.</p>"
}
},
"CertificateAuthorityConfiguration": {
"base": "<p>Contains configuration information for your private certificate authority (CA). This includes information about the class of public key algorithm and the key pair that your private CA creates when it issues a certificate, the signature algorithm it uses used when issuing certificates, and its X.500 distinguished name. You must specify this information when you call the <a>CreateCertificateAuthority</a> function. </p>",
"refs": {
"CertificateAuthority$CertificateAuthorityConfiguration": "<p>Your private CA configuration.</p>",
"CreateCertificateAuthorityRequest$CertificateAuthorityConfiguration": "<p>Name and bit size of the private key algorithm, the name of the signing algorithm, and X.500 certificate subject information.</p>"
}
},
"CertificateAuthorityStatus": {
"base": null,
"refs": {
"CertificateAuthority$Status": "<p>Status of your private CA.</p>",
"UpdateCertificateAuthorityRequest$Status": "<p>Status of your private CA.</p>"
}
},
"CertificateAuthorityType": {
"base": null,
"refs": {
"CertificateAuthority$Type": "<p>Type of your private CA.</p>",
"CreateCertificateAuthorityRequest$CertificateAuthorityType": "<p>The type of the certificate authority. Currently, this must be <b>SUBORDINATE</b>.</p>"
}
},
"CertificateBody": {
"base": null,
"refs": {
"GetCertificateAuthorityCertificateResponse$Certificate": "<p>Base64-encoded certificate authority (CA) certificate.</p>",
"GetCertificateResponse$Certificate": "<p>The base64 PEM-encoded certificate specified by the <code>CertificateArn</code> parameter.</p>"
}
},
"CertificateBodyBlob": {
"base": null,
"refs": {
"ImportCertificateAuthorityCertificateRequest$Certificate": "<p>The PEM-encoded certificate for your private CA. This must be signed by using your on-premises CA.</p>"
}
},
"CertificateChain": {
"base": null,
"refs": {
"GetCertificateAuthorityCertificateResponse$CertificateChain": "<p>Base64-encoded certificate chain that includes any intermediate certificates and chains up to root on-premises certificate that you used to sign your private CA certificate. The chain does not include your private CA certificate. </p>",
"GetCertificateResponse$CertificateChain": "<p>The base64 PEM-encoded certificate chain that chains up to the on-premises root CA certificate that you used to sign your private CA certificate. </p>"
}
},
"CertificateChainBlob": {
"base": null,
"refs": {
"ImportCertificateAuthorityCertificateRequest$CertificateChain": "<p>A PEM-encoded file that contains all of your certificates, other than the certificate you're importing, chaining up to your root CA. Your on-premises root certificate is the last in the chain, and each certificate in the chain signs the one preceding. </p>"
}
},
"CertificateMismatchException": {
"base": "<p>The certificate authority certificate you are importing does not comply with conditions specified in the certificate that signed it.</p>",
"refs": {
}
},
"ConcurrentModificationException": {
"base": "<p>A previous update to your private CA is still ongoing.</p>",
"refs": {
}
},
"CountryCodeString": {
"base": null,
"refs": {
"ASN1Subject$Country": "<p>Two digit code that specifies the country in which the certificate subject located.</p>"
}
},
"CreateCertificateAuthorityAuditReportRequest": {
"base": null,
"refs": {
}
},
"CreateCertificateAuthorityAuditReportResponse": {
"base": null,
"refs": {
}
},
"CreateCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"CreateCertificateAuthorityResponse": {
"base": null,
"refs": {
}
},
"CrlConfiguration": {
"base": "<p>Contains configuration information for a certificate revocation list (CRL). Your private certificate authority (CA) creates base CRLs. Delta CRLs are not supported. You can enable CRLs for your new or an existing private CA by setting the <b>Enabled</b> parameter to <code>true</code>. Your private CA writes CRLs to an S3 bucket that you specify in the <b>S3BucketName</b> parameter. You can hide the name of your bucket by specifying a value for the <b>CustomCname</b> parameter. Your private CA copies the CNAME or the S3 bucket name to the <b>CRL Distribution Points</b> extension of each certificate it issues. Your S3 bucket policy must give write permission to ACM PCA. </p> <p>Your private CA uses the value in the <b>ExpirationInDays</b> parameter to calculate the <b>nextUpdate</b> field in the CRL. The CRL is refreshed at 1/2 the age of next update or when a certificate is revoked. When a certificate is revoked, it is recorded in the next CRL that is generated and in the next audit report. Only time valid certificates are listed in the CRL. Expired certificates are not included. </p> <p>CRLs contain the following fields:</p> <ul> <li> <p> <b>Version</b>: The current version number defined in RFC 5280 is V2. The integer value is 0x1. </p> </li> <li> <p> <b>Signature Algorithm</b>: The name of the algorithm used to sign the CRL.</p> </li> <li> <p> <b>Issuer</b>: The X.500 distinguished name of your private CA that issued the CRL.</p> </li> <li> <p> <b>Last Update</b>: The issue date and time of this CRL.</p> </li> <li> <p> <b>Next Update</b>: The day and time by which the next CRL will be issued.</p> </li> <li> <p> <b>Revoked Certificates</b>: List of revoked certificates. Each list item contains the following information.</p> <ul> <li> <p> <b>Serial Number</b>: The serial number, in hexadecimal format, of the revoked certificate.</p> </li> <li> <p> <b>Revocation Date</b>: Date and time the certificate was revoked.</p> </li> <li> <p> <b>CRL Entry Extensions</b>: Optional extensions for the CRL entry.</p> <ul> <li> <p> <b>X509v3 CRL Reason Code</b>: Reason the certificate was revoked.</p> </li> </ul> </li> </ul> </li> <li> <p> <b>CRL Extensions</b>: Optional extensions for the CRL.</p> <ul> <li> <p> <b>X509v3 Authority Key Identifier</b>: Identifies the public key associated with the private key used to sign the certificate.</p> </li> <li> <p> <b>X509v3 CRL Number:</b>: Decimal sequence number for the CRL.</p> </li> </ul> </li> <li> <p> <b>Signature Algorithm</b>: Algorithm used by your private CA to sign the CRL.</p> </li> <li> <p> <b>Signature Value</b>: Signature computed over the CRL.</p> </li> </ul> <p>Certificate revocation lists created by ACM PCA are DER-encoded. You can use the following OpenSSL command to list a CRL.</p> <p> <code>openssl crl -inform DER -text -in <i>crl_path</i> -noout</code> </p>",
"refs": {
"RevocationConfiguration$CrlConfiguration": "<p>Configuration of the certificate revocation list (CRL), if any, maintained by your private CA.</p>"
}
},
"CsrBlob": {
"base": null,
"refs": {
"IssueCertificateRequest$Csr": "<p>The certificate signing request (CSR) for the certificate you want to issue. You can use the following OpenSSL command to create the CSR and a 2048 bit RSA private key. </p> <p> <code>openssl req -new -newkey rsa:2048 -days 365 -keyout private/test_cert_priv_key.pem -out csr/test_cert_.csr</code> </p> <p>If you have a configuration file, you can use the following OpenSSL command. The <code>usr_cert</code> block in the configuration file contains your X509 version 3 extensions. </p> <p> <code>openssl req -new -config openssl_rsa.cnf -extensions usr_cert -newkey rsa:2048 -days -365 -keyout private/test_cert_priv_key.pem -out csr/test_cert_.csr</code> </p>"
}
},
"CsrBody": {
"base": null,
"refs": {
"GetCertificateAuthorityCsrResponse$Csr": "<p>The base64 PEM-encoded certificate signing request (CSR) for your private CA certificate.</p>"
}
},
"DeleteCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"DescribeCertificateAuthorityAuditReportRequest": {
"base": null,
"refs": {
}
},
"DescribeCertificateAuthorityAuditReportResponse": {
"base": null,
"refs": {
}
},
"DescribeCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"DescribeCertificateAuthorityResponse": {
"base": null,
"refs": {
}
},
"DistinguishedNameQualifierString": {
"base": null,
"refs": {
"ASN1Subject$DistinguishedNameQualifier": "<p>Disambiguating information for the certificate subject.</p>"
}
},
"FailureReason": {
"base": null,
"refs": {
"CertificateAuthority$FailureReason": "<p>Reason the request to create your private CA failed.</p>"
}
},
"GetCertificateAuthorityCertificateRequest": {
"base": null,
"refs": {
}
},
"GetCertificateAuthorityCertificateResponse": {
"base": null,
"refs": {
}
},
"GetCertificateAuthorityCsrRequest": {
"base": null,
"refs": {
}
},
"GetCertificateAuthorityCsrResponse": {
"base": null,
"refs": {
}
},
"GetCertificateRequest": {
"base": null,
"refs": {
}
},
"GetCertificateResponse": {
"base": null,
"refs": {
}
},
"IdempotencyToken": {
"base": null,
"refs": {
"CreateCertificateAuthorityRequest$IdempotencyToken": "<p>Alphanumeric string that can be used to distinguish between calls to <b>CreateCertificateAuthority</b>. Idempotency tokens time out after five minutes. Therefore, if you call <b>CreateCertificateAuthority</b> multiple times with the same idempotency token within a five minute period, ACM PCA recognizes that you are requesting only one certificate and will issue only one. If you change the idempotency token for each call, however, ACM PCA recognizes that you are requesting multiple certificates.</p>",
"IssueCertificateRequest$IdempotencyToken": "<p>Custom string that can be used to distinguish between calls to the <b>IssueCertificate</b> function. Idempotency tokens time out after one hour. Therefore, if you call <b>IssueCertificate</b> multiple times with the same idempotency token within 5 minutes, ACM PCA recognizes that you are requesting only one certificate and will issue only one. If you change the idempotency token for each call, PCA recognizes that you are requesting multiple certificates.</p>"
}
},
"ImportCertificateAuthorityCertificateRequest": {
"base": null,
"refs": {
}
},
"Integer1To5000": {
"base": null,
"refs": {
"CrlConfiguration$ExpirationInDays": "<p>Number of days until a certificate expires.</p>"
}
},
"InvalidArgsException": {
"base": "<p>One or more of the specified arguments was not valid.</p>",
"refs": {
}
},
"InvalidArnException": {
"base": "<p>The requested Amazon Resource Name (ARN) does not refer to an existing resource.</p>",
"refs": {
}
},
"InvalidNextTokenException": {
"base": "<p>The token specified in the <code>NextToken</code> argument is not valid. Use the token returned from your previous call to <a>ListCertificateAuthorities</a>.</p>",
"refs": {
}
},
"InvalidPolicyException": {
"base": "<p>The S3 bucket policy is not valid. The policy must give ACM PCA rights to read from and write to the bucket and find the bucket location.</p>",
"refs": {
}
},
"InvalidStateException": {
"base": "<p>The private CA is in a state during which a report cannot be generated.</p>",
"refs": {
}
},
"InvalidTagException": {
"base": "<p>The tag associated with the CA is not valid. The invalid argument is contained in the message field.</p>",
"refs": {
}
},
"IssueCertificateRequest": {
"base": null,
"refs": {
}
},
"IssueCertificateResponse": {
"base": null,
"refs": {
}
},
"KeyAlgorithm": {
"base": null,
"refs": {
"CertificateAuthorityConfiguration$KeyAlgorithm": "<p>Type of the public key algorithm and size, in bits, of the key pair that your key pair creates when it issues a certificate.</p>"
}
},
"LimitExceededException": {
"base": "<p>An ACM PCA limit has been exceeded. See the exception message returned to determine the limit that was exceeded.</p>",
"refs": {
}
},
"ListCertificateAuthoritiesRequest": {
"base": null,
"refs": {
}
},
"ListCertificateAuthoritiesResponse": {
"base": null,
"refs": {
}
},
"ListTagsRequest": {
"base": null,
"refs": {
}
},
"ListTagsResponse": {
"base": null,
"refs": {
}
},
"MalformedCSRException": {
"base": "<p>The certificate signing request is invalid.</p>",
"refs": {
}
},
"MalformedCertificateException": {
"base": "<p>One or more fields in the certificate are invalid.</p>",
"refs": {
}
},
"MaxResults": {
"base": null,
"refs": {
"ListCertificateAuthoritiesRequest$MaxResults": "<p>Use this parameter when paginating results to specify the maximum number of items to return in the response on each page. If additional items exist beyond the number you specify, the <code>NextToken</code> element is sent in the response. Use this <code>NextToken</code> value in a subsequent request to retrieve additional items.</p>",
"ListTagsRequest$MaxResults": "<p>Use this parameter when paginating results to specify the maximum number of items to return in the response. If additional items exist beyond the number you specify, the <b>NextToken</b> element is sent in the response. Use this <b>NextToken</b> value in a subsequent request to retrieve additional items.</p>"
}
},
"NextToken": {
"base": null,
"refs": {
"ListCertificateAuthoritiesRequest$NextToken": "<p>Use this parameter when paginating results in a subsequent request after you receive a response with truncated results. Set it to the value of the <code>NextToken</code> parameter from the response you just received.</p>",
"ListCertificateAuthoritiesResponse$NextToken": "<p>When the list is truncated, this value is present and should be used for the <code>NextToken</code> parameter in a subsequent pagination request.</p>",
"ListTagsRequest$NextToken": "<p>Use this parameter when paginating results in a subsequent request after you receive a response with truncated results. Set it to the value of <b>NextToken</b> from the response you just received.</p>",
"ListTagsResponse$NextToken": "<p>When the list is truncated, this value is present and should be used for the <b>NextToken</b> parameter in a subsequent pagination request. </p>"
}
},
"PositiveLong": {
"base": null,
"refs": {
"Validity$Value": "<p>Time period.</p>"
}
},
"RequestAlreadyProcessedException": {
"base": "<p>Your request has already been completed.</p>",
"refs": {
}
},
"RequestFailedException": {
"base": "<p>The request has failed for an unspecified reason.</p>",
"refs": {
}
},
"RequestInProgressException": {
"base": "<p>Your request is already in progress.</p>",
"refs": {
}
},
"ResourceNotFoundException": {
"base": "<p>A resource such as a private CA, S3 bucket, certificate, or audit report cannot be found.</p>",
"refs": {
}
},
"RevocationConfiguration": {
"base": "<p>Certificate revocation information used by the <a>CreateCertificateAuthority</a> and <a>UpdateCertificateAuthority</a> functions. Your private certificate authority (CA) can create and maintain a certificate revocation list (CRL). A CRL contains information about certificates revoked by your CA. For more information, see <a>RevokeCertificate</a>.</p>",
"refs": {
"CertificateAuthority$RevocationConfiguration": "<p>Information about the certificate revocation list (CRL) created and maintained by your private CA. </p>",
"CreateCertificateAuthorityRequest$RevocationConfiguration": "<p>Contains a Boolean value that you can use to enable a certification revocation list (CRL) for the CA, the name of the S3 bucket to which ACM PCA will write the CRL, and an optional CNAME alias that you can use to hide the name of your bucket in the <b>CRL Distribution Points</b> extension of your CA certificate. For more information, see the <a>CrlConfiguration</a> structure. </p>",
"UpdateCertificateAuthorityRequest$RevocationConfiguration": "<p>Revocation information for your private CA.</p>"
}
},
"RevocationReason": {
"base": null,
"refs": {
"RevokeCertificateRequest$RevocationReason": "<p>Specifies why you revoked the certificate.</p>"
}
},
"RevokeCertificateRequest": {
"base": null,
"refs": {
}
},
"SigningAlgorithm": {
"base": null,
"refs": {
"CertificateAuthorityConfiguration$SigningAlgorithm": "<p>Name of the algorithm your private CA uses to sign certificate requests.</p>",
"IssueCertificateRequest$SigningAlgorithm": "<p>The name of the algorithm that will be used to sign the certificate to be issued.</p>"
}
},
"String": {
"base": null,
"refs": {
"CertificateAuthority$Serial": "<p>Serial number of your private CA.</p>",
"CertificateMismatchException$message": null,
"ConcurrentModificationException$message": null,
"CreateCertificateAuthorityAuditReportRequest$S3BucketName": "<p>Name of the S3 bucket that will contain the audit report.</p>",
"CreateCertificateAuthorityAuditReportResponse$S3Key": "<p>The <b>key</b> that uniquely identifies the report file in your S3 bucket.</p>",
"DescribeCertificateAuthorityAuditReportResponse$S3BucketName": "<p>Name of the S3 bucket that contains the report.</p>",
"DescribeCertificateAuthorityAuditReportResponse$S3Key": "<p>S3 <b>key</b> that uniquely identifies the report file in your S3 bucket.</p>",
"InvalidArgsException$message": null,
"InvalidArnException$message": null,
"InvalidNextTokenException$message": null,
"InvalidPolicyException$message": null,
"InvalidStateException$message": null,
"InvalidTagException$message": null,
"LimitExceededException$message": null,
"MalformedCSRException$message": null,
"MalformedCertificateException$message": null,
"RequestAlreadyProcessedException$message": null,
"RequestFailedException$message": null,
"RequestInProgressException$message": null,
"ResourceNotFoundException$message": null,
"TooManyTagsException$message": null
}
},
"String128": {
"base": null,
"refs": {
"ASN1Subject$State": "<p>State in which the subject of the certificate is located.</p>",
"ASN1Subject$Locality": "<p>The locality (such as a city or town) in which the certificate subject is located.</p>",
"ASN1Subject$Pseudonym": "<p>Typically a shortened version of a longer <b>GivenName</b>. For example, Jonathan is often shortened to John. Elizabeth is often shortened to Beth, Liz, or Eliza.</p>",
"RevokeCertificateRequest$CertificateSerial": "<p>Serial number of the certificate to be revoked. This must be in hexadecimal format. You can retrieve the serial number by calling <a>GetCertificate</a> with the Amazon Resource Name (ARN) of the certificate you want and the ARN of your private CA. The <b>GetCertificate</b> function retrieves the certificate in the PEM format. You can use the following OpenSSL command to list the certificate in text format and copy the hexadecimal serial number. </p> <p> <code>openssl x509 -in <i>file_path</i> -text -noout</code> </p> <p>You can also copy the serial number from the console or use the <a href=\"http://docs.aws.amazon.comacm/latest/APIReferenceAPI_DescribeCertificate.html\">DescribeCertificate</a> function in the <i>AWS Certificate Manager API Reference</i>. </p>"
}
},
"String16": {
"base": null,
"refs": {
"ASN1Subject$GivenName": "<p>First name.</p>"
}
},
"String253": {
"base": null,
"refs": {
"CrlConfiguration$CustomCname": "<p>Name inserted into the certificate <b>CRL Distribution Points</b> extension that enables the use of an alias for the CRL distribution point. Use this value if you don't want the name of your S3 bucket to be public.</p>"
}
},
"String3": {
"base": null,
"refs": {
"ASN1Subject$GenerationQualifier": "<p>Typically a qualifier appended to the name of an individual. Examples include Jr. for junior, Sr. for senior, and III for third.</p>"
}
},
"String3To255": {
"base": null,
"refs": {
"CrlConfiguration$S3BucketName": "<p>Name of the S3 bucket that contains the CRL. If you do not provide a value for the <b>CustomCname</b> argument, the name of your S3 bucket is placed into the <b>CRL Distribution Points</b> extension of the issued certificate. You can change the name of your bucket by calling the <a>UpdateCertificateAuthority</a> function. You must specify a bucket policy that allows ACM PCA to write the CRL to your bucket.</p>"
}
},
"String40": {
"base": null,
"refs": {
"ASN1Subject$Surname": "<p>Family name. In the US and the UK for example, the surname of an individual is ordered last. In Asian cultures the surname is typically ordered first.</p>"
}
},
"String5": {
"base": null,
"refs": {
"ASN1Subject$Initials": "<p>Concatenation that typically contains the first letter of the <b>GivenName</b>, the first letter of the middle name if one exists, and the first letter of the <b>SurName</b>.</p>"
}
},
"String64": {
"base": null,
"refs": {
"ASN1Subject$Organization": "<p>Legal name of the organization with which the certificate subject is affiliated. </p>",
"ASN1Subject$OrganizationalUnit": "<p>A subdivision or unit of the organization (such as sales or finance) with which the certificate subject is affiliated.</p>",
"ASN1Subject$CommonName": "<p>Fully qualified domain name (FQDN) associated with the certificate subject.</p>",
"ASN1Subject$SerialNumber": "<p>The certificate serial number.</p>",
"ASN1Subject$Title": "<p>A title such as Mr. or Ms. which is pre-pended to the name to refer formally to the certificate subject.</p>"
}
},
"TStamp": {
"base": null,
"refs": {
"CertificateAuthority$CreatedAt": "<p>Date and time at which your private CA was created.</p>",
"CertificateAuthority$LastStateChangeAt": "<p>Date and time at which your private CA was last updated.</p>",
"CertificateAuthority$NotBefore": "<p>Date and time before which your private CA certificate is not valid.</p>",
"CertificateAuthority$NotAfter": "<p>Date and time after which your private CA certificate is not valid.</p>",
"DescribeCertificateAuthorityAuditReportResponse$CreatedAt": "<p>The date and time at which the report was created.</p>"
}
},
"Tag": {
"base": "<p>Tags are labels that you can use to identify and organize your private CAs. Each tag consists of a key and an optional value. You can associate up to 50 tags with a private CA. To add one or more tags to a private CA, call the <a>TagCertificateAuthority</a> function. To remove a tag, call the <a>UntagCertificateAuthority</a> function. </p>",
"refs": {
"TagList$member": null
}
},
"TagCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"TagKey": {
"base": null,
"refs": {
"Tag$Key": "<p>Key (name) of the tag.</p>"
}
},
"TagList": {
"base": null,
"refs": {
"ListTagsResponse$Tags": "<p>The tags associated with your private CA.</p>",
"TagCertificateAuthorityRequest$Tags": "<p>List of tags to be associated with the CA.</p>",
"UntagCertificateAuthorityRequest$Tags": "<p>List of tags to be removed from the CA.</p>"
}
},
"TagValue": {
"base": null,
"refs": {
"Tag$Value": "<p>Value of the tag.</p>"
}
},
"TooManyTagsException": {
"base": "<p>You can associate up to 50 tags with a private CA. Exception information is contained in the exception message field.</p>",
"refs": {
}
},
"UntagCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"UpdateCertificateAuthorityRequest": {
"base": null,
"refs": {
}
},
"Validity": {
"base": "<p>Length of time for which the certificate issued by your private certificate authority (CA), or by the private CA itself, is valid in days, months, or years. You can issue a certificate by calling the <a>IssueCertificate</a> function.</p>",
"refs": {
"IssueCertificateRequest$Validity": "<p>The type of the validity period.</p>"
}
},
"ValidityPeriodType": {
"base": null,
"refs": {
"Validity$Type": "<p>Specifies whether the <code>Value</code> parameter represents days, months, or years.</p>"
}
}
}
}
@@ -0,0 +1,4 @@
{
"pagination": {
}
}
+220 -7
View File
@@ -7,6 +7,7 @@
"protocol":"json", "protocol":"json",
"serviceAbbreviation":"ACM", "serviceAbbreviation":"ACM",
"serviceFullName":"AWS Certificate Manager", "serviceFullName":"AWS Certificate Manager",
"serviceId":"ACM",
"signatureVersion":"v4", "signatureVersion":"v4",
"targetPrefix":"CertificateManager", "targetPrefix":"CertificateManager",
"uid":"acm-2015-12-08" "uid":"acm-2015-12-08"
@@ -52,6 +53,20 @@
{"shape":"InvalidArnException"} {"shape":"InvalidArnException"}
] ]
}, },
"ExportCertificate":{
"name":"ExportCertificate",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"ExportCertificateRequest"},
"output":{"shape":"ExportCertificateResponse"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"RequestInProgressException"},
{"shape":"InvalidArnException"}
]
},
"GetCertificate":{ "GetCertificate":{
"name":"GetCertificate", "name":"GetCertificate",
"http":{ "http":{
@@ -124,7 +139,8 @@
"output":{"shape":"RequestCertificateResponse"}, "output":{"shape":"RequestCertificateResponse"},
"errors":[ "errors":[
{"shape":"LimitExceededException"}, {"shape":"LimitExceededException"},
{"shape":"InvalidDomainValidationOptionsException"} {"shape":"InvalidDomainValidationOptionsException"},
{"shape":"InvalidArnException"}
] ]
}, },
"ResendValidationEmail":{ "ResendValidationEmail":{
@@ -140,6 +156,20 @@
{"shape":"InvalidArnException"}, {"shape":"InvalidArnException"},
{"shape":"InvalidDomainValidationOptionsException"} {"shape":"InvalidDomainValidationOptionsException"}
] ]
},
"UpdateCertificateOptions":{
"name":"UpdateCertificateOptions",
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"UpdateCertificateOptionsRequest"},
"errors":[
{"shape":"ResourceNotFoundException"},
{"shape":"LimitExceededException"},
{"shape":"InvalidStateException"},
{"shape":"InvalidArnException"}
]
} }
}, },
"shapes":{ "shapes":{
@@ -158,7 +188,7 @@
"type":"string", "type":"string",
"max":2048, "max":2048,
"min":20, "min":20,
"pattern":"arn:[\\w+=/,.@-]+:[\\w+=/,.@-]+:[\\w+=/,.@-]*:[0-9]+:[\\w+=,.@-]+(/[\\w+=/,.@-]+)*" "pattern":"arn:[\\w+=/,.@-]+:[\\w+=/,.@-]+:[\\w+=/,.@-]*:[0-9]+:[\\w+=,.@-]+(/[\\w+=,.@-]+)*"
}, },
"CertificateBody":{ "CertificateBody":{
"type":"string", "type":"string",
@@ -205,7 +235,18 @@
"InUseBy":{"shape":"InUseList"}, "InUseBy":{"shape":"InUseList"},
"FailureReason":{"shape":"FailureReason"}, "FailureReason":{"shape":"FailureReason"},
"Type":{"shape":"CertificateType"}, "Type":{"shape":"CertificateType"},
"RenewalSummary":{"shape":"RenewalSummary"} "RenewalSummary":{"shape":"RenewalSummary"},
"KeyUsages":{"shape":"KeyUsageList"},
"ExtendedKeyUsages":{"shape":"ExtendedKeyUsageList"},
"CertificateAuthorityArn":{"shape":"Arn"},
"RenewalEligibility":{"shape":"RenewalEligibility"},
"Options":{"shape":"CertificateOptions"}
}
},
"CertificateOptions":{
"type":"structure",
"members":{
"CertificateTransparencyLoggingPreference":{"shape":"CertificateTransparencyLoggingPreference"}
} }
}, },
"CertificateStatus":{ "CertificateStatus":{
@@ -235,11 +276,19 @@
"type":"list", "type":"list",
"member":{"shape":"CertificateSummary"} "member":{"shape":"CertificateSummary"}
}, },
"CertificateTransparencyLoggingPreference":{
"type":"string",
"enum":[
"ENABLED",
"DISABLED"
]
},
"CertificateType":{ "CertificateType":{
"type":"string", "type":"string",
"enum":[ "enum":[
"IMPORTED", "IMPORTED",
"AMAZON_ISSUED" "AMAZON_ISSUED",
"PRIVATE"
] ]
}, },
"DeleteCertificateRequest":{ "DeleteCertificateRequest":{
@@ -289,7 +338,9 @@
"DomainName":{"shape":"DomainNameString"}, "DomainName":{"shape":"DomainNameString"},
"ValidationEmails":{"shape":"ValidationEmailList"}, "ValidationEmails":{"shape":"ValidationEmailList"},
"ValidationDomain":{"shape":"DomainNameString"}, "ValidationDomain":{"shape":"DomainNameString"},
"ValidationStatus":{"shape":"DomainStatus"} "ValidationStatus":{"shape":"DomainStatus"},
"ResourceRecord":{"shape":"ResourceRecord"},
"ValidationMethod":{"shape":"ValidationMethod"}
} }
}, },
"DomainValidationList":{ "DomainValidationList":{
@@ -315,6 +366,57 @@
"max":100, "max":100,
"min":1 "min":1
}, },
"ExportCertificateRequest":{
"type":"structure",
"required":[
"CertificateArn",
"Passphrase"
],
"members":{
"CertificateArn":{"shape":"Arn"},
"Passphrase":{"shape":"PassphraseBlob"}
}
},
"ExportCertificateResponse":{
"type":"structure",
"members":{
"Certificate":{"shape":"CertificateBody"},
"CertificateChain":{"shape":"CertificateChain"},
"PrivateKey":{"shape":"PrivateKey"}
}
},
"ExtendedKeyUsage":{
"type":"structure",
"members":{
"Name":{"shape":"ExtendedKeyUsageName"},
"OID":{"shape":"String"}
}
},
"ExtendedKeyUsageFilterList":{
"type":"list",
"member":{"shape":"ExtendedKeyUsageName"}
},
"ExtendedKeyUsageList":{
"type":"list",
"member":{"shape":"ExtendedKeyUsage"}
},
"ExtendedKeyUsageName":{
"type":"string",
"enum":[
"TLS_WEB_SERVER_AUTHENTICATION",
"TLS_WEB_CLIENT_AUTHENTICATION",
"CODE_SIGNING",
"EMAIL_PROTECTION",
"TIME_STAMPING",
"OCSP_SIGNING",
"IPSEC_END_SYSTEM",
"IPSEC_TUNNEL",
"IPSEC_USER",
"ANY",
"NONE",
"CUSTOM"
]
},
"FailureReason":{ "FailureReason":{
"type":"string", "type":"string",
"enum":[ "enum":[
@@ -322,9 +424,24 @@
"ADDITIONAL_VERIFICATION_REQUIRED", "ADDITIONAL_VERIFICATION_REQUIRED",
"DOMAIN_NOT_ALLOWED", "DOMAIN_NOT_ALLOWED",
"INVALID_PUBLIC_DOMAIN", "INVALID_PUBLIC_DOMAIN",
"CAA_ERROR",
"PCA_LIMIT_EXCEEDED",
"PCA_INVALID_ARN",
"PCA_INVALID_STATE",
"PCA_REQUEST_FAILED",
"PCA_RESOURCE_NOT_FOUND",
"PCA_INVALID_ARGS",
"OTHER" "OTHER"
] ]
}, },
"Filters":{
"type":"structure",
"members":{
"extendedKeyUsage":{"shape":"ExtendedKeyUsageFilterList"},
"keyUsage":{"shape":"KeyUsageFilterList"},
"keyTypes":{"shape":"KeyAlgorithmList"}
}
},
"GetCertificateRequest":{ "GetCertificateRequest":{
"type":"structure", "type":"structure",
"required":["CertificateArn"], "required":["CertificateArn"],
@@ -401,7 +518,44 @@
"enum":[ "enum":[
"RSA_2048", "RSA_2048",
"RSA_1024", "RSA_1024",
"EC_prime256v1" "RSA_4096",
"EC_prime256v1",
"EC_secp384r1",
"EC_secp521r1"
]
},
"KeyAlgorithmList":{
"type":"list",
"member":{"shape":"KeyAlgorithm"}
},
"KeyUsage":{
"type":"structure",
"members":{
"Name":{"shape":"KeyUsageName"}
}
},
"KeyUsageFilterList":{
"type":"list",
"member":{"shape":"KeyUsageName"}
},
"KeyUsageList":{
"type":"list",
"member":{"shape":"KeyUsage"}
},
"KeyUsageName":{
"type":"string",
"enum":[
"DIGITAL_SIGNATURE",
"NON_REPUDIATION",
"KEY_ENCIPHERMENT",
"DATA_ENCIPHERMENT",
"KEY_AGREEMENT",
"CERTIFICATE_SIGNING",
"CRL_SIGNING",
"ENCIPHER_ONLY",
"DECIPHER_ONLY",
"ANY",
"CUSTOM"
] ]
}, },
"LimitExceededException":{ "LimitExceededException":{
@@ -415,6 +569,7 @@
"type":"structure", "type":"structure",
"members":{ "members":{
"CertificateStatuses":{"shape":"CertificateStatuses"}, "CertificateStatuses":{"shape":"CertificateStatuses"},
"Includes":{"shape":"Filters"},
"NextToken":{"shape":"NextToken"}, "NextToken":{"shape":"NextToken"},
"MaxItems":{"shape":"MaxItems"} "MaxItems":{"shape":"MaxItems"}
} }
@@ -450,12 +605,29 @@
"min":1, "min":1,
"pattern":"[\\u0009\\u000A\\u000D\\u0020-\\u00FF]*" "pattern":"[\\u0009\\u000A\\u000D\\u0020-\\u00FF]*"
}, },
"PassphraseBlob":{
"type":"blob",
"max":128,
"min":4,
"sensitive":true
},
"PrivateKey":{
"type":"string",
"max":524288,
"min":1,
"pattern":"-{5}BEGIN PRIVATE KEY-{5}\\u000D?\\u000A([A-Za-z0-9/+]{64}\\u000D?\\u000A)*[A-Za-z0-9/+]{1,64}={0,2}\\u000D?\\u000A-{5}END PRIVATE KEY-{5}(\\u000D?\\u000A)?",
"sensitive":true
},
"PrivateKeyBlob":{ "PrivateKeyBlob":{
"type":"blob", "type":"blob",
"max":524288, "max":524288,
"min":1, "min":1,
"sensitive":true "sensitive":true
}, },
"RecordType":{
"type":"string",
"enum":["CNAME"]
},
"RemoveTagsFromCertificateRequest":{ "RemoveTagsFromCertificateRequest":{
"type":"structure", "type":"structure",
"required":[ "required":[
@@ -467,6 +639,13 @@
"Tags":{"shape":"TagList"} "Tags":{"shape":"TagList"}
} }
}, },
"RenewalEligibility":{
"type":"string",
"enum":[
"ELIGIBLE",
"INELIGIBLE"
]
},
"RenewalStatus":{ "RenewalStatus":{
"type":"string", "type":"string",
"enum":[ "enum":[
@@ -492,9 +671,12 @@
"required":["DomainName"], "required":["DomainName"],
"members":{ "members":{
"DomainName":{"shape":"DomainNameString"}, "DomainName":{"shape":"DomainNameString"},
"ValidationMethod":{"shape":"ValidationMethod"},
"SubjectAlternativeNames":{"shape":"DomainList"}, "SubjectAlternativeNames":{"shape":"DomainList"},
"IdempotencyToken":{"shape":"IdempotencyToken"}, "IdempotencyToken":{"shape":"IdempotencyToken"},
"DomainValidationOptions":{"shape":"DomainValidationOptionList"} "DomainValidationOptions":{"shape":"DomainValidationOptionList"},
"Options":{"shape":"CertificateOptions"},
"CertificateAuthorityArn":{"shape":"Arn"}
} }
}, },
"RequestCertificateResponse":{ "RequestCertificateResponse":{
@@ -537,6 +719,19 @@
}, },
"exception":true "exception":true
}, },
"ResourceRecord":{
"type":"structure",
"required":[
"Name",
"Type",
"Value"
],
"members":{
"Name":{"shape":"String"},
"Type":{"shape":"RecordType"},
"Value":{"shape":"String"}
}
},
"RevocationReason":{ "RevocationReason":{
"type":"string", "type":"string",
"enum":[ "enum":[
@@ -587,9 +782,27 @@
}, },
"exception":true "exception":true
}, },
"UpdateCertificateOptionsRequest":{
"type":"structure",
"required":[
"CertificateArn",
"Options"
],
"members":{
"CertificateArn":{"shape":"Arn"},
"Options":{"shape":"CertificateOptions"}
}
},
"ValidationEmailList":{ "ValidationEmailList":{
"type":"list", "type":"list",
"member":{"shape":"String"} "member":{"shape":"String"}
},
"ValidationMethod":{
"type":"string",
"enum":[
"EMAIL",
"DNS"
]
} }
} }
} }
+191 -51
View File
@@ -1,17 +1,19 @@
{ {
"version": "2.0", "version": "2.0",
"service": "<fullname>AWS Certificate Manager</fullname> <p>Welcome to the AWS Certificate Manager (ACM) API documentation.</p> <p>You can use ACM to manage SSL/TLS certificates for your AWS-based websites and applications. For general information about using ACM, see the <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/\"> <i>AWS Certificate Manager User Guide</i> </a>.</p>", "service": "<fullname>AWS Certificate Manager</fullname> <p>Welcome to the AWS Certificate Manager (ACM) API documentation.</p> <p>You can use ACM to manage SSL/TLS certificates for your AWS-based websites and applications. For general information about using ACM, see the <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/\"> <i>AWS Certificate Manager User Guide</i> </a>.</p>",
"operations": { "operations": {
"AddTagsToCertificate": "<p>Adds one or more tags to an ACM Certificate. Tags are labels that you can use to identify and organize your AWS resources. Each tag consists of a <code>key</code> and an optional <code>value</code>. You specify the certificate on input by its Amazon Resource Name (ARN). You specify the tag by using a key-value pair.</p> <p>You can apply a tag to just one certificate if you want to identify a specific characteristic of that certificate, or you can apply the same tag to multiple certificates if you want to filter for a common relationship among those certificates. Similarly, you can apply the same tag to multiple resources if you want to specify a relationship among those resources. For example, you can add the same tag to an ACM Certificate and an Elastic Load Balancing load balancer to indicate that they are both used by the same website. For more information, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/tags.html\">Tagging ACM Certificates</a>.</p> <p>To remove one or more tags, use the <a>RemoveTagsFromCertificate</a> action. To view all of the tags that have been applied to the certificate, use the <a>ListTagsForCertificate</a> action.</p>", "AddTagsToCertificate": "<p>Adds one or more tags to an ACM certificate. Tags are labels that you can use to identify and organize your AWS resources. Each tag consists of a <code>key</code> and an optional <code>value</code>. You specify the certificate on input by its Amazon Resource Name (ARN). You specify the tag by using a key-value pair. </p> <p>You can apply a tag to just one certificate if you want to identify a specific characteristic of that certificate, or you can apply the same tag to multiple certificates if you want to filter for a common relationship among those certificates. Similarly, you can apply the same tag to multiple resources if you want to specify a relationship among those resources. For example, you can add the same tag to an ACM certificate and an Elastic Load Balancing load balancer to indicate that they are both used by the same website. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/tags.html\">Tagging ACM certificates</a>. </p> <p>To remove one or more tags, use the <a>RemoveTagsFromCertificate</a> action. To view all of the tags that have been applied to the certificate, use the <a>ListTagsForCertificate</a> action. </p>",
"DeleteCertificate": "<p>Deletes an ACM Certificate and its associated private key. If this action succeeds, the certificate no longer appears in the list of ACM Certificates that can be displayed by calling the <a>ListCertificates</a> action or be retrieved by calling the <a>GetCertificate</a> action. The certificate will not be available for use by other AWS services.</p> <note> <p>You cannot delete an ACM Certificate that is being used by another AWS service. To delete a certificate that is in use, the certificate association must first be removed.</p> </note>", "DeleteCertificate": "<p>Deletes a certificate and its associated private key. If this action succeeds, the certificate no longer appears in the list that can be displayed by calling the <a>ListCertificates</a> action or be retrieved by calling the <a>GetCertificate</a> action. The certificate will not be available for use by AWS services integrated with ACM. </p> <note> <p>You cannot delete an ACM certificate that is being used by another AWS service. To delete a certificate that is in use, the certificate association must first be removed.</p> </note>",
"DescribeCertificate": "<p>Returns detailed metadata about the specified ACM Certificate.</p>", "DescribeCertificate": "<p>Returns detailed metadata about the specified ACM certificate.</p>",
"GetCertificate": "<p>Retrieves an ACM Certificate and certificate chain for the certificate specified by an ARN. The chain is an ordered list of certificates that contains the root certificate, intermediate certificates of subordinate CAs, and the ACM Certificate. The certificate and certificate chain are base64 encoded. If you want to decode the certificate chain to see the individual certificate fields, you can use OpenSSL.</p> <note> <p>Currently, ACM Certificates can be used only with Elastic Load Balancing and Amazon CloudFront.</p> </note>", "ExportCertificate": "<p>Exports a certificate for use anywhere. You can export the certificate, the certificate chain, and the encrypted private key associated with the public key embedded in the certificate. You must store the private key securely. The private key is a 2048 bit RSA key. You must provide a passphrase for the private key when exporting it. You can use the following OpenSSL command to decrypt it later. Provide the passphrase when prompted. </p> <p> <code>openssl rsa -in encrypted_key.pem -out decrypted_key.pem</code> </p>",
"ImportCertificate": "<p>Imports an SSL/TLS certificate into AWS Certificate Manager (ACM) to use with <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-services.html\">ACM's integrated AWS services</a>.</p> <note> <p>ACM does not provide <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a> for certificates that you import.</p> </note> <p>For more information about importing certificates into ACM, including the differences between certificates that you import and those that ACM provides, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html\">Importing Certificates</a> in the <i>AWS Certificate Manager User Guide</i>.</p> <p>To import a certificate, you must provide the certificate and the matching private key. When the certificate is not self-signed, you must also provide a certificate chain. You can omit the certificate chain when importing a self-signed certificate.</p> <p>The certificate, private key, and certificate chain must be PEM-encoded. For more information about converting these items to PEM format, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html#import-certificate-troubleshooting\">Importing Certificates Troubleshooting</a> in the <i>AWS Certificate Manager User Guide</i>.</p> <p>To import a new certificate, omit the <code>CertificateArn</code> field. Include this field only when you want to replace a previously imported certificate.</p> <p>This operation returns the <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of the imported certificate.</p>", "GetCertificate": "<p>Retrieves a certificate specified by an ARN and its certificate chain . The chain is an ordered list of certificates that contains the end entity certificate, intermediate certificates of subordinate CAs, and the root certificate in that order. The certificate and certificate chain are base64 encoded. If you want to decode the certificate to see the individual fields, you can use OpenSSL.</p>",
"ListCertificates": "<p>Retrieves a list of ACM Certificates and the domain name for each. You can optionally filter the list to return only the certificates that match the specified status.</p>", "ImportCertificate": "<p>Imports a certificate into AWS Certificate Manager (ACM) to use with services that are integrated with ACM. Note that <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-services.html\">integrated services</a> allow only certificate types and keys they support to be associated with their resources. Further, their support differs depending on whether the certificate is imported into IAM or into ACM. For more information, see the documentation for each service. For more information about importing certificates into ACM, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/import-certificate.html\">Importing Certificates</a> in the <i>AWS Certificate Manager User Guide</i>. </p> <note> <p>ACM does not provide <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a> for certificates that you import.</p> </note> <p>Note the following guidelines when importing third party certificates:</p> <ul> <li> <p>You must enter the private key that matches the certificate you are importing.</p> </li> <li> <p>The private key must be unencrypted. You cannot import a private key that is protected by a password or a passphrase.</p> </li> <li> <p>If the certificate you are importing is not self-signed, you must enter its certificate chain.</p> </li> <li> <p>If a certificate chain is included, the issuer must be the subject of one of the certificates in the chain.</p> </li> <li> <p>The certificate, private key, and certificate chain must be PEM-encoded.</p> </li> <li> <p>The current time must be between the <code>Not Before</code> and <code>Not After</code> certificate fields.</p> </li> <li> <p>The <code>Issuer</code> field must not be empty.</p> </li> <li> <p>The OCSP authority URL, if present, must not exceed 1000 characters.</p> </li> <li> <p>To import a new certificate, omit the <code>CertificateArn</code> argument. Include this argument only when you want to replace a previously imported certificate.</p> </li> <li> <p>When you import a certificate by using the CLI or one of the SDKs, you must specify the certificate, the certificate chain, and the private key by their file names preceded by <code>file://</code>. For example, you can specify a certificate saved in the <code>C:\\temp</code> folder as <code>file://C:\\temp\\certificate_to_import.pem</code>. If you are making an HTTP or HTTPS Query request, include these arguments as BLOBs. </p> </li> </ul> <p>This operation returns the <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of the imported certificate.</p>",
"ListTagsForCertificate": "<p>Lists the tags that have been applied to the ACM Certificate. Use the certificate's Amazon Resource Name (ARN) to specify the certificate. To add a tag to an ACM Certificate, use the <a>AddTagsToCertificate</a> action. To delete a tag, use the <a>RemoveTagsFromCertificate</a> action.</p>", "ListCertificates": "<p>Retrieves a list of certificate ARNs and domain names. You can request that only certificates that match a specific status be listed. You can also filter by specific attributes of the certificate. </p>",
"RemoveTagsFromCertificate": "<p>Remove one or more tags from an ACM Certificate. A tag consists of a key-value pair. If you do not specify the value portion of the tag when calling this function, the tag will be removed regardless of value. If you specify a value, the tag is removed only if it is associated with the specified value.</p> <p>To add tags to a certificate, use the <a>AddTagsToCertificate</a> action. To view all of the tags that have been applied to a specific ACM Certificate, use the <a>ListTagsForCertificate</a> action.</p>", "ListTagsForCertificate": "<p>Lists the tags that have been applied to the ACM certificate. Use the certificate's Amazon Resource Name (ARN) to specify the certificate. To add a tag to an ACM certificate, use the <a>AddTagsToCertificate</a> action. To delete a tag, use the <a>RemoveTagsFromCertificate</a> action. </p>",
"RequestCertificate": "<p>Requests an ACM Certificate for use with other AWS services. To request an ACM Certificate, you must specify the fully qualified domain name (FQDN) for your site. You can also specify additional FQDNs if users can reach your site by using other names. For each domain name you specify, email is sent to the domain owner to request approval to issue the certificate. After receiving approval from the domain owner, the ACM Certificate is issued. For more information, see the <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/\">AWS Certificate Manager User Guide</a>.</p>", "RemoveTagsFromCertificate": "<p>Remove one or more tags from an ACM certificate. A tag consists of a key-value pair. If you do not specify the value portion of the tag when calling this function, the tag will be removed regardless of value. If you specify a value, the tag is removed only if it is associated with the specified value. </p> <p>To add tags to a certificate, use the <a>AddTagsToCertificate</a> action. To view all of the tags that have been applied to a specific ACM certificate, use the <a>ListTagsForCertificate</a> action. </p>",
"ResendValidationEmail": "<p>Resends the email that requests domain ownership validation. The domain owner or an authorized representative must approve the ACM Certificate before it can be issued. The certificate can be approved by clicking a link in the mail to navigate to the Amazon certificate approval website and then clicking <b>I Approve</b>. However, the validation email can be blocked by spam filters. Therefore, if you do not receive the original mail, you can request that the mail be resent within 72 hours of requesting the ACM Certificate. If more than 72 hours have elapsed since your original request or since your last attempt to resend validation mail, you must request a new certificate. For more information about setting up your contact email addresses, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/setup-email.html\">Configure Email for your Domain</a>. </p>" "RequestCertificate": "<p>Requests an ACM certificate for use with other AWS services. To request an ACM certificate, you must specify the fully qualified domain name (FQDN) for your site in the <code>DomainName</code> parameter. You can also specify additional FQDNs in the <code>SubjectAlternativeNames</code> parameter. </p> <p>Each domain name that you specify must be validated to verify that you own or control the domain. You can use <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-dns.html\">DNS validation</a> or <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-email.html\">email validation</a>. We recommend that you use DNS validation. </p> <p>If you choose email validation, email is sent to the domain owner to request approval to issue the certificate. Email is sent to three registered contact addresses in the WHOIS database and to five common system administration addresses formed from the <code>DomainName</code> you enter or the optional <code>ValidationDomain</code> parameter. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-email.html\">Validate with Email</a>. </p> <p>After receiving approval from the domain owner, the ACM certificate is issued.</p>",
"ResendValidationEmail": "<p>Resends the email that requests domain ownership validation. The domain owner or an authorized representative must approve the ACM certificate before it can be issued. The certificate can be approved by clicking a link in the mail to navigate to the Amazon certificate approval website and then clicking <b>I Approve</b>. However, the validation email can be blocked by spam filters. Therefore, if you do not receive the original mail, you can request that the mail be resent within 72 hours of requesting the ACM certificate. If more than 72 hours have elapsed since your original request or since your last attempt to resend validation mail, you must request a new certificate. For more information about setting up your contact email addresses, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/setup-email.html\">Configure Email for your Domain</a>. </p>",
"UpdateCertificateOptions": "<p>Updates a certificate. Currently, you can use this function to specify whether to opt in to or out of recording your certificate in a certificate transparency log. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-bestpractices.html#best-practices-transparency\"> Opting Out of Certificate Transparency Logging</a>. </p>"
}, },
"shapes": { "shapes": {
"AddTagsToCertificateRequest": { "AddTagsToCertificateRequest": {
@@ -22,50 +24,64 @@
"Arn": { "Arn": {
"base": null, "base": null,
"refs": { "refs": {
"AddTagsToCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM Certificate to which the tag is to be applied. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "AddTagsToCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM certificate to which the tag is to be applied. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>. </p>",
"CertificateDetail$CertificateArn": "<p>The Amazon Resource Name (ARN) of the certificate. For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a> in the <i>AWS General Reference</i>.</p>", "CertificateDetail$CertificateArn": "<p>The Amazon Resource Name (ARN) of the certificate. For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a> in the <i>AWS General Reference</i>.</p>",
"CertificateSummary$CertificateArn": "<p>Amazon Resource Name (ARN) of the certificate. This is of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "CertificateDetail$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) of the ACM PCA private certificate authority (CA) that issued the certificate. This has the following format: </p> <p> <code>arn:aws:acm-pca:region:account:certificate-authority/12345678-1234-1234-1234-123456789012</code> </p>",
"DeleteCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM Certificate to be deleted. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "CertificateSummary$CertificateArn": "<p>Amazon Resource Name (ARN) of the certificate. This is of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>. </p>",
"DescribeCertificateRequest$CertificateArn": "<p>The Amazon Resource Name (ARN) of the ACM Certificate. The ARN must have the following form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "DeleteCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM certificate to be deleted. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>",
"DescribeCertificateRequest$CertificateArn": "<p>The Amazon Resource Name (ARN) of the ACM certificate. The ARN must have the following form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>",
"ExportCertificateRequest$CertificateArn": "<p>An Amazon Resource Name (ARN) of the issued certificate. This must be of the form:</p> <p> <code>arn:aws:acm:region:account:certificate/12345678-1234-1234-1234-123456789012</code> </p>",
"GetCertificateRequest$CertificateArn": "<p>String that contains a certificate ARN in the following format:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "GetCertificateRequest$CertificateArn": "<p>String that contains a certificate ARN in the following format:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>",
"ImportCertificateRequest$CertificateArn": "<p>The <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of an imported certificate to replace. To import a new certificate, omit this field.</p>", "ImportCertificateRequest$CertificateArn": "<p>The <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of an imported certificate to replace. To import a new certificate, omit this field. </p>",
"ImportCertificateResponse$CertificateArn": "<p>The <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of the imported certificate.</p>", "ImportCertificateResponse$CertificateArn": "<p>The <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Name (ARN)</a> of the imported certificate.</p>",
"ListTagsForCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM Certificate for which you want to list the tags. This has the following form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "ListTagsForCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM certificate for which you want to list the tags. This must have the following form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>. </p>",
"RemoveTagsFromCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM Certificate with one or more tags that you want to remove. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>.</p>", "RemoveTagsFromCertificateRequest$CertificateArn": "<p>String that contains the ARN of the ACM Certificate with one or more tags that you want to remove. This must be of the form:</p> <p> <code>arn:aws:acm:region:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p> <p>For more information about ARNs, see <a href=\"http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html\">Amazon Resource Names (ARNs) and AWS Service Namespaces</a>. </p>",
"RequestCertificateRequest$CertificateAuthorityArn": "<p>The Amazon Resource Name (ARN) of the private certificate authority (CA) that will be used to issue the certificate. For more information about private CAs, see the <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm-pca/latest/userguide/PcaWelcome.html\">AWS Certificate Manager Private Certificate Authority (PCA)</a> user guide. The ARN must have the following form: </p> <p> <code>arn:aws:acm-pca:region:account:certificate-authority/12345678-1234-1234-1234-123456789012</code> </p>",
"RequestCertificateResponse$CertificateArn": "<p>String that contains the ARN of the issued certificate. This must be of the form:</p> <p> <code>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p>", "RequestCertificateResponse$CertificateArn": "<p>String that contains the ARN of the issued certificate. This must be of the form:</p> <p> <code>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p>",
"ResendValidationEmailRequest$CertificateArn": "<p>String that contains the ARN of the requested certificate. The certificate ARN is generated and returned by the <a>RequestCertificate</a> action as soon as the request is made. By default, using this parameter causes email to be sent to all top-level domains you specified in the certificate request.</p> <p>The ARN must be of the form:</p> <p> <code>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p>" "ResendValidationEmailRequest$CertificateArn": "<p>String that contains the ARN of the requested certificate. The certificate ARN is generated and returned by the <a>RequestCertificate</a> action as soon as the request is made. By default, using this parameter causes email to be sent to all top-level domains you specified in the certificate request. The ARN must be of the form: </p> <p> <code>arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012</code> </p>",
"UpdateCertificateOptionsRequest$CertificateArn": "<p>ARN of the requested certificate to update. This must be of the form:</p> <p> <code>arn:aws:acm:us-east-1:<i>account</i>:certificate/<i>12345678-1234-1234-1234-123456789012</i> </code> </p>"
} }
}, },
"CertificateBody": { "CertificateBody": {
"base": null, "base": null,
"refs": { "refs": {
"GetCertificateResponse$Certificate": "<p>String that contains the ACM Certificate represented by the ARN specified at input.</p>" "ExportCertificateResponse$Certificate": "<p>The base64 PEM-encoded certificate.</p>",
"GetCertificateResponse$Certificate": "<p>String that contains the ACM certificate represented by the ARN specified at input.</p>"
} }
}, },
"CertificateBodyBlob": { "CertificateBodyBlob": {
"base": null, "base": null,
"refs": { "refs": {
"ImportCertificateRequest$Certificate": "<p>The certificate to import. It must meet the following requirements:</p> <ul> <li> <p>Must be PEM-encoded.</p> </li> <li> <p>Must contain a 1024-bit or 2048-bit RSA public key.</p> </li> <li> <p>Must be valid at the time of import. You cannot import a certificate before its validity period begins (the certificate's <code>NotBefore</code> date) or after it expires (the certificate's <code>NotAfter</code> date).</p> </li> </ul>" "ImportCertificateRequest$Certificate": "<p>The certificate to import.</p>"
} }
}, },
"CertificateChain": { "CertificateChain": {
"base": null, "base": null,
"refs": { "refs": {
"ExportCertificateResponse$CertificateChain": "<p>The base64 PEM-encoded certificate chain. This does not include the certificate that you are exporting.</p>",
"GetCertificateResponse$CertificateChain": "<p>The certificate chain that contains the root certificate issued by the certificate authority (CA).</p>" "GetCertificateResponse$CertificateChain": "<p>The certificate chain that contains the root certificate issued by the certificate authority (CA).</p>"
} }
}, },
"CertificateChainBlob": { "CertificateChainBlob": {
"base": null, "base": null,
"refs": { "refs": {
"ImportCertificateRequest$CertificateChain": "<p>The certificate chain. It must be PEM-encoded.</p>" "ImportCertificateRequest$CertificateChain": "<p>The PEM encoded certificate chain.</p>"
} }
}, },
"CertificateDetail": { "CertificateDetail": {
"base": "<p>Contains metadata about an ACM certificate. This structure is returned in the response to a <a>DescribeCertificate</a> request.</p>", "base": "<p>Contains metadata about an ACM certificate. This structure is returned in the response to a <a>DescribeCertificate</a> request. </p>",
"refs": { "refs": {
"DescribeCertificateResponse$Certificate": "<p>Metadata about an ACM certificate.</p>" "DescribeCertificateResponse$Certificate": "<p>Metadata about an ACM certificate.</p>"
} }
}, },
"CertificateOptions": {
"base": "<p>Structure that contains options for your certificate. Currently, you can use this only to specify whether to opt in to or out of certificate transparency logging. Some browsers require that public certificates issued for your domain be recorded in a log. Certificates that are not logged typically generate a browser error. Transparency makes it possible for you to detect SSL/TLS certificates that have been mistakenly or maliciously issued for your domain. For general information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-concepts.html#concept-transparency\">Certificate Transparency Logging</a>. </p>",
"refs": {
"CertificateDetail$Options": "<p>Value that specifies whether to add the certificate to a transparency log. Certificate transparency makes it possible to detect SSL certificates that have been mistakenly or maliciously issued. A browser might respond to certificate that has not been logged by showing an error message. The logs are cryptographically secure. </p>",
"RequestCertificateRequest$Options": "<p>Currently, you can use this parameter to specify whether to add the certificate to a certificate transparency log. Certificate transparency makes it possible to detect SSL/TLS certificates that have been mistakenly or maliciously issued. Certificates that have not been logged typically produce an error message in a browser. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-bestpractices.html#best-practices-transparency\">Opting Out of Certificate Transparency Logging</a>.</p>",
"UpdateCertificateOptionsRequest$Options": "<p>Use to update the options for your certificate. Currently, you can specify whether to add your certificate to a transparency log. Certificate transparency makes it possible to detect SSL/TLS certificates that have been mistakenly or maliciously issued. Certificates that have not been logged typically produce an error message in a browser. </p>"
}
},
"CertificateStatus": { "CertificateStatus": {
"base": null, "base": null,
"refs": { "refs": {
@@ -76,11 +92,11 @@
"CertificateStatuses": { "CertificateStatuses": {
"base": null, "base": null,
"refs": { "refs": {
"ListCertificatesRequest$CertificateStatuses": "<p>The status or statuses on which to filter the list of ACM Certificates.</p>" "ListCertificatesRequest$CertificateStatuses": "<p>Filter the certificate list by status value.</p>"
} }
}, },
"CertificateSummary": { "CertificateSummary": {
"base": "<p>This structure is returned in the response object of <a>ListCertificates</a> action.</p>", "base": "<p>This structure is returned in the response object of <a>ListCertificates</a> action. </p>",
"refs": { "refs": {
"CertificateSummaryList$member": null "CertificateSummaryList$member": null
} }
@@ -88,13 +104,19 @@
"CertificateSummaryList": { "CertificateSummaryList": {
"base": null, "base": null,
"refs": { "refs": {
"ListCertificatesResponse$CertificateSummaryList": "<p>A list of ACM Certificates.</p>" "ListCertificatesResponse$CertificateSummaryList": "<p>A list of ACM certificates.</p>"
}
},
"CertificateTransparencyLoggingPreference": {
"base": null,
"refs": {
"CertificateOptions$CertificateTransparencyLoggingPreference": "<p>You can opt out of certificate transparency logging by specifying the <code>DISABLED</code> option. Opt in by specifying <code>ENABLED</code>. </p>"
} }
}, },
"CertificateType": { "CertificateType": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$Type": "<p>The source of the certificate. For certificates provided by ACM, this value is <code>AMAZON_ISSUED</code>. For certificates that you imported with <a>ImportCertificate</a>, this value is <code>IMPORTED</code>. ACM does not provide <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a> for imported certificates. For more information about the differences between certificates that you import and those that ACM provides, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/import-certificate.html\">Importing Certificates</a> in the <i>AWS Certificate Manager User Guide</i>.</p>" "CertificateDetail$Type": "<p>The source of the certificate. For certificates provided by ACM, this value is <code>AMAZON_ISSUED</code>. For certificates that you imported with <a>ImportCertificate</a>, this value is <code>IMPORTED</code>. ACM does not provide <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a> for imported certificates. For more information about the differences between certificates that you import and those that ACM provides, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/import-certificate.html\">Importing Certificates</a> in the <i>AWS Certificate Manager User Guide</i>. </p>"
} }
}, },
"DeleteCertificateRequest": { "DeleteCertificateRequest": {
@@ -115,8 +137,8 @@
"DomainList": { "DomainList": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$SubjectAlternativeNames": "<p>One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website.</p>", "CertificateDetail$SubjectAlternativeNames": "<p>One or more domain names (subject alternative names) included in the certificate. This list contains the domain names that are bound to the public key that is contained in the certificate. The subject alternative names include the canonical domain name (CN) of the certificate and additional domain names that can be used to connect to the website. </p>",
"RequestCertificateRequest$SubjectAlternativeNames": "<p>Additional FQDNs to be included in the Subject Alternative Name extension of the ACM Certificate. For example, add the name www.example.net to a certificate for which the <code>DomainName</code> field is www.example.com if users can reach your site by using either name. The maximum number of domain names that you can add to an ACM Certificate is 100. However, the initial limit is 10 domain names. If you need more than 10 names, you must request a limit increase. For more information, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-limits.html\">Limits</a>.</p>" "RequestCertificateRequest$SubjectAlternativeNames": "<p>Additional FQDNs to be included in the Subject Alternative Name extension of the ACM certificate. For example, add the name www.example.net to a certificate for which the <code>DomainName</code> field is www.example.com if users can reach your site by using either name. The maximum number of domain names that you can add to an ACM certificate is 100. However, the initial limit is 10 domain names. If you need more than 10 names, you must request a limit increase. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-limits.html\">Limits</a>.</p> <p> The maximum length of a SAN DNS name is 253 octets. The name is made up of multiple labels separated by periods. No label can be longer than 63 octets. Consider the following examples: </p> <ul> <li> <p> <code>(63 octets).(63 octets).(63 octets).(61 octets)</code> is legal because the total length is 253 octets (63+1+63+1+63+1+61) and no label exceeds 63 octets.</p> </li> <li> <p> <code>(64 octets).(63 octets).(63 octets).(61 octets)</code> is not legal because the total length exceeds 253 octets (64+1+63+1+63+1+61) and the first label exceeds 63 octets.</p> </li> <li> <p> <code>(63 octets).(63 octets).(63 octets).(62 octets)</code> is not legal because the total length of the DNS name (63+1+63+1+63+1+62) exceeds 253 octets.</p> </li> </ul>"
} }
}, },
"DomainNameString": { "DomainNameString": {
@@ -125,11 +147,11 @@
"CertificateDetail$DomainName": "<p>The fully qualified domain name for the certificate, such as www.example.com or example.com.</p>", "CertificateDetail$DomainName": "<p>The fully qualified domain name for the certificate, such as www.example.com or example.com.</p>",
"CertificateSummary$DomainName": "<p>Fully qualified domain name (FQDN), such as www.example.com or example.com, for the certificate.</p>", "CertificateSummary$DomainName": "<p>Fully qualified domain name (FQDN), such as www.example.com or example.com, for the certificate.</p>",
"DomainList$member": null, "DomainList$member": null,
"DomainValidation$DomainName": "<p>A fully qualified domain name (FQDN) in the certificate. For example, <code>www.example.com</code> or <code>example.com</code>.</p>", "DomainValidation$DomainName": "<p>A fully qualified domain name (FQDN) in the certificate. For example, <code>www.example.com</code> or <code>example.com</code>. </p>",
"DomainValidation$ValidationDomain": "<p>The domain name that ACM used to send domain validation emails.</p>", "DomainValidation$ValidationDomain": "<p>The domain name that ACM used to send domain validation emails.</p>",
"DomainValidationOption$DomainName": "<p>A fully qualified domain name (FQDN) in the certificate request.</p>", "DomainValidationOption$DomainName": "<p>A fully qualified domain name (FQDN) in the certificate request.</p>",
"DomainValidationOption$ValidationDomain": "<p>The domain name that you want ACM to use to send you validation emails. This domain name is the suffix of the email addresses that you want ACM to use. This must be the same as the <code>DomainName</code> value or a superdomain of the <code>DomainName</code> value. For example, if you request a certificate for <code>testing.example.com</code>, you can specify <code>example.com</code> for this value. In that case, ACM sends domain validation emails to the following five addresses:</p> <ul> <li> <p>admin@example.com</p> </li> <li> <p>administrator@example.com</p> </li> <li> <p>hostmaster@example.com</p> </li> <li> <p>postmaster@example.com</p> </li> <li> <p>webmaster@example.com</p> </li> </ul>", "DomainValidationOption$ValidationDomain": "<p>The domain name that you want ACM to use to send you validation emails. This domain name is the suffix of the email addresses that you want ACM to use. This must be the same as the <code>DomainName</code> value or a superdomain of the <code>DomainName</code> value. For example, if you request a certificate for <code>testing.example.com</code>, you can specify <code>example.com</code> for this value. In that case, ACM sends domain validation emails to the following five addresses:</p> <ul> <li> <p>admin@example.com</p> </li> <li> <p>administrator@example.com</p> </li> <li> <p>hostmaster@example.com</p> </li> <li> <p>postmaster@example.com</p> </li> <li> <p>webmaster@example.com</p> </li> </ul>",
"RequestCertificateRequest$DomainName": "<p> Fully qualified domain name (FQDN), such as www.example.com, of the site that you want to secure with an ACM Certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain. For example, *.example.com protects www.example.com, site.example.com, and images.example.com. </p> <p> The maximum length of a DNS name is 253 octets. The name is made up of multiple labels separated by periods. No label can be longer than 63 octets. Consider the following examples: </p> <p> <code>(63 octets).(63 octets).(63 octets).(61 octets)</code> is legal because the total length is 253 octets (63+1+63+1+63+1+61) and no label exceeds 63 octets. </p> <p> <code>(64 octets).(63 octets).(63 octets).(61 octets)</code> is not legal because the total length exceeds 253 octets (64+1+63+1+63+1+61) and the first label exceeds 63 octets. </p> <p> <code>(63 octets).(63 octets).(63 octets).(62 octets)</code> is not legal because the total length of the DNS name (63+1+63+1+63+1+62) exceeds 253 octets. </p>", "RequestCertificateRequest$DomainName": "<p> Fully qualified domain name (FQDN), such as www.example.com, of the site that you want to secure with an ACM Certificate. Use an asterisk (*) to create a wildcard certificate that protects several sites in the same domain. For example, *.example.com protects www.example.com, site.example.com, and images.example.com. </p> <p> The first domain name you enter cannot exceed 63 octets, including periods. Each subsequent Subject Alternative Name (SAN), however, can be up to 253 octets in length. </p>",
"ResendValidationEmailRequest$Domain": "<p>The fully qualified domain name (FQDN) of the certificate that needs to be validated.</p>", "ResendValidationEmailRequest$Domain": "<p>The fully qualified domain name (FQDN) of the certificate that needs to be validated.</p>",
"ResendValidationEmailRequest$ValidationDomain": "<p>The base validation domain that will act as the suffix of the email addresses that are used to send the emails. This must be the same as the <code>Domain</code> value or a superdomain of the <code>Domain</code> value. For example, if you requested a certificate for <code>site.subdomain.example.com</code> and specify a <b>ValidationDomain</b> of <code>subdomain.example.com</code>, ACM sends email to the domain registrant, technical contact, and administrative contact in WHOIS and the following five addresses:</p> <ul> <li> <p>admin@subdomain.example.com</p> </li> <li> <p>administrator@subdomain.example.com</p> </li> <li> <p>hostmaster@subdomain.example.com</p> </li> <li> <p>postmaster@subdomain.example.com</p> </li> <li> <p>webmaster@subdomain.example.com</p> </li> </ul>" "ResendValidationEmailRequest$ValidationDomain": "<p>The base validation domain that will act as the suffix of the email addresses that are used to send the emails. This must be the same as the <code>Domain</code> value or a superdomain of the <code>Domain</code> value. For example, if you requested a certificate for <code>site.subdomain.example.com</code> and specify a <b>ValidationDomain</b> of <code>subdomain.example.com</code>, ACM sends email to the domain registrant, technical contact, and administrative contact in WHOIS and the following five addresses:</p> <ul> <li> <p>admin@subdomain.example.com</p> </li> <li> <p>administrator@subdomain.example.com</p> </li> <li> <p>hostmaster@subdomain.example.com</p> </li> <li> <p>postmaster@subdomain.example.com</p> </li> <li> <p>webmaster@subdomain.example.com</p> </li> </ul>"
} }
@@ -137,7 +159,7 @@
"DomainStatus": { "DomainStatus": {
"base": null, "base": null,
"refs": { "refs": {
"DomainValidation$ValidationStatus": "<p>The validation status of the domain name.</p>" "DomainValidation$ValidationStatus": "<p>The validation status of the domain name. This can be one of the following values:</p> <ul> <li> <p> <code>PENDING_VALIDATION</code> </p> </li> <li> <p> <code/>SUCCESS</p> </li> <li> <p> <code/>FAILED</p> </li> </ul>"
} }
}, },
"DomainValidation": { "DomainValidation": {
@@ -149,12 +171,12 @@
"DomainValidationList": { "DomainValidationList": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$DomainValidationOptions": "<p>Contains information about the initial validation of each domain name that occurs as a result of the <a>RequestCertificate</a> request. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>", "CertificateDetail$DomainValidationOptions": "<p>Contains information about the initial validation of each domain name that occurs as a result of the <a>RequestCertificate</a> request. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>. </p>",
"RenewalSummary$DomainValidationOptions": "<p>Contains information about the validation of each domain name in the certificate, as it pertains to ACM's <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a>. This is different from the initial validation that occurs as a result of the <a>RequestCertificate</a> request. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>" "RenewalSummary$DomainValidationOptions": "<p>Contains information about the validation of each domain name in the certificate, as it pertains to ACM's <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a>. This is different from the initial validation that occurs as a result of the <a>RequestCertificate</a> request. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>"
} }
}, },
"DomainValidationOption": { "DomainValidationOption": {
"base": "<p>Contains information about the domain names that you want ACM to use to send you emails to validate your ownership of the domain.</p>", "base": "<p>Contains information about the domain names that you want ACM to use to send you emails that enable you to validate domain ownership.</p>",
"refs": { "refs": {
"DomainValidationOptionList$member": null "DomainValidationOptionList$member": null
} }
@@ -162,13 +184,54 @@
"DomainValidationOptionList": { "DomainValidationOptionList": {
"base": null, "base": null,
"refs": { "refs": {
"RequestCertificateRequest$DomainValidationOptions": "<p>The domain name that you want ACM to use to send you emails to validate your ownership of the domain.</p>" "RequestCertificateRequest$DomainValidationOptions": "<p>The domain name that you want ACM to use to send you emails so that you can validate domain ownership.</p>"
}
},
"ExportCertificateRequest": {
"base": null,
"refs": {
}
},
"ExportCertificateResponse": {
"base": null,
"refs": {
}
},
"ExtendedKeyUsage": {
"base": "<p>The Extended Key Usage X.509 v3 extension defines one or more purposes for which the public key can be used. This is in addition to or in place of the basic purposes specified by the Key Usage extension. </p>",
"refs": {
"ExtendedKeyUsageList$member": null
}
},
"ExtendedKeyUsageFilterList": {
"base": null,
"refs": {
"Filters$extendedKeyUsage": "<p>Specify one or more <a>ExtendedKeyUsage</a> extension values.</p>"
}
},
"ExtendedKeyUsageList": {
"base": null,
"refs": {
"CertificateDetail$ExtendedKeyUsages": "<p>Contains a list of Extended Key Usage X.509 v3 extension objects. Each object specifies a purpose for which the certificate public key can be used and consists of a name and an object identifier (OID). </p>"
}
},
"ExtendedKeyUsageName": {
"base": null,
"refs": {
"ExtendedKeyUsage$Name": "<p>The name of an Extended Key Usage value.</p>",
"ExtendedKeyUsageFilterList$member": null
} }
}, },
"FailureReason": { "FailureReason": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$FailureReason": "<p>The reason the certificate request failed. This value exists only when the certificate status is <code>FAILED</code>. For more information, see <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/troubleshooting.html#troubleshooting-failed\">Certificate Request Failed</a> in the <i>AWS Certificate Manager User Guide</i>.</p>" "CertificateDetail$FailureReason": "<p>The reason the certificate request failed. This value exists only when the certificate status is <code>FAILED</code>. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/troubleshooting.html#troubleshooting-failed\">Certificate Request Failed</a> in the <i>AWS Certificate Manager User Guide</i>. </p>"
}
},
"Filters": {
"base": "<p>This structure can be used in the <a>ListCertificates</a> action to filter the output of the certificate list. </p>",
"refs": {
"ListCertificatesRequest$Includes": "<p>Filter the certificate list. For more information, see the <a>Filters</a> structure.</p>"
} }
}, },
"GetCertificateRequest": { "GetCertificateRequest": {
@@ -200,7 +263,7 @@
"InUseList": { "InUseList": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$InUseBy": "<p>A list of ARNs for the AWS resources that are using the certificate. A certificate can be used by multiple AWS resources.</p>" "CertificateDetail$InUseBy": "<p>A list of ARNs for the AWS resources that are using the certificate. A certificate can be used by multiple AWS resources. </p>"
} }
}, },
"InvalidArnException": { "InvalidArnException": {
@@ -214,7 +277,7 @@
} }
}, },
"InvalidStateException": { "InvalidStateException": {
"base": "<p>Processing has reached an invalid state. For example, this exception can occur if the specified domain is not using email validation, or the current certificate status does not permit the requested operation. See the exception message returned by ACM to determine which state is not valid.</p>", "base": "<p>Processing has reached an invalid state.</p>",
"refs": { "refs": {
} }
}, },
@@ -226,11 +289,43 @@
"KeyAlgorithm": { "KeyAlgorithm": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$KeyAlgorithm": "<p>The algorithm that was used to generate the key pair (the public and private key).</p>" "CertificateDetail$KeyAlgorithm": "<p>The algorithm that was used to generate the public-private key pair.</p>",
"KeyAlgorithmList$member": null
}
},
"KeyAlgorithmList": {
"base": null,
"refs": {
"Filters$keyTypes": "<p>Specify one or more algorithms that can be used to generate key pairs.</p>"
}
},
"KeyUsage": {
"base": "<p>The Key Usage X.509 v3 extension defines the purpose of the public key contained in the certificate.</p>",
"refs": {
"KeyUsageList$member": null
}
},
"KeyUsageFilterList": {
"base": null,
"refs": {
"Filters$keyUsage": "<p>Specify one or more <a>KeyUsage</a> extension values.</p>"
}
},
"KeyUsageList": {
"base": null,
"refs": {
"CertificateDetail$KeyUsages": "<p>A list of Key Usage X.509 v3 extension objects. Each object is a string value that identifies the purpose of the public key contained in the certificate. Possible extension values include DIGITAL_SIGNATURE, KEY_ENCHIPHERMENT, NON_REPUDIATION, and more.</p>"
}
},
"KeyUsageName": {
"base": null,
"refs": {
"KeyUsage$Name": "<p>A string value that contains a Key Usage extension name.</p>",
"KeyUsageFilterList$member": null
} }
}, },
"LimitExceededException": { "LimitExceededException": {
"base": "<p>An ACM limit has been exceeded. For example, you may have input more domains than are allowed or you've requested too many certificates for your account. See the exception message returned by ACM to determine which limit you have violated. For more information about ACM limits, see the <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-limits.html\">Limits</a> topic.</p>", "base": "<p>An ACM limit has been exceeded.</p>",
"refs": { "refs": {
} }
}, },
@@ -267,10 +362,28 @@
"ListCertificatesResponse$NextToken": "<p>When the list is truncated, this value is present and contains the value to use for the <code>NextToken</code> parameter in a subsequent pagination request.</p>" "ListCertificatesResponse$NextToken": "<p>When the list is truncated, this value is present and contains the value to use for the <code>NextToken</code> parameter in a subsequent pagination request.</p>"
} }
}, },
"PassphraseBlob": {
"base": null,
"refs": {
"ExportCertificateRequest$Passphrase": "<p>Passphrase to associate with the encrypted exported private key. If you want to later decrypt the private key, you must have the passphrase. You can use the following OpenSSL command to decrypt a private key: </p> <p> <code>openssl rsa -in encrypted_key.pem -out decrypted_key.pem</code> </p>"
}
},
"PrivateKey": {
"base": null,
"refs": {
"ExportCertificateResponse$PrivateKey": "<p>The PEM-encoded private key associated with the public key in the certificate.</p>"
}
},
"PrivateKeyBlob": { "PrivateKeyBlob": {
"base": null, "base": null,
"refs": { "refs": {
"ImportCertificateRequest$PrivateKey": "<p>The private key that matches the public key in the certificate. It must meet the following requirements:</p> <ul> <li> <p>Must be PEM-encoded.</p> </li> <li> <p>Must be unencrypted. You cannot import a private key that is protected by a password or passphrase.</p> </li> </ul>" "ImportCertificateRequest$PrivateKey": "<p>The private key that matches the public key in the certificate.</p>"
}
},
"RecordType": {
"base": null,
"refs": {
"ResourceRecord$Type": "<p>The type of DNS record. Currently this can be <code>CNAME</code>.</p>"
} }
}, },
"RemoveTagsFromCertificateRequest": { "RemoveTagsFromCertificateRequest": {
@@ -278,16 +391,22 @@
"refs": { "refs": {
} }
}, },
"RenewalEligibility": {
"base": null,
"refs": {
"CertificateDetail$RenewalEligibility": "<p>Specifies whether the certificate is eligible for renewal.</p>"
}
},
"RenewalStatus": { "RenewalStatus": {
"base": null, "base": null,
"refs": { "refs": {
"RenewalSummary$RenewalStatus": "<p>The status of ACM's <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a> of the certificate.</p>" "RenewalSummary$RenewalStatus": "<p>The status of ACM's <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a> of the certificate.</p>"
} }
}, },
"RenewalSummary": { "RenewalSummary": {
"base": "<p>Contains information about the status of ACM's <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a> for the certificate. This structure exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>", "base": "<p>Contains information about the status of ACM's <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a> for the certificate. This structure exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>",
"refs": { "refs": {
"CertificateDetail$RenewalSummary": "<p>Contains information about the status of ACM's <a href=\"http://docs.aws.amazon.com/acm/latest/userguide/acm-renewal.html\">managed renewal</a> for the certificate. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>" "CertificateDetail$RenewalSummary": "<p>Contains information about the status of ACM's <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/acm-renewal.html\">managed renewal</a> for the certificate. This field exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>"
} }
}, },
"RequestCertificateRequest": { "RequestCertificateRequest": {
@@ -316,14 +435,20 @@
} }
}, },
"ResourceNotFoundException": { "ResourceNotFoundException": {
"base": "<p>The specified certificate cannot be found in the caller's account, or the caller's account cannot be found.</p>", "base": "<p>The specified certificate cannot be found in the caller's account or the caller's account cannot be found.</p>",
"refs": { "refs": {
} }
}, },
"ResourceRecord": {
"base": "<p>Contains a DNS record value that you can use to can use to validate ownership or control of a domain. This is used by the <a>DescribeCertificate</a> action. </p>",
"refs": {
"DomainValidation$ResourceRecord": "<p>Contains the CNAME record that you add to your DNS database for domain validation. For more information, see <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-dns.html\">Use DNS to Validate Domain Ownership</a>.</p>"
}
},
"RevocationReason": { "RevocationReason": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$RevocationReason": "<p>The reason the certificate was revoked. This value exists only when the certificate status is <code>REVOKED</code>.</p>" "CertificateDetail$RevocationReason": "<p>The reason the certificate was revoked. This value exists only when the certificate status is <code>REVOKED</code>. </p>"
} }
}, },
"String": { "String": {
@@ -333,6 +458,7 @@
"CertificateDetail$Subject": "<p>The name of the entity that is associated with the public key contained in the certificate.</p>", "CertificateDetail$Subject": "<p>The name of the entity that is associated with the public key contained in the certificate.</p>",
"CertificateDetail$Issuer": "<p>The name of the certificate authority that issued and signed the certificate.</p>", "CertificateDetail$Issuer": "<p>The name of the certificate authority that issued and signed the certificate.</p>",
"CertificateDetail$SignatureAlgorithm": "<p>The algorithm that was used to sign the certificate.</p>", "CertificateDetail$SignatureAlgorithm": "<p>The algorithm that was used to sign the certificate.</p>",
"ExtendedKeyUsage$OID": "<p>An object identifier (OID) for the extension value. OIDs are strings of numbers separated by periods. The following OIDs are defined in RFC 3280 and RFC 5280. </p> <ul> <li> <p> <code>1.3.6.1.5.5.7.3.1 (TLS_WEB_SERVER_AUTHENTICATION)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.2 (TLS_WEB_CLIENT_AUTHENTICATION)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.3 (CODE_SIGNING)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.4 (EMAIL_PROTECTION)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.8 (TIME_STAMPING)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.9 (OCSP_SIGNING)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.5 (IPSEC_END_SYSTEM)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.6 (IPSEC_TUNNEL)</code> </p> </li> <li> <p> <code>1.3.6.1.5.5.7.3.7 (IPSEC_USER)</code> </p> </li> </ul>",
"InUseList$member": null, "InUseList$member": null,
"InvalidArnException$message": null, "InvalidArnException$message": null,
"InvalidDomainValidationOptionsException$message": null, "InvalidDomainValidationOptionsException$message": null,
@@ -342,6 +468,8 @@
"RequestInProgressException$message": null, "RequestInProgressException$message": null,
"ResourceInUseException$message": null, "ResourceInUseException$message": null,
"ResourceNotFoundException$message": null, "ResourceNotFoundException$message": null,
"ResourceRecord$Name": "<p>The name of the DNS record to create in your domain. This is supplied by ACM.</p>",
"ResourceRecord$Value": "<p>The value of the CNAME record to add to your DNS database. This is supplied by ACM.</p>",
"TooManyTagsException$message": null, "TooManyTagsException$message": null,
"ValidationEmailList$member": null "ValidationEmailList$member": null
} }
@@ -349,10 +477,10 @@
"TStamp": { "TStamp": {
"base": null, "base": null,
"refs": { "refs": {
"CertificateDetail$CreatedAt": "<p>The time at which the certificate was requested. This value exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>", "CertificateDetail$CreatedAt": "<p>The time at which the certificate was requested. This value exists only when the certificate type is <code>AMAZON_ISSUED</code>. </p>",
"CertificateDetail$IssuedAt": "<p>The time at which the certificate was issued. This value exists only when the certificate type is <code>AMAZON_ISSUED</code>.</p>", "CertificateDetail$IssuedAt": "<p>The time at which the certificate was issued. This value exists only when the certificate type is <code>AMAZON_ISSUED</code>. </p>",
"CertificateDetail$ImportedAt": "<p>The date and time at which the certificate was imported. This value exists only when the certificate type is <code>IMPORTED</code>.</p>", "CertificateDetail$ImportedAt": "<p>The date and time at which the certificate was imported. This value exists only when the certificate type is <code>IMPORTED</code>. </p>",
"CertificateDetail$RevokedAt": "<p>The time at which the certificate was revoked. This value exists only when the certificate status is <code>REVOKED</code>.</p>", "CertificateDetail$RevokedAt": "<p>The time at which the certificate was revoked. This value exists only when the certificate status is <code>REVOKED</code>. </p>",
"CertificateDetail$NotBefore": "<p>The time before which the certificate is not valid.</p>", "CertificateDetail$NotBefore": "<p>The time before which the certificate is not valid.</p>",
"CertificateDetail$NotAfter": "<p>The time after which the certificate is not valid.</p>" "CertificateDetail$NotAfter": "<p>The time after which the certificate is not valid.</p>"
} }
@@ -388,11 +516,23 @@
"refs": { "refs": {
} }
}, },
"UpdateCertificateOptionsRequest": {
"base": null,
"refs": {
}
},
"ValidationEmailList": { "ValidationEmailList": {
"base": null, "base": null,
"refs": { "refs": {
"DomainValidation$ValidationEmails": "<p>A list of email addresses that ACM used to send domain validation emails.</p>" "DomainValidation$ValidationEmails": "<p>A list of email addresses that ACM used to send domain validation emails.</p>"
} }
},
"ValidationMethod": {
"base": null,
"refs": {
"DomainValidation$ValidationMethod": "<p>Specifies the domain validation method.</p>",
"RequestCertificateRequest$ValidationMethod": "<p>The method you want to use to validate that you own or control domain. You can <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-dns.html\">validate with DNS</a> or <a href=\"http://docs.aws.amazon.com/http:/docs.aws.amazon.comacm/latest/userguide/gs-acm-validate-email.html\">validate with email</a>. We recommend that you use DNS validation. </p>"
}
} }
} }
} }
+18
View File
@@ -0,0 +1,18 @@
{
"version": 1,
"defaultRegion": "us-west-2",
"testCases": [
{
"operationName": "ListCertificates",
"input": {},
"errorExpectedFromService": false
},
{
"operationName": "GetCertificate",
"input": {
"CertificateArn": "arn:aws:acm:region:123456789012:certificate\/12345678-1234-1234-1234-123456789012"
},
"errorExpectedFromService": true
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
{
"pagination": {
"ListSkills": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"ListTags": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchAddressBooks": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchContacts": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchDevices": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchProfiles": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchRooms": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchSkillGroups": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
},
"SearchUsers": {
"input_token": "NextToken",
"output_token": "NextToken",
"limit_key": "MaxResults"
}
}
}

Some files were not shown because too many files have changed in this diff Show More