From 4312771b1096a817ba4773dc6c7477ec4cd78870 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Thu, 16 Nov 2023 11:44:01 -0500 Subject: [PATCH 01/16] feat: Add pod identity association resource --- go.mod | 2 + go.sum | 7 +- internal/service/eks/const.go | 6 + .../service/eks/pod_identity_association.go | 306 +++++++++++++++ .../eks/pod_identity_association_test.go | 367 ++++++++++++++++++ ...eks_pod_identity_association.html.markdown | 69 ++++ 6 files changed, 755 insertions(+), 2 deletions(-) create mode 100644 internal/service/eks/const.go create mode 100644 internal/service/eks/pod_identity_association.go create mode 100644 internal/service/eks/pod_identity_association_test.go create mode 100644 website/docs/r/eks_pod_identity_association.html.markdown diff --git a/go.mod b/go.mod index 9703fd9f9f89..08602d147ab2 100644 --- a/go.mod +++ b/go.mod @@ -204,6 +204,8 @@ require ( replace github.com/hashicorp/terraform-plugin-log => github.com/gdavison/terraform-plugin-log v0.0.0-20230928191232-6c653d8ef8fb +replace github.com/aws/aws-sdk-go-v2/service/eks => /Users/brybiggs/Documents/awsSdkGoV2/service/eks + exclude ( // Contains INI parsing regression github.com/aws/aws-sdk-go-v2/config v1.21.0 github.com/aws/aws-sdk-go-v2/config v1.22.0 diff --git a/go.sum b/go.sum index 967f803fc9ae..e5b3f773eaae 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,7 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.47.12 h1:1daICVijigVEXCzhg27A5d7hbkR4wODPGn9GHyBclKM= github.com/aws/aws-sdk-go v1.47.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= github.com/aws/aws-sdk-go-v2 v1.22.2 h1:lV0U8fnhAnPz8YcdmZVV60+tr6CakHzqA6P8T46ExJI= github.com/aws/aws-sdk-go-v2 v1.22.2/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 h1:hHgLiIrTRtddC0AKcJr5s7i/hLgcpTt+q/FKxf1Zayk= @@ -36,8 +37,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 h1:G5KawTAkyHH6WyKQCdHiW4h github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3/go.mod h1:hugKmSFnZB+HgNI1sYGT14BUPZkO6alC/e0AWu+0IAQ= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.2 h1:XmPqt2VLMB7dfZ/cGNXBJOFq+Q+VsnEcPW3MqyKAsvY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.2/go.mod h1:Y9RO68QWibKfkJpic8lh2G36x1eUJsAznj+1jiyYmsU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 h1:AaQsr5vvGR7rmeSWBtTCcw16tT9r51mWijuCQhzLnq8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2/go.mod h1:o1IiRn7CWocIFTXJjGKJDOwxv1ibL53NpcvcqGWyRBA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 h1:UZx8SXZ0YtzRiALzYAWcjb9Y9hZUR7MBKaBQ5ouOjPs= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2/go.mod h1:ipuRpcSaklmxR6C39G187TpBAO132gUfleTGccUPs8c= github.com/aws/aws-sdk-go-v2/internal/ini v1.4.0 h1:21tlTXq3ev10yLMAjXZzpkZbrl49h3ElSjmxD57tD/E= @@ -86,8 +89,6 @@ github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.0 h1:wAG9NailFhGhg8Ngg2YeCtz github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.0/go.mod h1:ByrosnNlEq6xkA0d+FwB4f0HH/5KWCcgBqVxAt+Rsps= github.com/aws/aws-sdk-go-v2/service/ec2 v1.133.0 h1:g4qMdFWe9UVMI6PKytU8BBfW7v80dCMdEnLqc8lIDxw= github.com/aws/aws-sdk-go-v2/service/ec2 v1.133.0/go.mod h1:NOPsghjhZRkrVvKIxrDrEL7zhVIFYJsHqdeol50Eodk= -github.com/aws/aws-sdk-go-v2/service/eks v1.33.0 h1:6a7enmqn5ehMT6WBCnc/Bq4UAEtiZ1/3OXDVr09NWUM= -github.com/aws/aws-sdk-go-v2/service/eks v1.33.0/go.mod h1:Caw+bBvj+bjrHNuqmS9ZqSZow77sgF5TQGZy3zY0egA= github.com/aws/aws-sdk-go-v2/service/emrserverless v1.13.1 h1:HhYBETZd3aNmtrlMbJxRf9DlGRPKrOFI0lnvzooWGMA= github.com/aws/aws-sdk-go-v2/service/emrserverless v1.13.1/go.mod h1:GI2hWXh+a2U1N4a4ODnXQ3aON1Cjt2jijCa9H+3S6kc= github.com/aws/aws-sdk-go-v2/service/finspace v1.16.1 h1:g1qJUOmCGs4UH5XyBKDr+jgz3bzFv4WBduMytd/2sOs= @@ -208,6 +209,7 @@ github.com/aws/aws-sdk-go-v2/service/workspaces v1.33.1 h1:9pj1eIIwWJqGrITnkH6gg github.com/aws/aws-sdk-go-v2/service/workspaces v1.33.1/go.mod h1:WpCZtgHZRiYfv0jN6WsgKYvpsGp5H/QxUF+VYnTwmcE= github.com/aws/aws-sdk-go-v2/service/xray v1.22.1 h1:EfeYTIUR/HgFijILpqvRh3Xgky0qOW998lOOmMSPqRY= github.com/aws/aws-sdk-go-v2/service/xray v1.22.1/go.mod h1:4+g9QRvp2RZko4VZF62ICz59N3t1yUPllfzMy6yg8yg= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik= github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= @@ -258,6 +260,7 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= diff --git a/internal/service/eks/const.go b/internal/service/eks/const.go new file mode 100644 index 000000000000..27009859bf91 --- /dev/null +++ b/internal/service/eks/const.go @@ -0,0 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +const idSeparator = ":" diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go new file mode 100644 index 000000000000..178dcb8a52c5 --- /dev/null +++ b/internal/service/eks/pod_identity_association.go @@ -0,0 +1,306 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +import ( + "context" + "errors" + "fmt" + "strings" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + awstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// Function annotations are used for resource registration to the Provider. DO NOT EDIT. +// @FrameworkResource(name="Pod Identity Association") +// @Tags(identifierAttribute="arn") +func newResourcePodIdentityAssociation(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &resourcePodIdentityAssociation{} + + return r, nil +} + +type resourcePodIdentityAssociationData struct { + AssociationArn types.String `tfsdk:"association_arn"` + AssociationId types.String `tfsdk:"association_id"` + ClusterName types.String `tfsdk:"cluster_name"` + CreatedAt types.String `tfsdk:"created_at"` + Namespace types.String `tfsdk:"namespace"` + ModifiedAt types.String `tfsdk:"modified_at"` + RoleArn types.String `tfsdk:"role_arn"` + ServiceAccount types.String `tfsdk:"service_account"` + Tags types.Map `tfsdk:"tags"` + TagsAll types.Map `tfsdk:"tags_all"` +} + +const ( + ResNamePodIdentityAssociation = "Pod Identity Association" +) + +type resourcePodIdentityAssociation struct { + framework.ResourceWithConfigure +} + +func (r *resourcePodIdentityAssociation) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "aws_eks_pod_identity_association" +} + +func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "association_arn": framework.ARNAttributeComputedOnly(), + "association_id": schema.StringAttribute{ + Computed: true, + }, + "cluster_name": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "created_at": schema.StringAttribute{ + Computed: true, + }, + "id": framework.IDAttribute(), + "modified_at": schema.StringAttribute{ + Computed: true, + }, + "namespace": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "role_arn": schema.StringAttribute{ + Required: true, + CustomType: fwtypes.ARNType, + }, + "service_account": schema.StringAttribute{ + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + names.AttrTags: tftags.TagsAttribute(), + names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), + }, + } +} + +func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + conn := r.Meta().EKSClient(ctx) + + var plan resourcePodIdentityAssociationData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + if resp.Diagnostics.HasError() { + return + } + + in := &eks.CreatePodIdentityAssociationInput{ + ClusterName: flex.StringFromFramework(ctx, plan.ClusterName), + ClientRequestToken: aws.String(sdkid.UniqueId()), + Namespace: flex.StringFromFramework(ctx, plan.Namespace), + RoleArn: flex.StringFromFramework(ctx, plan.RoleArn), + ServiceAccount: flex.StringFromFramework(ctx, plan.ServiceAccount), + Tags: getTagsIn(ctx), + } + + out, err := conn.CreatePodIdentityAssociation(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationId.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Association == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationId.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + plan.AssociationArn = flex.StringToFramework(ctx, out.Association.AssociationArn) + plan.AssociationId = flex.StringToFramework(ctx, out.Association.AssociationId) + plan.CreatedAt = flex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) + + resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) +} + +func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + conn := r.Meta().EKSClient(ctx) + + var state resourcePodIdentityAssociationData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + out, err := findPodIdentityAssociationByTwoPartKey(ctx, conn, state.AssociationId.ValueString(), state.ClusterName.ValueString()) + if tfresource.NotFound(err) { + resp.State.RemoveResource(ctx) + return + } + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionSetting, ResNamePodIdentityAssociation, state.AssociationId.String(), err), + err.Error(), + ) + return + } + + state.AssociationArn = flex.StringToFramework(ctx, out.AssociationArn) + state.AssociationId = flex.StringToFramework(ctx, out.AssociationId) + state.ClusterName = flex.StringToFramework(ctx, out.ClusterName) + state.CreatedAt = flex.StringToFramework(ctx, aws.String(out.CreatedAt.Format(time.RFC3339))) + state.ModifiedAt = flex.StringToFramework(ctx, aws.String(out.ModifiedAt.Format(time.RFC3339))) + state.Namespace = flex.StringToFramework(ctx, out.Namespace) + state.RoleArn = flex.StringToFramework(ctx, out.RoleArn) + state.ServiceAccount = flex.StringToFramework(ctx, out.ServiceAccount) + + resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) +} + +func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + conn := r.Meta().EKSClient(ctx) + + var plan, state resourcePodIdentityAssociationData + resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + if !plan.RoleArn.Equal(state.RoleArn) { + + in := &eks.UpdatePodIdentityAssociationInput{ + AssociationId: aws.String(plan.AssociationId.ValueString()), + ClusterName: aws.String(plan.ClusterName.ValueString()), + } + + if !plan.RoleArn.IsNull() { + in.RoleArn = aws.String(plan.RoleArn.ValueString()) + } + + out, err := conn.UpdatePodIdentityAssociation(ctx, in) + if err != nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationId.String(), err), + err.Error(), + ) + return + } + if out == nil || out.Association == nil { + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationId.String(), nil), + errors.New("empty output").Error(), + ) + return + } + + // Using the output from the update function, re-set any computed attributes + plan.AssociationArn = flex.StringToFramework(ctx, out.Association.AssociationArn) + plan.AssociationId = flex.StringToFramework(ctx, out.Association.AssociationId) + plan.CreatedAt = flex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) + plan.ModifiedAt = flex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) + } + + resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) +} + +func (r *resourcePodIdentityAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + conn := r.Meta().EKSClient(ctx) + + var state resourcePodIdentityAssociationData + resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + in := &eks.DeletePodIdentityAssociationInput{ + AssociationId: aws.String(state.AssociationId.ValueString()), + ClusterName: aws.String(state.ClusterName.ValueString()), + } + + _, err := conn.DeletePodIdentityAssociation(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return + } + resp.Diagnostics.AddError( + create.ProblemStandardMessage(names.EKS, create.ErrActionDeleting, ResNamePodIdentityAssociation, state.AssociationId.String(), err), + err.Error(), + ) + return + } +} + +func (r *resourcePodIdentityAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + parts := strings.Split(req.ID, idSeparator) + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + err := fmt.Errorf("unexpected format for ID (%[1]s), expected association-id%[2]scluster-name", req.ID, idSeparator) + resp.Diagnostics.AddError(fmt.Sprintf("importing Pod Identity Association (%s)", req.ID), err.Error()) + return + } + + state := resourcePodIdentityAssociationData{ + AssociationId: types.StringValue(parts[0]), + ClusterName: types.StringValue(parts[1]), + } + + diags := resp.State.Set(ctx, &state) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } +} +func (r *resourcePodIdentityAssociation) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Client, AssociationId, ClusterName string) (*awstypes.PodIdentityAssociation, error) { + in := &eks.DescribePodIdentityAssociationInput{ + AssociationId: aws.String(AssociationId), + ClusterName: aws.String(ClusterName), + } + + out, err := conn.DescribePodIdentityAssociation(ctx, in) + if err != nil { + var nfe *awstypes.ResourceNotFoundException + if errors.As(err, &nfe) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + return nil, err + } + + if out == nil || out.Association == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out.Association, nil +} diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go new file mode 100644 index 000000000000..6dac5026ae3b --- /dev/null +++ b/internal/service/eks/pod_identity_association_test.go @@ -0,0 +1,367 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/YakDriver/regexache" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/names" + + // TIP: You will often need to import the package that this test file lives + // in. Since it is in the "test" context, it must import the package to use + // any normal context constants, variables, or functions. + tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" +) + +// TIP: File Structure. The basic outline for all test files should be as +// follows. Improve this resource's maintainability by following this +// outline. +// +// 1. Package declaration (add "_test" since this is a test file) +// 2. Imports +// 3. Unit tests +// 4. Basic test +// 5. Disappears test +// 6. All the other tests +// 7. Helper functions (exists, destroy, check, etc.) +// 8. Functions that return Terraform configurations + +// TIP: ==== UNIT TESTS ==== +// This is an example of a unit test. Its name is not prefixed with +// "TestAcc" like an acceptance test. +// +// Unlike acceptance tests, unit tests do not access AWS and are focused on a +// function (or method). Because of this, they are quick and cheap to run. +// +// In designing a resource's implementation, isolate complex bits from AWS bits +// so that they can be tested through a unit test. We encourage more unit tests +// in the provider. +// +// Cut and dry functions using well-used patterns, like typical flatteners and +// expanders, don't need unit testing. However, if they are complex or +// intricate, they should be unit tested. +func TestPodIdentityAssociationExampleUnitTest(t *testing.T) { + t.Parallel() + + testCases := []struct { + TestName string + Input string + Expected string + Error bool + }{ + { + TestName: "empty", + Input: "", + Expected: "", + Error: true, + }, + { + TestName: "descriptive name", + Input: "some input", + Expected: "some output", + Error: false, + }, + { + TestName: "another descriptive name", + Input: "more input", + Expected: "more output", + Error: false, + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.TestName, func(t *testing.T) { + t.Parallel() + got, err := tfeks.FunctionFromResource(testCase.Input) + + if err != nil && !testCase.Error { + t.Errorf("got error (%s), expected no error", err) + } + + if err == nil && testCase.Error { + t.Errorf("got (%s) and no error, expected error", got) + } + + if got != testCase.Expected { + t.Errorf("got %s, expected %s", got, testCase.Expected) + } + }) + } +} + +// TIP: ==== ACCEPTANCE TESTS ==== +// This is an example of a basic acceptance test. This should test as much of +// standard functionality of the resource as possible, and test importing, if +// applicable. We prefix its name with "TestAcc", the service, and the +// resource name. +// +// Acceptance test access AWS and cost money to run. +func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { + ctx := acctest.Context(t) + // TIP: This is a long-running test guard for tests that run longer than + // 300s (5 min) generally. + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var podidentityassociation eks.DescribePodIdentityAssociationOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_pod_identity_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) + testAccPreCheckPodIdentity(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPodIdentityAssociationConfig_basic(rName, "1.28"), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ + "console_access": "false", + "groups.#": "0", + "username": "Test", + "password": "TestTest1234", + }), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexache.MustCompile(`podidentityassociation:+.`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + }, + }, + }) +} + +func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var podidentityassociation eks.DescribePodIdentityAssociationOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_pod_identity_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) + testAccPreCheckPodIdentity(t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPodIdentityAssociationConfig_basic(rName, testAccPodIdentityAssociationVersionNewer), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, + // but expects a new resource factory function as the third argument. To expose this + // private function to the testing package, you may need to add a line like the following + // to exports_test.go: + // + // var ResourcePodIdentityAssociation = newResourcePodIdentityAssociation + acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfeks.ResourcePodIdentityAssociation, resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckPodIdentityAssociationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_eks_pod_identity_association" { + continue + } + + _, err := conn.DescribePodIdentityAssociation(ctx, &eks.DescribePodIdentityAssociationInput{ + AssociationId: aws.String(rs.Primary.ID), + ClusterName: aws.String(rs.Primary.Attributes["cluster_name"]), + }) + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil + } + if err != nil { + return create.Error(names.EKS, create.ErrActionCheckingDestroyed, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, err) + } + + return create.Error(names.EKS, create.ErrActionCheckingDestroyed, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, errors.New("not destroyed")) + } + + return nil + } +} + +func testAccCheckPodIdentityAssociationExists(ctx context.Context, name string, podidentityassociation *eks.DescribePodIdentityAssociationOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, name, errors.New("not found")) + } + + if rs.Primary.ID == "" { + return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, name, errors.New("not set")) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + resp, err := conn.DescribePodIdentityAssociation(ctx, &eks.DescribePodIdentityAssociationInput{ + AssociationId: aws.String(rs.Primary.ID), + ClusterName: aws.String(rs.Primary.Attributes["cluster_name"]), + }) + + if err != nil { + return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, err) + } + + *podidentityassociation = *resp + + return nil + } +} + +func testAccPreCheckPodIdentity(ctx context.Context, t *testing.T) { + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + input := &eks.ListPodIdentityAssociationsInput{} + _, err := conn.ListPodIdentityAssociations(ctx, input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} + +func testAccCheckPodIdentityAssociationNotRecreated(before, after *eks.DescribePodIdentityAssociationOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.ToString(before.Association.AssociationId), aws.ToString(after.Association.AssociationId); before != after { + return create.Error(names.EKS, create.ErrActionCheckingNotRecreated, tfeks.ResNamePodIdentityAssociation, before, errors.New("recreated")) + } + + return nil + } +} + +func testAccAddonConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = <` +# Resource: aws_eks_pod_identity_association + +Terraform resource for managing an AWS EKS (Elastic Kubernetes) Pod Identity Association. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_eks_pod_identity_association" "example" { +} +``` + +## Argument Reference + +The following arguments are required: + +* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +The following arguments are optional: + +* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `arn` - ARN of the Pod Identity Association. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `60m`) +* `update` - (Default `180m`) +* `delete` - (Default `90m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS (Elastic Kubernetes) Pod Identity Association using the `example_id_arg`. For example: + +```terraform +import { + to = aws_eks_pod_identity_association.example + id = "pod_identity_association-id-12345678" +} +``` + +Using `terraform import`, import EKS (Elastic Kubernetes) Pod Identity Association using the `example_id_arg`. For example: + +```console +% terraform import aws_eks_pod_identity_association.example pod_identity_association-id-12345678 +``` From 5932e1d5cd6af8d22722966e0e2865b48afddef3 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 09:04:07 -0500 Subject: [PATCH 02/16] chore: Update go mods to use public SDK with pod identity apis --- go.mod | 12 +++++------- go.sum | 23 ++++++++++------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 08602d147ab2..58442770f952 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c github.com/YakDriver/regexache v0.23.0 github.com/aws/aws-sdk-go v1.47.12 - github.com/aws/aws-sdk-go-v2 v1.22.2 + github.com/aws/aws-sdk-go-v2 v1.23.1 github.com/aws/aws-sdk-go-v2/config v1.20.0 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.2 @@ -30,7 +30,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/directoryservice v1.21.1 github.com/aws/aws-sdk-go-v2/service/docdbelastic v1.5.1 github.com/aws/aws-sdk-go-v2/service/ec2 v1.133.0 - github.com/aws/aws-sdk-go-v2/service/eks v1.33.0 + github.com/aws/aws-sdk-go-v2/service/eks v1.34.0 github.com/aws/aws-sdk-go-v2/service/emrserverless v1.13.1 github.com/aws/aws-sdk-go-v2/service/finspace v1.16.1 github.com/aws/aws-sdk-go-v2/service/fis v1.19.1 @@ -132,8 +132,8 @@ require ( github.com/armon/go-radix v1.0.0 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.15.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.4.0 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 // indirect github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.0 // indirect @@ -145,7 +145,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.17.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.19.1 // indirect - github.com/aws/smithy-go v1.16.0 // indirect + github.com/aws/smithy-go v1.17.0 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/boombuler/barcode v1.0.1 // indirect github.com/bufbuild/protocompile v0.6.0 // indirect @@ -204,8 +204,6 @@ require ( replace github.com/hashicorp/terraform-plugin-log => github.com/gdavison/terraform-plugin-log v0.0.0-20230928191232-6c653d8ef8fb -replace github.com/aws/aws-sdk-go-v2/service/eks => /Users/brybiggs/Documents/awsSdkGoV2/service/eks - exclude ( // Contains INI parsing regression github.com/aws/aws-sdk-go-v2/config v1.21.0 github.com/aws/aws-sdk-go-v2/config v1.22.0 diff --git a/go.sum b/go.sum index e5b3f773eaae..eda980768ea3 100644 --- a/go.sum +++ b/go.sum @@ -24,9 +24,8 @@ github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go v1.47.12 h1:1daICVijigVEXCzhg27A5d7hbkR4wODPGn9GHyBclKM= github.com/aws/aws-sdk-go v1.47.12/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= -github.com/aws/aws-sdk-go-v2 v1.22.2 h1:lV0U8fnhAnPz8YcdmZVV60+tr6CakHzqA6P8T46ExJI= -github.com/aws/aws-sdk-go-v2 v1.22.2/go.mod h1:Kd0OJtkW3Q0M0lUWGszapWjEvrXDzRW+D21JNsroB+c= +github.com/aws/aws-sdk-go-v2 v1.23.1 h1:qXaFsOOMA+HsZtX8WoCa+gJnbyW7qyFFBlPqvTSzbaI= +github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0 h1:hHgLiIrTRtddC0AKcJr5s7i/hLgcpTt+q/FKxf1Zayk= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.0/go.mod h1:w4I/v3NOWgD+qvs1NPEwhd++1h3XPHFaVxasfY6HlYQ= github.com/aws/aws-sdk-go-v2/config v1.20.0 h1:q2+/mqFhY0J9m3Tb5RGFE3R4sdaUkIe4k2EuDfE3c08= @@ -37,12 +36,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3 h1:G5KawTAkyHH6WyKQCdHiW4h github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.3/go.mod h1:hugKmSFnZB+HgNI1sYGT14BUPZkO6alC/e0AWu+0IAQ= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.2 h1:XmPqt2VLMB7dfZ/cGNXBJOFq+Q+VsnEcPW3MqyKAsvY= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.13.2/go.mod h1:Y9RO68QWibKfkJpic8lh2G36x1eUJsAznj+1jiyYmsU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2 h1:AaQsr5vvGR7rmeSWBtTCcw16tT9r51mWijuCQhzLnq8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.2/go.mod h1:o1IiRn7CWocIFTXJjGKJDOwxv1ibL53NpcvcqGWyRBA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2 h1:UZx8SXZ0YtzRiALzYAWcjb9Y9hZUR7MBKaBQ5ouOjPs= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.2/go.mod h1:ipuRpcSaklmxR6C39G187TpBAO132gUfleTGccUPs8c= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 h1:LAm3Ycm9HJfbSCd5I+wqC2S9Ej7FPrgr5CQoOljJZcE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 h1:4GV0kKZzUxiWxSVpn/9gwR0g21NF1Jsyduzo9rHgC/Q= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= github.com/aws/aws-sdk-go-v2/internal/ini v1.4.0 h1:21tlTXq3ev10yLMAjXZzpkZbrl49h3ElSjmxD57tD/E= github.com/aws/aws-sdk-go-v2/internal/ini v1.4.0/go.mod h1:d9YrBHJhyzDCv5UsEVRizHlFV6Q0sLemFq6uxuqWfUw= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.2 h1:pyVrNAf7Hwz0u39dLKN5t+n0+K/3rMYKuiOoIum3AsU= @@ -89,6 +86,8 @@ github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.0 h1:wAG9NailFhGhg8Ngg2YeCtz github.com/aws/aws-sdk-go-v2/service/dynamodb v1.25.0/go.mod h1:ByrosnNlEq6xkA0d+FwB4f0HH/5KWCcgBqVxAt+Rsps= github.com/aws/aws-sdk-go-v2/service/ec2 v1.133.0 h1:g4qMdFWe9UVMI6PKytU8BBfW7v80dCMdEnLqc8lIDxw= github.com/aws/aws-sdk-go-v2/service/ec2 v1.133.0/go.mod h1:NOPsghjhZRkrVvKIxrDrEL7zhVIFYJsHqdeol50Eodk= +github.com/aws/aws-sdk-go-v2/service/eks v1.34.0 h1:g3m365rWn0MLZagA77BSuQAzTqG8VB+azzCVtpmgnpg= +github.com/aws/aws-sdk-go-v2/service/eks v1.34.0/go.mod h1:DInudKNZjEy7SJ0KfRh4VxaqY04B52Lq2+QRuvObfNQ= github.com/aws/aws-sdk-go-v2/service/emrserverless v1.13.1 h1:HhYBETZd3aNmtrlMbJxRf9DlGRPKrOFI0lnvzooWGMA= github.com/aws/aws-sdk-go-v2/service/emrserverless v1.13.1/go.mod h1:GI2hWXh+a2U1N4a4ODnXQ3aON1Cjt2jijCa9H+3S6kc= github.com/aws/aws-sdk-go-v2/service/finspace v1.16.1 h1:g1qJUOmCGs4UH5XyBKDr+jgz3bzFv4WBduMytd/2sOs= @@ -209,9 +208,8 @@ github.com/aws/aws-sdk-go-v2/service/workspaces v1.33.1 h1:9pj1eIIwWJqGrITnkH6gg github.com/aws/aws-sdk-go-v2/service/workspaces v1.33.1/go.mod h1:WpCZtgHZRiYfv0jN6WsgKYvpsGp5H/QxUF+VYnTwmcE= github.com/aws/aws-sdk-go-v2/service/xray v1.22.1 h1:EfeYTIUR/HgFijILpqvRh3Xgky0qOW998lOOmMSPqRY= github.com/aws/aws-sdk-go-v2/service/xray v1.22.1/go.mod h1:4+g9QRvp2RZko4VZF62ICz59N3t1yUPllfzMy6yg8yg= -github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/aws/smithy-go v1.16.0 h1:gJZEH/Fqh+RsvlJ1Zt4tVAtV6bKkp3cC+R6FCZMNzik= -github.com/aws/smithy-go v1.16.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI= +github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw= github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= @@ -260,7 +258,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= From da0aa6e26918a3656cf4a1e3b08ab5a2fb6e8dbc Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 10:22:04 -0500 Subject: [PATCH 03/16] chore: Update tests and website docs --- internal/service/eks/exports_test.go | 1 + .../service/eks/pod_identity_association.go | 3 +- .../eks/pod_identity_association_test.go | 231 +++++------------- ...eks_pod_identity_association.html.markdown | 76 ++++-- 4 files changed, 118 insertions(+), 193 deletions(-) diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index 3edfc733ce68..b42193399002 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -10,6 +10,7 @@ var ( ResourceFargateProfile = resourceFargateProfile ResourceIdentityProviderConfig = resourceIdentityProviderConfig ResourceNodeGroup = resourceNodeGroup + ResourcePodIdentityAssociation = newResourcePodIdentityAssociation FindAddonByTwoPartKey = findAddonByTwoPartKey FindClusterByName = findClusterByName diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index 178dcb8a52c5..38cbcc0e88fa 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -13,7 +13,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" awstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" - "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -32,7 +31,7 @@ import ( // Function annotations are used for resource registration to the Provider. DO NOT EDIT. // @FrameworkResource(name="Pod Identity Association") -// @Tags(identifierAttribute="arn") +// @Tags(identifierAttribute="association_id") func newResourcePodIdentityAssociation(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourcePodIdentityAssociation{} diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 6dac5026ae3b..13e569c4b621 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -9,8 +9,6 @@ import ( "fmt" "testing" - "github.com/YakDriver/regexache" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/eks/types" @@ -21,105 +19,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" - "github.com/hashicorp/terraform-provider-aws/names" - - // TIP: You will often need to import the package that this test file lives - // in. Since it is in the "test" context, it must import the package to use - // any normal context constants, variables, or functions. tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" + "github.com/hashicorp/terraform-provider-aws/names" ) -// TIP: File Structure. The basic outline for all test files should be as -// follows. Improve this resource's maintainability by following this -// outline. -// -// 1. Package declaration (add "_test" since this is a test file) -// 2. Imports -// 3. Unit tests -// 4. Basic test -// 5. Disappears test -// 6. All the other tests -// 7. Helper functions (exists, destroy, check, etc.) -// 8. Functions that return Terraform configurations - -// TIP: ==== UNIT TESTS ==== -// This is an example of a unit test. Its name is not prefixed with -// "TestAcc" like an acceptance test. -// -// Unlike acceptance tests, unit tests do not access AWS and are focused on a -// function (or method). Because of this, they are quick and cheap to run. -// -// In designing a resource's implementation, isolate complex bits from AWS bits -// so that they can be tested through a unit test. We encourage more unit tests -// in the provider. -// -// Cut and dry functions using well-used patterns, like typical flatteners and -// expanders, don't need unit testing. However, if they are complex or -// intricate, they should be unit tested. -func TestPodIdentityAssociationExampleUnitTest(t *testing.T) { - t.Parallel() - - testCases := []struct { - TestName string - Input string - Expected string - Error bool - }{ - { - TestName: "empty", - Input: "", - Expected: "", - Error: true, - }, - { - TestName: "descriptive name", - Input: "some input", - Expected: "some output", - Error: false, - }, - { - TestName: "another descriptive name", - Input: "more input", - Expected: "more output", - Error: false, - }, - } - - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.TestName, func(t *testing.T) { - t.Parallel() - got, err := tfeks.FunctionFromResource(testCase.Input) - - if err != nil && !testCase.Error { - t.Errorf("got error (%s), expected no error", err) - } - - if err == nil && testCase.Error { - t.Errorf("got (%s) and no error, expected error", got) - } - - if got != testCase.Expected { - t.Errorf("got %s, expected %s", got, testCase.Expected) - } - }) - } -} - -// TIP: ==== ACCEPTANCE TESTS ==== -// This is an example of a basic acceptance test. This should test as much of -// standard functionality of the resource as possible, and test importing, if -// applicable. We prefix its name with "TestAcc", the service, and the -// resource name. -// -// Acceptance test access AWS and cost money to run. func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { ctx := acctest.Context(t) - // TIP: This is a long-running test guard for tests that run longer than - // 300s (5 min) generally. - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } var podidentityassociation eks.DescribePodIdentityAssociationOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -129,32 +34,26 @@ func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) - testAccPreCheckPodIdentity(ctx, t) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPodIdentityAssociationConfig_basic(rName, "1.28"), + Config: testAccPodIdentityAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), - resource.TestCheckResourceAttr(resourceName, "auto_minor_version_upgrade", "false"), - resource.TestCheckResourceAttrSet(resourceName, "maintenance_window_start_time.0.day_of_week"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "user.*", map[string]string{ - "console_access": "false", - "groups.#": "0", - "username": "Test", - "password": "TestTest1234", - }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexache.MustCompile(`podidentityassociation:+.`)), + resource.TestCheckResourceAttrSet(resourceName, "cluster_name"), + resource.TestCheckResourceAttrSet(resourceName, "namespace"), + resource.TestCheckResourceAttrSet(resourceName, "role_arn"), + resource.TestCheckResourceAttrSet(resourceName, "service_account"), ), }, { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"apply_immediately", "user"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -162,9 +61,6 @@ func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { ctx := acctest.Context(t) - if testing.Short() { - t.Skip("skipping long-running test in short mode") - } var podidentityassociation eks.DescribePodIdentityAssociationOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -174,22 +70,16 @@ func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t) acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) - testAccPreCheckPodIdentity(t) + testAccPreCheck(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccPodIdentityAssociationConfig_basic(rName, testAccPodIdentityAssociationVersionNewer), + Config: testAccPodIdentityAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), - // TIP: The Plugin-Framework disappears helper is similar to the Plugin-SDK version, - // but expects a new resource factory function as the third argument. To expose this - // private function to the testing package, you may need to add a line like the following - // to exports_test.go: - // - // var ResourcePodIdentityAssociation = newResourcePodIdentityAssociation acctest.CheckFrameworkResourceDisappears(ctx, acctest.Provider, tfeks.ResourcePodIdentityAssociation, resourceName), ), ExpectNonEmptyPlan: true, @@ -252,20 +142,6 @@ func testAccCheckPodIdentityAssociationExists(ctx context.Context, name string, } } -func testAccPreCheckPodIdentity(ctx context.Context, t *testing.T) { - conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) - - input := &eks.ListPodIdentityAssociationsInput{} - _, err := conn.ListPodIdentityAssociations(ctx, input) - - if acctest.PreCheckSkipError(err) { - t.Skipf("skipping acceptance testing: %s", err) - } - if err != nil { - t.Fatalf("unexpected PreCheck error: %s", err) - } -} - func testAccCheckPodIdentityAssociationNotRecreated(before, after *eks.DescribePodIdentityAssociationOutput) resource.TestCheckFunc { return func(s *terraform.State) error { if before, after := aws.ToString(before.Association.AssociationId), aws.ToString(after.Association.AssociationId); before != after { @@ -276,12 +152,12 @@ func testAccCheckPodIdentityAssociationNotRecreated(before, after *eks.DescribeP } } -func testAccAddonConfig_base(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -data "aws_partition" "current" {} - +func testAccPodIdentityAssociationConfig_clusterBase(rName string) string { + return acctest.ConfigCompose( + acctest.ConfigAvailableAZsNoOptInDefaultExclude(), + fmt.Sprintf(` resource "aws_iam_role" "test" { - name = %[1]q + name_prefix = %[1]q assume_role_policy = <` + # Resource: aws_eks_pod_identity_association Terraform resource for managing an AWS EKS (Elastic Kubernetes) Pod Identity Association. +Creates an EKS Pod Identity association between a service account in an Amazon EKS cluster and an IAM role with EKS Pod Identity. Use EKS Pod Identity to give temporary IAM credentials to pods and the credentials are rotated automatically. + +Amazon EKS Pod Identity associations provide the ability to manage credentials for your applications, similar to the way that EC2 instance profiles provide credentials to Amazon EC2 instances. + +If a pod uses a service account that has an association, Amazon EKS sets environment variables in the containers of the pod. The environment variables configure the Amazon Web Services SDKs, including the Command Line Interface, to use the EKS Pod Identity credentials. + +Pod Identity is a simpler method than IAM roles for service accounts, as this method doesn’t use OIDC identity providers. Additionally, you can configure a role for Pod Identity once, and reuse it across clusters. + ## Example Usage ### Basic Usage ```terraform +data "aws_iam_policy_document" "assume_role" { + statement { + effect = "Allow" + + principals { + type = "Service" + identifiers = ["pods.eks.amazonaws.com"] + } + + actions = [ + "sts:AssumeRole", + "sts:TagSession" + ] + } +} + +resource "aws_iam_role" "example" { + name = "eks-pod-identity-example" + assume_role_policy = data.aws_iam_policy_document.assume_role.json +} + +resource "aws_iam_role_policy_attachment" "example_s3" { + policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + role = aws_iam_role.example.name +} + resource "aws_eks_pod_identity_association" "example" { + cluster_name = aws_eks_cluster.example.name + namespace = "example" + service_account = "example-sa" + role_arn = aws_iam_role.example.arn } ``` @@ -30,40 +61,37 @@ resource "aws_eks_pod_identity_association" "example" { The following arguments are required: -* `example_arg` - (Required) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `cluster_name` - (Required) The name of the cluster to create the association in. +* `namespace` - (Required) The name of the Kubernetes namespace inside the cluster to create the association in. The service account and the pods that use the service account must be in this namespace. +* `service_account` - (Required) The name of the Kubernetes service account inside the cluster to associate the IAM credentials with. +* `role_arn` - (Required) The Amazon Resource Name (ARN) of the IAM role to associate with the service account. The EKS Pod Identity agent manages credentials to assume this role for applications in the containers in the pods that use this service account. The following arguments are optional: -* `optional_arg` - (Optional) Concise argument description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. +* `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. ## Attribute Reference This resource exports the following attributes in addition to the arguments above: -* `arn` - ARN of the Pod Identity Association. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. -* `example_attribute` - Concise description. Do not begin the description with "An", "The", "Defines", "Indicates", or "Specifies," as these are verbose. In other words, "Indicates the amount of storage," can be rewritten as "Amount of storage," without losing any information. - -## Timeouts - -[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): - -* `create` - (Default `60m`) -* `update` - (Default `180m`) -* `delete` - (Default `90m`) +* `association_arn` - The Amazon Resource Name (ARN) of the association. +* `association_id` - The ID of the association. +* `created_at` - The timestamp that the association was created at. +* `modified_at` - The most recent timestamp that the association was modified at. ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS (Elastic Kubernetes) Pod Identity Association using the `example_id_arg`. For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a colon (`:`). For example: ```terraform import { to = aws_eks_pod_identity_association.example - id = "pod_identity_association-id-12345678" + id = "example:a-12345678" } ``` -Using `terraform import`, import EKS (Elastic Kubernetes) Pod Identity Association using the `example_id_arg`. For example: +Using `terraform import`, import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a colon (`:`). For example: ```console -% terraform import aws_eks_pod_identity_association.example pod_identity_association-id-12345678 +% terraform import aws_eks_pod_identity_association.example example:a-12345678 ``` From 701571ce4f45fbb99a7d31d2a258e5a9eb7839c9 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 10:38:22 -0500 Subject: [PATCH 04/16] chore: Add changelog --- .changelog/34566.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/34566.txt diff --git a/.changelog/34566.txt b/.changelog/34566.txt new file mode 100644 index 000000000000..2c0cbdd2d903 --- /dev/null +++ b/.changelog/34566.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_eks_pod_identity_association +``` From 57bb95e778f38f25f91945b3c7b1c28168f676d1 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 10:57:03 -0500 Subject: [PATCH 05/16] fix: Run `make gen` and use partition in tests --- internal/service/eks/pod_identity_association_test.go | 6 ++++-- internal/service/eks/service_package_gen.go | 10 +++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 13e569c4b621..def834e55788 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -156,6 +156,8 @@ func testAccPodIdentityAssociationConfig_clusterBase(rName string) string { return acctest.ConfigCompose( acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` +data "aws_partition" "current" {} + resource "aws_iam_role" "test" { name_prefix = %[1]q @@ -179,7 +181,7 @@ POLICY } resource "aws_iam_role_policy_attachment" "test_cluster" { - policy_arn = "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy" + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonEKSClusterPolicy" role = aws_iam_role.test.name } @@ -244,7 +246,7 @@ POLICY } resource "aws_iam_role_policy_attachment" "example" { - policy_arn = "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess" + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonS3ReadOnlyAccess" role = aws_iam_role.example.name } `, rName)) diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index 3f8d01c02437..99ef9c90bd3f 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -19,7 +19,15 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv } func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { - return []*types.ServicePackageFrameworkResource{} + return []*types.ServicePackageFrameworkResource{ + { + Factory: newResourcePodIdentityAssociation, + Name: "Pod Identity Association", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "association_id", + }, + }, + } } func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { From f5536dc24cfeee7a456db8c80c27e153953db74f Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 12:58:20 -0500 Subject: [PATCH 06/16] chore: Updates from testing --- .../service/eks/pod_identity_association.go | 19 +++++++++++---- .../eks/pod_identity_association_test.go | 24 ++++++------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index 38cbcc0e88fa..47f689274774 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -45,7 +45,7 @@ type resourcePodIdentityAssociationData struct { CreatedAt types.String `tfsdk:"created_at"` Namespace types.String `tfsdk:"namespace"` ModifiedAt types.String `tfsdk:"modified_at"` - RoleArn types.String `tfsdk:"role_arn"` + RoleArn fwtypes.ARN `tfsdk:"role_arn"` ServiceAccount types.String `tfsdk:"service_account"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` @@ -69,6 +69,9 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc "association_arn": framework.ARNAttributeComputedOnly(), "association_id": schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "cluster_name": schema.StringAttribute{ Required: true, @@ -78,10 +81,15 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc }, "created_at": schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, - "id": framework.IDAttribute(), "modified_at": schema.StringAttribute{ Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "namespace": schema.StringAttribute{ Required: true, @@ -90,8 +98,11 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc }, }, "role_arn": schema.StringAttribute{ - Required: true, CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, }, "service_account": schema.StringAttribute{ Required: true, @@ -174,7 +185,7 @@ func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource. state.CreatedAt = flex.StringToFramework(ctx, aws.String(out.CreatedAt.Format(time.RFC3339))) state.ModifiedAt = flex.StringToFramework(ctx, aws.String(out.ModifiedAt.Format(time.RFC3339))) state.Namespace = flex.StringToFramework(ctx, out.Namespace) - state.RoleArn = flex.StringToFramework(ctx, out.RoleArn) + state.RoleArn = fwtypes.ARNValue(aws.ToString(out.RoleArn)) state.ServiceAccount = flex.StringToFramework(ctx, out.ServiceAccount) resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index def834e55788..79a50faa0159 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -142,23 +142,13 @@ func testAccCheckPodIdentityAssociationExists(ctx context.Context, name string, } } -func testAccCheckPodIdentityAssociationNotRecreated(before, after *eks.DescribePodIdentityAssociationOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - if before, after := aws.ToString(before.Association.AssociationId), aws.ToString(after.Association.AssociationId); before != after { - return create.Error(names.EKS, create.ErrActionCheckingNotRecreated, tfeks.ResNamePodIdentityAssociation, before, errors.New("recreated")) - } - - return nil - } -} - func testAccPodIdentityAssociationConfig_clusterBase(rName string) string { return acctest.ConfigCompose( acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` data "aws_partition" "current" {} -resource "aws_iam_role" "test" { +resource "aws_iam_role" "cluster" { name_prefix = %[1]q assume_role_policy = < Date: Mon, 27 Nov 2023 14:40:44 -0500 Subject: [PATCH 07/16] fix: Correct provider drift --- .../service/eks/pod_identity_association.go | 100 +++++++++++------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index 47f689274774..69a4d29c8ef9 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -21,8 +21,9 @@ import ( sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" - "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" @@ -66,7 +67,12 @@ func (r *resourcePodIdentityAssociation) Metadata(_ context.Context, req resourc func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ - "association_arn": framework.ARNAttributeComputedOnly(), + "association_arn": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, "association_id": schema.StringAttribute{ Computed: true, PlanModifiers: []planmodifier.String{ @@ -100,9 +106,6 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc "role_arn": schema.StringAttribute{ CustomType: fwtypes.ARNType, Required: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, "service_account": schema.StringAttribute{ Required: true, @@ -126,11 +129,11 @@ func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resourc } in := &eks.CreatePodIdentityAssociationInput{ - ClusterName: flex.StringFromFramework(ctx, plan.ClusterName), + ClusterName: fwflex.StringFromFramework(ctx, plan.ClusterName), ClientRequestToken: aws.String(sdkid.UniqueId()), - Namespace: flex.StringFromFramework(ctx, plan.Namespace), - RoleArn: flex.StringFromFramework(ctx, plan.RoleArn), - ServiceAccount: flex.StringFromFramework(ctx, plan.ServiceAccount), + Namespace: fwflex.StringFromFramework(ctx, plan.Namespace), + RoleArn: fwflex.StringFromFramework(ctx, plan.RoleArn), + ServiceAccount: fwflex.StringFromFramework(ctx, plan.ServiceAccount), Tags: getTagsIn(ctx), } @@ -150,9 +153,10 @@ func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resourc return } - plan.AssociationArn = flex.StringToFramework(ctx, out.Association.AssociationArn) - plan.AssociationId = flex.StringToFramework(ctx, out.Association.AssociationId) - plan.CreatedAt = flex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) + plan.AssociationArn = fwflex.StringToFramework(ctx, out.Association.AssociationArn) + plan.AssociationId = fwflex.StringToFramework(ctx, out.Association.AssociationId) + plan.CreatedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) + plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } @@ -160,49 +164,63 @@ func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resourc func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { conn := r.Meta().EKSClient(ctx) - var state resourcePodIdentityAssociationData - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + var data resourcePodIdentityAssociationData + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - out, err := findPodIdentityAssociationByTwoPartKey(ctx, conn, state.AssociationId.ValueString(), state.ClusterName.ValueString()) + out, err := findPodIdentityAssociationByTwoPartKey(ctx, conn, data.AssociationId.ValueString(), data.ClusterName.ValueString()) if tfresource.NotFound(err) { + resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) resp.State.RemoveResource(ctx) + return } + if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionSetting, ResNamePodIdentityAssociation, state.AssociationId.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionSetting, ResNamePodIdentityAssociation, data.AssociationId.String(), err), err.Error(), ) return } - state.AssociationArn = flex.StringToFramework(ctx, out.AssociationArn) - state.AssociationId = flex.StringToFramework(ctx, out.AssociationId) - state.ClusterName = flex.StringToFramework(ctx, out.ClusterName) - state.CreatedAt = flex.StringToFramework(ctx, aws.String(out.CreatedAt.Format(time.RFC3339))) - state.ModifiedAt = flex.StringToFramework(ctx, aws.String(out.ModifiedAt.Format(time.RFC3339))) - state.Namespace = flex.StringToFramework(ctx, out.Namespace) - state.RoleArn = fwtypes.ARNValue(aws.ToString(out.RoleArn)) - state.ServiceAccount = flex.StringToFramework(ctx, out.ServiceAccount) + // Set attributes for import. + resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &data)...) + if resp.Diagnostics.HasError() { + return + } + + tags, err := listTags(ctx, conn, data.AssociationArn.ValueString()) + + if err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("listing tags for Pod Identity Association (%s)", data.AssociationId.ValueString()), err.Error()) + + return + } + + setTagsOut(ctx, Tags(tags)) - resp.Diagnostics.Append(resp.State.Set(ctx, &state)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - conn := r.Meta().EKSClient(ctx) - var plan, state resourcePodIdentityAssociationData - resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) - resp.Diagnostics.Append(req.State.Get(ctx, &state)...) + + resp.Diagnostics.Append(req.State.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - if !plan.RoleArn.Equal(state.RoleArn) { + resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + if resp.Diagnostics.HasError() { + return + } + + conn := r.Meta().EKSClient(ctx) + if !plan.RoleArn.Equal(state.RoleArn) { in := &eks.UpdatePodIdentityAssociationInput{ AssociationId: aws.String(plan.AssociationId.ValueString()), ClusterName: aws.String(plan.ClusterName.ValueString()), @@ -228,28 +246,32 @@ func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resourc return } - // Using the output from the update function, re-set any computed attributes - plan.AssociationArn = flex.StringToFramework(ctx, out.Association.AssociationArn) - plan.AssociationId = flex.StringToFramework(ctx, out.Association.AssociationId) - plan.CreatedAt = flex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) - plan.ModifiedAt = flex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) + plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) + } + + if oldTagsAll, newTagsAll := state.TagsAll, plan.TagsAll; !newTagsAll.Equal(oldTagsAll) { + if err := updateTags(ctx, conn, plan.AssociationArn.ValueString(), oldTagsAll, newTagsAll); err != nil { + resp.Diagnostics.AddError(fmt.Sprintf("updating tags for Pod Identity Association (%s)", plan.AssociationId.ValueString()), err.Error()) + + return + } } resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } func (r *resourcePodIdentityAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - conn := r.Meta().EKSClient(ctx) - var state resourcePodIdentityAssociationData resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return } + conn := r.Meta().EKSClient(ctx) + in := &eks.DeletePodIdentityAssociationInput{ - AssociationId: aws.String(state.AssociationId.ValueString()), - ClusterName: aws.String(state.ClusterName.ValueString()), + AssociationId: fwflex.StringFromFramework(ctx, state.AssociationId), + ClusterName: fwflex.StringFromFramework(ctx, state.ClusterName), } _, err := conn.DeletePodIdentityAssociation(ctx, in) From 7ba6674317c26d042f5308f80c3510e3b9ce9324 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 17:53:22 -0500 Subject: [PATCH 08/16] fix: Add import function to split on separator --- .../service/eks/pod_identity_association.go | 18 +++++++++++------- .../eks/pod_identity_association_test.go | 12 ++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index 69a4d29c8ef9..abe5c57fff4a 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -44,6 +44,7 @@ type resourcePodIdentityAssociationData struct { AssociationId types.String `tfsdk:"association_id"` ClusterName types.String `tfsdk:"cluster_name"` CreatedAt types.String `tfsdk:"created_at"` + ID types.String `tfsdk:"id"` Namespace types.String `tfsdk:"namespace"` ModifiedAt types.String `tfsdk:"modified_at"` RoleArn fwtypes.ARN `tfsdk:"role_arn"` @@ -113,6 +114,7 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc stringplanmodifier.RequiresReplace(), }, }, + names.AttrID: framework.IDAttribute(), names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, @@ -145,7 +147,7 @@ func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resourc ) return } - if out == nil || out.Association == nil { + if out == nil { resp.Diagnostics.AddError( create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationId.String(), nil), errors.New("empty output").Error(), @@ -156,6 +158,7 @@ func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resourc plan.AssociationArn = fwflex.StringToFramework(ctx, out.Association.AssociationArn) plan.AssociationId = fwflex.StringToFramework(ctx, out.Association.AssociationId) plan.CreatedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) + plan.ID = fwflex.StringToFramework(ctx, out.Association.AssociationId) plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) @@ -291,14 +294,14 @@ func (r *resourcePodIdentityAssociation) Delete(ctx context.Context, req resourc func (r *resourcePodIdentityAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { parts := strings.Split(req.ID, idSeparator) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - err := fmt.Errorf("unexpected format for ID (%[1]s), expected association-id%[2]scluster-name", req.ID, idSeparator) + err := fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sassociation-id", req.ID, idSeparator) resp.Diagnostics.AddError(fmt.Sprintf("importing Pod Identity Association (%s)", req.ID), err.Error()) return } state := resourcePodIdentityAssociationData{ - AssociationId: types.StringValue(parts[0]), - ClusterName: types.StringValue(parts[1]), + AssociationId: types.StringValue(parts[1]), + ClusterName: types.StringValue(parts[0]), } diags := resp.State.Set(ctx, &state) @@ -311,13 +314,14 @@ func (r *resourcePodIdentityAssociation) ModifyPlan(ctx context.Context, request r.SetTagsAll(ctx, request, response) } -func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Client, AssociationId, ClusterName string) (*awstypes.PodIdentityAssociation, error) { +func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Client, AssociationId, ClusterName string) (*eks.DescribePodIdentityAssociationOutput, error) { in := &eks.DescribePodIdentityAssociationInput{ AssociationId: aws.String(AssociationId), ClusterName: aws.String(ClusterName), } out, err := conn.DescribePodIdentityAssociation(ctx, in) + if err != nil { var nfe *awstypes.ResourceNotFoundException if errors.As(err, &nfe) { @@ -330,9 +334,9 @@ func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Clien return nil, err } - if out == nil || out.Association == nil { + if out == nil { return nil, tfresource.NewEmptyResultError(in) } - return out.Association, nil + return out, nil } diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 79a50faa0159..f2f63245ff97 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -52,6 +52,7 @@ func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { }, { ResourceName: resourceName, + ImportStateIdFunc: testAccCheckPodIdentityAssociationImportStateIdFunc(resourceName), ImportState: true, ImportStateVerify: true, }, @@ -142,6 +143,17 @@ func testAccCheckPodIdentityAssociationExists(ctx context.Context, name string, } } +func testAccCheckPodIdentityAssociationImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("not found: %s", resourceName) + } + + return fmt.Sprintf("%s:%s", rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["association_id"]), nil + } +} + func testAccPodIdentityAssociationConfig_clusterBase(rName string) string { return acctest.ConfigCompose( acctest.ConfigAvailableAZsNoOptInDefaultExclude(), From f0e7f3e0cb74620569b4416143f5d045abd9d9d4 Mon Sep 17 00:00:00 2001 From: Bryant Biggs Date: Mon, 27 Nov 2023 21:22:38 -0500 Subject: [PATCH 09/16] fix: Remove tag manipulation logic --- .../service/eks/pod_identity_association.go | 20 +------------------ .../eks/pod_identity_association_test.go | 4 ++++ internal/service/eks/service_package_gen.go | 2 +- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index abe5c57fff4a..74a04f4a341a 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -32,7 +32,7 @@ import ( // Function annotations are used for resource registration to the Provider. DO NOT EDIT. // @FrameworkResource(name="Pod Identity Association") -// @Tags(identifierAttribute="association_id") +// @Tags(identifierAttribute="association_arn") func newResourcePodIdentityAssociation(_ context.Context) (resource.ResourceWithConfigure, error) { r := &resourcePodIdentityAssociation{} @@ -195,16 +195,6 @@ func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource. return } - tags, err := listTags(ctx, conn, data.AssociationArn.ValueString()) - - if err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("listing tags for Pod Identity Association (%s)", data.AssociationId.ValueString()), err.Error()) - - return - } - - setTagsOut(ctx, Tags(tags)) - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } @@ -252,14 +242,6 @@ func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resourc plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) } - if oldTagsAll, newTagsAll := state.TagsAll, plan.TagsAll; !newTagsAll.Equal(oldTagsAll) { - if err := updateTags(ctx, conn, plan.AssociationArn.ValueString(), oldTagsAll, newTagsAll); err != nil { - resp.Diagnostics.AddError(fmt.Sprintf("updating tags for Pod Identity Association (%s)", plan.AssociationId.ValueString()), err.Error()) - - return - } - } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index f2f63245ff97..44fa5b7c517e 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -263,6 +263,10 @@ resource "aws_eks_pod_identity_association" "test" { namespace = %[1]q service_account = "%[1]s-sa" role_arn = aws_iam_role.test.arn + + tags = { + Name = %[1]q + } } `, rName)) } diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index 99ef9c90bd3f..1b613524db27 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -24,7 +24,7 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic Factory: newResourcePodIdentityAssociation, Name: "Pod Identity Association", Tags: &types.ServicePackageResourceTags{ - IdentifierAttribute: "association_id", + IdentifierAttribute: "association_arn", }, }, } From 8891840128dac3e8c904be966f4225ecb6a76b7c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 12:06:38 -0500 Subject: [PATCH 10/16] r/aws_eks_pod_identity_association: Use AutoFlEx. --- internal/service/eks/exports_test.go | 2 +- .../service/eks/pod_identity_association.go | 190 ++++++++---------- internal/service/eks/service_package_gen.go | 2 +- 3 files changed, 85 insertions(+), 109 deletions(-) diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index b42193399002..5759cf538910 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -10,7 +10,7 @@ var ( ResourceFargateProfile = resourceFargateProfile ResourceIdentityProviderConfig = resourceIdentityProviderConfig ResourceNodeGroup = resourceNodeGroup - ResourcePodIdentityAssociation = newResourcePodIdentityAssociation + ResourcePodIdentityAssociation = newPodIdentityAssociationResource FindAddonByTwoPartKey = findAddonByTwoPartKey FindClusterByName = findClusterByName diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index 74a04f4a341a..fefab7c9fef0 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -5,10 +5,8 @@ package eks import ( "context" - "errors" "fmt" "strings" - "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" @@ -21,6 +19,7 @@ import ( sdkid "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" "github.com/hashicorp/terraform-provider-aws/internal/framework" fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" @@ -30,42 +29,43 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// Function annotations are used for resource registration to the Provider. DO NOT EDIT. // @FrameworkResource(name="Pod Identity Association") // @Tags(identifierAttribute="association_arn") -func newResourcePodIdentityAssociation(_ context.Context) (resource.ResourceWithConfigure, error) { - r := &resourcePodIdentityAssociation{} +func newPodIdentityAssociationResource(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &podIdentityAssociationResource{} return r, nil } -type resourcePodIdentityAssociationData struct { - AssociationArn types.String `tfsdk:"association_arn"` - AssociationId types.String `tfsdk:"association_id"` +type podIdentityAssociationResourceModel struct { + AssociationARN types.String `tfsdk:"association_arn"` + AssociationID types.String `tfsdk:"association_id"` ClusterName types.String `tfsdk:"cluster_name"` - CreatedAt types.String `tfsdk:"created_at"` ID types.String `tfsdk:"id"` Namespace types.String `tfsdk:"namespace"` - ModifiedAt types.String `tfsdk:"modified_at"` - RoleArn fwtypes.ARN `tfsdk:"role_arn"` + RoleARN fwtypes.ARN `tfsdk:"role_arn"` ServiceAccount types.String `tfsdk:"service_account"` Tags types.Map `tfsdk:"tags"` TagsAll types.Map `tfsdk:"tags_all"` } +func (model *podIdentityAssociationResourceModel) setID() { + model.ID = model.AssociationID +} + const ( ResNamePodIdentityAssociation = "Pod Identity Association" ) -type resourcePodIdentityAssociation struct { +type podIdentityAssociationResource struct { framework.ResourceWithConfigure } -func (r *resourcePodIdentityAssociation) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { +func (r *podIdentityAssociationResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { resp.TypeName = "aws_eks_pod_identity_association" } -func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { +func (r *podIdentityAssociationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ Attributes: map[string]schema.Attribute{ "association_arn": schema.StringAttribute{ @@ -86,18 +86,7 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc stringplanmodifier.RequiresReplace(), }, }, - "created_at": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, - "modified_at": schema.StringAttribute{ - Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, - }, + names.AttrID: framework.IDAttribute(), "namespace": schema.StringAttribute{ Required: true, PlanModifiers: []planmodifier.String{ @@ -114,66 +103,59 @@ func (r *resourcePodIdentityAssociation) Schema(ctx context.Context, req resourc stringplanmodifier.RequiresReplace(), }, }, - names.AttrID: framework.IDAttribute(), names.AttrTags: tftags.TagsAttribute(), names.AttrTagsAll: tftags.TagsAttributeComputedOnly(), }, } } -func (r *resourcePodIdentityAssociation) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { - conn := r.Meta().EKSClient(ctx) - - var plan resourcePodIdentityAssociationData +func (r *podIdentityAssociationResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var plan podIdentityAssociationResourceModel resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { return } - in := &eks.CreatePodIdentityAssociationInput{ - ClusterName: fwflex.StringFromFramework(ctx, plan.ClusterName), - ClientRequestToken: aws.String(sdkid.UniqueId()), - Namespace: fwflex.StringFromFramework(ctx, plan.Namespace), - RoleArn: fwflex.StringFromFramework(ctx, plan.RoleArn), - ServiceAccount: fwflex.StringFromFramework(ctx, plan.ServiceAccount), - Tags: getTagsIn(ctx), + conn := r.Meta().EKSClient(ctx) + + input := &eks.CreatePodIdentityAssociationInput{} + resp.Diagnostics.Append(fwflex.Expand(ctx, plan, input)...) + if resp.Diagnostics.HasError() { + return } - out, err := conn.CreatePodIdentityAssociation(ctx, in) + input.ClientRequestToken = aws.String(sdkid.UniqueId()) + input.Tags = getTagsIn(ctx) + + output, err := conn.CreatePodIdentityAssociation(ctx, input) + if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationId.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationID.String(), err), err.Error(), ) return } - if out == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionCreating, ResNamePodIdentityAssociation, plan.AssociationId.String(), nil), - errors.New("empty output").Error(), - ) - return - } - plan.AssociationArn = fwflex.StringToFramework(ctx, out.Association.AssociationArn) - plan.AssociationId = fwflex.StringToFramework(ctx, out.Association.AssociationId) - plan.CreatedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.CreatedAt.Format(time.RFC3339))) - plan.ID = fwflex.StringToFramework(ctx, out.Association.AssociationId) - plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) + // Set values for unknowns. + plan.AssociationARN = fwflex.StringToFramework(ctx, output.Association.AssociationArn) + plan.AssociationID = fwflex.StringToFramework(ctx, output.Association.AssociationId) + plan.setID() resp.Diagnostics.Append(resp.State.Set(ctx, plan)...) } -func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { +func (r *podIdentityAssociationResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { conn := r.Meta().EKSClient(ctx) - var data resourcePodIdentityAssociationData + var data podIdentityAssociationResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &data)...) if resp.Diagnostics.HasError() { return } - out, err := findPodIdentityAssociationByTwoPartKey(ctx, conn, data.AssociationId.ValueString(), data.ClusterName.ValueString()) + pia, err := findPodIdentityAssociationByTwoPartKey(ctx, conn, data.AssociationID.ValueString(), data.ClusterName.ValueString()) + if tfresource.NotFound(err) { resp.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) resp.State.RemoveResource(ctx) @@ -183,23 +165,25 @@ func (r *resourcePodIdentityAssociation) Read(ctx context.Context, req resource. if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionSetting, ResNamePodIdentityAssociation, data.AssociationId.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionSetting, ResNamePodIdentityAssociation, data.AssociationID.String(), err), err.Error(), ) return } // Set attributes for import. - resp.Diagnostics.Append(fwflex.Flatten(ctx, out, &data)...) + resp.Diagnostics.Append(fwflex.Flatten(ctx, pia, &data)...) if resp.Diagnostics.HasError() { return } + setTagsOut(ctx, pia.Tags) + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) } -func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan, state resourcePodIdentityAssociationData +func (r *podIdentityAssociationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var plan, state podIdentityAssociationResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &plan)...) if resp.Diagnostics.HasError() { @@ -213,40 +197,31 @@ func (r *resourcePodIdentityAssociation) Update(ctx context.Context, req resourc conn := r.Meta().EKSClient(ctx) - if !plan.RoleArn.Equal(state.RoleArn) { - in := &eks.UpdatePodIdentityAssociationInput{ - AssociationId: aws.String(plan.AssociationId.ValueString()), - ClusterName: aws.String(plan.ClusterName.ValueString()), + if !plan.RoleARN.Equal(state.RoleARN) { + input := &eks.UpdatePodIdentityAssociationInput{} + resp.Diagnostics.Append(fwflex.Expand(ctx, plan, input)...) + if resp.Diagnostics.HasError() { + return } - if !plan.RoleArn.IsNull() { - in.RoleArn = aws.String(plan.RoleArn.ValueString()) - } + input.ClientRequestToken = aws.String(sdkid.UniqueId()) + + _, err := conn.UpdatePodIdentityAssociation(ctx, input) - out, err := conn.UpdatePodIdentityAssociation(ctx, in) if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationId.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationID.String(), err), err.Error(), ) return } - if out == nil || out.Association == nil { - resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationId.String(), nil), - errors.New("empty output").Error(), - ) - return - } - - plan.ModifiedAt = fwflex.StringToFramework(ctx, aws.String(out.Association.ModifiedAt.Format(time.RFC3339))) } resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) } -func (r *resourcePodIdentityAssociation) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { - var state resourcePodIdentityAssociationData +func (r *podIdentityAssociationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var state podIdentityAssociationResourceModel resp.Diagnostics.Append(req.State.Get(ctx, &state)...) if resp.Diagnostics.HasError() { return @@ -254,26 +229,28 @@ func (r *resourcePodIdentityAssociation) Delete(ctx context.Context, req resourc conn := r.Meta().EKSClient(ctx) - in := &eks.DeletePodIdentityAssociationInput{ - AssociationId: fwflex.StringFromFramework(ctx, state.AssociationId), - ClusterName: fwflex.StringFromFramework(ctx, state.ClusterName), + input := &eks.DeletePodIdentityAssociationInput{} + resp.Diagnostics.Append(fwflex.Expand(ctx, state, input)...) + if resp.Diagnostics.HasError() { + return + } + + _, err := conn.DeletePodIdentityAssociation(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return } - _, err := conn.DeletePodIdentityAssociation(ctx, in) if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return - } resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionDeleting, ResNamePodIdentityAssociation, state.AssociationId.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionDeleting, ResNamePodIdentityAssociation, state.AssociationID.String(), err), err.Error(), ) return } } -func (r *resourcePodIdentityAssociation) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { +func (r *podIdentityAssociationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { parts := strings.Split(req.ID, idSeparator) if len(parts) != 2 || parts[0] == "" || parts[1] == "" { err := fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sassociation-id", req.ID, idSeparator) @@ -281,8 +258,8 @@ func (r *resourcePodIdentityAssociation) ImportState(ctx context.Context, req re return } - state := resourcePodIdentityAssociationData{ - AssociationId: types.StringValue(parts[1]), + state := podIdentityAssociationResourceModel{ + AssociationID: types.StringValue(parts[1]), ClusterName: types.StringValue(parts[0]), } @@ -292,33 +269,32 @@ func (r *resourcePodIdentityAssociation) ImportState(ctx context.Context, req re return } } -func (r *resourcePodIdentityAssociation) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { +func (r *podIdentityAssociationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { r.SetTagsAll(ctx, request, response) } -func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Client, AssociationId, ClusterName string) (*eks.DescribePodIdentityAssociationOutput, error) { - in := &eks.DescribePodIdentityAssociationInput{ - AssociationId: aws.String(AssociationId), - ClusterName: aws.String(ClusterName), +func findPodIdentityAssociationByTwoPartKey(ctx context.Context, conn *eks.Client, associationID, clusterName string) (*awstypes.PodIdentityAssociation, error) { + input := &eks.DescribePodIdentityAssociationInput{ + AssociationId: aws.String(associationID), + ClusterName: aws.String(clusterName), } - out, err := conn.DescribePodIdentityAssociation(ctx, in) + output, err := conn.DescribePodIdentityAssociation(ctx, input) - if err != nil { - var nfe *awstypes.ResourceNotFoundException - if errors.As(err, &nfe) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, } + } + if err != nil { return nil, err } - if out == nil { - return nil, tfresource.NewEmptyResultError(in) + if output == nil || output.Association == nil { + return nil, tfresource.NewEmptyResultError(input) } - return out, nil + return output.Association, nil } diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index 1b613524db27..f798ef47f68e 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -21,7 +21,7 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ { - Factory: newResourcePodIdentityAssociation, + Factory: newPodIdentityAssociationResource, Name: "Pod Identity Association", Tags: &types.ServicePackageResourceTags{ IdentifierAttribute: "association_arn", From c39f57c7ed1c03c2ec6d152e9c3b5c499b0ef65f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 12:34:54 -0500 Subject: [PATCH 11/16] r/aws_eks_pod_identity_association: Fix import. --- internal/service/eks/const.go | 6 ----- .../service/eks/pod_identity_association.go | 25 ++++++++----------- .../eks/pod_identity_association_test.go | 2 +- ...eks_pod_identity_association.html.markdown | 13 +++++----- 4 files changed, 18 insertions(+), 28 deletions(-) delete mode 100644 internal/service/eks/const.go diff --git a/internal/service/eks/const.go b/internal/service/eks/const.go deleted file mode 100644 index 27009859bf91..000000000000 --- a/internal/service/eks/const.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (c) HashiCorp, Inc. -// SPDX-License-Identifier: MPL-2.0 - -package eks - -const idSeparator = ":" diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index fefab7c9fef0..c8cd8053deea 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -6,11 +6,11 @@ package eks import ( "context" "fmt" - "strings" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" awstypes "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" @@ -21,6 +21,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/framework" fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" @@ -251,24 +252,20 @@ func (r *podIdentityAssociationResource) Delete(ctx context.Context, req resourc } func (r *podIdentityAssociationResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { - parts := strings.Split(req.ID, idSeparator) - if len(parts) != 2 || parts[0] == "" || parts[1] == "" { - err := fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sassociation-id", req.ID, idSeparator) + const ( + partCount = 2 + ) + parts, err := flex.ExpandResourceId(req.ID, partCount, false) + if err != nil { resp.Diagnostics.AddError(fmt.Sprintf("importing Pod Identity Association (%s)", req.ID), err.Error()) return } - state := podIdentityAssociationResourceModel{ - AssociationID: types.StringValue(parts[1]), - ClusterName: types.StringValue(parts[0]), - } - - diags := resp.State.Set(ctx, &state) - resp.Diagnostics.Append(diags...) - if resp.Diagnostics.HasError() { - return - } + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("id"), parts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("association_id"), parts[1])...) + resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("cluster_name"), parts[0])...) } + func (r *podIdentityAssociationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { r.SetTagsAll(ctx, request, response) } diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 44fa5b7c517e..766efa62d0c3 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -150,7 +150,7 @@ func testAccCheckPodIdentityAssociationImportStateIdFunc(resourceName string) re return "", fmt.Errorf("not found: %s", resourceName) } - return fmt.Sprintf("%s:%s", rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["association_id"]), nil + return fmt.Sprintf("%s,%s", rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["association_id"]), nil } } diff --git a/website/docs/r/eks_pod_identity_association.html.markdown b/website/docs/r/eks_pod_identity_association.html.markdown index d40febb1b785..12550bbe8944 100644 --- a/website/docs/r/eks_pod_identity_association.html.markdown +++ b/website/docs/r/eks_pod_identity_association.html.markdown @@ -63,8 +63,8 @@ The following arguments are required: * `cluster_name` - (Required) The name of the cluster to create the association in. * `namespace` - (Required) The name of the Kubernetes namespace inside the cluster to create the association in. The service account and the pods that use the service account must be in this namespace. -* `service_account` - (Required) The name of the Kubernetes service account inside the cluster to associate the IAM credentials with. * `role_arn` - (Required) The Amazon Resource Name (ARN) of the IAM role to associate with the service account. The EKS Pod Identity agent manages credentials to assume this role for applications in the containers in the pods that use this service account. +* `service_account` - (Required) The name of the Kubernetes service account inside the cluster to associate the IAM credentials with. The following arguments are optional: @@ -76,22 +76,21 @@ This resource exports the following attributes in addition to the arguments abov * `association_arn` - The Amazon Resource Name (ARN) of the association. * `association_id` - The ID of the association. -* `created_at` - The timestamp that the association was created at. -* `modified_at` - The most recent timestamp that the association was modified at. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). ## Import -In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a colon (`:`). For example: +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a comma (`,`). For example: ```terraform import { to = aws_eks_pod_identity_association.example - id = "example:a-12345678" + id = "example,a-12345678" } ``` -Using `terraform import`, import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a colon (`:`). For example: +Using `terraform import`, import EKS (Elastic Kubernetes) Pod Identity Association using the `cluster_name` and `association_id` separated by a comma (`,`). For example: ```console -% terraform import aws_eks_pod_identity_association.example example:a-12345678 +% terraform import aws_eks_pod_identity_association.example example,a-12345678 ``` From ecba6beba7d1b00631a210065d89e453dd0c7b50 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 13:43:29 -0500 Subject: [PATCH 12/16] Acceptance test output: % make testacc TESTARGS='-run=TestAccEKSPodIdentityAssociation_basic' PKG=eks ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/eks/... -v -count 1 -parallel 20 -run=TestAccEKSPodIdentityAssociation_basic -timeout 360m === RUN TestAccEKSPodIdentityAssociation_basic === PAUSE TestAccEKSPodIdentityAssociation_basic === CONT TestAccEKSPodIdentityAssociation_basic --- PASS: TestAccEKSPodIdentityAssociation_basic (626.40s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/eks 638.338s From f80bd55547c0cc430992fffedaee5e033d06477d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 13:55:35 -0500 Subject: [PATCH 13/16] Add 'TestAccEKSPodIdentityAssociation_tags'. --- .../eks/pod_identity_association_test.go | 94 ++++++++++++++++++- 1 file changed, 91 insertions(+), 3 deletions(-) diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 766efa62d0c3..825ced62864f 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -42,12 +42,13 @@ func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccPodIdentityAssociationConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), resource.TestCheckResourceAttrSet(resourceName, "cluster_name"), resource.TestCheckResourceAttrSet(resourceName, "namespace"), resource.TestCheckResourceAttrSet(resourceName, "role_arn"), resource.TestCheckResourceAttrSet(resourceName, "service_account"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -89,6 +90,58 @@ func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { }) } +func TestAccEKSPodIdentityAssociation_tags(t *testing.T) { + ctx := acctest.Context(t) + + var podidentityassociation eks.DescribePodIdentityAssociationOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_pod_identity_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPodIdentityAssociationConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckPodIdentityAssociationImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPodIdentityAssociationConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccPodIdentityAssociationConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + func testAccCheckPodIdentityAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) @@ -253,11 +306,26 @@ resource "aws_iam_role_policy_attachment" "test" { } `, rName)) } + func testAccPodIdentityAssociationConfig_basic(rName string) string { return acctest.ConfigCompose( testAccPodIdentityAssociationConfig_clusterBase(rName), testAccPodIdentityAssociationConfig_podIdentityRoleBase(rName), fmt.Sprintf(` +resource "aws_eks_pod_identity_association" "test" { + cluster_name = aws_eks_cluster.test.name + namespace = %[1]q + service_account = "%[1]s-sa" + role_arn = aws_iam_role.test.arn +} +`, rName)) +} + +func testAccPodIdentityAssociationConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose( + testAccPodIdentityAssociationConfig_clusterBase(rName), + testAccPodIdentityAssociationConfig_podIdentityRoleBase(rName), + fmt.Sprintf(` resource "aws_eks_pod_identity_association" "test" { cluster_name = aws_eks_cluster.test.name namespace = %[1]q @@ -265,8 +333,28 @@ resource "aws_eks_pod_identity_association" "test" { role_arn = aws_iam_role.test.arn tags = { - Name = %[1]q + %[2]q = %[3]q } } -`, rName)) +`, rName, tagKey1, tagValue1)) +} + +func testAccPodIdentityAssociationConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose( + testAccPodIdentityAssociationConfig_clusterBase(rName), + testAccPodIdentityAssociationConfig_podIdentityRoleBase(rName), + fmt.Sprintf(` +resource "aws_eks_pod_identity_association" "test" { + cluster_name = aws_eks_cluster.test.name + namespace = %[1]q + service_account = "%[1]s-sa" + role_arn = aws_iam_role.test.arn + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } + +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } From 98ee6646e3e92a5183321f11b7e1b205e9425e51 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 14:00:34 -0500 Subject: [PATCH 14/16] Add 'TestAccEKSPodIdentityAssociation_updateRoleARN'. --- .../eks/pod_identity_association_test.go | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index 825ced62864f..abbdead2028d 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -142,6 +142,47 @@ func TestAccEKSPodIdentityAssociation_tags(t *testing.T) { }) } +func TestAccEKSPodIdentityAssociation_updateRoleARN(t *testing.T) { + ctx := acctest.Context(t) + + var podidentityassociation eks.DescribePodIdentityAssociationOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_pod_identity_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckPartitionHasService(t, names.EKSEndpointID) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckPodIdentityAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccPodIdentityAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttrPair(resourceName, "role_arn", "aws_iam_role.test", "arn"), + ), + }, + { + ResourceName: resourceName, + ImportStateIdFunc: testAccCheckPodIdentityAssociationImportStateIdFunc(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccPodIdentityAssociationConfig_updatedRoleARN(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckPodIdentityAssociationExists(ctx, resourceName, &podidentityassociation), + resource.TestCheckResourceAttrPair(resourceName, "role_arn", "aws_iam_role.test2", "arn"), + ), + }, + }, + }) +} + func testAccCheckPodIdentityAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) @@ -358,3 +399,44 @@ resource "aws_eks_pod_identity_association" "test" { } `, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } + +func testAccPodIdentityAssociationConfig_updatedRoleARN(rName string) string { + return acctest.ConfigCompose( + testAccPodIdentityAssociationConfig_clusterBase(rName), + testAccPodIdentityAssociationConfig_podIdentityRoleBase(rName), + fmt.Sprintf(` +resource "aws_iam_role" "test2" { + name = "%[1]s-2" + + assume_role_policy = < Date: Wed, 29 Nov 2023 14:06:21 -0500 Subject: [PATCH 15/16] Add and use 'FindPodIdentityAssociationByTwoPartKey'. --- internal/service/eks/exports_test.go | 1 + .../eks/pod_identity_association_test.go | 51 +++++++------------ 2 files changed, 20 insertions(+), 32 deletions(-) diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index 5759cf538910..ae7dc3b663ba 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -17,4 +17,5 @@ var ( FindFargateProfileByTwoPartKey = findFargateProfileByTwoPartKey FindNodegroupByTwoPartKey = findNodegroupByTwoPartKey FindOIDCIdentityProviderConfigByTwoPartKey = findOIDCIdentityProviderConfigByTwoPartKey + FindPodIdentityAssociationByTwoPartKey = findPodIdentityAssociationByTwoPartKey ) diff --git a/internal/service/eks/pod_identity_association_test.go b/internal/service/eks/pod_identity_association_test.go index abbdead2028d..e4056b14e3a8 100644 --- a/internal/service/eks/pod_identity_association_test.go +++ b/internal/service/eks/pod_identity_association_test.go @@ -9,8 +9,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" - "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/eks/types" sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -18,15 +16,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" - "github.com/hashicorp/terraform-provider-aws/internal/errs" tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { ctx := acctest.Context(t) - - var podidentityassociation eks.DescribePodIdentityAssociationOutput + var podidentityassociation types.PodIdentityAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_eks_pod_identity_association.test" @@ -63,8 +60,7 @@ func TestAccEKSPodIdentityAssociation_basic(t *testing.T) { func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { ctx := acctest.Context(t) - - var podidentityassociation eks.DescribePodIdentityAssociationOutput + var podidentityassociation types.PodIdentityAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_eks_pod_identity_association.test" @@ -92,8 +88,7 @@ func TestAccEKSPodIdentityAssociation_disappears(t *testing.T) { func TestAccEKSPodIdentityAssociation_tags(t *testing.T) { ctx := acctest.Context(t) - - var podidentityassociation eks.DescribePodIdentityAssociationOutput + var podidentityassociation types.PodIdentityAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_eks_pod_identity_association.test" @@ -144,8 +139,7 @@ func TestAccEKSPodIdentityAssociation_tags(t *testing.T) { func TestAccEKSPodIdentityAssociation_updateRoleARN(t *testing.T) { ctx := acctest.Context(t) - - var podidentityassociation eks.DescribePodIdentityAssociationOutput + var podidentityassociation types.PodIdentityAssociation rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_eks_pod_identity_association.test" @@ -192,46 +186,39 @@ func testAccCheckPodIdentityAssociationDestroy(ctx context.Context) resource.Tes continue } - _, err := conn.DescribePodIdentityAssociation(ctx, &eks.DescribePodIdentityAssociationInput{ - AssociationId: aws.String(rs.Primary.ID), - ClusterName: aws.String(rs.Primary.Attributes["cluster_name"]), - }) - if errs.IsA[*types.ResourceNotFoundException](err) { - return nil + _, err := tfeks.FindPodIdentityAssociationByTwoPartKey(ctx, conn, rs.Primary.Attributes["association_id"], rs.Primary.Attributes["cluster_name"]) + + if tfresource.NotFound(err) { + continue } + if err != nil { - return create.Error(names.EKS, create.ErrActionCheckingDestroyed, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, err) + return err } - return create.Error(names.EKS, create.ErrActionCheckingDestroyed, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, errors.New("not destroyed")) + return fmt.Errorf("EKS Pod Identity Association %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckPodIdentityAssociationExists(ctx context.Context, name string, podidentityassociation *eks.DescribePodIdentityAssociationOutput) resource.TestCheckFunc { +func testAccCheckPodIdentityAssociationExists(ctx context.Context, n string, v *types.PodIdentityAssociation) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, name, errors.New("not found")) - } - - if rs.Primary.ID == "" { - return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, name, errors.New("not set")) + return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, n, errors.New("not found")) } conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) - resp, err := conn.DescribePodIdentityAssociation(ctx, &eks.DescribePodIdentityAssociationInput{ - AssociationId: aws.String(rs.Primary.ID), - ClusterName: aws.String(rs.Primary.Attributes["cluster_name"]), - }) + + output, err := tfeks.FindPodIdentityAssociationByTwoPartKey(ctx, conn, rs.Primary.Attributes["association_id"], rs.Primary.Attributes["cluster_name"]) if err != nil { - return create.Error(names.EKS, create.ErrActionCheckingExistence, tfeks.ResNamePodIdentityAssociation, rs.Primary.ID, err) + return err } - *podidentityassociation = *resp + *v = *output return nil } From a386c383c7da59b643602577edafc99f1e279ca7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 29 Nov 2023 16:10:21 -0500 Subject: [PATCH 16/16] r/aws_eks_pod_identity_association: Fix update. --- internal/service/eks/pod_identity_association.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/service/eks/pod_identity_association.go b/internal/service/eks/pod_identity_association.go index c8cd8053deea..b5f6e46d2b28 100644 --- a/internal/service/eks/pod_identity_association.go +++ b/internal/service/eks/pod_identity_association.go @@ -184,23 +184,23 @@ func (r *podIdentityAssociationResource) Read(ctx context.Context, req resource. } func (r *podIdentityAssociationResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { - var plan, state podIdentityAssociationResourceModel + var old, new podIdentityAssociationResourceModel - resp.Diagnostics.Append(req.State.Get(ctx, &plan)...) + resp.Diagnostics.Append(req.State.Get(ctx, &old)...) if resp.Diagnostics.HasError() { return } - resp.Diagnostics.Append(req.Plan.Get(ctx, &state)...) + resp.Diagnostics.Append(req.Plan.Get(ctx, &new)...) if resp.Diagnostics.HasError() { return } conn := r.Meta().EKSClient(ctx) - if !plan.RoleARN.Equal(state.RoleARN) { + if !new.RoleARN.Equal(old.RoleARN) { input := &eks.UpdatePodIdentityAssociationInput{} - resp.Diagnostics.Append(fwflex.Expand(ctx, plan, input)...) + resp.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) if resp.Diagnostics.HasError() { return } @@ -211,14 +211,14 @@ func (r *podIdentityAssociationResource) Update(ctx context.Context, req resourc if err != nil { resp.Diagnostics.AddError( - create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, plan.AssociationID.String(), err), + create.ProblemStandardMessage(names.EKS, create.ErrActionUpdating, ResNamePodIdentityAssociation, new.AssociationID.String(), err), err.Error(), ) return } } - resp.Diagnostics.Append(resp.State.Set(ctx, &plan)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &new)...) } func (r *podIdentityAssociationResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {