Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tagging tests: Adds tests for ignoring tags #39734

Merged
merged 62 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
e0fd19f
Adds tests for `ignore_tags` on creation for `batch`
gdavison May 29, 2024
d40c9af
Adds interface for `ListTags` functions
gdavison May 29, 2024
001047d
Uses valid signature for `mockService` `UpdateTags` function
gdavison May 29, 2024
7332514
Adds interface for `UpdateTags` functions
gdavison May 29, 2024
c36b204
Generates `expectFullTags` test for `batch`
gdavison May 30, 2024
66b4544
Adds test steps for updating ignored tags for `batch`
gdavison May 30, 2024
1631626
Adds test for default tags to data sources
gdavison Sep 28, 2024
52fa559
Adds test for ignoring default tag
gdavison Oct 4, 2024
8597eea
Adds test for ignoring resource tag
gdavison Oct 4, 2024
3f0bb11
`accessanalyzer`
gdavison May 30, 2024
59dfcf8
`acm`
gdavison May 30, 2024
3024331
`acmpca`
gdavison May 30, 2024
43a0894
`iam`
gdavison May 31, 2024
1d3809e
Handles data sources which don't use `ListTags`
gdavison Oct 4, 2024
802ed43
`s3`
gdavison Jun 1, 2024
c336fb3
`xray`
gdavison Jun 1, 2024
6102c3d
Skip generating unused functions
gdavison Oct 5, 2024
1fbb40a
`amp`
gdavison Oct 5, 2024
78b6b2b
`amplify`
gdavison Oct 5, 2024
aaea950
`appautoscaling`
gdavison Oct 7, 2024
e50523a
`appconfig`
gdavison Oct 7, 2024
f9318a7
`apigatewayv2`
gdavison Oct 7, 2024
8bd9720
`appflow`
gdavison Oct 7, 2024
2107862
Adds check to tag test generator for missing tag identifier attribute…
gdavison Oct 7, 2024
db8ca71
`sqs`
gdavison Oct 7, 2024
b0a8519
`sns`
gdavison Oct 7, 2024
55f9904
Adds missing `arn` attributes.
gdavison Oct 7, 2024
84fe7fc
`apigateway`
gdavison Oct 7, 2024
c28a442
`apprunner`
gdavison Oct 8, 2024
787653e
`cloudwatch`
gdavison Oct 8, 2024
f227722
`appfabric`
gdavison Oct 8, 2024
6a33d16
`applicationinsights`
gdavison Oct 8, 2024
cd47f4b
`appintegrations`
gdavison Oct 8, 2024
0475359
`appmesh`
gdavison Oct 8, 2024
9702eab
`bcmdataexports`
gdavison Oct 8, 2024
3148288
`drs`
gdavison Oct 8, 2024
9192884
`logs`
gdavison Oct 8, 2024
e4ffbb7
Updates FullTags checks
gdavison Oct 8, 2024
42ab678
`kms`
gdavison Oct 11, 2024
d9bb551
`secretsmanager`
gdavison Oct 11, 2024
026d445
`m2`
gdavison Oct 11, 2024
52e55a6
`networkmonitor`
gdavison Oct 11, 2024
6539b37
`dynamodb`
gdavison Oct 11, 2024
04926b7
`cognitoidp`
gdavison Oct 11, 2024
88d4821
`timestreaminfluxdb`
gdavison Oct 11, 2024
b8ef99a
`lambda`
gdavison Oct 11, 2024
08719a3
`medialive`
gdavison Oct 12, 2024
a4440dc
`elbv2`
gdavison Oct 12, 2024
198d4d6
`ssm`
gdavison Oct 12, 2024
5c73b16
`rds`
gdavison Oct 13, 2024
608b9fe
Improves error message when `ListTags` requires `ResourceType`, but i…
gdavison Oct 15, 2024
d47269e
`ec2`
gdavison Oct 15, 2024
b737386
Adds annotation `tagsUpdateGetTagsIn` to ignore update errors
gdavison Oct 15, 2024
290e461
`servicecatalog`
gdavison Oct 15, 2024
8395e1b
`fms`
gdavison Oct 15, 2024
7858e67
Adds CHANGELOG entry
gdavison Oct 15, 2024
645ad91
Fixes `terraform_unused_declarations` `tflint` error
gdavison Oct 15, 2024
51ec676
Fixes attribute constants
gdavison Oct 15, 2024
645fd54
`sesv2`
gdavison Oct 16, 2024
760e0e3
`quicksight`
gdavison Oct 16, 2024
464bde4
`datapipeline`
gdavison Oct 16, 2024
96fbcc9
`dms`
gdavison Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions .changelog/39734.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
```release-note:bug
data-source/aws_batch_job_definition: Properly handles ignored tags.
```

```release-note:bug
resource/aws_dynamodb_table_replica: Properly handles default and ignored tags.
```

```release-note:bug
resource/aws_cognito_user_pool: Properly handles ignored tags.
```

```release-note:bug
data-source/aws_cognito_user_pool: Properly handles ignored tags.
```
48 changes: 48 additions & 0 deletions .ci/.semgrep-test-constants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,30 @@ rules:
options:
constant_propagation: false

- id: literal-ProviderValue1Again-string-test-constant
languages: [go]
message: Use the constant `acctest.CtProviderValue1Again` for the string literal "providervalue1again" in test files
paths:
include:
- "internal/service/**/*_test.go"
pattern: '"providervalue1again"'
severity: ERROR
fix: "acctest.CtProviderValue1Again"
options:
constant_propagation: false

- id: literal-ProviderValue1Updated-string-test-constant
languages: [go]
message: Use the constant `acctest.CtProviderValue1Updated` for the string literal "providervalue1updated" in test files
paths:
include:
- "internal/service/**/*_test.go"
pattern: '"providervalue1updated"'
severity: ERROR
fix: "acctest.CtProviderValue1Updated"
options:
constant_propagation: false

- id: literal-RName-string-test-constant
languages: [go]
message: Use the constant `acctest.CtRName` for the string literal "rName" in test files
Expand Down Expand Up @@ -324,6 +348,18 @@ rules:
options:
constant_propagation: false

- id: literal-ResourceValue1Again-string-test-constant
languages: [go]
message: Use the constant `acctest.CtResourceValue1Again` for the string literal "resourcevalue1again" in test files
paths:
include:
- "internal/service/**/*_test.go"
pattern: '"resourcevalue1again"'
severity: ERROR
fix: "acctest.CtResourceValue1Again"
options:
constant_propagation: false

- id: literal-ResourceValue1Updated-string-test-constant
languages: [go]
message: Use the constant `acctest.CtResourceValue1Updated` for the string literal "resourcevalue1updated" in test files
Expand All @@ -348,6 +384,18 @@ rules:
options:
constant_propagation: false

- id: literal-ResourceValue2Updated-string-test-constant
languages: [go]
message: Use the constant `acctest.CtResourceValue2Updated` for the string literal "resourcevalue2updated" in test files
paths:
include:
- "internal/service/**/*_test.go"
pattern: '"resourcevalue2updated"'
severity: ERROR
fix: "acctest.CtResourceValue2Updated"
options:
constant_propagation: false

- id: literal-RulePound-string-test-constant
languages: [go]
message: Use the constant `acctest.CtRulePound` for the string literal "rule.#" in test files
Expand Down
8 changes: 8 additions & 0 deletions .ci/semgrep/tags/update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ rules:
metavariable: "$FUNC"
regex: "^resource\\w*(Create|Put|Set|Upsert|Enable)$"
severity: WARNING

- id: attrtags-in-haschanges
languages: [go]
message: Do not include `names.AttrTags` in `HasChanges`, use `names.AttrTagsAll`
patterns:
- pattern: |
$D.HasChanges(..., names.AttrTags, ...)
severity: ERROR
2 changes: 1 addition & 1 deletion .teamcity/components/generated/services_all.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ val services = mapOf(
"appconfig" to ServiceSpec("AppConfig"),
"appfabric" to ServiceSpec("AppFabric", regionOverride = "us-east-1"),
"appflow" to ServiceSpec("AppFlow"),
"appintegrations" to ServiceSpec("AppIntegrations"),
"appintegrations" to ServiceSpec("AppIntegrations", parallelismOverride = 10),
"applicationinsights" to ServiceSpec("CloudWatch Application Insights"),
"applicationsignals" to ServiceSpec("Application Signals"),
"appmesh" to ServiceSpec("App Mesh"),
Expand Down
15 changes: 13 additions & 2 deletions docs/resource-tagging.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,12 @@ Most services can use a facility we call _transparent_ (or _implicit_) _tagging_
}
```

The `identifierAttribute` argument to the `@Tags` annotation identifies the attribute in the resource's schema whose value is used in tag listing and updating API calls. Common values are `"arn"` and "`id`".
Once the annotation has been added to the resource's code, run `make gen` to register the resource for transparent tagging. This will add an entry to the `service_package_gen.go` file located in the service package folder.
The `identifierAttribute` argument to the `@Tags` annotation identifies the attribute in the resource type's schema whose value is used in tag listing and updating API calls.
Common values are `"arn"` and `"id"`.
If the resource type does not need separate `createTags`, `listTags`, or `updateTags` functions, do not specify an `identifierAttribute`.

Once the annotation has been added to the resource's code, run `make gen` to register the resource for transparent tagging.
This will add an entry to the `service_package_gen.go` file located in the service package folder.

#### Resource Create Operation

Expand Down Expand Up @@ -559,6 +563,13 @@ Use the annotation `@Testing(checkDestroyNoop=true)`.
For some resource types, tags cannot be modified without recreating the resource.
Use the annotation `@Testing(tagsUpdateForceNew=true)`.

Resource types which pass the result of `getTagsIn` directly onto their Update Input may have an error where ignored tags are not correctly excluded from the update.
Use the annotation `@Testing(tagsUpdateGetTagsIn=true)`.

Some tests read the tag values directly from the AWS API.
If the resource type does not specify `identifierAttribute` in its `@Tags` annotation, specify a `@Testing(tagsIdentifierAttribute=<attribute name>)` annotation to identify which attribute value should be used by the `listTags` function.
If a resource type is also needed for the `listTags` function, also specify the `tagsResourceType` annotation.

At least one resource type, the Service Catalog Provisioned Product, does not support removing tags.
This is likely an error on the AWS side.
Add the annotation `@Testing(noRemoveTags=true)` as a workaround.
Expand Down
4 changes: 4 additions & 0 deletions internal/acctest/consts.csv
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ private_key_pem,PrivateKeyPEM
provider_tags,ProviderTags
providerkey1,ProviderKey1
providervalue1,ProviderValue1
providervalue1updated,ProviderValue1Updated
providervalue1again,ProviderValue1Again
rName,RName
resource_owner,ResourceOwner
resource_tags,ResourceTags
resourcekey1,ResourceKey1
resourcekey2,ResourceKey2
resourcevalue1,ResourceValue1
resourcevalue1updated,ResourceValue1Updated
resourcevalue1again,ResourceValue1Again
resourcevalue2,ResourceValue2
resourcevalue2updated,ResourceValue2Updated
rule.#,RulePound
tags.%,TagsPercent
tags.key1,TagsKey1
Expand Down
4 changes: 4 additions & 0 deletions internal/acctest/consts_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions internal/acctest/generate/const_or_quote_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

132 changes: 109 additions & 23 deletions internal/acctest/statecheck/full_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
"github.com/hashicorp/terraform-plugin-testing/statecheck"
Expand All @@ -19,10 +20,19 @@ import (

var _ statecheck.StateCheck = expectFullTagsCheck{}

type entity string

const (
entityResource entity = "resource"
entityDataSource entity = "data source"
)

type expectFullTagsCheck struct {
base Base
knownValue knownvalue.Check
servicePackage conns.ServicePackage
tagSpecFinder tagSpecFinder
entity entity
}

func (e expectFullTagsCheck) CheckState(ctx context.Context, req statecheck.CheckStateRequest, resp *statecheck.CheckStateResponse) {
Expand All @@ -31,36 +41,24 @@ func (e expectFullTagsCheck) CheckState(ctx context.Context, req statecheck.Chec
return
}

var tagsSpec *types.ServicePackageResourceTags
sp := e.servicePackage
for _, r := range sp.FrameworkResources(ctx) {
foo, _ := r.Factory(ctx)
var metadata resource.MetadataResponse
foo.Metadata(ctx, resource.MetadataRequest{}, &metadata)
if res.Type == metadata.TypeName {
tagsSpec = r.Tags
break
}
}
if tagsSpec == nil {
for _, r := range sp.SDKResources(ctx) {
if res.Type == r.TypeName {
tagsSpec = r.Tags
break
}
}
}

tagsSpec := e.tagSpecFinder(ctx, sp, res.Type)

if tagsSpec == nil {
resp.Error = fmt.Errorf("no tagging specification found for resource type %s", res.Type)
resp.Error = fmt.Errorf("no tagging specification found for %s type %s", e.entity, res.Type)
return
}

identifierAttr := tagsSpec.IdentifierAttribute
if identifierAttr == "" {
resp.Error = fmt.Errorf("no tag identifier attribute defined for %s type %s", e.entity, res.Type)
return
}

identifier, ok := res.AttributeValues[identifierAttr]
if !ok {
resp.Error = fmt.Errorf("attribute %q not found in resource %s", identifierAttr, e.base.ResourceAddress())
resp.Error = fmt.Errorf("attribute %q not found in %s %s", identifierAttr, e.entity, e.base.ResourceAddress())
return
}

Expand All @@ -69,8 +67,12 @@ func (e expectFullTagsCheck) CheckState(ctx context.Context, req statecheck.Chec
var err error
if v, ok := sp.(tftags.ServiceTagLister); ok {
err = v.ListTags(ctx, acctest.Provider.Meta(), identifier.(string)) // Sets tags in Context
} else if v, ok := sp.(tftags.ResourceTypeTagLister); ok && tagsSpec.ResourceType != "" {
err = v.ListTags(ctx, acctest.Provider.Meta(), identifier.(string), tagsSpec.ResourceType) // Sets tags in Context
} else if v, ok := sp.(tftags.ResourceTypeTagLister); ok {
if tagsSpec.ResourceType == "" {
err = fmt.Errorf("ListTags method for service %s requires ResourceType, but none was set", sp.ServicePackageName())
} else {
err = v.ListTags(ctx, acctest.Provider.Meta(), identifier.(string), tagsSpec.ResourceType) // Sets tags in Context
}
} else {
err = fmt.Errorf("no ListTags method found for service %s", sp.ServicePackageName())
}
Expand All @@ -93,6 +95,8 @@ func (e expectFullTagsCheck) CheckState(ctx context.Context, req statecheck.Chec
return
}

tags = tags.IgnoreSystem(sp.ServicePackageName())

tagsMap := tfmaps.ApplyToAllValues(tags.Map(), func(s string) any {
return s
})
Expand All @@ -103,10 +107,92 @@ func (e expectFullTagsCheck) CheckState(ctx context.Context, req statecheck.Chec
}
}

func ExpectFullTags(servicePackage conns.ServicePackage, resourceAddress string, knownValue knownvalue.Check) expectFullTagsCheck {
func ExpectFullResourceTags(servicePackage conns.ServicePackage, resourceAddress string, knownValue knownvalue.Check) expectFullTagsCheck {
return expectFullTagsCheck{
base: NewBase(resourceAddress),
knownValue: knownValue,
servicePackage: servicePackage,
tagSpecFinder: findResourceTagSpec,
entity: entityResource,
}
}

func ExpectFullResourceTagsSpecTags(servicePackage conns.ServicePackage, resourceAddress string, tagsSpec *types.ServicePackageResourceTags, knownValue knownvalue.Check) expectFullTagsCheck {
return expectFullTagsCheck{
base: NewBase(resourceAddress),
knownValue: knownValue,
servicePackage: servicePackage,
tagSpecFinder: identityTagSpec(tagsSpec),
entity: entityResource,
}
}

func ExpectFullDataSourceTags(servicePackage conns.ServicePackage, resourceAddress string, knownValue knownvalue.Check) expectFullTagsCheck {
return expectFullTagsCheck{
base: NewBase(resourceAddress),
knownValue: knownValue,
servicePackage: servicePackage,
tagSpecFinder: findDataSourceTagSpec,
entity: entityDataSource,
}
}

func ExpectFullDataSourceTagsSpecTags(servicePackage conns.ServicePackage, resourceAddress string, tagsSpec *types.ServicePackageResourceTags, knownValue knownvalue.Check) expectFullTagsCheck {
return expectFullTagsCheck{
base: NewBase(resourceAddress),
knownValue: knownValue,
servicePackage: servicePackage,
tagSpecFinder: identityTagSpec(tagsSpec),
entity: entityDataSource,
}
}

type tagSpecFinder func(context.Context, conns.ServicePackage, string) *types.ServicePackageResourceTags

func findResourceTagSpec(ctx context.Context, sp conns.ServicePackage, typeName string) (tagsSpec *types.ServicePackageResourceTags) {
for _, r := range sp.FrameworkResources(ctx) {
factory, _ := r.Factory(ctx)
var metadata resource.MetadataResponse
factory.Metadata(ctx, resource.MetadataRequest{}, &metadata)
if metadata.TypeName == typeName {
tagsSpec = r.Tags
break
}
}
if tagsSpec == nil {
for _, r := range sp.SDKResources(ctx) {
if r.TypeName == typeName {
tagsSpec = r.Tags
break
}
}
}
return tagsSpec
}

func findDataSourceTagSpec(ctx context.Context, sp conns.ServicePackage, typeName string) (tagsSpec *types.ServicePackageResourceTags) {
for _, r := range sp.FrameworkDataSources(ctx) {
factory, _ := r.Factory(ctx)
var metadata datasource.MetadataResponse
factory.Metadata(ctx, datasource.MetadataRequest{}, &metadata)
if metadata.TypeName == typeName {
tagsSpec = r.Tags
break
}
}
if tagsSpec == nil {
for _, r := range sp.SDKDataSources(ctx) {
if r.TypeName == typeName {
tagsSpec = r.Tags
break
}
}
}
return tagsSpec
}

func identityTagSpec(tagsSpec *types.ServicePackageResourceTags) tagSpecFinder {
return func(ctx context.Context, sp conns.ServicePackage, typeName string) *types.ServicePackageResourceTags {
return tagsSpec
}
}
Loading
Loading