Skip to content

Commit

Permalink
Merge pull request #39879 from hashicorp/td-transparent-tags-framewor…
Browse files Browse the repository at this point in the history
…k-datasource

Implement transparent tagging for Framework data sources
  • Loading branch information
gdavison authored Oct 25, 2024
2 parents 7d695f8 + 3e79eb4 commit 3478405
Show file tree
Hide file tree
Showing 43 changed files with 6,125 additions and 320 deletions.
101 changes: 100 additions & 1 deletion internal/provider/fwprovider/intercept.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"fmt"

"github.com/hashicorp/aws-sdk-go-base/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
"github.com/hashicorp/terraform-provider-aws/internal/slices"
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/internal/types"
"github.com/hashicorp/terraform-provider-aws/internal/types/option"
"github.com/hashicorp/terraform-provider-aws/names"
Expand Down Expand Up @@ -252,7 +254,104 @@ type tagsDataSourceInterceptor struct {
}

func (r tagsDataSourceInterceptor) read(ctx context.Context, request datasource.ReadRequest, response *datasource.ReadResponse, meta *conns.AWSClient, when when, diags diag.Diagnostics) (context.Context, diag.Diagnostics) {
// TODO
if r.tags == nil {
return ctx, diags
}

inContext, ok := conns.FromContext(ctx)
if !ok {
return ctx, diags
}

sp, ok := meta.ServicePackages[inContext.ServicePackageName]
if !ok {
return ctx, diags
}

serviceName, err := names.HumanFriendly(inContext.ServicePackageName)
if err != nil {
serviceName = "<service>"
}

resourceName := inContext.ResourceName
if resourceName == "" {
resourceName = "<thing>"
}

tagsInContext, ok := tftags.FromContext(ctx)
if !ok {
return ctx, diags
}

switch when {
case Before:
var configTags tftags.Map
diags.Append(request.Config.GetAttribute(ctx, path.Root(names.AttrTags), &configTags)...)
if diags.HasError() {
return ctx, diags
}

tags := tftags.New(ctx, configTags)

tagsInContext.TagsIn = option.Some(tags)

case After:
// If the R handler didn't set tags, try and read them from the service API.
if tagsInContext.TagsOut.IsNone() {
if identifierAttribute := r.tags.IdentifierAttribute; identifierAttribute != "" {
var identifier string
diags.Append(response.State.GetAttribute(ctx, path.Root(identifierAttribute), &identifier)...)
if diags.HasError() {
return ctx, diags
}

// If the service package has a generic resource list tags methods, call it.
var err error
if v, ok := sp.(interface {
ListTags(context.Context, any, string) error
}); ok {
err = v.ListTags(ctx, meta, identifier) // Sets tags in Context
} else if v, ok := sp.(interface {
ListTags(context.Context, any, string, string) error
}); ok && r.tags.ResourceType != "" {
err = v.ListTags(ctx, meta, identifier, r.tags.ResourceType) // Sets tags in Context
} else {
tflog.Warn(ctx, "No ListTags method found", map[string]interface{}{
"ServicePackage": sp.ServicePackageName(),
"ResourceType": r.tags.ResourceType,
})
}

// ISO partitions may not support tagging, giving error.
if errs.IsUnsupportedOperationInPartitionError(meta.Partition, err) {
return ctx, diags
}

if inContext.ServicePackageName == names.DynamoDB && err != nil {
// When a DynamoDB Table is `ARCHIVED`, ListTags returns `ResourceNotFoundException`.
if tfresource.NotFound(err) || tfawserr.ErrMessageContains(err, "UnknownOperationException", "Tagging is not currently supported in DynamoDB Local.") {
err = nil
}
}

if err != nil {
diags.AddError(fmt.Sprintf("listing tags for %s %s (%s)", serviceName, resourceName, identifier), err.Error())
return ctx, diags
}
}
}

tags := tagsInContext.TagsOut.UnwrapOrDefault()

// Remove any provider configured ignore_tags and system tags from those returned from the service API.
stateTags := flex.FlattenFrameworkStringValueMapLegacy(ctx, tags.IgnoreSystem(inContext.ServicePackageName).IgnoreConfig(tagsInContext.IgnoreConfig).Map())
diags.Append(response.State.SetAttribute(ctx, path.Root(names.AttrTags), tftags.NewMapFromMapValue(stateTags))...)

if diags.HasError() {
return ctx, diags
}
}

return ctx, diags
}

Expand Down
6 changes: 2 additions & 4 deletions internal/service/batch/job_definition_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
)

