@ -4,6 +4,7 @@
package s3
import (
"context"
"encoding/base64"
"fmt"
"net/url"
@ -13,11 +14,14 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
dynamodbtypes "github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
"github.com/aws/aws-sdk-go-v2/service/s3"
s3types "github.com/aws/aws-sdk-go-v2/service/s3/types"
"github.com/google/go-cmp/cmp"
awsbase "github.com/hashicorp/aws-sdk-go-base"
"github.com/hashicorp/aws-sdk-go-base/v2/mockdata"
"github.com/hashicorp/aws-sdk-go-base/v2/servicemocks"
"github.com/hashicorp/terraform/internal/backend"
"github.com/hashicorp/terraform/internal/configs/configschema"
"github.com/hashicorp/terraform/internal/configs/hcl2shim"
@ -52,6 +56,9 @@ func TestBackend_impl(t *testing.T) {
func TestBackendConfig_original ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
config := map [ string ] interface { } {
"region" : "us-west-1" ,
"bucket" : "tf-test" ,
@ -60,12 +67,15 @@ func TestBackendConfig_original(t *testing.T) {
"dynamodb_table" : "dynamoTable" ,
}
// var buf bytes.Buffer
// configureLogger(&buf)
b := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( config ) ) . ( * Backend )
if aws . StringValue ( b . s3Client . Config . Region ) != "us-west-1" {
if b. aw sConfig. Region != "us-west-1" {
t . Fatalf ( "Incorrect region was populated" )
}
if aws. IntValue ( b . s3Client . Config . MaxRetries ) != 5 {
if b. awsConfig . RetryMaxAttempts != 5 {
t . Fatalf ( "Default max_retries was not set" )
}
if b . bucketName != "tf-test" {
@ -75,11 +85,11 @@ func TestBackendConfig_original(t *testing.T) {
t . Fatalf ( "Incorrect keyName was populated" )
}
checkClientEndpoint ( t , b . s3Client . Config , "" )
// checkClientEndpoint(t, b.s3Client.Config, "")
checkClientEndpoint ( t , b . dynClient . Config , "" )
// checkClientEndpoint(t, b.dynClient.Config, "")
credentials , err := b . s3Client. Config. Credentials . Get( )
credentials , err := b . aw sConfig. Credentials . Retrieve( ctx )
if err != nil {
t . Fatalf ( "Error when requesting credentials" )
}
@ -89,14 +99,30 @@ func TestBackendConfig_original(t *testing.T) {
if credentials . SecretAccessKey == "" {
t . Fatalf ( "No Secret Access Key was populated" )
}
}
// expectedCreds := aws.Credentials{}
// if diff := cmp.Diff(credentials, expectedCreds, cmpopts.IgnoreFields(aws.Credentials{}, "Expires")); diff != "" {
// t.Fatalf("unexpected credentials: (- got, + expected)\n%s", diff)
// }
func checkClientEndpoint ( t * testing . T , config aws . Config , expected string ) {
if a := aws . StringValue ( config . Endpoint ) ; a != expected {
t . Errorf ( "expected endpoint %q, got %q" , expected , a )
}
// logs := buf.String()
// strRead := strings.NewReader(logs)
// entries, err := multilineJSONDecode(strRead)
// if err != nil {
// t.Fatalf("Decoding log entries: %s", err.Error())
// }
// t.Logf("entries: %d", len(entries))
// t.Errorf("log:\n%s", logs)
}
// func checkClientEndpoint(t *testing.T, config aws.Config, expected string) {
// if a := aws.StringValue(config.Endpoint); a != expected {
// t.Errorf("expected endpoint %q, got %q", expected, a)
// }
// }
func TestBackendConfig_InvalidRegion ( t * testing . T ) {
testACC ( t )
@ -188,126 +214,126 @@ func TestBackendConfig_RegionEnvVar(t *testing.T) {
b := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( config ) ) . ( * Backend )
if aws. StringValue ( b. s3Client. Config. Region ) != "us-west-1" {
if b. aw sConfig. Region != "us-west-1" {
t . Fatalf ( "Incorrect region was populated" )
}
} )
}
}
func TestBackendConfig_DynamoDBEndpoint ( t * testing . T ) {
testACC ( t )
cases := map [ string ] struct {
config map [ string ] any
vars map [ string ] string
expected string
} {
"none" : {
expected : "" ,
} ,
"config" : {
config : map [ string ] any {
"dynamodb_endpoint" : "dynamo.test" ,
} ,
expected : "dynamo.test" ,
} ,
"envvar" : {
vars : map [ string ] string {
"AWS_DYNAMODB_ENDPOINT" : "dynamo.test" ,
} ,
expected : "dynamo.test" ,
} ,
}
for name , tc := range cases {
t . Run ( name , func ( t * testing . T ) {
config := map [ string ] interface { } {
"region" : "us-west-1" ,
"bucket" : "tf-test" ,
"key" : "state" ,
}
if tc . vars != nil {
for k , v := range tc . vars {
os . Setenv ( k , v )
}
t . Cleanup ( func ( ) {
for k := range tc . vars {
os . Unsetenv ( k )
}
} )
}
if tc . config != nil {
for k , v := range tc . config {
config [ k ] = v
}
}
b := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( config ) ) . ( * Backend )
checkClientEndpoint ( t , b . dynClient . Config , tc . expected )
} )
}
}
func TestBackendConfig_S3Endpoint ( t * testing . T ) {
testACC ( t )
cases := map [ string ] struct {
config map [ string ] any
vars map [ string ] string
expected string
} {
"none" : {
expected : "" ,
} ,
"config" : {
config : map [ string ] any {
"endpoint" : "s3.test" ,
} ,
expected : "s3.test" ,
} ,
"envvar" : {
vars : map [ string ] string {
"AWS_S3_ENDPOINT" : "s3.test" ,
} ,
expected : "s3.test" ,
} ,
}
for name , tc := range cases {
t . Run ( name , func ( t * testing . T ) {
config := map [ string ] interface { } {
"region" : "us-west-1" ,
"bucket" : "tf-test" ,
"key" : "state" ,
}
if tc . vars != nil {
for k , v := range tc . vars {
os . Setenv ( k , v )
}
t . Cleanup ( func ( ) {
for k := range tc . vars {
os . Unsetenv ( k )
}
} )
}
if tc . config != nil {
for k , v := range tc . config {
config [ k ] = v
}
}
b := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( config ) ) . ( * Backend )
checkClientEndpoint ( t , b . s3Client . Config , tc . expected )
} )
}
}
// func TestBackendConfig_DynamoDBEndpoint(t *testing.T) {
// testACC(t)
// cases := map[string]struct {
// config map[string]any
// vars map[string]string
// expected string
// }{
// "none": {
// expected: "",
// },
// "config": {
// config: map[string]any{
// "dynamodb_endpoint": "dynamo.test",
// },
// expected: "dynamo.test",
// },
// "envvar": {
// vars: map[string]string{
// "AWS_DYNAMODB_ENDPOINT": "dynamo.test",
// },
// expected: "dynamo.test",
// },
// }
// for name, tc := range cases {
// t.Run(name, func(t *testing.T) {
// config := map[string]interface{}{
// "region": "us-west-1",
// "bucket": "tf-test",
// "key": "state",
// }
// if tc.vars != nil {
// for k, v := range tc.vars {
// os.Setenv(k, v)
// }
// t.Cleanup(func() {
// for k := range tc.vars {
// os.Unsetenv(k)
// }
// })
// }
// if tc.config != nil {
// for k, v := range tc.config {
// config[k] = v
// }
// }
// b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
// checkClientEndpoint(t, b.dynClient.Config, tc.expected)
// })
// }
// }
// func TestBackendConfig_S3Endpoint(t *testing.T) {
// testACC(t)
// cases := map[string]struct {
// config map[string]any
// vars map[string]string
// expected string
// }{
// "none": {
// expected: "",
// },
// "config": {
// config: map[string]any{
// "endpoint": "s3.test",
// },
// expected: "s3.test",
// },
// "envvar": {
// vars: map[string]string{
// "AWS_S3_ENDPOINT": "s3.test",
// },
// expected: "s3.test",
// },
// }
// for name, tc := range cases {
// t.Run(name, func(t *testing.T) {
// config := map[string]interface{}{
// "region": "us-west-1",
// "bucket": "tf-test",
// "key": "state",
// }
// if tc.vars != nil {
// for k, v := range tc.vars {
// os.Setenv(k, v)
// }
// t.Cleanup(func() {
// for k := range tc.vars {
// os.Unsetenv(k)
// }
// })
// }
// if tc.config != nil {
// for k, v := range tc.config {
// config[k] = v
// }
// }
// b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
// checkClientEndpoint(t, b.s3Client.Config, tc.expected)
// })
// }
// }
func TestBackendConfig_AssumeRole ( t * testing . T ) {
testACC ( t )
@ -315,31 +341,31 @@ func TestBackendConfig_AssumeRole(t *testing.T) {
testCases := [ ] struct {
Config map [ string ] interface { }
Description string
MockStsEndpoints [ ] * awsbase . MockEndpoint
MockStsEndpoints [ ] * servicemocks . MockEndpoint
} {
{
Config : map [ string ] interface { } {
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "role_arn" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
@ -349,172 +375,172 @@ func TestBackendConfig_AssumeRole(t *testing.T) {
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "assume_role_duration_seconds" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "3600" } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
{
Config : map [ string ] interface { } {
"bucket" : "tf-test" ,
"external_id" : awsbase . MockStsAssumeRoleExternalId ,
"external_id" : servicemocks . MockStsAssumeRoleExternalId ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "external_id" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"ExternalId" : [ ] string { awsbase . MockStsAssumeRoleExternalId } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"ExternalId" : [ ] string { servicemocks . MockStsAssumeRoleExternalId } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
{
Config : map [ string ] interface { } {
"assume_role_policy" : awsbase . MockStsAssumeRolePolicy ,
"assume_role_policy" : servicemocks . MockStsAssumeRolePolicy ,
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "assume_role_policy" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"Policy" : [ ] string { awsbase . MockStsAssumeRolePolicy } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"Policy" : [ ] string { servicemocks . MockStsAssumeRolePolicy } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
{
Config : map [ string ] interface { } {
"assume_role_policy_arns" : [ ] interface { } { awsbase . MockStsAssumeRolePolicyArn } ,
"assume_role_policy_arns" : [ ] interface { } { servicemocks . MockStsAssumeRolePolicyArn } ,
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "assume_role_policy_arns" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"PolicyArns.member.1.arn" : [ ] string { awsbase . MockStsAssumeRolePolicyArn } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"PolicyArns.member.1.arn" : [ ] string { servicemocks . MockStsAssumeRolePolicyArn } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
{
Config : map [ string ] interface { } {
"assume_role_tags" : map [ string ] interface { } {
awsbase. MockStsAssumeRoleTagKey : awsbase . MockStsAssumeRoleTagValue ,
servicemocks. MockStsAssumeRoleTagKey : servicemocks . MockStsAssumeRoleTagValue ,
} ,
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "assume_role_tags" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"Tags.member.1.Key" : [ ] string { awsbase . MockStsAssumeRoleTagKey } ,
"Tags.member.1.Value" : [ ] string { awsbase . MockStsAssumeRoleTagValue } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Tags.member.1.Key" : [ ] string { servicemocks . MockStsAssumeRoleTagKey } ,
"Tags.member.1.Value" : [ ] string { servicemocks . MockStsAssumeRoleTagValue } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
{
Config : map [ string ] interface { } {
"assume_role_tags" : map [ string ] interface { } {
awsbase. MockStsAssumeRoleTagKey : awsbase . MockStsAssumeRoleTagValue ,
servicemocks. MockStsAssumeRoleTagKey : servicemocks . MockStsAssumeRoleTagValue ,
} ,
"assume_role_transitive_tag_keys" : [ ] interface { } { awsbase . MockStsAssumeRoleTagKey } ,
"assume_role_transitive_tag_keys" : [ ] interface { } { servicemocks . MockStsAssumeRoleTagKey } ,
"bucket" : "tf-test" ,
"key" : "state" ,
"region" : "us-west-1" ,
"role_arn" : awsbase . MockStsAssumeRoleArn ,
"session_name" : awsbase . MockStsAssumeRoleSessionName ,
"role_arn" : servicemocks . MockStsAssumeRoleArn ,
"session_name" : servicemocks . MockStsAssumeRoleSessionName ,
} ,
Description : "assume_role_transitive_tag_keys" ,
MockStsEndpoints : [ ] * awsbase . MockEndpoint {
MockStsEndpoints : [ ] * servicemocks . MockEndpoint {
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : url . Values {
"Action" : [ ] string { "AssumeRole" } ,
"DurationSeconds" : [ ] string { "900" } ,
"RoleArn" : [ ] string { awsbase . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { awsbase . MockStsAssumeRoleSessionName } ,
"Tags.member.1.Key" : [ ] string { awsbase . MockStsAssumeRoleTagKey } ,
"Tags.member.1.Value" : [ ] string { awsbase . MockStsAssumeRoleTagValue } ,
"TransitiveTagKeys.member.1" : [ ] string { awsbase . MockStsAssumeRoleTagKey } ,
"RoleArn" : [ ] string { servicemocks . MockStsAssumeRoleArn } ,
"RoleSessionName" : [ ] string { servicemocks . MockStsAssumeRoleSessionName } ,
"Tags.member.1.Key" : [ ] string { servicemocks . MockStsAssumeRoleTagKey } ,
"Tags.member.1.Value" : [ ] string { servicemocks . MockStsAssumeRoleTagValue } ,
"TransitiveTagKeys.member.1" : [ ] string { servicemocks . MockStsAssumeRoleTagKey } ,
"Version" : [ ] string { "2011-06-15" } ,
} . Encode ( ) } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsAssumeRoleValidResponseBody , ContentType : "text/xml" } ,
} ,
{
Request : & awsbase . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & awsbase . MockResponse { StatusCode : 200 , Body : awsbase . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
Request : & servicemocks . MockRequest { Method : "POST" , Uri : "/" , Body : mockStsGetCallerIdentityRequestBody } ,
Response : & servicemocks . MockResponse { StatusCode : 200 , Body : servicemocks . MockStsGetCallerIdentityValidResponseBody , ContentType : "text/xml" } ,
} ,
} ,
} ,
@ -524,16 +550,10 @@ func TestBackendConfig_AssumeRole(t *testing.T) {
testCase := testCase
t . Run ( testCase . Description , func ( t * testing . T ) {
closeSts , mockStsSession, err := awsbase . GetMockedAwsApiSession ( "STS" , testCase . MockStsEndpoints )
closeSts , _, stsEndpoint := mockdata . GetMockedAwsApiSession ( "STS" , testCase . MockStsEndpoints )
defer closeSts ( )
if err != nil {
t . Fatalf ( "unexpected error creating mock STS server: %s" , err )
}
if mockStsSession != nil && mockStsSession . Config != nil {
testCase . Config [ "sts_endpoint" ] = aws . StringValue ( mockStsSession . Config . Endpoint )
}
testCase . Config [ "sts_endpoint" ] = stsEndpoint
b := New ( )
diags := b . Configure ( populateSchema ( t , b . ConfigSchema ( ) , hcl2shim . HCL2ValueFromConfigValue ( testCase . Config ) ) )
@ -743,6 +763,8 @@ func TestBackendConfig_PrepareConfigWithEnvVars(t *testing.T) {
func TestBackend ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
bucketName := fmt . Sprintf ( "terraform-remote-s3-test-%x" , time . Now ( ) . Unix ( ) )
keyName := "testState"
@ -753,8 +775,8 @@ func TestBackend(t *testing.T) {
"region" : "us-west-1" ,
} ) ) . ( * Backend )
createS3Bucket ( t, b . s3Client , bucketName )
defer deleteS3Bucket ( t, b . s3Client , bucketName )
createS3Bucket ( ctx, t, b . s3Client , bucketName , b . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b . s3Client , bucketName )
backend . TestBackendStates ( t , b )
}
@ -762,6 +784,8 @@ func TestBackend(t *testing.T) {
func TestBackendLocked ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
bucketName := fmt . Sprintf ( "terraform-remote-s3-test-%x" , time . Now ( ) . Unix ( ) )
keyName := "test/state"
@ -781,10 +805,10 @@ func TestBackendLocked(t *testing.T) {
"region" : "us-west-1" ,
} ) ) . ( * Backend )
createS3Bucket ( t, b1 . s3Client , bucketName )
defer deleteS3Bucket ( t, b1 . s3Client , bucketName )
createDynamoDBTable ( t, b1 . dynClient , bucketName )
defer deleteDynamoDBTable ( t, b1 . dynClient , bucketName )
createS3Bucket ( ctx, t, b1 . s3Client , bucketName , b1 . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b1 . s3Client , bucketName )
createDynamoDBTable ( ctx, t, b1 . dynClient , bucketName )
defer deleteDynamoDBTable ( ctx, t, b1 . dynClient , bucketName )
backend . TestBackendStateLocks ( t , b1 , b2 )
backend . TestBackendStateForceUnlock ( t , b1 , b2 )
@ -793,6 +817,8 @@ func TestBackendLocked(t *testing.T) {
func TestBackendSSECustomerKeyConfig ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
testCases := map [ string ] struct {
customerKey string
expectedErr string
@ -843,8 +869,8 @@ func TestBackendSSECustomerKeyConfig(t *testing.T) {
t . Fatal ( "unexpected value for customer encryption key" )
}
createS3Bucket ( t, b . s3Client , bucketName )
defer deleteS3Bucket ( t, b . s3Client , bucketName )
createS3Bucket ( ctx, t, b . s3Client , bucketName , b . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b . s3Client , bucketName )
backend . TestBackendStates ( t , b )
}
@ -855,6 +881,8 @@ func TestBackendSSECustomerKeyConfig(t *testing.T) {
func TestBackendSSECustomerKeyEnvVar ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
testCases := map [ string ] struct {
customerKey string
expectedErr string
@ -909,8 +937,8 @@ func TestBackendSSECustomerKeyEnvVar(t *testing.T) {
t . Fatal ( "unexpected value for customer encryption key" )
}
createS3Bucket ( t, b . s3Client , bucketName )
defer deleteS3Bucket ( t, b . s3Client , bucketName )
createS3Bucket ( ctx, t, b . s3Client , bucketName , b . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b . s3Client , bucketName )
backend . TestBackendStates ( t , b )
}
@ -921,6 +949,9 @@ func TestBackendSSECustomerKeyEnvVar(t *testing.T) {
// add some extra junk in S3 to try and confuse the env listing.
func TestBackendExtraPaths ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
bucketName := fmt . Sprintf ( "terraform-remote-s3-test-%x" , time . Now ( ) . Unix ( ) )
keyName := "test/state/tfstate"
@ -930,8 +961,8 @@ func TestBackendExtraPaths(t *testing.T) {
"encrypt" : true ,
} ) ) . ( * Backend )
createS3Bucket ( t, b . s3Client , bucketName )
defer deleteS3Bucket ( t, b . s3Client , bucketName )
createS3Bucket ( ctx, t, b . s3Client , bucketName , b . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b . s3Client , bucketName )
// put multiple states in old env paths.
s1 := states . NewState ( )
@ -1060,6 +1091,9 @@ func TestBackendExtraPaths(t *testing.T) {
// of the workspace name itself.
func TestBackendPrefixInWorkspace ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
bucketName := fmt . Sprintf ( "terraform-remote-s3-test-%x" , time . Now ( ) . Unix ( ) )
b := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( map [ string ] interface { } {
@ -1068,8 +1102,8 @@ func TestBackendPrefixInWorkspace(t *testing.T) {
"workspace_key_prefix" : "env" ,
} ) ) . ( * Backend )
createS3Bucket ( t, b . s3Client , bucketName )
defer deleteS3Bucket ( t, b . s3Client , bucketName )
createS3Bucket ( ctx, t, b . s3Client , bucketName , b . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b . s3Client , bucketName )
// get a state that contains the prefix as a substring
sMgr , err := b . StateMgr ( "env-1" )
@ -1087,6 +1121,9 @@ func TestBackendPrefixInWorkspace(t *testing.T) {
func TestKeyEnv ( t * testing . T ) {
testACC ( t )
ctx := context . TODO ( )
keyName := "some/paths/tfstate"
bucket0Name := fmt . Sprintf ( "terraform-remote-s3-test-%x-0" , time . Now ( ) . Unix ( ) )
@ -1097,8 +1134,8 @@ func TestKeyEnv(t *testing.T) {
"workspace_key_prefix" : "" ,
} ) ) . ( * Backend )
createS3Bucket ( t, b0 . s3Client , bucket0Name )
defer deleteS3Bucket ( t, b0 . s3Client , bucket0Name )
createS3Bucket ( ctx, t, b0 . s3Client , bucket0Name , b0 . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b0 . s3Client , bucket0Name )
bucket1Name := fmt . Sprintf ( "terraform-remote-s3-test-%x-1" , time . Now ( ) . Unix ( ) )
b1 := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( map [ string ] interface { } {
@ -1108,8 +1145,8 @@ func TestKeyEnv(t *testing.T) {
"workspace_key_prefix" : "project/env:" ,
} ) ) . ( * Backend )
createS3Bucket ( t, b1 . s3Client , bucket1Name )
defer deleteS3Bucket ( t, b1 . s3Client , bucket1Name )
createS3Bucket ( ctx, t, b1 . s3Client , bucket1Name , b1 . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b1 . s3Client , bucket1Name )
bucket2Name := fmt . Sprintf ( "terraform-remote-s3-test-%x-2" , time . Now ( ) . Unix ( ) )
b2 := backend . TestBackendConfig ( t , New ( ) , backend . TestWrapConfig ( map [ string ] interface { } {
@ -1118,8 +1155,8 @@ func TestKeyEnv(t *testing.T) {
"encrypt" : true ,
} ) ) . ( * Backend )
createS3Bucket ( t, b2 . s3Client , bucket2Name )
defer deleteS3Bucket ( t, b2 . s3Client , bucket2Name )
createS3Bucket ( ctx, t, b2 . s3Client , bucket2Name , b2 . awsConfig . Region )
defer deleteS3Bucket ( ctx, t, b2 . s3Client , bucket2Name )
if err := testGetWorkspaceForKey ( b0 , "some/paths/tfstate" , "" ) ; err != nil {
t . Fatal ( err )
@ -1337,65 +1374,70 @@ func checkStateList(b backend.Backend, expected []string) error {
return nil
}
func createS3Bucket ( t * testing . T , s3Client * s3 . S3, bucketName string ) {
func createS3Bucket ( ctx context . Context , t * testing . T , s3Client * s3 . Client, bucketName , region string ) {
createBucketReq := & s3 . CreateBucketInput {
Bucket : & bucketName ,
}
if region != "us-east-1" {
createBucketReq . CreateBucketConfiguration = & s3types . CreateBucketConfiguration {
LocationConstraint : s3types . BucketLocationConstraint ( region ) ,
}
}
// Be clear about what we're doing in case the user needs to clean
// this up later.
t . Logf ( "creating S3 bucket %s in %s" , bucketName , aws . StringValue ( s3Client . Config . Region ) )
_ , err := s3Client . CreateBucket ( createBucketReq )
t . Logf ( "creating S3 bucket %s in %s" , bucketName , region)
_ , err := s3Client . CreateBucket ( c tx, c reateBucketReq)
if err != nil {
t . Fatal ( "failed to create test S3 bucket:" , err )
}
}
func deleteS3Bucket ( t * testing . T , s3Client * s3 . S3 , bucketName string ) {
func deleteS3Bucket ( ctx context . Context , t * testing . T , s3Client * s3 . Client , bucketName string ) {
warning := "WARNING: Failed to delete the test S3 bucket. It may have been left in your AWS account and may incur storage charges. (error was %s)"
// first we have to get rid of the env objects, or we can't delete the bucket
resp , err := s3Client . ListObjects ( & s3 . ListObjectsInput { Bucket : & bucketName } )
resp , err := s3Client . ListObjects ( ctx , & s3 . ListObjectsInput { Bucket : & bucketName } )
if err != nil {
t . Logf ( warning , err )
return
}
for _ , obj := range resp . Contents {
if _ , err := s3Client . DeleteObject ( & s3 . DeleteObjectInput { Bucket : & bucketName , Key : obj . Key } ) ; err != nil {
if _ , err := s3Client . DeleteObject ( ctx , & s3 . DeleteObjectInput { Bucket : & bucketName , Key : obj . Key } ) ; err != nil {
// this will need cleanup no matter what, so just warn and exit
t . Logf ( warning , err )
return
}
}
if _ , err := s3Client . DeleteBucket ( & s3 . DeleteBucketInput { Bucket : & bucketName } ) ; err != nil {
if _ , err := s3Client . DeleteBucket ( ctx , & s3 . DeleteBucketInput { Bucket : & bucketName } ) ; err != nil {
t . Logf ( warning , err )
}
}
// create the dynamoDB table, and wait until we can query it.
func createDynamoDBTable ( t * testing . T , dynClient * dynamodb . DynamoDB , tableName string ) {
func createDynamoDBTable ( ctx context . Context , t * testing . T , dynClient * dynamodb . Client , tableName string ) {
createInput := & dynamodb . CreateTableInput {
AttributeDefinitions : [ ] * dynamodb . AttributeDefinition {
AttributeDefinitions : [ ] dynamodb types . AttributeDefinition {
{
AttributeName : aws . String ( "LockID" ) ,
AttributeType : aws. String ( "S" ) ,
AttributeType : dynamodbtypes. ScalarAttributeTypeS ,
} ,
} ,
KeySchema : [ ] * dynamodb . KeySchemaElement {
KeySchema : [ ] dynamodb types . KeySchemaElement {
{
AttributeName : aws . String ( "LockID" ) ,
KeyType : aws. String ( "HASH" ) ,
KeyType : dynamodbtypes. KeyTypeHash ,
} ,
} ,
ProvisionedThroughput : & dynamodb . ProvisionedThroughput {
ProvisionedThroughput : & dynamodb types . ProvisionedThroughput {
ReadCapacityUnits : aws . Int64 ( 5 ) ,
WriteCapacityUnits : aws . Int64 ( 5 ) ,
} ,
TableName : aws . String ( tableName ) ,
}
_ , err := dynClient . CreateTable ( c reateInput)
_ , err := dynClient . CreateTable ( c tx, c reateInput)
if err != nil {
t . Fatal ( err )
}
@ -1409,12 +1451,12 @@ func createDynamoDBTable(t *testing.T, dynClient *dynamodb.DynamoDB, tableName s
}
for {
resp , err := dynClient . DescribeTable ( describeInput)
resp , err := dynClient . DescribeTable ( ctx, describeInput)
if err != nil {
t . Fatal ( err )
}
if * resp . Table . TableStatus == "ACTIVE" {
if resp . Table . TableStatus == dynamodbtypes . TableStatusActive {
return
}
@ -1427,11 +1469,11 @@ func createDynamoDBTable(t *testing.T, dynClient *dynamodb.DynamoDB, tableName s
}
func deleteDynamoDBTable ( t * testing . T , dynClient * dynamodb . DynamoDB , tableName string ) {
func deleteDynamoDBTable ( ctx context . Context , t * testing . T , dynClient * dynamodb . Client , tableName string ) {
params := & dynamodb . DeleteTableInput {
TableName : aws . String ( tableName ) ,
}
_ , err := dynClient . DeleteTable ( params)
_ , err := dynClient . DeleteTable ( ctx, params)
if err != nil {
t . Logf ( "WARNING: Failed to delete the test DynamoDB table %q. It has been left in your AWS account and may incur charges. (error was %s)" , tableName , err )
}