diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 85848bdc440c..e1cb51f3987d 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -227,143 +227,143 @@ }, { "ImportPath": "github.com/aws/aws-sdk-go/aws", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/awserr", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/client", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/defaults", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/request", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/aws/session", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/endpoints", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/jsonrpc", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restjson", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/restxml", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/private/waiter", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/apigateway", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/autoscaling", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudformation", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudfront", @@ -372,148 +372,148 @@ }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudtrail", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatchevents", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatchlogs", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/codecommit", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/codedeploy", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/directoryservice", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/dynamodb", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/ec2", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/ecr", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/ecs", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/efs", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/elasticache", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/elasticbeanstalk", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/elasticsearchservice", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/elb", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/firehose", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/glacier", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/iam", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/kinesis", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/kms", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/lambda", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/opsworks", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/rds", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/redshift", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/route53", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/s3", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/sns", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/aws/aws-sdk-go/service/sqs", - "Comment": "v1.1.14", - "Rev": "6876e9922ff299adf36e43e04c94820077968b3b" + "Comment": "v1.1.15", + "Rev": "e7cf1e5986499eea7d4a87868f1eb578c8f2045a" }, { "ImportPath": "github.com/bgentry/speakeasy", diff --git a/builtin/providers/aws/auth_helpers.go b/builtin/providers/aws/auth_helpers.go new file mode 100644 index 000000000000..914c7e97174d --- /dev/null +++ b/builtin/providers/aws/auth_helpers.go @@ -0,0 +1,134 @@ +package aws + +import ( + "fmt" + "log" + "os" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/ec2metadata" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/go-cleanhttp" +) + +func GetAccountId(iamconn *iam.IAM, authProviderName string) (string, error) { + // If we have creds from instance profile, we can use metadata API + if authProviderName == ec2rolecreds.ProviderName { + log.Println("[DEBUG] Trying to get account ID via AWS Metadata API") + + cfg := &aws.Config{} + setOptionalEndpoint(cfg) + metadataClient := ec2metadata.New(session.New(cfg)) + info, err := metadataClient.IAMInfo() + if err != nil { + // This can be triggered when no IAM Role is assigned + // or AWS just happens to return invalid response + return "", fmt.Errorf("Failed getting EC2 IAM info: %s", err) + } + + return parseAccountIdFromArn(info.InstanceProfileArn) + } + + // Then try IAM GetUser + log.Println("[DEBUG] Trying to get account ID via iam:GetUser") + outUser, err := iamconn.GetUser(nil) + if err == nil { + return parseAccountIdFromArn(*outUser.User.Arn) + } + + // Then try IAM ListRoles + awsErr, ok := err.(awserr.Error) + // AccessDenied and ValidationError can be raised + // if credentials belong to federated profile, so we ignore these + if !ok || (awsErr.Code() != "AccessDenied" && awsErr.Code() != "ValidationError") { + return "", fmt.Errorf("Failed getting account ID via 'iam:GetUser': %s", err) + } + + log.Printf("[DEBUG] Getting account ID via iam:GetUser failed: %s", err) + log.Println("[DEBUG] Trying to get account ID via iam:ListRoles instead") + outRoles, err := iamconn.ListRoles(&iam.ListRolesInput{ + MaxItems: aws.Int64(int64(1)), + }) + if err != nil { + return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': %s", err) + } + + if len(outRoles.Roles) < 1 { + return "", fmt.Errorf("Failed getting account ID via 'iam:ListRoles': No roles available") + } + + return parseAccountIdFromArn(*outRoles.Roles[0].Arn) +} + +func parseAccountIdFromArn(arn string) (string, error) { + parts := strings.Split(arn, ":") + if len(parts) < 5 { + return "", fmt.Errorf("Unable to parse ID from invalid ARN: %q", arn) + } + return parts[4], nil +} + +// This function is responsible for reading credentials from the +// environment in the case that they're not explicitly specified +// in the Terraform configuration. +func GetCredentials(key, secret, token, profile, credsfile string) *awsCredentials.Credentials { + // build a chain provider, lazy-evaulated by aws-sdk + providers := []awsCredentials.Provider{ + &awsCredentials.StaticProvider{Value: awsCredentials.Value{ + AccessKeyID: key, + SecretAccessKey: secret, + SessionToken: token, + }}, + &awsCredentials.EnvProvider{}, + &awsCredentials.SharedCredentialsProvider{ + Filename: credsfile, + Profile: profile, + }, + } + + // Build isolated HTTP client to avoid issues with globally-shared settings + client := cleanhttp.DefaultClient() + + // Keep the timeout low as we don't want to wait in non-EC2 environments + client.Timeout = 100 * time.Millisecond + cfg := &aws.Config{ + HTTPClient: client, + } + usedEndpoint := setOptionalEndpoint(cfg) + + // Real AWS should reply to a simple metadata request. + // We check it actually does to ensure something else didn't just + // happen to be listening on the same IP:Port + metadataClient := ec2metadata.New(session.New(cfg)) + if metadataClient.Available() { + providers = append(providers, &ec2rolecreds.EC2RoleProvider{ + Client: metadataClient, + }) + log.Printf("[INFO] AWS EC2 instance detected via default metadata" + + " API endpoint, EC2RoleProvider added to the auth chain") + } else { + if usedEndpoint == "" { + usedEndpoint = "default location" + } + log.Printf("[WARN] Ignoring AWS metadata API endpoint at %s "+ + "as it doesn't return any instance-id", usedEndpoint) + } + + return awsCredentials.NewChainCredentials(providers) +} + +func setOptionalEndpoint(cfg *aws.Config) string { + endpoint := os.Getenv("AWS_METADATA_URL") + if endpoint != "" { + log.Printf("[INFO] Setting custom metadata endpoint: %q", endpoint) + cfg.Endpoint = aws.String(endpoint) + return endpoint + } + return "" +} diff --git a/builtin/providers/aws/auth_helpers_test.go b/builtin/providers/aws/auth_helpers_test.go new file mode 100644 index 000000000000..b3f13403923e --- /dev/null +++ b/builtin/providers/aws/auth_helpers_test.go @@ -0,0 +1,757 @@ +package aws + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "os" + "testing" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/iam" +) + +func TestAWSGetAccountId_shouldBeValid_fromEC2Role(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + awsTs := awsEnv(t) + defer awsTs() + + iamEndpoints := []*iamEndpoint{} + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName) + if err != nil { + t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) + } + + expectedAccountId := "123456789013" + if id != expectedAccountId { + t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) + } +} + +func TestAWSGetAccountId_shouldBeValid_EC2RoleHasPriority(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + awsTs := awsEnv(t) + defer awsTs() + + iamEndpoints := []*iamEndpoint{ + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, + }, + } + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, ec2rolecreds.ProviderName) + if err != nil { + t.Fatalf("Getting account ID from EC2 metadata API failed: %s", err) + } + + expectedAccountId := "123456789013" + if id != expectedAccountId { + t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) + } +} + +func TestAWSGetAccountId_shouldBeValid_fromIamUser(t *testing.T) { + iamEndpoints := []*iamEndpoint{ + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &iamResponse{200, iamResponse_GetUser_valid, "text/xml"}, + }, + } + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, "") + if err != nil { + t.Fatalf("Getting account ID via GetUser failed: %s", err) + } + + expectedAccountId := "123456789012" + if id != expectedAccountId { + t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) + } +} + +func TestAWSGetAccountId_shouldBeValid_fromIamListRoles(t *testing.T) { + iamEndpoints := []*iamEndpoint{ + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, + }, + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, + }, + } + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, "") + if err != nil { + t.Fatalf("Getting account ID via ListRoles failed: %s", err) + } + + expectedAccountId := "123456789012" + if id != expectedAccountId { + t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) + } +} + +func TestAWSGetAccountId_shouldBeValid_federatedRole(t *testing.T) { + iamEndpoints := []*iamEndpoint{ + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &iamResponse{400, iamResponse_GetUser_federatedFailure, "text/xml"}, + }, + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &iamResponse{200, iamResponse_ListRoles_valid, "text/xml"}, + }, + } + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, "") + if err != nil { + t.Fatalf("Getting account ID via ListRoles failed: %s", err) + } + + expectedAccountId := "123456789012" + if id != expectedAccountId { + t.Fatalf("Expected account ID: %s, given: %s", expectedAccountId, id) + } +} + +func TestAWSGetAccountId_shouldError_unauthorizedFromIam(t *testing.T) { + iamEndpoints := []*iamEndpoint{ + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=GetUser&Version=2010-05-08"}, + Response: &iamResponse{403, iamResponse_GetUser_unauthorized, "text/xml"}, + }, + &iamEndpoint{ + Request: &iamRequest{"POST", "/", "Action=ListRoles&MaxItems=1&Version=2010-05-08"}, + Response: &iamResponse{403, iamResponse_ListRoles_unauthorized, "text/xml"}, + }, + } + ts, iamConn := getMockedAwsIamApi(iamEndpoints) + defer ts() + + id, err := GetAccountId(iamConn, "") + if err == nil { + t.Fatal("Expected error when getting account ID") + } + + if id != "" { + t.Fatalf("Expected no account ID, given: %s", id) + } +} + +func TestAWSParseAccountIdFromArn(t *testing.T) { + validArn := "arn:aws:iam::101636750127:instance-profile/aws-elasticbeanstalk-ec2-role" + expectedId := "101636750127" + id, err := parseAccountIdFromArn(validArn) + if err != nil { + t.Fatalf("Expected no error when parsing valid ARN: %s", err) + } + if id != expectedId { + t.Fatalf("Parsed id doesn't match with expected (%q != %q)", id, expectedId) + } + + invalidArn := "blablah" + id, err = parseAccountIdFromArn(invalidArn) + if err == nil { + t.Fatalf("Expected error when parsing invalid ARN (%q)", invalidArn) + } +} + +func TestAWSGetCredentials_shouldError(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + cfg := Config{} + + c := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) + _, err := c.Get() + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() != "NoCredentialProviders" { + t.Fatalf("Expected NoCredentialProviders error") + } + } + if err == nil { + t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config") + } +} + +func TestAWSGetCredentials_shouldBeStatic(t *testing.T) { + simple := []struct { + Key, Secret, Token string + }{ + { + Key: "test", + Secret: "secret", + }, { + Key: "test", + Secret: "test", + Token: "test", + }, + } + + for _, c := range simple { + cfg := Config{ + AccessKey: c.Key, + SecretKey: c.Secret, + Token: c.Token, + } + + creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) + if creds == nil { + t.Fatalf("Expected a static creds provider to be returned") + } + v, err := creds.Get() + if err != nil { + t.Fatalf("Error gettings creds: %s", err) + } + if v.AccessKeyID != c.Key { + t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) + } + if v.SecretAccessKey != c.Secret { + t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) + } + if v.SessionToken != c.Token { + t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) + } + } +} + +// TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform +// from an EC2 instance, without environment variables or manually supplied +// credentials. +func TestAWSGetCredentials_shouldIAM(t *testing.T) { + // clear AWS_* environment variables + resetEnv := unsetEnv(t) + defer resetEnv() + + // capture the test server's close method, to call after the test returns + ts := awsEnv(t) + defer ts() + + // An empty config, no key supplied + cfg := Config{} + + creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) + if creds == nil { + t.Fatalf("Expected a static creds provider to be returned") + } + + v, err := creds.Get() + if err != nil { + t.Fatalf("Error gettings creds: %s", err) + } + if v.AccessKeyID != "somekey" { + t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) + } + if v.SecretAccessKey != "somesecret" { + t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) + } + if v.SessionToken != "sometoken" { + t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) + } +} + +// TestAWSGetCredentials_shouldIAM is designed to test the scenario of running Terraform +// from an EC2 instance, without environment variables or manually supplied +// credentials. +func TestAWSGetCredentials_shouldIgnoreIAM(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + ts := awsEnv(t) + defer ts() + simple := []struct { + Key, Secret, Token string + }{ + { + Key: "test", + Secret: "secret", + }, { + Key: "test", + Secret: "test", + Token: "test", + }, + } + + for _, c := range simple { + cfg := Config{ + AccessKey: c.Key, + SecretKey: c.Secret, + Token: c.Token, + } + + creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) + if creds == nil { + t.Fatalf("Expected a static creds provider to be returned") + } + v, err := creds.Get() + if err != nil { + t.Fatalf("Error gettings creds: %s", err) + } + if v.AccessKeyID != c.Key { + t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) + } + if v.SecretAccessKey != c.Secret { + t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) + } + if v.SessionToken != c.Token { + t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) + } + } +} + +func TestAWSGetCredentials_shouldErrorWithInvalidEndpoint(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + ts := invalidAwsEnv(t) + defer ts() + + creds := GetCredentials("", "", "", "", "") + v, err := creds.Get() + if err == nil { + t.Fatal("Expected error returned when getting creds w/ invalid EC2 endpoint") + } + + if v.ProviderName != "" { + t.Fatalf("Expected provider name to be empty, %q given", v.ProviderName) + } +} + +func TestAWSGetCredentials_shouldIgnoreInvalidEndpoint(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + ts := invalidAwsEnv(t) + defer ts() + + creds := GetCredentials("accessKey", "secretKey", "", "", "") + v, err := creds.Get() + if err != nil { + t.Fatalf("Getting static credentials w/ invalid EC2 endpoint failed: %s", err) + } + + if v.ProviderName != "StaticProvider" { + t.Fatalf("Expected provider name to be %q, %q given", "StaticProvider", v.ProviderName) + } + + if v.AccessKeyID != "accessKey" { + t.Fatalf("Static Access Key %q doesn't match: %s", "accessKey", v.AccessKeyID) + } + + if v.SecretAccessKey != "secretKey" { + t.Fatalf("Static Secret Key %q doesn't match: %s", "secretKey", v.SecretAccessKey) + } +} + +func TestAWSGetCredentials_shouldCatchEC2RoleProvider(t *testing.T) { + resetEnv := unsetEnv(t) + defer resetEnv() + // capture the test server's close method, to call after the test returns + ts := awsEnv(t) + defer ts() + + creds := GetCredentials("", "", "", "", "") + if creds == nil { + t.Fatalf("Expected an EC2Role creds provider to be returned") + } + v, err := creds.Get() + if err != nil { + t.Fatalf("Expected no error when getting creds: %s", err) + } + expectedProvider := "EC2RoleProvider" + if v.ProviderName != expectedProvider { + t.Fatalf("Expected provider name to be %q, %q given", + expectedProvider, v.ProviderName) + } +} + +var credentialsFileContents = `[myprofile] +aws_access_key_id = accesskey +aws_secret_access_key = secretkey +` + +func TestAWSGetCredentials_shouldBeShared(t *testing.T) { + file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred") + if err != nil { + t.Fatalf("Error writing temporary credentials file: %s", err) + } + _, err = file.WriteString(credentialsFileContents) + if err != nil { + t.Fatalf("Error writing temporary credentials to file: %s", err) + } + err = file.Close() + if err != nil { + t.Fatalf("Error closing temporary credentials file: %s", err) + } + + defer os.Remove(file.Name()) + + resetEnv := unsetEnv(t) + defer resetEnv() + + if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil { + t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) + } + if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil { + t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) + } + + creds := GetCredentials("", "", "", "myprofile", file.Name()) + if creds == nil { + t.Fatalf("Expected a provider chain to be returned") + } + v, err := creds.Get() + if err != nil { + t.Fatalf("Error gettings creds: %s", err) + } + + if v.AccessKeyID != "accesskey" { + t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) + } + + if v.SecretAccessKey != "secretkey" { + t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) + } +} + +func TestAWSGetCredentials_shouldBeENV(t *testing.T) { + // need to set the environment variables to a dummy string, as we don't know + // what they may be at runtime without hardcoding here + s := "some_env" + resetEnv := setEnv(s, t) + + defer resetEnv() + + cfg := Config{} + creds := GetCredentials(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) + if creds == nil { + t.Fatalf("Expected a static creds provider to be returned") + } + v, err := creds.Get() + if err != nil { + t.Fatalf("Error gettings creds: %s", err) + } + if v.AccessKeyID != s { + t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID) + } + if v.SecretAccessKey != s { + t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey) + } + if v.SessionToken != s { + t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken) + } +} + +// unsetEnv unsets enviornment variables for testing a "clean slate" with no +// credentials in the environment +func unsetEnv(t *testing.T) func() { + // Grab any existing AWS keys and preserve. In some tests we'll unset these, so + // we need to have them and restore them after + e := getEnv() + if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil { + t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err) + } + if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil { + t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err) + } + if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil { + t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err) + } + if err := os.Unsetenv("AWS_PROFILE"); err != nil { + t.Fatalf("Error unsetting env var AWS_TOKEN: %s", err) + } + if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil { + t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) + } + + return func() { + // re-set all the envs we unset above + if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { + t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) + } + if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { + t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) + } + if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { + t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) + } + if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { + t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) + } + if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil { + t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) + } + } +} + +func setEnv(s string, t *testing.T) func() { + e := getEnv() + // Set all the envs to a dummy value + if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil { + t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err) + } + if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil { + t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err) + } + if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil { + t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err) + } + if err := os.Setenv("AWS_PROFILE", s); err != nil { + t.Fatalf("Error setting env var AWS_PROFILE: %s", err) + } + if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { + t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) + } + + return func() { + // re-set all the envs we unset above + if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { + t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) + } + if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { + t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) + } + if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { + t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) + } + if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { + t.Fatalf("Error setting env var AWS_PROFILE: %s", err) + } + if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { + t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) + } + } +} + +// awsEnv establishes a httptest server to mock out the internal AWS Metadata +// service. IAM Credentials are retrieved by the EC2RoleProvider, which makes +// API calls to this internal URL. By replacing the server with a test server, +// we can simulate an AWS environment +func awsEnv(t *testing.T) func() { + routes := routes{} + if err := json.Unmarshal([]byte(metadataApiRoutes), &routes); err != nil { + t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) + } + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/plain") + w.Header().Add("Server", "MockEC2") + log.Printf("[DEBUG] Mocker server received request to %q", r.RequestURI) + for _, e := range routes.Endpoints { + if r.RequestURI == e.Uri { + fmt.Fprintln(w, e.Body) + w.WriteHeader(200) + return + } + } + w.WriteHeader(400) + })) + + os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") + return ts.Close +} + +// invalidAwsEnv establishes a httptest server to simulate behaviour +// when endpoint doesn't respond as expected +func invalidAwsEnv(t *testing.T) func() { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(400) + })) + + os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") + return ts.Close +} + +// getMockedAwsIamApi establishes a httptest server to simulate behaviour +// of a real AWS' IAM server +func getMockedAwsIamApi(endpoints []*iamEndpoint) (func(), *iam.IAM) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + requestBody := buf.String() + + log.Printf("[DEBUG] Received IAM API %q request to %q: %s", + r.Method, r.RequestURI, requestBody) + + for _, e := range endpoints { + if r.Method == e.Request.Method && r.RequestURI == e.Request.Uri && requestBody == e.Request.Body { + log.Printf("[DEBUG] Mock API responding with %d: %s", e.Response.StatusCode, e.Response.Body) + + w.WriteHeader(e.Response.StatusCode) + w.Header().Set("Content-Type", e.Response.ContentType) + w.Header().Set("X-Amzn-Requestid", "1b206dd1-f9a8-11e5-becf-051c60f11c4a") + w.Header().Set("Date", time.Now().Format(time.RFC1123)) + + fmt.Fprintln(w, e.Response.Body) + return + } + } + + w.WriteHeader(400) + return + })) + + sc := awsCredentials.NewStaticCredentials("accessKey", "secretKey", "") + + sess := session.New(&aws.Config{ + Credentials: sc, + Region: aws.String("us-east-1"), + Endpoint: aws.String(ts.URL), + CredentialsChainVerboseErrors: aws.Bool(true), + }) + iamConn := iam.New(sess) + + return ts.Close, iamConn +} + +func getEnv() *currentEnv { + // Grab any existing AWS keys and preserve. In some tests we'll unset these, so + // we need to have them and restore them after + return ¤tEnv{ + Key: os.Getenv("AWS_ACCESS_KEY_ID"), + Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"), + Token: os.Getenv("AWS_SESSION_TOKEN"), + Profile: os.Getenv("AWS_TOKEN"), + CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), + } +} + +// struct to preserve the current environment +type currentEnv struct { + Key, Secret, Token, Profile, CredsFilename string +} + +type routes struct { + Endpoints []*endpoint `json:"endpoints"` +} +type endpoint struct { + Uri string `json:"uri"` + Body string `json:"body"` +} + +const metadataApiRoutes = ` +{ + "endpoints": [ + { + "uri": "/latest/meta-data/instance-id", + "body": "mock-instance-id" + }, + { + "uri": "/latest/meta-data/iam/info", + "body": "{\"Code\": \"Success\",\"LastUpdated\": \"2016-03-17T12:27:32Z\",\"InstanceProfileArn\": \"arn:aws:iam::123456789013:instance-profile/my-instance-profile\",\"InstanceProfileId\": \"AIPAABCDEFGHIJKLMN123\"}" + }, + { + "uri": "/latest/meta-data/iam/security-credentials", + "body": "test_role" + }, + { + "uri": "/latest/meta-data/iam/security-credentials/test_role", + "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}" + } + ] +} +` + +type iamEndpoint struct { + Request *iamRequest + Response *iamResponse +} + +type iamRequest struct { + Method string + Uri string + Body string +} + +type iamResponse struct { + StatusCode int + Body string + ContentType string +} + +const iamResponse_GetUser_valid = ` + + + AIDACKCEVSQ6C2EXAMPLE + /division_abc/subdivision_xyz/ + Bob + arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/Bob + 2013-10-02T17:01:44Z + 2014-10-10T14:37:51Z + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +` + +const iamResponse_GetUser_unauthorized = ` + + Sender + AccessDenied + User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:GetUser on resource: arn:aws:iam::123456789012:user/Bob + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE +` + +const iamResponse_GetUser_federatedFailure = ` + + Sender + ValidationError + Must specify userName when calling with non-User credentials + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE +` + +const iamResponse_ListRoles_valid = ` + + true + AWceSSsKsazQ4IEplT9o4hURCzBs00iavlEvEXAMPLE + + + / + %7B%22Version%22%3A%222008-10-17%22%2C%22Statement%22%3A%5B%7B%22Sid%22%3A%22%22%2C%22Effect%22%3A%22Allow%22%2C%22Principal%22%3A%7B%22Service%22%3A%22ec2.amazonaws.com%22%7D%2C%22Action%22%3A%22sts%3AAssumeRole%22%7D%5D%7D + AROACKCEVSQ6C2EXAMPLE + elasticbeanstalk-role + arn:aws:iam::123456789012:role/elasticbeanstalk-role + 2013-10-02T17:01:44Z + + + + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE + +` + +const iamResponse_ListRoles_unauthorized = ` + + Sender + AccessDenied + User: arn:aws:iam::123456789012:user/Bob is not authorized to perform: iam:ListRoles on resource: arn:aws:iam::123456789012:role/ + + 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE +` diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index f521c755a13a..82a82e016f49 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -4,9 +4,7 @@ import ( "fmt" "log" "net/http" - "os" "strings" - "time" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-multierror" @@ -17,9 +15,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" - awsCredentials "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds" - "github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/apigateway" @@ -133,10 +128,10 @@ func (c *Config) Client() (interface{}, error) { client.region = c.Region log.Println("[INFO] Building AWS auth structure") - creds := getCreds(c.AccessKey, c.SecretKey, c.Token, c.Profile, c.CredsFilename) + creds := GetCredentials(c.AccessKey, c.SecretKey, c.Token, c.Profile, c.CredsFilename) // Call Get to check for credential provider. If nothing found, we'll get an // error, and we can present it nicely to the user - _, err = creds.Get() + cp, err := creds.Get() if err != nil { if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { errs = append(errs, fmt.Errorf(`No valid credential sources found for AWS Provider. @@ -147,6 +142,9 @@ func (c *Config) Client() (interface{}, error) { } return nil, &multierror.Error{Errors: errs} } + + log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) + awsConfig := &aws.Config{ Credentials: creds, Region: aws.String(c.Region), @@ -177,6 +175,7 @@ func (c *Config) Client() (interface{}, error) { err = c.ValidateCredentials(client.iamconn) if err != nil { errs = append(errs, err) + return nil, &multierror.Error{Errors: errs} } // Some services exist only in us-east-1, e.g. because they manage @@ -216,7 +215,7 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing Elastic Beanstalk Connection") client.elasticbeanstalkconn = elasticbeanstalk.New(sess) - authErr := c.ValidateAccountId(client.iamconn) + authErr := c.ValidateAccountId(client.iamconn, cp.ProviderName) if authErr != nil { errs = append(errs, authErr) } @@ -323,7 +322,7 @@ func (c *Config) ValidateCredentials(iamconn *iam.IAM) error { if awsErr, ok := err.(awserr.Error); ok { if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" { - log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile") + log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM role") // User may be an IAM instance profile, or otherwise IAM role without the // GetUser permissions, so fail silently return nil @@ -339,31 +338,17 @@ func (c *Config) ValidateCredentials(iamconn *iam.IAM) error { // ValidateAccountId returns a context-specific error if the configured account // id is explicitly forbidden or not authorised; and nil if it is authorised. -func (c *Config) ValidateAccountId(iamconn *iam.IAM) error { +func (c *Config) ValidateAccountId(iamconn *iam.IAM, authProviderName string) error { if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil { return nil } log.Printf("[INFO] Validating account ID") - - out, err := iamconn.GetUser(nil) - + account_id, err := GetAccountId(iamconn, authProviderName) if err != nil { - awsErr, _ := err.(awserr.Error) - if awsErr.Code() == "ValidationError" { - log.Printf("[WARN] ValidationError with iam.GetUser, assuming its an IAM profile") - // User may be an IAM instance profile, so fail silently. - // If it is an IAM instance profile - // validating account might be superfluous - return nil - } else { - return fmt.Errorf("Failed getting account ID from IAM: %s", err) - // return error if the account id is explicitly not authorised - } + return err } - account_id := strings.Split(*out.User.Arn, ":")[4] - if c.ForbiddenAccountIds != nil { for _, id := range c.ForbiddenAccountIds { if id == account_id { @@ -384,59 +369,6 @@ func (c *Config) ValidateAccountId(iamconn *iam.IAM) error { return nil } -// This function is responsible for reading credentials from the -// environment in the case that they're not explicitly specified -// in the Terraform configuration. -func getCreds(key, secret, token, profile, credsfile string) *awsCredentials.Credentials { - // build a chain provider, lazy-evaulated by aws-sdk - providers := []awsCredentials.Provider{ - &awsCredentials.StaticProvider{Value: awsCredentials.Value{ - AccessKeyID: key, - SecretAccessKey: secret, - SessionToken: token, - }}, - &awsCredentials.EnvProvider{}, - &awsCredentials.SharedCredentialsProvider{ - Filename: credsfile, - Profile: profile, - }, - } - - // We only look in the EC2 metadata API if we can connect - // to the metadata service within a reasonable amount of time - metadataURL := os.Getenv("AWS_METADATA_URL") - if metadataURL == "" { - metadataURL = "http://169.254.169.254:80/latest" - } - c := http.Client{ - Timeout: 100 * time.Millisecond, - } - - r, err := c.Get(metadataURL) - // Flag to determine if we should add the EC2Meta data provider. Default false - var useIAM bool - if err == nil { - // AWS will add a "Server: EC2ws" header value for the metadata request. We - // check the headers for this value to ensure something else didn't just - // happent to be listening on that IP:Port - if r.Header["Server"] != nil && strings.Contains(r.Header["Server"][0], "EC2") { - useIAM = true - } - } - - if useIAM { - log.Printf("[DEBUG] EC2 Metadata service found, adding EC2 Role Credential Provider") - providers = append(providers, &ec2rolecreds.EC2RoleProvider{ - Client: ec2metadata.New(session.New(&aws.Config{ - Endpoint: aws.String(metadataURL), - })), - }) - } else { - log.Printf("[DEBUG] EC2 Metadata service not found, not adding EC2 Role Credential Provider") - } - return awsCredentials.NewChainCredentials(providers) -} - // addTerraformVersionToUserAgent is a named handler that will add Terraform's // version information to requests made by the AWS SDK. var addTerraformVersionToUserAgent = request.NamedHandler{ diff --git a/builtin/providers/aws/config_test.go b/builtin/providers/aws/config_test.go deleted file mode 100644 index 5c58a57290fb..000000000000 --- a/builtin/providers/aws/config_test.go +++ /dev/null @@ -1,376 +0,0 @@ -package aws - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/aws/aws-sdk-go/aws/awserr" -) - -func TestAWSConfig_shouldError(t *testing.T) { - resetEnv := unsetEnv(t) - defer resetEnv() - cfg := Config{} - - c := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) - _, err := c.Get() - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() != "NoCredentialProviders" { - t.Fatalf("Expected NoCredentialProviders error") - } - } - if err == nil { - t.Fatalf("Expected an error with empty env, keys, and IAM in AWS Config") - } -} - -func TestAWSConfig_shouldBeStatic(t *testing.T) { - simple := []struct { - Key, Secret, Token string - }{ - { - Key: "test", - Secret: "secret", - }, { - Key: "test", - Secret: "test", - Token: "test", - }, - } - - for _, c := range simple { - cfg := Config{ - AccessKey: c.Key, - SecretKey: c.Secret, - Token: c.Token, - } - - creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) - if creds == nil { - t.Fatalf("Expected a static creds provider to be returned") - } - v, err := creds.Get() - if err != nil { - t.Fatalf("Error gettings creds: %s", err) - } - if v.AccessKeyID != c.Key { - t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) - } - if v.SecretAccessKey != c.Secret { - t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) - } - if v.SessionToken != c.Token { - t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) - } - } -} - -// TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform -// from an EC2 instance, without environment variables or manually supplied -// credentials. -func TestAWSConfig_shouldIAM(t *testing.T) { - // clear AWS_* environment variables - resetEnv := unsetEnv(t) - defer resetEnv() - - // capture the test server's close method, to call after the test returns - ts := awsEnv(t) - defer ts() - - // An empty config, no key supplied - cfg := Config{} - - creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) - if creds == nil { - t.Fatalf("Expected a static creds provider to be returned") - } - - v, err := creds.Get() - if err != nil { - t.Fatalf("Error gettings creds: %s", err) - } - if v.AccessKeyID != "somekey" { - t.Fatalf("AccessKeyID mismatch, expected: (somekey), got (%s)", v.AccessKeyID) - } - if v.SecretAccessKey != "somesecret" { - t.Fatalf("SecretAccessKey mismatch, expected: (somesecret), got (%s)", v.SecretAccessKey) - } - if v.SessionToken != "sometoken" { - t.Fatalf("SessionToken mismatch, expected: (sometoken), got (%s)", v.SessionToken) - } -} - -// TestAWSConfig_shouldIAM is designed to test the scenario of running Terraform -// from an EC2 instance, without environment variables or manually supplied -// credentials. -func TestAWSConfig_shouldIgnoreIAM(t *testing.T) { - resetEnv := unsetEnv(t) - defer resetEnv() - // capture the test server's close method, to call after the test returns - ts := awsEnv(t) - defer ts() - simple := []struct { - Key, Secret, Token string - }{ - { - Key: "test", - Secret: "secret", - }, { - Key: "test", - Secret: "test", - Token: "test", - }, - } - - for _, c := range simple { - cfg := Config{ - AccessKey: c.Key, - SecretKey: c.Secret, - Token: c.Token, - } - - creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) - if creds == nil { - t.Fatalf("Expected a static creds provider to be returned") - } - v, err := creds.Get() - if err != nil { - t.Fatalf("Error gettings creds: %s", err) - } - if v.AccessKeyID != c.Key { - t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", c.Key, v.AccessKeyID) - } - if v.SecretAccessKey != c.Secret { - t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", c.Secret, v.SecretAccessKey) - } - if v.SessionToken != c.Token { - t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", c.Token, v.SessionToken) - } - } -} - -var credentialsFileContents = `[myprofile] -aws_access_key_id = accesskey -aws_secret_access_key = secretkey -` - -func TestAWSConfig_shouldBeShared(t *testing.T) { - file, err := ioutil.TempFile(os.TempDir(), "terraform_aws_cred") - if err != nil { - t.Fatalf("Error writing temporary credentials file: %s", err) - } - _, err = file.WriteString(credentialsFileContents) - if err != nil { - t.Fatalf("Error writing temporary credentials to file: %s", err) - } - err = file.Close() - if err != nil { - t.Fatalf("Error closing temporary credentials file: %s", err) - } - - defer os.Remove(file.Name()) - - resetEnv := unsetEnv(t) - defer resetEnv() - - if err := os.Setenv("AWS_PROFILE", "myprofile"); err != nil { - t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) - } - if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", file.Name()); err != nil { - t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) - } - - creds := getCreds("", "", "", "myprofile", file.Name()) - if creds == nil { - t.Fatalf("Expected a provider chain to be returned") - } - v, err := creds.Get() - if err != nil { - t.Fatalf("Error gettings creds: %s", err) - } - - if v.AccessKeyID != "accesskey" { - t.Fatalf("AccessKeyID mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) - } - - if v.SecretAccessKey != "secretkey" { - t.Fatalf("SecretAccessKey mismatch, expected (%s), got (%s)", "accesskey", v.AccessKeyID) - } -} - -func TestAWSConfig_shouldBeENV(t *testing.T) { - // need to set the environment variables to a dummy string, as we don't know - // what they may be at runtime without hardcoding here - s := "some_env" - resetEnv := setEnv(s, t) - - defer resetEnv() - - cfg := Config{} - creds := getCreds(cfg.AccessKey, cfg.SecretKey, cfg.Token, cfg.Profile, cfg.CredsFilename) - if creds == nil { - t.Fatalf("Expected a static creds provider to be returned") - } - v, err := creds.Get() - if err != nil { - t.Fatalf("Error gettings creds: %s", err) - } - if v.AccessKeyID != s { - t.Fatalf("AccessKeyID mismatch, expected: (%s), got (%s)", s, v.AccessKeyID) - } - if v.SecretAccessKey != s { - t.Fatalf("SecretAccessKey mismatch, expected: (%s), got (%s)", s, v.SecretAccessKey) - } - if v.SessionToken != s { - t.Fatalf("SessionToken mismatch, expected: (%s), got (%s)", s, v.SessionToken) - } -} - -// unsetEnv unsets enviornment variables for testing a "clean slate" with no -// credentials in the environment -func unsetEnv(t *testing.T) func() { - // Grab any existing AWS keys and preserve. In some tests we'll unset these, so - // we need to have them and restore them after - e := getEnv() - if err := os.Unsetenv("AWS_ACCESS_KEY_ID"); err != nil { - t.Fatalf("Error unsetting env var AWS_ACCESS_KEY_ID: %s", err) - } - if err := os.Unsetenv("AWS_SECRET_ACCESS_KEY"); err != nil { - t.Fatalf("Error unsetting env var AWS_SECRET_ACCESS_KEY: %s", err) - } - if err := os.Unsetenv("AWS_SESSION_TOKEN"); err != nil { - t.Fatalf("Error unsetting env var AWS_SESSION_TOKEN: %s", err) - } - if err := os.Unsetenv("AWS_PROFILE"); err != nil { - t.Fatalf("Error unsetting env var AWS_TOKEN: %s", err) - } - if err := os.Unsetenv("AWS_SHARED_CREDENTIALS_FILE"); err != nil { - t.Fatalf("Error unsetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) - } - - return func() { - // re-set all the envs we unset above - if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { - t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) - } - if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { - t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) - } - if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { - t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) - } - if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { - t.Fatalf("Error resetting env var AWS_PROFILE: %s", err) - } - if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", e.CredsFilename); err != nil { - t.Fatalf("Error resetting env var AWS_SHARED_CREDENTIALS_FILE: %s", err) - } - } -} - -func setEnv(s string, t *testing.T) func() { - e := getEnv() - // Set all the envs to a dummy value - if err := os.Setenv("AWS_ACCESS_KEY_ID", s); err != nil { - t.Fatalf("Error setting env var AWS_ACCESS_KEY_ID: %s", err) - } - if err := os.Setenv("AWS_SECRET_ACCESS_KEY", s); err != nil { - t.Fatalf("Error setting env var AWS_SECRET_ACCESS_KEY: %s", err) - } - if err := os.Setenv("AWS_SESSION_TOKEN", s); err != nil { - t.Fatalf("Error setting env var AWS_SESSION_TOKEN: %s", err) - } - if err := os.Setenv("AWS_PROFILE", s); err != nil { - t.Fatalf("Error setting env var AWS_PROFILE: %s", err) - } - if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { - t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) - } - - return func() { - // re-set all the envs we unset above - if err := os.Setenv("AWS_ACCESS_KEY_ID", e.Key); err != nil { - t.Fatalf("Error resetting env var AWS_ACCESS_KEY_ID: %s", err) - } - if err := os.Setenv("AWS_SECRET_ACCESS_KEY", e.Secret); err != nil { - t.Fatalf("Error resetting env var AWS_SECRET_ACCESS_KEY: %s", err) - } - if err := os.Setenv("AWS_SESSION_TOKEN", e.Token); err != nil { - t.Fatalf("Error resetting env var AWS_SESSION_TOKEN: %s", err) - } - if err := os.Setenv("AWS_PROFILE", e.Profile); err != nil { - t.Fatalf("Error setting env var AWS_PROFILE: %s", err) - } - if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", s); err != nil { - t.Fatalf("Error setting env var AWS_SHARED_CREDENTIALS_FLE: %s", err) - } - } -} - -// awsEnv establishes a httptest server to mock out the internal AWS Metadata -// service. IAM Credentials are retrieved by the EC2RoleProvider, which makes -// API calls to this internal URL. By replacing the server with a test server, -// we can simulate an AWS environment -func awsEnv(t *testing.T) func() { - routes := routes{} - if err := json.Unmarshal([]byte(aws_routes), &routes); err != nil { - t.Fatalf("Failed to unmarshal JSON in AWS ENV test: %s", err) - } - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/plain") - w.Header().Add("Server", "MockEC2") - for _, e := range routes.Endpoints { - if r.RequestURI == e.Uri { - fmt.Fprintln(w, e.Body) - } - } - })) - - os.Setenv("AWS_METADATA_URL", ts.URL+"/latest") - return ts.Close -} - -func getEnv() *currentEnv { - // Grab any existing AWS keys and preserve. In some tests we'll unset these, so - // we need to have them and restore them after - return ¤tEnv{ - Key: os.Getenv("AWS_ACCESS_KEY_ID"), - Secret: os.Getenv("AWS_SECRET_ACCESS_KEY"), - Token: os.Getenv("AWS_SESSION_TOKEN"), - Profile: os.Getenv("AWS_TOKEN"), - CredsFilename: os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), - } -} - -// struct to preserve the current environment -type currentEnv struct { - Key, Secret, Token, Profile, CredsFilename string -} - -type routes struct { - Endpoints []*endpoint `json:"endpoints"` -} -type endpoint struct { - Uri string `json:"uri"` - Body string `json:"body"` -} - -const aws_routes = ` -{ - "endpoints": [ - { - "uri": "/latest/meta-data/iam/security-credentials", - "body": "test_role" - }, - { - "uri": "/latest/meta-data/iam/security-credentials/test_role", - "body": "{\"Code\":\"Success\",\"LastUpdated\":\"2015-12-11T17:17:25Z\",\"Type\":\"AWS-HMAC\",\"AccessKeyId\":\"somekey\",\"SecretAccessKey\":\"somesecret\",\"Token\":\"sometoken\"}" - } - ] -} -` diff --git a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go index 83badef7cc81..f664caf0946e 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/client/default_retryer.go @@ -32,19 +32,37 @@ func (d DefaultRetryer) MaxRetries() int { // RetryRules returns the delay duration before retrying this request again func (d DefaultRetryer) RetryRules(r *request.Request) time.Duration { // Set the upper limit of delay in retrying at ~five minutes + minTime := 30 + throttle := d.shouldThrottle(r) + if throttle { + minTime = 1000 + } + retryCount := r.RetryCount if retryCount > 13 { retryCount = 13 + } else if throttle && retryCount > 8 { + retryCount = 8 } - delay := (1 << uint(retryCount)) * (rand.Intn(30) + 30) + delay := (1 << uint(retryCount)) * (rand.Intn(30) + minTime) return time.Duration(delay) * time.Millisecond } -// ShouldRetry returns if the request should be retried. +// ShouldRetry returns true if the request should be retried. func (d DefaultRetryer) ShouldRetry(r *request.Request) bool { if r.HTTPResponse.StatusCode >= 500 { return true } - return r.IsErrorRetryable() + return r.IsErrorRetryable() || d.shouldThrottle(r) +} + +// ShouldThrottle returns true if the request should be throttled. +func (d DefaultRetryer) shouldThrottle(r *request.Request) bool { + if r.HTTPResponse.StatusCode == 502 || + r.HTTPResponse.StatusCode == 503 || + r.HTTPResponse.StatusCode == 504 { + return true + } + return r.IsErrorThrottle() } diff --git a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go index 8edcfc926bd9..669c813a00d5 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/ec2metadata/api.go @@ -2,6 +2,7 @@ package ec2metadata import ( "encoding/json" + "fmt" "path" "strings" "time" @@ -49,7 +50,7 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument resp, err := c.GetDynamicData("instance-identity/document") if err != nil { return EC2InstanceIdentityDocument{}, - awserr.New("EC2RoleRequestError", + awserr.New("EC2MetadataRequestError", "failed to get EC2 instance identity document", err) } @@ -63,6 +64,31 @@ func (c *EC2Metadata) GetInstanceIdentityDocument() (EC2InstanceIdentityDocument return doc, nil } +// IAMInfo retrieves IAM info from the metadata API +func (c *EC2Metadata) IAMInfo() (EC2IAMInfo, error) { + resp, err := c.GetMetadata("iam/info") + if err != nil { + return EC2IAMInfo{}, + awserr.New("EC2MetadataRequestError", + "failed to get EC2 IAM info", err) + } + + info := EC2IAMInfo{} + if err := json.NewDecoder(strings.NewReader(resp)).Decode(&info); err != nil { + return EC2IAMInfo{}, + awserr.New("SerializationError", + "failed to decode EC2 IAM info", err) + } + + if info.Code != "Success" { + errMsg := fmt.Sprintf("failed to get EC2 IAM Info (%s)", info.Code) + return EC2IAMInfo{}, + awserr.New("EC2MetadataError", errMsg, nil) + } + + return info, nil +} + // Region returns the region the instance is running in. func (c *EC2Metadata) Region() (string, error) { resp, err := c.GetMetadata("placement/availability-zone") @@ -85,6 +111,15 @@ func (c *EC2Metadata) Available() bool { return true } +// An EC2IAMInfo provides the shape for unmarshalling +// an IAM info from the metadata API +type EC2IAMInfo struct { + Code string + LastUpdated time.Time + InstanceProfileArn string + InstanceProfileID string +} + // An EC2InstanceIdentityDocument provides the shape for unmarshalling // an instance identity document type EC2InstanceIdentityDocument struct { diff --git a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go index ab6fff5ac842..8cc8b015ae6c 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/request/retryer.go @@ -26,8 +26,11 @@ func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config { // retryableCodes is a collection of service response codes which are retry-able // without any further action. var retryableCodes = map[string]struct{}{ - "RequestError": {}, - "RequestTimeout": {}, + "RequestError": {}, + "RequestTimeout": {}, +} + +var throttleCodes = map[string]struct{}{ "ProvisionedThroughputExceededException": {}, "Throttling": {}, "ThrottlingException": {}, @@ -46,6 +49,11 @@ var credsExpiredCodes = map[string]struct{}{ "RequestExpired": {}, // EC2 Only } +func isCodeThrottle(code string) bool { + _, ok := throttleCodes[code] + return ok +} + func isCodeRetryable(code string) bool { if _, ok := retryableCodes[code]; ok { return true @@ -70,6 +78,17 @@ func (r *Request) IsErrorRetryable() bool { return false } +// IsErrorThrottle returns whether the error is to be throttled based on its code. +// Returns false if the request has no Error set +func (r *Request) IsErrorThrottle() bool { + if r.Error != nil { + if err, ok := r.Error.(awserr.Error); ok { + return isCodeThrottle(err.Code()) + } + } + return false +} + // IsErrorExpired returns whether the error code is a credential expiry error. // Returns false if the request has no Error set. func (r *Request) IsErrorExpired() bool { diff --git a/vendor/github.com/aws/aws-sdk-go/aws/types.go b/vendor/github.com/aws/aws-sdk-go/aws/types.go index 0f067c57f4e2..fa014b49e1d7 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/types.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/types.go @@ -61,23 +61,41 @@ func (r ReaderSeekerCloser) Close() error { type WriteAtBuffer struct { buf []byte m sync.Mutex + + // GrowthCoeff defines the growth rate of the internal buffer. By + // default, the growth rate is 1, where expanding the internal + // buffer will allocate only enough capacity to fit the new expected + // length. + GrowthCoeff float64 +} + +// NewWriteAtBuffer creates a WriteAtBuffer with an internal buffer +// provided by buf. +func NewWriteAtBuffer(buf []byte) *WriteAtBuffer { + return &WriteAtBuffer{buf: buf} } // WriteAt writes a slice of bytes to a buffer starting at the position provided // The number of bytes written will be returned, or error. Can overwrite previous // written slices if the write ats overlap. func (b *WriteAtBuffer) WriteAt(p []byte, pos int64) (n int, err error) { + pLen := len(p) + expLen := pos + int64(pLen) b.m.Lock() defer b.m.Unlock() - - expLen := pos + int64(len(p)) if int64(len(b.buf)) < expLen { - newBuf := make([]byte, expLen) - copy(newBuf, b.buf) - b.buf = newBuf + if int64(cap(b.buf)) < expLen { + if b.GrowthCoeff < 1 { + b.GrowthCoeff = 1 + } + newBuf := make([]byte, expLen, int64(b.GrowthCoeff*float64(expLen))) + copy(newBuf, b.buf) + b.buf = newBuf + } + b.buf = b.buf[:expLen] } copy(b.buf[pos:], p) - return len(p), nil + return pLen, nil } // Bytes returns a slice of bytes written to the buffer. diff --git a/vendor/github.com/aws/aws-sdk-go/aws/version.go b/vendor/github.com/aws/aws-sdk-go/aws/version.go index 458620f88b4e..805dc711dbb0 100644 --- a/vendor/github.com/aws/aws-sdk-go/aws/version.go +++ b/vendor/github.com/aws/aws-sdk-go/aws/version.go @@ -5,4 +5,4 @@ package aws const SDKName = "aws-sdk-go" // SDKVersion is the version of this SDK -const SDKVersion = "1.1.14" +const SDKVersion = "1.1.15" diff --git a/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go index 42ffccb852dc..42595d2178b0 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go +++ b/vendor/github.com/aws/aws-sdk-go/service/autoscaling/waiters.go @@ -14,15 +14,15 @@ func (c *AutoScaling) WaitUntilGroupExists(input *DescribeAutoScalingGroupsInput Acceptors: []waiter.WaitAcceptor{ { State: "success", - Matcher: "pathAll", - Argument: "length(AutoScalingGroups)", - Expected: 1, + Matcher: "path", + Argument: "length(AutoScalingGroups) > `0`", + Expected: true, }, { State: "retry", - Matcher: "pathAll", - Argument: "length(AutoScalingGroups)", - Expected: 0, + Matcher: "path", + Argument: "length(AutoScalingGroups) > `0`", + Expected: false, }, }, } @@ -43,13 +43,13 @@ func (c *AutoScaling) WaitUntilGroupInService(input *DescribeAutoScalingGroupsIn Acceptors: []waiter.WaitAcceptor{ { State: "success", - Matcher: "pathAll", + Matcher: "path", Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)", Expected: false, }, { State: "retry", - Matcher: "pathAll", + Matcher: "path", Argument: "contains(AutoScalingGroups[].[length(Instances[?LifecycleState=='InService']) >= MinSize][], `false`)", Expected: true, }, @@ -72,15 +72,15 @@ func (c *AutoScaling) WaitUntilGroupNotExists(input *DescribeAutoScalingGroupsIn Acceptors: []waiter.WaitAcceptor{ { State: "success", - Matcher: "pathAll", - Argument: "length(AutoScalingGroups)", - Expected: 0, + Matcher: "path", + Argument: "length(AutoScalingGroups) > `0`", + Expected: false, }, { State: "retry", - Matcher: "pathAll", - Argument: "length(AutoScalingGroups)", - Expected: 1, + Matcher: "path", + Argument: "length(AutoScalingGroups) > `0`", + Expected: true, }, }, } diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go index ca79c979cfa2..7ace19458c8a 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/api.go @@ -83,6 +83,46 @@ func (c *CloudFormation) ContinueUpdateRollback(input *ContinueUpdateRollbackInp return out, err } +const opCreateChangeSet = "CreateChangeSet" + +// CreateChangeSetRequest generates a request for the CreateChangeSet operation. +func (c *CloudFormation) CreateChangeSetRequest(input *CreateChangeSetInput) (req *request.Request, output *CreateChangeSetOutput) { + op := &request.Operation{ + Name: opCreateChangeSet, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &CreateChangeSetInput{} + } + + req = c.newRequest(op, input, output) + output = &CreateChangeSetOutput{} + req.Data = output + return +} + +// Creates a list of changes for a stack. AWS CloudFormation generates the change +// set by comparing the stack's information with the information that you submit. +// A change set can help you understand which resources AWS CloudFormation will +// change and how it will change them before you update your stack. Change sets +// allow you to check before you make a change so that you don't delete or replace +// critical resources. +// +// AWS CloudFormation doesn't make any changes to the stack when you create +// a change set. To make the specified changes, you must execute the change +// set by using the ExecuteChangeSet action. +// +// After the call successfully completes, AWS CloudFormation starts creating +// the change set. To check the status of the change set, use the DescribeChangeSet +// action. +func (c *CloudFormation) CreateChangeSet(input *CreateChangeSetInput) (*CreateChangeSetOutput, error) { + req, out := c.CreateChangeSetRequest(input) + err := req.Send() + return out, err +} + const opCreateStack = "CreateStack" // CreateStackRequest generates a request for the CreateStack operation. @@ -112,6 +152,37 @@ func (c *CloudFormation) CreateStack(input *CreateStackInput) (*CreateStackOutpu return out, err } +const opDeleteChangeSet = "DeleteChangeSet" + +// DeleteChangeSetRequest generates a request for the DeleteChangeSet operation. +func (c *CloudFormation) DeleteChangeSetRequest(input *DeleteChangeSetInput) (req *request.Request, output *DeleteChangeSetOutput) { + op := &request.Operation{ + Name: opDeleteChangeSet, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &DeleteChangeSetInput{} + } + + req = c.newRequest(op, input, output) + output = &DeleteChangeSetOutput{} + req.Data = output + return +} + +// Deletes the specified change set. Deleting change sets ensures that no one +// executes the wrong change set. +// +// If the call successfully completes, AWS CloudFormation successfully deleted +// the change set. +func (c *CloudFormation) DeleteChangeSet(input *DeleteChangeSetInput) (*DeleteChangeSetOutput, error) { + req, out := c.DeleteChangeSetRequest(input) + err := req.Send() + return out, err +} + const opDeleteStack = "DeleteStack" // DeleteStackRequest generates a request for the DeleteStack operation. @@ -171,6 +242,36 @@ func (c *CloudFormation) DescribeAccountLimits(input *DescribeAccountLimitsInput return out, err } +const opDescribeChangeSet = "DescribeChangeSet" + +// DescribeChangeSetRequest generates a request for the DescribeChangeSet operation. +func (c *CloudFormation) DescribeChangeSetRequest(input *DescribeChangeSetInput) (req *request.Request, output *DescribeChangeSetOutput) { + op := &request.Operation{ + Name: opDescribeChangeSet, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &DescribeChangeSetInput{} + } + + req = c.newRequest(op, input, output) + output = &DescribeChangeSetOutput{} + req.Data = output + return +} + +// Returns the inputs for the change set and a list of changes that AWS CloudFormation +// will make if you execute the change set. For more information, see Updating +// Stacks Using Change Sets (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-changesets.html) +// in the AWS CloudFormation User Guide. +func (c *CloudFormation) DescribeChangeSet(input *DescribeChangeSetInput) (*DescribeChangeSetOutput, error) { + req, out := c.DescribeChangeSetRequest(input) + err := req.Send() + return out, err +} + const opDescribeStackEvents = "DescribeStackEvents" // DescribeStackEventsRequest generates a request for the DescribeStackEvents operation. @@ -361,6 +462,44 @@ func (c *CloudFormation) EstimateTemplateCost(input *EstimateTemplateCostInput) return out, err } +const opExecuteChangeSet = "ExecuteChangeSet" + +// ExecuteChangeSetRequest generates a request for the ExecuteChangeSet operation. +func (c *CloudFormation) ExecuteChangeSetRequest(input *ExecuteChangeSetInput) (req *request.Request, output *ExecuteChangeSetOutput) { + op := &request.Operation{ + Name: opExecuteChangeSet, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &ExecuteChangeSetInput{} + } + + req = c.newRequest(op, input, output) + output = &ExecuteChangeSetOutput{} + req.Data = output + return +} + +// Updates a stack using the input information that was provided when the specified +// change set was created. After the call successfully completes, AWS CloudFormation +// starts updating the stack. Use the DescribeStacks action to view the status +// of the update. +// +// When you execute a change set, AWS CloudFormation deletes all other change +// sets associated with the stack because they aren't valid for the updated +// stack. +// +// If a stack policy is associated with the stack, AWS CloudFormation enforces +// the policy during the update. You can't specify a temporary stack policy +// that overrides the current policy. +func (c *CloudFormation) ExecuteChangeSet(input *ExecuteChangeSetInput) (*ExecuteChangeSetOutput, error) { + req, out := c.ExecuteChangeSetRequest(input) + err := req.Send() + return out, err +} + const opGetStackPolicy = "GetStackPolicy" // GetStackPolicyRequest generates a request for the GetStackPolicy operation. @@ -458,6 +597,35 @@ func (c *CloudFormation) GetTemplateSummary(input *GetTemplateSummaryInput) (*Ge return out, err } +const opListChangeSets = "ListChangeSets" + +// ListChangeSetsRequest generates a request for the ListChangeSets operation. +func (c *CloudFormation) ListChangeSetsRequest(input *ListChangeSetsInput) (req *request.Request, output *ListChangeSetsOutput) { + op := &request.Operation{ + Name: opListChangeSets, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &ListChangeSetsInput{} + } + + req = c.newRequest(op, input, output) + output = &ListChangeSetsOutput{} + req.Data = output + return +} + +// Returns the ID and status of each active change set for a stack. For example, +// AWS CloudFormation lists change sets that are in the CREATE_IN_PROGRESS or +// CREATE_PENDING state. +func (c *CloudFormation) ListChangeSets(input *ListChangeSetsInput) (*ListChangeSetsOutput, error) { + req, out := c.ListChangeSetsRequest(input) + err := req.Send() + return out, err +} + const opListStackResources = "ListStackResources" // ListStackResourcesRequest generates a request for the ListStackResources operation. @@ -725,6 +893,72 @@ func (s CancelUpdateStackOutput) GoString() string { return s.String() } +// The Change structure describes the changes AWS CloudFormation will perform +// if you execute the change set. +type Change struct { + _ struct{} `type:"structure"` + + // A ResourceChange structure that describes the resource and action that AWS + // CloudFormation will perform. + ResourceChange *ResourceChange `type:"structure"` + + // The type of entity that AWS CloudFormation changes. Currently, the only entity + // type is Resource. + Type *string `type:"string" enum:"ChangeType"` +} + +// String returns the string representation +func (s Change) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s Change) GoString() string { + return s.String() +} + +// The ChangeSetSummary structure describes a change set, its status, and the +// stack with which it's associated. +type ChangeSetSummary struct { + _ struct{} `type:"structure"` + + // The ID of the change set. + ChangeSetId *string `min:"1" type:"string"` + + // The name of the change set. + ChangeSetName *string `min:"1" type:"string"` + + // The start time when the change set was created, in UTC. + CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601"` + + // Descriptive information about the change set. + Description *string `min:"1" type:"string"` + + // The ID of the stack with which the change set is associated. + StackId *string `type:"string"` + + // The name of the stack with which the change set is associated. + StackName *string `type:"string"` + + // The state of the change set, such as CREATE_IN_PROGRESS, CREATE_COMPLETE, + // or FAILED. + Status *string `type:"string" enum:"ChangeSetStatus"` + + // A description of the change set's status. For example, if your change set + // is in the FAILED state, AWS CloudFormation shows the error message. + StatusReason *string `type:"string"` +} + +// String returns the string representation +func (s ChangeSetSummary) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ChangeSetSummary) GoString() string { + return s.String() +} + // The input for the ContinueUpdateRollback action. type ContinueUpdateRollbackInput struct { _ struct{} `type:"structure"` @@ -759,13 +993,137 @@ func (s ContinueUpdateRollbackOutput) GoString() string { return s.String() } +// The input for the CreateChangeSet action. +type CreateChangeSetInput struct { + _ struct{} `type:"structure"` + + // A list of capabilities that you must specify before AWS CloudFormation can + // update certain stacks. Some stack templates might include resources that + // can affect permissions in your AWS account, for example, by creating new + // AWS Identity and Access Management (IAM) users. For those stacks, you must + // explicitly acknowledge their capabilities by specifying this parameter. + // + // Currently, the only valid value is CAPABILITY_IAM, which is required for + // the following resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html), + // AWS::IAM::Group (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html), + // AWS::IAM::InstanceProfile (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html), + // AWS::IAM::Policy (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html), + // AWS::IAM::Role (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html), + // AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html), + // and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html). + // If your stack template contains these resources, we recommend that you review + // all permissions associated with them and edit their permissions if necessary. + // If your template contains any of the listed resources and you don't specify + // this parameter, this action returns an InsufficientCapabilities error. + Capabilities []*string `type:"list"` + + // The name of the change set. The name must be unique among all change sets + // that are associated with the specified stack. + // + // A change set name can contain only alphanumeric, case sensitive characters + // and hyphens. It must start with an alphabetic character and cannot exceed + // 128 characters. + ChangeSetName *string `min:"1" type:"string" required:"true"` + + // A unique identifier for this CreateChangeSet request. Specify this token + // if you plan to retry requests so that AWS CloudFormation knows that you're + // not attempting to create another change set with the same name. You might + // retry CreateChangeSet requests to ensure that AWS CloudFormation successfully + // received them. + ClientToken *string `min:"1" type:"string"` + + // A description to help you identify this change set. + Description *string `min:"1" type:"string"` + + // The Amazon Resource Names (ARNs) of Amazon Simple Notification Service (Amazon + // SNS) topics that AWS CloudFormation associates with the stack. To remove + // all associated notification topics, specify an empty list. + NotificationARNs []*string `type:"list"` + + // A list of Parameter structures that specify input parameters for the change + // set. For more information, see the Parameter (http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Parameter.html) + // data type. + Parameters []*Parameter `type:"list"` + + // The template resource types that you have permissions to work with if you + // execute this change set, such as AWS::EC2::Instance, AWS::EC2::*, or Custom::MyCustomInstance. + // + // If the list of resource types doesn't include a resource type that you're + // updating, the stack update fails. By default, AWS CloudFormation grants permissions + // to all resource types. AWS Identity and Access Management (IAM) uses this + // parameter for condition keys in IAM policies for AWS CloudFormation. For + // more information, see Controlling Access with AWS Identity and Access Management + // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-iam-template.html) + // in the AWS CloudFormation User Guide. + ResourceTypes []*string `type:"list"` + + // The name or the unique ID of the stack for which you are creating a change + // set. AWS CloudFormation generates the change set by comparing this stack's + // information with the information that you submit, such as a modified template + // or different parameter input values. + StackName *string `min:"1" type:"string" required:"true"` + + // Key-value pairs to associate with this stack. AWS CloudFormation also propagates + // these tags to resources in the stack. You can specify a maximum of 10 tags. + Tags []*Tag `type:"list"` + + // A structure that contains the body of the revised template, with a minimum + // length of 1 byte and a maximum length of 51,200 bytes. AWS CloudFormation + // generates the change set by comparing this template with the template of + // the stack that you specified. + // + // Conditional: You must specify only TemplateBody or TemplateURL. + TemplateBody *string `min:"1" type:"string"` + + // The location of the file that contains the revised template. The URL must + // point to a template (max size: 460,800 bytes) that is located in an S3 bucket. + // AWS CloudFormation generates the change set by comparing this template with + // the stack that you specified. + // + // Conditional: You must specify only TemplateBody or TemplateURL. + TemplateURL *string `min:"1" type:"string"` + + // Whether to reuse the template that is associated with the stack to create + // the change set. + UsePreviousTemplate *bool `type:"boolean"` +} + +// String returns the string representation +func (s CreateChangeSetInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateChangeSetInput) GoString() string { + return s.String() +} + +// The output for the CreateChangeSet action. +type CreateChangeSetOutput struct { + _ struct{} `type:"structure"` + + // The Amazon Resource Name (ARN) of the change set. + Id *string `min:"1" type:"string"` +} + +// String returns the string representation +func (s CreateChangeSetOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s CreateChangeSetOutput) GoString() string { + return s.String() +} + // The input for CreateStack action. type CreateStackInput struct { _ struct{} `type:"structure"` // A list of capabilities that you must specify before AWS CloudFormation can - // create or update certain stacks. Some stack templates might include resources - // that can affect permissions in your AWS account. For those stacks, you must + // create certain stacks. Some stack templates might include resources that + // can affect permissions in your AWS account, for example, by creating new + // AWS Identity and Access Management (IAM) users. For those stacks, you must // explicitly acknowledge their capabilities by specifying this parameter. // // Currently, the only valid value is CAPABILITY_IAM, which is required for @@ -777,8 +1135,9 @@ type CreateStackInput struct { // AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html), // and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html). // If your stack template contains these resources, we recommend that you review - // any permissions associated with them. If you don't specify this parameter, - // this action returns an InsufficientCapabilities error. + // all permissions associated with them and edit their permissions if necessary. + // If your template contains any of the listed resources and you don't specify + // this parameter, this action returns an InsufficientCapabilities error. Capabilities []*string `type:"list"` // Set to true to disable rollback of the stack if stack creation failed. You @@ -897,6 +1256,44 @@ func (s CreateStackOutput) GoString() string { return s.String() } +// The input for the DeleteChangeSet action. +type DeleteChangeSetInput struct { + _ struct{} `type:"structure"` + + // The name or Amazon Resource Name (ARN) of the change set that you want to + // delete. + ChangeSetName *string `min:"1" type:"string" required:"true"` + + // If you specified the name of a change set to delete, specify the stack name + // or ID (ARN) that is associated with it. + StackName *string `min:"1" type:"string"` +} + +// String returns the string representation +func (s DeleteChangeSetInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteChangeSetInput) GoString() string { + return s.String() +} + +// The output for the DeleteChangeSet action. +type DeleteChangeSetOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s DeleteChangeSetOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DeleteChangeSetOutput) GoString() string { + return s.String() +} + // The input for DeleteStack action. type DeleteStackInput struct { _ struct{} `type:"structure"` @@ -978,6 +1375,100 @@ func (s DescribeAccountLimitsOutput) GoString() string { return s.String() } +// The input for the DescribeChangeSet action. +type DescribeChangeSetInput struct { + _ struct{} `type:"structure"` + + // The name or Amazon Resource Name (ARN) of the change set that you want to + // describe. + ChangeSetName *string `min:"1" type:"string" required:"true"` + + // A string (provided by the DescribeChangeSet response output) that identifies + // the next page of information that you want to retrieve. + NextToken *string `min:"1" type:"string"` + + // If you specified the name of a change set, specify the stack name or ID (ARN) + // of the change set you want to describe. + StackName *string `min:"1" type:"string"` +} + +// String returns the string representation +func (s DescribeChangeSetInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DescribeChangeSetInput) GoString() string { + return s.String() +} + +// The output for the DescribeChangeSet action. +type DescribeChangeSetOutput struct { + _ struct{} `type:"structure"` + + // If you execute the change set, the list of capabilities that were explicitly + // acknowledged when the change set was created. + Capabilities []*string `type:"list"` + + // The ARN of the change set. + ChangeSetId *string `min:"1" type:"string"` + + // The name of the change set. + ChangeSetName *string `min:"1" type:"string"` + + // A list of Change structures that describes the resources AWS CloudFormation + // changes if you execute the change set. + Changes []*Change `type:"list"` + + // The start time when the change set was created, in UTC. + CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601"` + + // Information about the change set. + Description *string `min:"1" type:"string"` + + // If the output exceeds 1 MB, a string that identifies the next page of changes. + // If there is no additional page, this value is null. + NextToken *string `min:"1" type:"string"` + + // The ARNs of the Amazon Simple Notification Service (Amazon SNS) topics that + // will be associated with the stack if you execute the change set. + NotificationARNs []*string `type:"list"` + + // A list of Parameter structures that describes the input parameters and their + // values used to create the change set. For more information, see the Parameter + // (http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_Parameter.html) + // data type. + Parameters []*Parameter `type:"list"` + + // The ARN of the stack that is associated with the change set. + StackId *string `type:"string"` + + // The name of the stack that is associated with the change set. + StackName *string `type:"string"` + + // The current status of the change set, such as CREATE_IN_PROGRESS, CREATE_COMPLETE, + // or FAILED. + Status *string `type:"string" enum:"ChangeSetStatus"` + + // A description of the change set's status. For example, if your attempt to + // create a change set failed, AWS CloudFormation shows the error message. + StatusReason *string `type:"string"` + + // If you execute the change set, the tags that will be associated with the + // stack. + Tags []*Tag `type:"list"` +} + +// String returns the string representation +func (s DescribeChangeSetOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s DescribeChangeSetOutput) GoString() string { + return s.String() +} + // The input for DescribeStackEvents action. type DescribeStackEventsInput struct { _ struct{} `type:"structure"` @@ -1238,6 +1729,44 @@ func (s EstimateTemplateCostOutput) GoString() string { return s.String() } +// The input for the ExecuteChangeSet action. +type ExecuteChangeSetInput struct { + _ struct{} `type:"structure"` + + // The name or ARN of the change set that you want use to update the specified + // stack. + ChangeSetName *string `min:"1" type:"string" required:"true"` + + // If you specified the name of a change set, specify the stack name or ID (ARN) + // that is associated with the change set you want to execute. + StackName *string `min:"1" type:"string"` +} + +// String returns the string representation +func (s ExecuteChangeSetInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ExecuteChangeSetInput) GoString() string { + return s.String() +} + +// The output for the ExecuteChangeSet action. +type ExecuteChangeSetOutput struct { + _ struct{} `type:"structure"` +} + +// String returns the string representation +func (s ExecuteChangeSetOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ExecuteChangeSetOutput) GoString() string { + return s.String() +} + // The input for the GetStackPolicy action. type GetStackPolicyInput struct { _ struct{} `type:"structure"` @@ -1378,7 +1907,7 @@ type GetTemplateSummaryOutput struct { CapabilitiesReason *string `type:"string"` // The value that is defined in the Description property of the template. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // The value that is defined for the Metadata property of the template. Metadata *string `type:"string"` @@ -1406,6 +1935,52 @@ func (s GetTemplateSummaryOutput) GoString() string { return s.String() } +// The input for the ListChangeSets action. +type ListChangeSetsInput struct { + _ struct{} `type:"structure"` + + // A string (provided by the ListChangeSets response output) that identifies + // the next page of change sets that you want to retrieve. + NextToken *string `min:"1" type:"string"` + + // The name or the Amazon Resource Name (ARN) of the stack for which you want + // to list change sets. + StackName *string `min:"1" type:"string" required:"true"` +} + +// String returns the string representation +func (s ListChangeSetsInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ListChangeSetsInput) GoString() string { + return s.String() +} + +// The output for the ListChangeSets action. +type ListChangeSetsOutput struct { + _ struct{} `type:"structure"` + + // If the output exceeds 1 MB, a string that identifies the next page of change + // sets. If there is no additional page, this value is null. + NextToken *string `min:"1" type:"string"` + + // A list of ChangeSetSummary structures that provides the ID and status of + // each change set for the specified stack. + Summaries []*ChangeSetSummary `type:"list"` +} + +// String returns the string representation +func (s ListChangeSetsOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ListChangeSetsOutput) GoString() string { + return s.String() +} + // The input for the ListStackResource action. type ListStackResourcesInput struct { _ struct{} `type:"structure"` @@ -1437,8 +2012,8 @@ func (s ListStackResourcesInput) GoString() string { type ListStackResourcesOutput struct { _ struct{} `type:"structure"` - // If the output exceeds 1 MB in size, a string that identifies the next page - // of stack resources. If no additional page exists, this value is null. + // If the output exceeds 1 MB, a string that identifies the next page of stack + // resources. If no additional page exists, this value is null. NextToken *string `min:"1" type:"string"` // A list of StackResourceSummary structures. @@ -1506,7 +2081,7 @@ type Output struct { _ struct{} `type:"structure"` // User defined description associated with the output. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // The key associated with the output. OutputKey *string `type:"string"` @@ -1581,7 +2156,7 @@ type ParameterDeclaration struct { DefaultValue *string `type:"string"` // The description that is associate with the parameter. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // Flag that indicates whether the parameter value is shown as plain text in // logs and in the AWS Management Console. @@ -1607,6 +2182,152 @@ func (s ParameterDeclaration) GoString() string { return s.String() } +// The ResourceChange structure describes the resource and the action that AWS +// CloudFormation will perform on it if you execute this change set. +type ResourceChange struct { + _ struct{} `type:"structure"` + + // The action that AWS CloudFormation takes on the resource, such as Add (adds + // a new resource), Modify (changes a resource), or Remove (deletes a resource). + Action *string `type:"string" enum:"ChangeAction"` + + // For the Modify action, a list of ResourceChangeDetail structures that describes + // the changes that AWS CloudFormation will make to the resource. + Details []*ResourceChangeDetail `type:"list"` + + // The resource's logical ID, which is defined in the stack's template. + LogicalResourceId *string `type:"string"` + + // The resource's physical ID (resource name). Resources that you are adding + // don't have physical IDs because they haven't been created. + PhysicalResourceId *string `type:"string"` + + // For the Modify action, indicates whether AWS CloudFormation will replace + // the resource by creating a new one and deleting the old one. This value depends + // on the value of the RequiresRecreation property in the ResourceTargetDefinition + // structure. For example, if the RequiresRecreation field is Always and the + // Evaluation field is Static, Replacement is True. If the RequiresRecreation + // field is Always and the Evaluation field is Dynamic, Replacement is Conditionally. + // + // If you have multiple changes with different RequiresRecreation values, the + // Replacement value depends on the change with the most impact. A RequiresRecreation + // value of Always has the most impact, followed by Conditionally, and then + // Never. + Replacement *string `type:"string" enum:"Replacement"` + + // The type of AWS CloudFormation resource, such as AWS::S3::Bucket. + ResourceType *string `min:"1" type:"string"` + + // For the Modify action, indicates which resource attribute is triggering this + // update, such as a change in the resource attribute's Metadata, Properties, + // or Tags. + Scope []*string `type:"list"` +} + +// String returns the string representation +func (s ResourceChange) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ResourceChange) GoString() string { + return s.String() +} + +// For a resource with Modify as the action, the ResourceChange structure describes +// the changes AWS CloudFormation will make to that resource. +type ResourceChangeDetail struct { + _ struct{} `type:"structure"` + + // The identity of the entity that triggered this change. This entity is a member + // of the group that is specified by the ChangeSource field. For example, if + // you modified the value of the KeyPairName parameter, the CausingEntity is + // the name of the parameter (KeyPairName). + // + // If the ChangeSource value is DirectModification, no value is given for CausingEntity. + CausingEntity *string `type:"string"` + + // The group to which the CausingEntity value belongs. There are five entity + // groups: + // + // ResourceReference entities are Ref intrinsic functions that refer to resources + // in the template, such as { "Ref" : "MyEC2InstanceResource" }. ParameterReference + // entities are Ref intrinsic functions that get template parameter values, + // such as { "Ref" : "MyPasswordParameter" }. ResourceAttribute entities are + // Fn::GetAtt intrinsic functions that get resource attribute values, such as + // { "Fn::GetAtt" : [ "MyEC2InstanceResource", "PublicDnsName" ] }. DirectModification + // entities are changes that are made directly to the template. Automatic entities + // are AWS::CloudFormation::Stack resource types, which are also known as nested + // stacks. If you made no changes to the AWS::CloudFormation::Stack resource, + // AWS CloudFormation sets the ChangeSource to Automatic because the nested + // stack's template might have changed. Changes to a nested stack's template + // aren't visible to AWS CloudFormation until you run an update on the parent + // stack. + ChangeSource *string `type:"string" enum:"ChangeSource"` + + // Indicates whether AWS CloudFormation can determine the target value, and + // whether the target value will change before you execute a change set. + // + // For Static evaluations, AWS CloudFormation can determine that the target + // value will change, and its value. For example, if you directly modify the + // InstanceType property of an EC2 instance, AWS CloudFormation knows that this + // property value will change, and its value, so this is a Static evaluation. + // + // For Dynamic evaluations, cannot determine the target value because it depends + // on the result of an intrinsic function, such as a Ref or Fn::GetAtt intrinsic + // function, when the stack is updated. For example, if your template includes + // a reference to a resource that is conditionally recreated, the value of the + // reference (the physical ID of the resource) might change, depending on if + // the resource is recreated. If the resource is recreated, it will have a new + // physical ID, so all references to that resource will also be updated. + Evaluation *string `type:"string" enum:"EvaluationType"` + + // A ResourceTargetDefinition structure that describes the field that AWS CloudFormation + // will change and whether the resource will be recreated. + Target *ResourceTargetDefinition `type:"structure"` +} + +// String returns the string representation +func (s ResourceChangeDetail) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ResourceChangeDetail) GoString() string { + return s.String() +} + +// The field that AWS CloudFormation will change, such as the name of a resource's +// property, and whether the resource will be recreated. +type ResourceTargetDefinition struct { + _ struct{} `type:"structure"` + + // Indicates which resource attribute is triggering this update, such as a change + // in the resource attribute's Metadata, Properties, or Tags. + Attribute *string `type:"string" enum:"ResourceAttribute"` + + // If the Attribute value is Properties, the name of the property. For all other + // attributes, the value is null. + Name *string `type:"string"` + + // If the Attribute value is Properties, indicates whether a change to this + // property causes the resource to be recreated. The value can be Never, Always, + // or Conditionally. To determine the conditions for a Conditionally recreation, + // see the update behavior for that property (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) + // in the AWS CloudFormation User Guide. + RequiresRecreation *string `type:"string" enum:"RequiresRecreation"` +} + +// String returns the string representation +func (s ResourceTargetDefinition) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ResourceTargetDefinition) GoString() string { + return s.String() +} + // The input for the SetStackPolicy action. type SetStackPolicyInput struct { _ struct{} `type:"structure"` @@ -1621,7 +2342,7 @@ type SetStackPolicyInput struct { StackPolicyBody *string `min:"1" type:"string"` // Location of a file containing the stack policy. The URL must point to a policy - // (max size: 16KB) located in an S3 bucket in the same region as the stack. + // (maximum size: 16 KB) located in an S3 bucket in the same region as the stack. // You can specify either the StackPolicyBody or the StackPolicyURL parameter, // but not both. StackPolicyURL *string `min:"1" type:"string"` @@ -1709,7 +2430,7 @@ type Stack struct { CreationTime *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"` // A user-defined description associated with the stack. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // Boolean to enable or disable rollback on stack creation failures: // @@ -1741,7 +2462,7 @@ type Stack struct { // Success/failure message associated with the stack status. StackStatusReason *string `type:"string"` - // A list of Tags that specify cost allocation information for the stack. + // A list of Tags that specify information about the stack. Tags []*Tag `type:"list"` // The amount of time within which stack creation should complete. @@ -1784,7 +2505,7 @@ type StackEvent struct { // Type of resource. (For more information, go to AWS Resource Types Reference // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) // in the AWS CloudFormation User Guide.) - ResourceType *string `type:"string"` + ResourceType *string `min:"1" type:"string"` // The unique ID name of the instance of the stack. StackId *string `type:"string" required:"true"` @@ -1811,7 +2532,7 @@ type StackResource struct { _ struct{} `type:"structure"` // User defined description associated with the resource. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // The logical name of the resource specified in the template. LogicalResourceId *string `type:"string" required:"true"` @@ -1829,7 +2550,7 @@ type StackResource struct { // Type of resource. (For more information, go to AWS Resource Types Reference // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) // in the AWS CloudFormation User Guide.) - ResourceType *string `type:"string" required:"true"` + ResourceType *string `min:"1" type:"string" required:"true"` // Unique identifier of the stack. StackId *string `type:"string"` @@ -1856,7 +2577,7 @@ type StackResourceDetail struct { _ struct{} `type:"structure"` // User defined description associated with the resource. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // Time the status was updated. LastUpdatedTimestamp *time.Time `type:"timestamp" timestampFormat:"iso8601" required:"true"` @@ -1882,7 +2603,7 @@ type StackResourceDetail struct { // Type of resource. ((For more information, go to AWS Resource Types Reference // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) // in the AWS CloudFormation User Guide.) - ResourceType *string `type:"string" required:"true"` + ResourceType *string `min:"1" type:"string" required:"true"` // Unique identifier of the stack. StackId *string `type:"string"` @@ -1924,7 +2645,7 @@ type StackResourceSummary struct { // Type of resource. (For more information, go to AWS Resource Types Reference // (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html) // in the AWS CloudFormation User Guide.) - ResourceType *string `type:"string" required:"true"` + ResourceType *string `min:"1" type:"string" required:"true"` } // String returns the string representation @@ -1977,9 +2698,8 @@ func (s StackSummary) GoString() string { return s.String() } -// The Tag type is used by CreateStack in the Tags parameter. It allows you -// to specify a key-value pair that can be used to store information related -// to cost allocation for an AWS CloudFormation stack. +// The Tag type enables you to specify a key-value pair that can be used to +// store information about an AWS CloudFormation stack. type Tag struct { _ struct{} `type:"structure"` @@ -2011,7 +2731,7 @@ type TemplateParameter struct { DefaultValue *string `type:"string"` // User defined description associated with the parameter. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // Flag indicating whether the parameter should be displayed as plain text in // logs and UIs. @@ -2036,11 +2756,13 @@ type UpdateStackInput struct { _ struct{} `type:"structure"` // A list of capabilities that you must specify before AWS CloudFormation can - // create or update certain stacks. Some stack templates might include resources - // that can affect permissions in your AWS account. For those stacks, you must - // explicitly acknowledge their capabilities by specifying this parameter. Currently, - // the only valid value is CAPABILITY_IAM, which is required for the following - // resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html), + // update certain stacks. Some stack templates might include resources that + // can affect permissions in your AWS account, for example, by creating new + // AWS Identity and Access Management (IAM) users. For those stacks, you must + // explicitly acknowledge their capabilities by specifying this parameter. + // + // Currently, the only valid value is CAPABILITY_IAM, which is required for + // the following resources: AWS::IAM::AccessKey (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-accesskey.html), // AWS::IAM::Group (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-group.html), // AWS::IAM::InstanceProfile (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-instanceprofile.html), // AWS::IAM::Policy (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-policy.html), @@ -2048,8 +2770,9 @@ type UpdateStackInput struct { // AWS::IAM::User (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-user.html), // and AWS::IAM::UserToGroupAddition (http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-iam-addusertogroup.html). // If your stack template contains these resources, we recommend that you review - // any permissions associated with them. If you don't specify this parameter, - // this action returns an InsufficientCapabilities error. + // all permissions associated with them and edit their permissions if necessary. + // If your template contains any of the listed resources and you don't specify + // this parameter, this action returns an InsufficientCapabilities error. Capabilities []*string `type:"list"` // Amazon Simple Notification Service topic Amazon Resource Names (ARNs) that @@ -2222,7 +2945,7 @@ type ValidateTemplateOutput struct { CapabilitiesReason *string `type:"string"` // The description found within the template. - Description *string `type:"string"` + Description *string `min:"1" type:"string"` // A list of TemplateParameter structures. Parameters []*TemplateParameter `type:"list"` @@ -2243,6 +2966,53 @@ const ( CapabilityCapabilityIam = "CAPABILITY_IAM" ) +const ( + // @enum ChangeAction + ChangeActionAdd = "Add" + // @enum ChangeAction + ChangeActionModify = "Modify" + // @enum ChangeAction + ChangeActionRemove = "Remove" +) + +const ( + // @enum ChangeSetStatus + ChangeSetStatusCreatePending = "CREATE_PENDING" + // @enum ChangeSetStatus + ChangeSetStatusCreateInProgress = "CREATE_IN_PROGRESS" + // @enum ChangeSetStatus + ChangeSetStatusCreateComplete = "CREATE_COMPLETE" + // @enum ChangeSetStatus + ChangeSetStatusDeleteComplete = "DELETE_COMPLETE" + // @enum ChangeSetStatus + ChangeSetStatusFailed = "FAILED" +) + +const ( + // @enum ChangeSource + ChangeSourceResourceReference = "ResourceReference" + // @enum ChangeSource + ChangeSourceParameterReference = "ParameterReference" + // @enum ChangeSource + ChangeSourceResourceAttribute = "ResourceAttribute" + // @enum ChangeSource + ChangeSourceDirectModification = "DirectModification" + // @enum ChangeSource + ChangeSourceAutomatic = "Automatic" +) + +const ( + // @enum ChangeType + ChangeTypeResource = "Resource" +) + +const ( + // @enum EvaluationType + EvaluationTypeStatic = "Static" + // @enum EvaluationType + EvaluationTypeDynamic = "Dynamic" +) + const ( // @enum OnFailure OnFailureDoNothing = "DO_NOTHING" @@ -2252,6 +3022,39 @@ const ( OnFailureDelete = "DELETE" ) +const ( + // @enum Replacement + ReplacementTrue = "True" + // @enum Replacement + ReplacementFalse = "False" + // @enum Replacement + ReplacementConditional = "Conditional" +) + +const ( + // @enum RequiresRecreation + RequiresRecreationNever = "Never" + // @enum RequiresRecreation + RequiresRecreationConditionally = "Conditionally" + // @enum RequiresRecreation + RequiresRecreationAlways = "Always" +) + +const ( + // @enum ResourceAttribute + ResourceAttributeProperties = "Properties" + // @enum ResourceAttribute + ResourceAttributeMetadata = "Metadata" + // @enum ResourceAttribute + ResourceAttributeCreationPolicy = "CreationPolicy" + // @enum ResourceAttribute + ResourceAttributeUpdatePolicy = "UpdatePolicy" + // @enum ResourceAttribute + ResourceAttributeDeletionPolicy = "DeletionPolicy" + // @enum ResourceAttribute + ResourceAttributeTags = "Tags" +) + const ( // @enum ResourceSignalStatus ResourceSignalStatusSuccess = "SUCCESS" diff --git a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go index cd90e41d7af8..f8ca675144dd 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go +++ b/vendor/github.com/aws/aws-sdk-go/service/cloudformation/waiters.go @@ -10,7 +10,7 @@ func (c *CloudFormation) WaitUntilStackCreateComplete(input *DescribeStacksInput waiterCfg := waiter.Config{ Operation: "DescribeStacks", Delay: 30, - MaxAttempts: 50, + MaxAttempts: 120, Acceptors: []waiter.WaitAcceptor{ { State: "success", @@ -24,6 +24,48 @@ func (c *CloudFormation) WaitUntilStackCreateComplete(input *DescribeStacksInput Argument: "Stacks[].StackStatus", Expected: "CREATE_FAILED", }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "DELETE_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "DELETE_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "DELETE_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "error", + Argument: "", + Expected: "ValidationError", + }, }, } @@ -39,7 +81,7 @@ func (c *CloudFormation) WaitUntilStackDeleteComplete(input *DescribeStacksInput waiterCfg := waiter.Config{ Operation: "DescribeStacks", Delay: 30, - MaxAttempts: 25, + MaxAttempts: 120, Acceptors: []waiter.WaitAcceptor{ { State: "success", @@ -59,6 +101,113 @@ func (c *CloudFormation) WaitUntilStackDeleteComplete(input *DescribeStacksInput Argument: "Stacks[].StackStatus", Expected: "DELETE_FAILED", }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "CREATE_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "CREATE_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "CREATE_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "ROLLBACK_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_IN_PROGRESS", + }, + }, + } + + w := waiter.Waiter{ + Client: c, + Input: input, + Config: waiterCfg, + } + return w.Wait() +} + +func (c *CloudFormation) WaitUntilStackExists(input *DescribeStacksInput) error { + waiterCfg := waiter.Config{ + Operation: "DescribeStacks", + Delay: 5, + MaxAttempts: 20, + Acceptors: []waiter.WaitAcceptor{ + { + State: "success", + Matcher: "status", + Argument: "", + Expected: 200, + }, + { + State: "retry", + Matcher: "error", + Argument: "", + Expected: "ValidationError", + }, }, } @@ -74,7 +223,7 @@ func (c *CloudFormation) WaitUntilStackUpdateComplete(input *DescribeStacksInput waiterCfg := waiter.Config{ Operation: "DescribeStacks", Delay: 30, - MaxAttempts: 5, + MaxAttempts: 120, Acceptors: []waiter.WaitAcceptor{ { State: "success", @@ -88,6 +237,36 @@ func (c *CloudFormation) WaitUntilStackUpdateComplete(input *DescribeStacksInput Argument: "Stacks[].StackStatus", Expected: "UPDATE_FAILED", }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_COMPLETE", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_FAILED", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "pathAny", + Argument: "Stacks[].StackStatus", + Expected: "UPDATE_ROLLBACK_IN_PROGRESS", + }, + { + State: "failure", + Matcher: "error", + Argument: "", + Expected: "ValidationError", + }, }, } diff --git a/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go b/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go index 7bc6da339abc..c4cf4891961a 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/codedeploy/api.go @@ -2803,7 +2803,7 @@ type ListDeploymentsInput struct { // queued deployments in the resulting list. In Progress: Include in-progress // deployments in the resulting list. Succeeded: Include successful deployments // in the resulting list. Failed: Include failed deployments in the resulting - // list. Aborted: Include aborted deployments in the resulting list. + // list. Stopped: Include stopped deployments in the resulting list. IncludeOnlyStatuses []*string `locationName:"includeOnlyStatuses" type:"list"` // An identifier returned from the previous list deployments call. It can be diff --git a/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go b/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go index c381d3381137..907aa394d55f 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/elasticache/api.go @@ -1884,8 +1884,6 @@ type CopySnapshotInput struct { // The name of an existing snapshot from which to copy. SourceSnapshotName *string `type:"string" required:"true"` - TargetBucket *string `type:"string"` - // A name for the copied snapshot. TargetSnapshotName *string `type:"string" required:"true"` } @@ -4737,7 +4735,7 @@ type ResetCacheParameterGroupInput struct { // An array of parameter names to be reset. If you are not resetting the entire // cache parameter group, you must specify at least one parameter name. - ParameterNameValues []*ParameterNameValue `locationNameList:"ParameterNameValue" type:"list"` + ParameterNameValues []*ParameterNameValue `locationNameList:"ParameterNameValue" type:"list" required:"true"` // If true, all parameters in the cache parameter group will be reset to default // values. If false, no such action occurs. diff --git a/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go index 5d5755d049a0..b1c9a526aaec 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go +++ b/vendor/github.com/aws/aws-sdk-go/service/elb/waiters.go @@ -29,6 +29,35 @@ func (c *ELB) WaitUntilAnyInstanceInService(input *DescribeInstanceHealthInput) return w.Wait() } +func (c *ELB) WaitUntilInstanceDeregistered(input *DescribeInstanceHealthInput) error { + waiterCfg := waiter.Config{ + Operation: "DescribeInstanceHealth", + Delay: 15, + MaxAttempts: 40, + Acceptors: []waiter.WaitAcceptor{ + { + State: "success", + Matcher: "pathAll", + Argument: "InstanceStates[].State", + Expected: "OutOfService", + }, + { + State: "success", + Matcher: "error", + Argument: "", + Expected: "InvalidInstance", + }, + }, + } + + w := waiter.Waiter{ + Client: c, + Input: input, + Config: waiterCfg, + } + return w.Wait() +} + func (c *ELB) WaitUntilInstanceInService(input *DescribeInstanceHealthInput) error { waiterCfg := waiter.Config{ Operation: "DescribeInstanceHealth", diff --git a/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go b/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go index 69315d35e8a0..0a250ba953eb 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/redshift/api.go @@ -1696,9 +1696,9 @@ func (c *Redshift) DescribeTableRestoreStatusRequest(input *DescribeTableRestore // Lists the status of one or more table restore requests made using the RestoreTableFromClusterSnapshot // API action. If you don't specify a value for the TableRestoreRequestId parameter, -// then DescribeTableRestoreStatus returns the status of all in-progress table -// restore requests. Otherwise DescribeTableRestoreStatus returns the status -// of the table specified by TableRestoreRequestId. +// then DescribeTableRestoreStatus returns the status of all table restore requests +// ordered by the date and time of the request in ascending order. Otherwise +// DescribeTableRestoreStatus returns the status of the table specified by TableRestoreRequestId. func (c *Redshift) DescribeTableRestoreStatus(input *DescribeTableRestoreStatusInput) (*DescribeTableRestoreStatusOutput, error) { req, out := c.DescribeTableRestoreStatusRequest(input) err := req.Send() @@ -1903,6 +1903,36 @@ func (c *Redshift) ModifyCluster(input *ModifyClusterInput) (*ModifyClusterOutpu return out, err } +const opModifyClusterIamRoles = "ModifyClusterIamRoles" + +// ModifyClusterIamRolesRequest generates a request for the ModifyClusterIamRoles operation. +func (c *Redshift) ModifyClusterIamRolesRequest(input *ModifyClusterIamRolesInput) (req *request.Request, output *ModifyClusterIamRolesOutput) { + op := &request.Operation{ + Name: opModifyClusterIamRoles, + HTTPMethod: "POST", + HTTPPath: "/", + } + + if input == nil { + input = &ModifyClusterIamRolesInput{} + } + + req = c.newRequest(op, input, output) + output = &ModifyClusterIamRolesOutput{} + req.Data = output + return +} + +// Modifies the list of AWS Identity and Access Management (IAM) roles that +// can be used by the cluster to access other AWS services. +// +// A cluster can have up to 10 IAM roles associated at any time. +func (c *Redshift) ModifyClusterIamRoles(input *ModifyClusterIamRolesInput) (*ModifyClusterIamRolesOutput, error) { + req, out := c.ModifyClusterIamRolesRequest(input) + err := req.Send() + return out, err +} + const opModifyClusterParameterGroup = "ModifyClusterParameterGroup" // ModifyClusterParameterGroupRequest generates a request for the ModifyClusterParameterGroup operation. @@ -2492,6 +2522,10 @@ type Cluster struct { // Values: active, applying HsmStatus *HsmStatus `type:"structure"` + // A list of AWS Identity and Access Management (IAM) roles that can be used + // by the cluster to access other AWS services. + IamRoles []*ClusterIamRole `locationNameList:"ClusterIamRole" type:"list"` + // The AWS Key Management Service (KMS) key ID of the encryption key used to // encrypt data in the cluster. KmsKeyId *string `type:"string"` @@ -2545,6 +2579,34 @@ func (s Cluster) GoString() string { return s.String() } +// An AWS Identity and Access Management (IAM) role that can be used by the +// associated Amazon Redshift cluster to access other AWS services. +type ClusterIamRole struct { + _ struct{} `type:"structure"` + + // Describes the status of the IAM role's association with an Amazon Redshift + // cluster. + // + // The following are possible statuses and descriptions. in-sync: The role + // is available for use by the cluster. adding: The role is in the process of + // being associated with the cluster. removing: The role is in the process of + // being disassociated with the cluster. + ApplyStatus *string `type:"string"` + + // The Amazon Resource Name (ARN) of the IAM role. For example, arn:aws:iam::123456789012:role/RedshiftCopyUnload. + IamRoleArn *string `type:"string"` +} + +// String returns the string representation +func (s ClusterIamRole) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ClusterIamRole) GoString() string { + return s.String() +} + // The identifier of a node in a cluster. type ClusterNode struct { _ struct{} `type:"structure"` @@ -3011,6 +3073,14 @@ type CreateClusterInput struct { // the Amazon Redshift cluster can use to retrieve and store keys in an HSM. HsmConfigurationIdentifier *string `type:"string"` + // A list of AWS Identity and Access Management (IAM) roles that can be used + // by the cluster to access other AWS services. You must supply the IAM roles + // in their Amazon Resource Name (ARN) format. You can supply up to 10 IAM roles + // in a single request. + // + // A cluster can have up to 10 IAM roles associated at any time. + IamRoles []*string `locationNameList:"IamRoleArn" type:"list"` + // The AWS Key Management Service (KMS) key ID of the encryption key that you // want to use to encrypt data in the cluster. KmsKeyId *string `type:"string"` @@ -6005,6 +6075,50 @@ func (s LoggingStatus) GoString() string { return s.String() } +type ModifyClusterIamRolesInput struct { + _ struct{} `type:"structure"` + + // Zero or more IAM roles (in their ARN format) to associate with the cluster. + // You can associate up to 10 IAM roles with a single cluster in a single request. + AddIamRoles []*string `locationNameList:"IamRoleArn" type:"list"` + + // The unique identifier of the cluster for which you want to associate or disassociate + // IAM roles. + ClusterIdentifier *string `type:"string" required:"true"` + + // Zero or more IAM roles (in their ARN format) to disassociate from the cluster. + // You can disassociate up to 10 IAM roles from a single cluster in a single + // request. + RemoveIamRoles []*string `locationNameList:"IamRoleArn" type:"list"` +} + +// String returns the string representation +func (s ModifyClusterIamRolesInput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ModifyClusterIamRolesInput) GoString() string { + return s.String() +} + +type ModifyClusterIamRolesOutput struct { + _ struct{} `type:"structure"` + + // Describes a cluster. + Cluster *Cluster `type:"structure"` +} + +// String returns the string representation +func (s ModifyClusterIamRolesOutput) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ModifyClusterIamRolesOutput) GoString() string { + return s.String() +} + type ModifyClusterInput struct { _ struct{} `type:"structure"` @@ -6816,6 +6930,14 @@ type RestoreFromClusterSnapshotInput struct { // the Amazon Redshift cluster can use to retrieve and store keys in an HSM. HsmConfigurationIdentifier *string `type:"string"` + // A list of AWS Identity and Access Management (IAM) roles that can be used + // by the cluster to access other AWS services. You must supply the IAM roles + // in their Amazon Resource Name (ARN) format. You can supply up to 10 IAM roles + // in a single request. + // + // A cluster can have up to 10 IAM roles associated at any time. + IamRoles []*string `locationNameList:"IamRoleArn" type:"list"` + // The AWS Key Management Service (KMS) key ID of the encryption key that you // want to use to encrypt data in the cluster that you restore from a shared // snapshot. @@ -6965,7 +7087,8 @@ type RestoreTableFromClusterSnapshotInput struct { // The name of the source database that contains the table to restore from. SourceDatabaseName *string `type:"string" required:"true"` - // The name of the source schema that contains the table to restore from. + // The name of the source schema that contains the table to restore from. If + // you do not specify a SourceSchemaName value, the default is public. SourceSchemaName *string `type:"string"` // The name of the source table to restore from. @@ -7316,7 +7439,7 @@ type TableRestoreStatus struct { ClusterIdentifier *string `type:"string"` // A description of the status of the table restore request. Status values include - // SUCCEEDED, FAILED, CANCELLED, PENDING, IN_PROGRESS. + // SUCCEEDED, FAILED, CANCELED, PENDING, IN_PROGRESS. Message *string `type:"string"` // The name of the table to create as a result of the table restore request. @@ -7343,7 +7466,7 @@ type TableRestoreStatus struct { // A value that describes the current state of the table restore request. // - // Valid Values: SUCCEEDED, FAILED, CANCELLED, PENDING, IN_PROGRESS + // Valid Values: SUCCEEDED, FAILED, CANCELED, PENDING, IN_PROGRESS Status *string `type:"string" enum:"TableRestoreStatusType"` // The unique identifier for the table restore request. diff --git a/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go b/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go new file mode 100644 index 000000000000..04786169e2a6 --- /dev/null +++ b/vendor/github.com/aws/aws-sdk-go/service/route53/waiters.go @@ -0,0 +1,30 @@ +// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. + +package route53 + +import ( + "github.com/aws/aws-sdk-go/private/waiter" +) + +func (c *Route53) WaitUntilResourceRecordSetsChanged(input *GetChangeInput) error { + waiterCfg := waiter.Config{ + Operation: "GetChange", + Delay: 30, + MaxAttempts: 60, + Acceptors: []waiter.WaitAcceptor{ + { + State: "success", + Matcher: "path", + Argument: "ChangeInfo.Status", + Expected: "INSYNC", + }, + }, + } + + w := waiter.Waiter{ + Client: c, + Input: input, + Config: waiterCfg, + } + return w.Wait() +} diff --git a/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go b/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go index 8671d162728c..103abb3c8016 100644 --- a/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go +++ b/vendor/github.com/aws/aws-sdk-go/service/sqs/api.go @@ -47,9 +47,7 @@ func (c *SQS) AddPermissionRequest(input *AddPermissionInput) (req *request.Requ // // Some API actions take lists of parameters. These lists are specified using // the param.n notation. Values of n are integers starting from 1. For example, -// a parameter list with two elements looks like this: &Attribute.1=this -// -// &Attribute.2=that +// a parameter list with two elements looks like this: func (c *SQS) AddPermission(input *AddPermissionInput) (*AddPermissionOutput, error) { req, out := c.AddPermissionRequest(input) err := req.Send() @@ -145,9 +143,7 @@ func (c *SQS) ChangeMessageVisibilityBatchRequest(input *ChangeMessageVisibility // returns an HTTP status code of 200. Some API actions take lists of parameters. // These lists are specified using the param.n notation. Values of n are integers // starting from 1. For example, a parameter list with two elements looks like -// this: &Attribute.1=this -// -// &Attribute.2=that +// this: func (c *SQS) ChangeMessageVisibilityBatch(input *ChangeMessageVisibilityBatchInput) (*ChangeMessageVisibilityBatchOutput, error) { req, out := c.ChangeMessageVisibilityBatchRequest(input) err := req.Send() @@ -196,9 +192,7 @@ func (c *SQS) CreateQueueRequest(input *CreateQueueInput) (req *request.Request, // // Some API actions take lists of parameters. These lists are specified using // the param.n notation. Values of n are integers starting from 1. For example, -// a parameter list with two elements looks like this: &Attribute.1=this -// -// &Attribute.2=that +// a parameter list with two elements looks like this: func (c *SQS) CreateQueue(input *CreateQueueInput) (*CreateQueueOutput, error) { req, out := c.CreateQueueRequest(input) err := req.Send() @@ -283,9 +277,7 @@ func (c *SQS) DeleteMessageBatchRequest(input *DeleteMessageBatchInput) (req *re // // Some API actions take lists of parameters. These lists are specified using // the param.n notation. Values of n are integers starting from 1. For example, -// a parameter list with two elements looks like this: &Attribute.1=this -// -// &Attribute.2=that +// a parameter list with two elements looks like this: func (c *SQS) DeleteMessageBatch(input *DeleteMessageBatchInput) (*DeleteMessageBatchOutput, error) { req, out := c.DeleteMessageBatchRequest(input) err := req.Send() @@ -358,27 +350,27 @@ func (c *SQS) GetQueueAttributesRequest(input *GetQueueAttributesInput) (req *re } // Gets attributes for the specified queue. The following attributes are supported: -// All - returns all values. ApproximateNumberOfMessages - returns the approximate +// All - returns all values. ApproximateNumberOfMessages - returns the approximate // number of visible messages in a queue. For more information, see Resources // Required to Process Messages (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/ApproximateNumber.html) -// in the Amazon SQS Developer Guide. ApproximateNumberOfMessagesNotVisible +// in the Amazon SQS Developer Guide. ApproximateNumberOfMessagesNotVisible // - returns the approximate number of messages that are not timed-out and not // deleted. For more information, see Resources Required to Process Messages // (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/ApproximateNumber.html) -// in the Amazon SQS Developer Guide. VisibilityTimeout - returns the visibility +// in the Amazon SQS Developer Guide. VisibilityTimeout - returns the visibility // timeout for the queue. For more information about visibility timeout, see // Visibility Timeout (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html) -// in the Amazon SQS Developer Guide. CreatedTimestamp - returns the time when -// the queue was created (epoch time in seconds). LastModifiedTimestamp - returns -// the time when the queue was last changed (epoch time in seconds). Policy -// - returns the queue's policy. MaximumMessageSize - returns the limit of -// how many bytes a message can contain before Amazon SQS rejects it. MessageRetentionPeriod -// - returns the number of seconds Amazon SQS retains a message. QueueArn - -// returns the queue's Amazon resource name (ARN). ApproximateNumberOfMessagesDelayed +// in the Amazon SQS Developer Guide. CreatedTimestamp - returns the time when +// the queue was created (epoch time in seconds). LastModifiedTimestamp - returns +// the time when the queue was last changed (epoch time in seconds). Policy +// - returns the queue's policy. MaximumMessageSize - returns the limit of how +// many bytes a message can contain before Amazon SQS rejects it. MessageRetentionPeriod +// - returns the number of seconds Amazon SQS retains a message. QueueArn - +// returns the queue's Amazon resource name (ARN). ApproximateNumberOfMessagesDelayed // - returns the approximate number of messages that are pending to be added -// to the queue. DelaySeconds - returns the default delay on the queue in seconds. -// ReceiveMessageWaitTimeSeconds - returns the time for which a ReceiveMessage -// call will wait for a message to arrive. RedrivePolicy - returns the parameters +// to the queue. DelaySeconds - returns the default delay on the queue in seconds. +// ReceiveMessageWaitTimeSeconds - returns the time for which a ReceiveMessage +// call will wait for a message to arrive. RedrivePolicy - returns the parameters // for dead letter queue functionality of the source queue. For more information // about RedrivePolicy and dead letter queues, see Using Amazon SQS Dead Letter // Queues (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/SQSDeadLetterQueue.html) @@ -389,9 +381,7 @@ func (c *SQS) GetQueueAttributesRequest(input *GetQueueAttributesInput) (req *re // handle new attributes gracefully. Some API actions take lists of parameters. // These lists are specified using the param.n notation. Values of n are integers // starting from 1. For example, a parameter list with two elements looks like -// this: &Attribute.1=this -// -// &Attribute.2=that +// this: func (c *SQS) GetQueueAttributes(input *GetQueueAttributesInput) (*GetQueueAttributesOutput, error) { req, out := c.GetQueueAttributesRequest(input) err := req.Send() @@ -708,9 +698,7 @@ func (c *SQS) SendMessageBatchRequest(input *SendMessageBatchInput) (req *reques // returns an HTTP status code of 200. Some API actions take lists of parameters. // These lists are specified using the param.n notation. Values of n are integers // starting from 1. For example, a parameter list with two elements looks like -// this: &Attribute.1=this -// -// &Attribute.2=that +// this: func (c *SQS) SendMessageBatch(input *SendMessageBatchInput) (*SendMessageBatchOutput, error) { req, out := c.SendMessageBatchRequest(input) err := req.Send() @@ -886,11 +874,9 @@ func (s ChangeMessageVisibilityBatchOutput) GoString() string { // starting with 1. For example, a parameter list for this action might look // like this: // -// &ChangeMessageVisibilityBatchRequestEntry.1.Id=change_visibility_msg_2 // -// &ChangeMessageVisibilityBatchRequestEntry.1.ReceiptHandle=Your_Receipt_Handle // -// &ChangeMessageVisibilityBatchRequestEntry.1.VisibilityTimeout=45 +// Your_Receipt_Handle]]> type ChangeMessageVisibilityBatchRequestEntry struct { _ struct{} `type:"structure"` @@ -981,19 +967,19 @@ type CreateQueueInput struct { // The following lists the names, descriptions, and values of the special request // parameters the CreateQueue action uses: // - // DelaySeconds - The time in seconds that the delivery of all messages - // in the queue will be delayed. An integer from 0 to 900 (15 minutes). The - // default for this attribute is 0 (zero). MaximumMessageSize - The limit of - // how many bytes a message can contain before Amazon SQS rejects it. An integer - // from 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this - // attribute is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds - // Amazon SQS retains a message. Integer representing seconds, from 60 (1 minute) - // to 1209600 (14 days). The default for this attribute is 345600 (4 days). - // Policy - The queue's policy. A valid AWS policy. For more information about - // policy structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html) - // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for + // DelaySeconds - The time in seconds that the delivery of all messages in + // the queue will be delayed. An integer from 0 to 900 (15 minutes). The default + // for this attribute is 0 (zero). MaximumMessageSize - The limit of how many + // bytes a message can contain before Amazon SQS rejects it. An integer from + // 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this attribute + // is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds Amazon + // SQS retains a message. Integer representing seconds, from 60 (1 minute) to + // 1209600 (14 days). The default for this attribute is 345600 (4 days). Policy + // - The queue's policy. A valid AWS policy. For more information about policy + // structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html) + // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for // which a ReceiveMessage call will wait for a message to arrive. An integer - // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout + // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout // - The visibility timeout for the queue. An integer from 0 to 43200 (12 hours). // The default for this attribute is 30. For more information about visibility // timeout, see Visibility Timeout (http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html) @@ -1460,13 +1446,13 @@ type ReceiveMessageInput struct { // The following lists the names and descriptions of the attributes that can // be returned: // - // All - returns all values. ApproximateFirstReceiveTimestamp - returns - // the time when the message was first received from the queue (epoch time in - // milliseconds). ApproximateReceiveCount - returns the number of times a message - // has been received from the queue but not deleted. SenderId - returns the - // AWS account number (or the IP address, if anonymous access is allowed) of - // the sender. SentTimestamp - returns the time when the message was sent to - // the queue (epoch time in milliseconds). + // All - returns all values. ApproximateFirstReceiveTimestamp - returns the + // time when the message was first received from the queue (epoch time in milliseconds). + // ApproximateReceiveCount - returns the number of times a message has been + // received from the queue but not deleted. SenderId - returns the AWS account + // number (or the IP address, if anonymous access is allowed) of the sender. + // SentTimestamp - returns the time when the message was sent to the queue (epoch + // time in milliseconds). AttributeNames []*string `locationNameList:"AttributeName" type:"list" flattened:"true"` // The maximum number of messages to return. Amazon SQS never returns more messages @@ -1745,22 +1731,22 @@ type SetQueueAttributesInput struct { // The following lists the names, descriptions, and values of the special request // parameters the SetQueueAttributes action uses: // - // DelaySeconds - The time in seconds that the delivery of all messages - // in the queue will be delayed. An integer from 0 to 900 (15 minutes). The - // default for this attribute is 0 (zero). MaximumMessageSize - The limit of - // how many bytes a message can contain before Amazon SQS rejects it. An integer - // from 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this - // attribute is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds - // Amazon SQS retains a message. Integer representing seconds, from 60 (1 minute) - // to 1209600 (14 days). The default for this attribute is 345600 (4 days). - // Policy - The queue's policy. A valid AWS policy. For more information about - // policy structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html) - // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for + // DelaySeconds - The time in seconds that the delivery of all messages in + // the queue will be delayed. An integer from 0 to 900 (15 minutes). The default + // for this attribute is 0 (zero). MaximumMessageSize - The limit of how many + // bytes a message can contain before Amazon SQS rejects it. An integer from + // 1024 bytes (1 KiB) up to 262144 bytes (256 KiB). The default for this attribute + // is 262144 (256 KiB). MessageRetentionPeriod - The number of seconds Amazon + // SQS retains a message. Integer representing seconds, from 60 (1 minute) to + // 1209600 (14 days). The default for this attribute is 345600 (4 days). Policy + // - The queue's policy. A valid AWS policy. For more information about policy + // structure, see Overview of AWS IAM Policies (http://docs.aws.amazon.com/IAM/latest/UserGuide/PoliciesOverview.html) + // in the Amazon IAM User Guide. ReceiveMessageWaitTimeSeconds - The time for // which a ReceiveMessage call will wait for a message to arrive. An integer - // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout + // from 0 to 20 (seconds). The default for this attribute is 0. VisibilityTimeout // - The visibility timeout for the queue. An integer from 0 to 43200 (12 hours). // The default for this attribute is 30. For more information about visibility - // timeout, see Visibility Timeout in the Amazon SQS Developer Guide. RedrivePolicy + // timeout, see Visibility Timeout in the Amazon SQS Developer Guide. RedrivePolicy // - The parameters for dead letter queue functionality of the source queue. // For more information about RedrivePolicy and dead letter queues, see Using // Amazon SQS Dead Letter Queues in the Amazon SQS Developer Guide. diff --git a/website/source/docs/providers/aws/index.html.markdown b/website/source/docs/providers/aws/index.html.markdown index 7bc328dad4cd..949c67f623ce 100644 --- a/website/source/docs/providers/aws/index.html.markdown +++ b/website/source/docs/providers/aws/index.html.markdown @@ -39,7 +39,7 @@ explained below: - Static credentials - Environment variables - Shared credentials file - +- EC2 Role ### Static credentials ### @@ -96,6 +96,21 @@ provider "aws" { } ``` +###EC2 Role + +If you're running Terraform from an EC2 instance with IAM Instance Profile +using IAM Role, Terraform will just ask +[the metadata API](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#instance-metadata-security-credentials) +endpoint for credentials. + +This is a preferred approach over any other when running in EC2 as you can avoid +hardcoding credentials. Instead these are leased on-the-fly by Terraform +which reduces the chance of leakage. + +You can provide custom metadata API endpoint via `AWS_METADATA_ENDPOINT` variable +which expects the endpoint URL including the version +and defaults to `http://169.254.169.254:80/latest`. + ## Argument Reference The following arguments are supported in the `provider` block: @@ -156,4 +171,24 @@ Nested `endpoints` block supports the followings: * `elb` - (Optional) Use this to override the default endpoint URL constructed from the `region`. It's typically used to connect to - custom elb endpoints. \ No newline at end of file + custom elb endpoints. + +## Getting the Account ID + +If you use either `allowed_account_ids` or `forbidden_account_ids`, +Terraform uses several approaches to get the actual account ID +in order to compare it with allowed/forbidden ones. + +Approaches differ per auth providers: + + * EC2 instance w/ IAM Instance Profile - [Metadata API](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html) + is always used + * All other providers (ENV vars, shared creds file, ...) + will try two approaches in the following order + * `iam:GetUser` - typically useful for IAM Users. It also means + that each user needs to be privileged to call `iam:GetUser` for themselves. + * `iam:ListRoles` - this is specifically useful for IdP-federated profiles + which cannot use `iam:GetUser`. It also means that each federated user + need to be _assuming_ an IAM role which allows `iam:ListRoles`. + There is currently no better clean way to get account ID + out of the API when using federated account unfortunately.