// @FrameworkDataSource("aws_batch_job_definition", name="Job Definition")
// @Testing(tagsTest=true)
// @Tags
// @Testing(tagsIdentifierAttribute="arn")
func newJobDefinitionDataSource(context.Context) (datasource.DataSourceWithConfigure, error) {
return &jobDefinitionDataSource{}, nil
Expand Down Expand Up @@ -200,9 +200,7 @@ func (d *jobDefinitionDataSource) Read(ctx context.Context, request datasource.R
arnPrefix := strings.TrimSuffix(aws.ToString(jd.JobDefinitionArn), fmt.Sprintf(":%d", aws.ToInt32(jd.Revision)))
data.ARNPrefix = types.StringValue(arnPrefix)

ignoreTagsConfig := d.Meta().IgnoreTagsConfig(ctx)
tags := KeyValueTags(ctx, jd.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
data.Tags = tftags.FlattenStringValueMap(ctx, tags.Map())
setTagsOut(ctx, jd.Tags)

response.Diagnostics.Append(response.State.Set(ctx, &data)...)
}
Expand Down
1 change: 1 addition & 0 deletions internal/service/batch/service_package_gen.go

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

1 change: 1 addition & 0 deletions internal/service/cognitoidp/user_pool_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func (d *userPoolDataSource) Read(ctx context.Context, request datasource.ReadRe

data.ID = data.UserPoolID

// Cannot use Transparent Tagging because of UserPoolTags
ignoreTagsConfig := d.Meta().IgnoreTagsConfig(ctx)
tags := KeyValueTags(ctx, output.UserPoolTags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
data.Tags = tftags.FlattenStringValueMap(ctx, tags.Map())
Expand Down
11 changes: 1 addition & 10 deletions internal/service/elbv2/listener_rule_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import (
)

// @FrameworkDataSource("aws_lb_listener_rule", name="Listener Rule")
// @Testing(tagsTest=true)
// @Testing(tagsIdentifierAttribute="arn")
// @Tags(identifierAttribute="arn")
func newDataSourceListenerRule(context.Context) (datasource.DataSourceWithConfigure, error) {
return &dataSourceListenerRule{}, nil
}
Expand Down Expand Up @@ -349,14 +348,6 @@ func (d *dataSourceListenerRule) Read(ctx context.Context, req datasource.ReadRe
}
data.Priority = types.Int32Value(int32(priority))

tags, err := listTags(ctx, conn, aws.ToString(out.RuleArn))
if err != nil {
resp.Diagnostics.AddError("listing tags for ELBv2 Listener Rule", err.Error())
return
}
ignoreTagsConfig := d.Meta().IgnoreTagsConfig(ctx)
data.Tags = tftags.FlattenStringValueMap(ctx, tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map())

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

Expand Down
14 changes: 2 additions & 12 deletions internal/service/elbv2/listener_rule_data_source_tags_gen_test.go

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

3 changes: 3 additions & 0 deletions internal/service/elbv2/service_package_gen.go

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

6 changes: 2 additions & 4 deletions internal/service/medialive/input_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

// @FrameworkDataSource("aws_medialive_input", name="Input")
// @Testing(tagsTest=true)
// @Tags
// @Testing(tagsIdentifierAttribute="arn")
func newDataSourceInput(_ context.Context) (datasource.DataSourceWithConfigure, error) {
return &dataSourceInput{}, nil
Expand Down Expand Up @@ -123,9 +123,7 @@ func (d *dataSourceInput) Read(ctx context.Context, req datasource.ReadRequest,
return
}

ignoreTagsConfig := d.Meta().IgnoreTagsConfig(ctx)
tags := KeyValueTags(ctx, out.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig)
data.Tags = tftags.FlattenStringValueMap(ctx, tags.Map())
setTagsOut(ctx, out.Tags)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
Expand Down
1 change: 1 addition & 0 deletions internal/service/medialive/service_package_gen.go

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

6 changes: 4 additions & 2 deletions internal/service/ssmcontacts/contact.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ import (
"github.com/hashicorp/terraform-provider-aws/names"
)

// @SDKResource("aws_ssmcontacts_contact", name="Context")
// @Tags(identifierAttribute="id")
// @SDKResource("aws_ssmcontacts_contact", name="Contact")
// @Tags(identifierAttribute="arn")
// @Testing(skipEmptyTags=true, skipNullTags=true)
// @Testing(serialize=true)
func ResourceContact() *schema.Resource {
return &schema.Resource{
CreateWithoutTimeout: resourceContactCreate,
Expand Down
13 changes: 2 additions & 11 deletions internal/service/ssmcontacts/contact_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import (
)

// @SDKDataSource("aws_ssmcontacts_contact")
// @Tags(identifierAttribute="arn")
// @Testing(serialize=true)
func DataSourceContact() *schema.Resource {
return &schema.Resource{
ReadWithoutTimeout: dataSourceContactRead,
Expand Down Expand Up @@ -63,16 +65,5 @@ func dataSourceContactRead(ctx context.Context, d *schema.ResourceData, meta int
return create.AppendDiagError(diags, names.SSMContacts, create.ErrActionSetting, DSNameContact, d.Id(), err)
}

tags, err := listTags(ctx, conn, d.Id())
if err != nil {
return create.AppendDiagError(diags, names.SSMContacts, create.ErrActionReading, DSNameContact, d.Id(), err)
}

ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig(ctx)

if err := d.Set(names.AttrTags, tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return create.AppendDiagError(diags, names.SSMContacts, create.ErrActionSetting, DSNameContact, d.Id(), err)
}

return diags
}
Loading

0 comments on commit 3478405

Please sign in to comment.