From 9c4f570d9b613ebbcc13e44a0d79e087eee497a2 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Mon, 8 Mar 2021 00:12:29 -0500 Subject: [PATCH] provider-level default tags support --- .changelog/17941.txt | 3 + aws/config.go | 9 +- aws/internal/keyvaluetags/key_value_tags.go | 76 +++ .../keyvaluetags/key_value_tags_test.go | 502 ++++++++++++++++++ aws/provider.go | 32 ++ aws/provider_test.go | 335 +++++++++--- 6 files changed, 873 insertions(+), 84 deletions(-) create mode 100644 .changelog/17941.txt diff --git a/.changelog/17941.txt b/.changelog/17941.txt new file mode 100644 index 00000000000..aab4db303e4 --- /dev/null +++ b/.changelog/17941.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +provider: Add `default_tags` argument (in public preview, see note above) +``` diff --git a/aws/config.go b/aws/config.go index 0493b8d71a0..fae75f76583 100644 --- a/aws/config.go +++ b/aws/config.go @@ -191,9 +191,10 @@ type Config struct { AllowedAccountIds []string ForbiddenAccountIds []string - Endpoints map[string]string - IgnoreTagsConfig *keyvaluetags.IgnoreConfig - Insecure bool + DefaultTagsConfig *keyvaluetags.DefaultConfig + Endpoints map[string]string + IgnoreTagsConfig *keyvaluetags.IgnoreConfig + Insecure bool SkipCredsValidation bool SkipGetEC2Platforms bool @@ -249,6 +250,7 @@ type AWSClient struct { datapipelineconn *datapipeline.DataPipeline datasyncconn *datasync.DataSync daxconn *dax.DAX + DefaultTagsConfig *keyvaluetags.DefaultConfig devicefarmconn *devicefarm.DeviceFarm dlmconn *dlm.DLM dmsconn *databasemigrationservice.DatabaseMigrationService @@ -492,6 +494,7 @@ func (c *Config) Client() (interface{}, error) { datapipelineconn: datapipeline.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["datapipeline"])})), datasyncconn: datasync.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["datasync"])})), daxconn: dax.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["dax"])})), + DefaultTagsConfig: c.DefaultTagsConfig, devicefarmconn: devicefarm.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["devicefarm"])})), dlmconn: dlm.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["dlm"])})), dmsconn: databasemigrationservice.New(sess.Copy(&aws.Config{Endpoint: aws.String(c.Endpoints["dms"])})), diff --git a/aws/internal/keyvaluetags/key_value_tags.go b/aws/internal/keyvaluetags/key_value_tags.go index f9af201a7c0..eb69d119a61 100644 --- a/aws/internal/keyvaluetags/key_value_tags.go +++ b/aws/internal/keyvaluetags/key_value_tags.go @@ -25,6 +25,11 @@ const ( ServerlessApplicationRepositoryTagKeyPrefix = `serverlessrepo:` ) +// DefaultConfig contains tags to default across all resources. +type DefaultConfig struct { + Tags KeyValueTags +} + // IgnoreConfig contains various options for removing resource tags. type IgnoreConfig struct { Keys KeyValueTags @@ -50,6 +55,32 @@ func (tags KeyValueTags) IgnoreAws() KeyValueTags { return result } +// Merge calls keyvaluetags.Merge() on the given DefaultConfig.Tags, if any, +// with KeyValueTags provided as an argument, overriding the value +// of any tag with a matching key. +func (config *DefaultConfig) Merge(tags KeyValueTags) KeyValueTags { + if len(config.Tags) == 0 { + return tags + } + + return config.Tags.Merge(tags) +} + +// TagsEqual returns true if the given configuration's Tags +// are equal to those passed in as an argument; +// otherwise returns false +func (config *DefaultConfig) TagsEqual(tags KeyValueTags) bool { + if len(config.Tags) == 0 { + return len(tags) == 0 + } + + if len(tags) == 0 { + return false + } + + return config.Tags.ContainsAll(tags) +} + // IgnoreConfig returns any tags not removed by a given configuration. func (tags KeyValueTags) IgnoreConfig(config *IgnoreConfig) KeyValueTags { if config == nil { @@ -401,6 +432,27 @@ func (tags KeyValueTags) Hash() int { return hash } +// RemoveDefaultConfig returns tags not present in a DefaultConfig object +// in addition to tags with key/value pairs that override those in a DefaultConfig; +// however, if all tags present in the DefaultConfig object are equivalent to those +// in the given KeyValueTags, then the KeyValueTags are returned, effectively +// bypassing the need to remove differing tags. +func (tags KeyValueTags) RemoveDefaultConfig(config *DefaultConfig) KeyValueTags { + if config == nil || len(config.Tags) == 0 { + return tags + } + + result := make(KeyValueTags) + + for k, v := range tags { + if defaultVal, ok := config.Tags[k]; !ok || !v.Equal(defaultVal) { + result[k] = v + } + } + + return result +} + // String returns the default string representation of the KeyValueTags. func (tags KeyValueTags) String() string { var builder strings.Builder @@ -435,6 +487,30 @@ func (tags KeyValueTags) UrlEncode() string { return values.Encode() } +// MergeConfigTags creates KeyValueTags by merging KeyValueTags nested in +// a configuration object with those represented as a map[string]interface{}. +// Currently only supports the DefaultConfig type when passed to the interface{} argument. +func MergeConfigTags(config interface{}, tags map[string]interface{}) KeyValueTags { + kvTags := New(tags) + + if config == nil { + return kvTags + } + + var result KeyValueTags + + switch t := config.(type) { + case *DefaultConfig: + if t == nil || len(t.Tags) == 0 { + result = kvTags + } else { + result = t.Merge(kvTags) + } + } + + return result +} + // New creates KeyValueTags from common Terraform Provider SDK types. // Supports map[string]string, map[string]*string, map[string]interface{}, and []interface{}. // When passed []interface{}, all elements are treated as keys and assigned nil values. diff --git a/aws/internal/keyvaluetags/key_value_tags_test.go b/aws/internal/keyvaluetags/key_value_tags_test.go index 08a9376a2bc..289df0a9151 100644 --- a/aws/internal/keyvaluetags/key_value_tags_test.go +++ b/aws/internal/keyvaluetags/key_value_tags_test.go @@ -4,6 +4,239 @@ import ( "testing" ) +func TestKeyValueTagsDefaultConfigMerge(t *testing.T) { + testCases := []struct { + name string + tags KeyValueTags + defaultConfig *DefaultConfig + want map[string]string + }{ + { + name: "empty config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{}, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: nil, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no tags", + tags: New(map[string]string{}), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys all matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys some matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys some overridden", + tags: New(map[string]string{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys none matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key4": "value4", + "key5": "value5", + "key6": "value6", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + "key6": "value6", + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var got KeyValueTags + if testCase.defaultConfig != nil { + got = testCase.defaultConfig.Merge(testCase.tags) + } else { + got = testCase.tags + } + + testKeyValueTagsVerifyMap(t, got.Map(), testCase.want) + }) + } +} + +func TestKeyValueTagsDefaultConfigTagsEqual(t *testing.T) { + testCases := []struct { + name string + tags KeyValueTags + defaultConfig *DefaultConfig + want bool + }{ + { + name: "empty config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{}, + want: false, + }, + { + name: "no config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: nil, + want: false, + }, + { + name: "no tags", + tags: New(map[string]string{}), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: false, + }, + { + name: "keys and values all matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: true, + }, + { + name: "only keys matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value0", + "key2": "value1", + "key3": "value2", + }), + }, + want: false, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var got bool + if testCase.defaultConfig != nil { + got = testCase.defaultConfig.TagsEqual(testCase.tags) + } + + if got != testCase.want { + t.Errorf("got %t; want %t", got, testCase.want) + } + }) + } +} + func TestKeyValueTagsIgnoreAws(t *testing.T) { testCases := []struct { name string @@ -1578,6 +1811,135 @@ func TestKeyValueTagsHash(t *testing.T) { } } +func TestKeyValueTagsRemoveDefaultConfig(t *testing.T) { + testCases := []struct { + name string + tags KeyValueTags + defaultConfig *DefaultConfig + want map[string]string + }{ + { + name: "empty config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{}, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no config", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: nil, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no tags", + tags: New(map[string]string{}), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{}, + }, + { + name: "keys all matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{}, + }, + { + name: "keys some matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys some overridden", + tags: New(map[string]string{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys none matching", + tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key4": "value4", + "key5": "value5", + "key6": "value6", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + got := testCase.tags.RemoveDefaultConfig(testCase.defaultConfig) + + testKeyValueTagsVerifyMap(t, got.Map(), testCase.want) + }) + } +} + func TestKeyValueTagsUrlEncode(t *testing.T) { testCases := []struct { name string @@ -1634,6 +1996,146 @@ func TestKeyValueTagsUrlEncode(t *testing.T) { } } +func TestMergeConfigTags(t *testing.T) { + testCases := []struct { + name string + tags map[string]interface{} + defaultConfig *DefaultConfig + want map[string]string + }{ + { + name: "empty config", + tags: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: &DefaultConfig{}, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no config", + tags: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: nil, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "no tags", + tags: map[string]interface{}{}, + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys all matching", + tags: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys some matching", + tags: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys some overridden", + tags: map[string]interface{}{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key1": "value1", + }), + }, + want: map[string]string{ + "key1": "value2", + "key2": "value2", + "key3": "value3", + }, + }, + { + name: "keys none matching", + tags: map[string]interface{}{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + }, + defaultConfig: &DefaultConfig{ + Tags: New(map[string]string{ + "key4": "value4", + "key5": "value5", + "key6": "value6", + }), + }, + want: map[string]string{ + "key1": "value1", + "key2": "value2", + "key3": "value3", + "key4": "value4", + "key5": "value5", + "key6": "value6", + }, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + got := MergeConfigTags(testCase.defaultConfig, testCase.tags) + + testKeyValueTagsVerifyMap(t, got.Map(), testCase.want) + }) + } +} + func TestTagDataEqual(t *testing.T) { testCases := []struct { name string diff --git a/aws/provider.go b/aws/provider.go index 5de81c4636c..3010d126756 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -89,6 +89,23 @@ func Provider() *schema.Provider { Set: schema.HashString, }, + "default_tags": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "Configuration block with settings to default resource tags across all resources.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "tags": { + Type: schema.TypeMap, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Resource tags to default across all resources", + }, + }, + }, + }, + "endpoints": endpointsSchema(), "ignore_tags": { @@ -1337,6 +1354,7 @@ func providerConfigure(d *schema.ResourceData, terraformVersion string) (interfa Token: d.Get("token").(string), Region: d.Get("region").(string), CredsFilename: d.Get("shared_credentials_file").(string), + DefaultTagsConfig: expandProviderDefaultTags(d.Get("default_tags").([]interface{})), Endpoints: make(map[string]string), MaxRetries: d.Get("max_retries").(int), IgnoreTagsConfig: expandProviderIgnoreTags(d.Get("ignore_tags").([]interface{})), @@ -1522,6 +1540,20 @@ func endpointsSchema() *schema.Schema { } } +func expandProviderDefaultTags(l []interface{}) *keyvaluetags.DefaultConfig { + if len(l) == 0 || l[0] == nil { + return nil + } + + defaultConfig := &keyvaluetags.DefaultConfig{} + m := l[0].(map[string]interface{}) + + if v, ok := m["tags"].(map[string]interface{}); ok { + defaultConfig.Tags = keyvaluetags.New(v) + } + return defaultConfig +} + func expandProviderIgnoreTags(l []interface{}) *keyvaluetags.IgnoreConfig { if len(l) == 0 || l[0] == nil { return nil diff --git a/aws/provider_test.go b/aws/provider_test.go index 70af59809db..d579883dd66 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -1269,6 +1269,81 @@ func TestAccAWSProvider_IgnoreTags_Keys_Multiple(t *testing.T) { }) } +func TestAccProvider_DefaultTags_EmptyConfigurationBlock(t *testing.T) { + var providers []*schema.Provider + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactoriesInternal(&providers), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccProviderConfigDefaultTagsEmptyConfigurationBlock(), + Check: resource.ComposeTestCheckFunc( + testAccCheckProviderDefaultTags_Tags(&providers, map[string]string{}), + ), + }, + }, + }) +} + +func TestAccProvider_DefaultTags_Tags_None(t *testing.T) { + var providers []*schema.Provider + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactoriesInternal(&providers), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccProviderConfigDefaultTags_Tags0(), + Check: resource.ComposeTestCheckFunc( + testAccCheckProviderDefaultTags_Tags(&providers, map[string]string{}), + ), + }, + }, + }) +} + +func TestAccProvider_DefaultTags_Tags_One(t *testing.T) { + var providers []*schema.Provider + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactoriesInternal(&providers), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccProviderConfigDefaultTags_Tags1("test", "value"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProviderDefaultTags_Tags(&providers, map[string]string{"test": "value"}), + ), + }, + }, + }) +} + +func TestAccProvider_DefaultTags_Tags_Multiple(t *testing.T) { + var providers []*schema.Provider + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: testAccProviderFactoriesInternal(&providers), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testAccProviderConfigDefaultTags_Tags2("test1", "value1", "test2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckProviderDefaultTags_Tags(&providers, map[string]string{ + "test1": "value1", + "test2": "value2", + }), + ), + }, + }, + }) +} + func TestAccAWSProvider_Region_AwsC2S(t *testing.T) { var providers []*schema.Provider @@ -1613,6 +1688,69 @@ func testAccCheckAWSProviderIgnoreTagsKeys(providers *[]*schema.Provider, expect } } +func testAccCheckProviderDefaultTags_Tags(providers *[]*schema.Provider, expectedTags map[string]string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if providers == nil { + return fmt.Errorf("no providers initialized") + } + + for _, provider := range *providers { + if provider == nil || provider.Meta() == nil || provider.Meta().(*AWSClient) == nil { + continue + } + + providerClient := provider.Meta().(*AWSClient) + defaultTagsConfig := providerClient.DefaultTagsConfig + + if defaultTagsConfig == nil || len(defaultTagsConfig.Tags) == 0 { + if len(expectedTags) != 0 { + return fmt.Errorf("expected keys (%d) length, got: 0", len(expectedTags)) + } + + continue + } + + actualTags := defaultTagsConfig.Tags + + if len(actualTags) != len(expectedTags) { + return fmt.Errorf("expected tags (%d) length, got: %d", len(expectedTags), len(actualTags)) + } + + for _, expectedElement := range expectedTags { + var found bool + + for _, actualElement := range actualTags { + if *actualElement.Value == expectedElement { + found = true + break + } + } + + if !found { + return fmt.Errorf("expected tags element, but was missing: %s", expectedElement) + } + } + + for _, actualElement := range actualTags { + var found bool + + for _, expectedElement := range expectedTags { + if *actualElement.Value == expectedElement { + found = true + break + } + } + + if !found { + return fmt.Errorf("unexpected tags element: %s", actualElement) + } + } + } + + return nil + } +} + func testAccCheckAWSProviderPartition(providers *[]*schema.Provider, expectedPartition string) resource.TestCheckFunc { return func(s *terraform.State) error { if providers == nil { @@ -1711,7 +1849,9 @@ func testAccDefaultSubnetCount(t *testing.T) int { func testAccAWSProviderConfigEndpoints(endpoints string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { skip_credentials_validation = true skip_get_ec2_platforms = true @@ -1722,19 +1862,14 @@ provider "aws" { %[1]s } } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, endpoints) +`, endpoints)) } func testAccAWSProviderConfigIgnoreTagsEmptyConfigurationBlock() string { //lintignore:AT004 - return ` + return composeConfig( + testAccProviderConfigBase, + ` provider "aws" { ignore_tags {} @@ -1743,38 +1878,28 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -` +`) } func testAccAWSProviderConfigIgnoreTagsKeyPrefixes0() string { //lintignore:AT004 - return ` + return composeConfig( + testAccProviderConfigBase, + ` provider "aws" { skip_credentials_validation = true skip_get_ec2_platforms = true skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -` +`) } func testAccAWSProviderConfigIgnoreTagsKeyPrefixes1(tagPrefix1 string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { ignore_tags { key_prefixes = [%[1]q] @@ -1785,19 +1910,14 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, tagPrefix1) +`, tagPrefix1)) } func testAccAWSProviderConfigIgnoreTagsKeyPrefixes2(tagPrefix1, tagPrefix2 string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { ignore_tags { key_prefixes = [%[1]q, %[2]q] @@ -1808,38 +1928,28 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, tagPrefix1, tagPrefix2) +`, tagPrefix1, tagPrefix2)) } func testAccAWSProviderConfigIgnoreTagsKeys0() string { //lintignore:AT004 - return ` + return composeConfig( + testAccProviderConfigBase, + ` provider "aws" { skip_credentials_validation = true skip_get_ec2_platforms = true skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -` +`) } func testAccAWSProviderConfigIgnoreTagsKeys1(tag1 string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { ignore_tags { keys = [%[1]q] @@ -1850,19 +1960,14 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, tag1) +`, tag1)) } func testAccAWSProviderConfigIgnoreTagsKeys2(tag1, tag2 string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { ignore_tags { keys = [%[1]q, %[2]q] @@ -1873,19 +1978,14 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, tag1, tag2) +`, tag1, tag2)) } func testAccAWSProviderConfigRegion(region string) string { //lintignore:AT004 - return fmt.Sprintf(` + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` provider "aws" { region = %[1]q skip_credentials_validation = true @@ -1893,14 +1993,7 @@ provider "aws" { skip_metadata_api_check = true skip_requesting_account_id = true } - -data "aws_partition" "provider_test" {} - -# Required to initialize the provider -data "aws_arn" "test" { - arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" -} -`, region) +`, region)) } func testAccAssumeRoleARNPreCheck(t *testing.T) { @@ -1928,6 +2021,86 @@ provider "aws" { data "aws_caller_identity" "current" {} ` //lintignore:AT004 +const testAccProviderConfigBase = ` +data "aws_partition" "provider_test" {} + +# Required to initialize the provider +data "aws_arn" "test" { + arn = "arn:${data.aws_partition.provider_test.partition}:s3:::test" +} +` + +func testAccProviderConfigDefaultTagsEmptyConfigurationBlock() string { + //lintignore:AT004 + return composeConfig( + testAccProviderConfigBase, + ` +provider "aws" { + default_tags {} + + skip_credentials_validation = true + skip_get_ec2_platforms = true + skip_metadata_api_check = true + skip_requesting_account_id = true +} +`) +} + +func testAccProviderConfigDefaultTags_Tags0() string { + //lintignore:AT004 + return composeConfig( + testAccProviderConfigBase, + ` +provider "aws" { + skip_credentials_validation = true + skip_get_ec2_platforms = true + skip_metadata_api_check = true + skip_requesting_account_id = true +} +`) +} + +func testAccProviderConfigDefaultTags_Tags1(tag1, value1 string) string { + //lintignore:AT004 + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` +provider "aws" { + default_tags { + tags = { + %q = %q + } + } + + skip_credentials_validation = true + skip_get_ec2_platforms = true + skip_metadata_api_check = true + skip_requesting_account_id = true +} +`, tag1, value1)) +} + +func testAccProviderConfigDefaultTags_Tags2(tag1, value1, tag2, value2 string) string { + //lintignore:AT004 + return composeConfig( + testAccProviderConfigBase, + fmt.Sprintf(` +provider "aws" { + default_tags { + tags = { + %q = %q + %q = %q + } + } + + skip_credentials_validation = true + skip_get_ec2_platforms = true + skip_metadata_api_check = true + skip_requesting_account_id = true +} +`, tag1, value1, tag2, value2)) +} + func testCheckResourceAttrIsSortedCsv(resourceName, attributeName string) resource.TestCheckFunc { return func(s *terraform.State) error { is, err := primaryInstanceState(s, resourceName)