From 493059c99d4a04898b2a763d1125815c7e459a0a Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 23 Nov 2022 13:35:30 -0800 Subject: [PATCH 01/68] Adds semgrep rule --- .ci/.semgrep.yml | 10 +++++++++- internal/service/apigateway/api_key_test.go | 10 ++-------- internal/service/apigateway/authorizer_test.go | 8 ++------ internal/service/apigateway/client_certificate_test.go | 10 ++-------- internal/service/apigateway/gateway_response_test.go | 10 ++-------- .../service/apigateway/integration_response_test.go | 10 ++-------- internal/service/apigateway/integration_test.go | 10 ++-------- internal/service/apigateway/method_response_test.go | 10 ++-------- internal/service/apigateway/method_test.go | 10 ++-------- internal/service/apigateway/model_test.go | 10 ++-------- internal/service/apigateway/request_validator_test.go | 10 ++-------- internal/service/apigateway/resource_test.go | 10 ++-------- internal/service/apigateway/usage_plan_key_test.go | 10 ++-------- internal/service/efs/mount_target_test.go | 8 +------- .../elasticbeanstalk/application_version_test.go | 9 +++------ internal/service/elasticsearch/domain_test.go | 10 ++-------- internal/service/elb/lb_ssl_negotiation_policy_test.go | 10 ++-------- internal/service/elb/load_balancer_test.go | 10 ++-------- internal/service/iam/account_password_policy_test.go | 9 ++------- internal/service/iam/group_test.go | 10 ++-------- internal/service/iam/user_ssh_key_test.go | 9 ++------- internal/service/neptune/subnet_group_test.go | 9 ++------- internal/service/opensearch/domain_test.go | 10 ++-------- internal/service/rds/cluster_parameter_group.go | 7 +------ internal/service/rds/cluster_parameter_group_test.go | 8 ++------ internal/service/rds/parameter_group_test.go | 9 ++------- internal/service/redshift/parameter_group_test.go | 9 ++------- internal/service/sagemaker/model_test.go | 8 ++------ 28 files changed, 62 insertions(+), 201 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index bbcb5176414..e5873c5a1fd 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -800,7 +800,7 @@ rules: - id: aws-go-sdk-error-code-helper languages: [go] - message: 'Use `tfawserr` helpers for checking AWS Go SDK errors (e.g. `tfawserr.ErrMessageContains(err, CODE, MESSAGE)`)' + message: 'Use `tfawserr` helpers for checking AWS Go SDK v1 errors (e.g. `tfawserr.ErrMessageContains(err, CODE, MESSAGE)`)' paths: include: - internal/ @@ -811,6 +811,14 @@ rules: if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK { if $AWSERR.Code() == $CODE { $BODY } } + - pattern: | + $AWSERR, $OK := $ORIGINALERR.(awserr.Error) + if !$OK { + ... + } + if ... { + ... + } severity: WARNING - id: fmt-Errorf-awserr-Error-Code diff --git a/internal/service/apigateway/api_key_test.go b/internal/service/apigateway/api_key_test.go index 97eb2f335ce..a91950b7b35 100644 --- a/internal/service/apigateway/api_key_test.go +++ b/internal/service/apigateway/api_key_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -262,15 +262,9 @@ func testAccCheckAPIKeyDestroy(s *terraform.State) error { } } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/authorizer_test.go b/internal/service/apigateway/authorizer_test.go index 043414c3867..105b5554a1b 100644 --- a/internal/service/apigateway/authorizer_test.go +++ b/internal/service/apigateway/authorizer_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -353,11 +353,7 @@ func testAccCheckAuthorizerDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Authorizer still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } diff --git a/internal/service/apigateway/client_certificate_test.go b/internal/service/apigateway/client_certificate_test.go index afa3c5b6898..01f2c78c37f 100644 --- a/internal/service/apigateway/client_certificate_test.go +++ b/internal/service/apigateway/client_certificate_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -159,15 +159,9 @@ func testAccCheckClientCertificateDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Client Certificate still exists: %s", out) } - awsErr, ok := err.(awserr.Error) - if !ok { - return err - } - if awsErr.Code() != apigateway.ErrCodeNotFoundException { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/gateway_response_test.go b/internal/service/apigateway/gateway_response_test.go index 9e6691a01ca..68f7db4ca6d 100644 --- a/internal/service/apigateway/gateway_response_test.go +++ b/internal/service/apigateway/gateway_response_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -128,15 +128,9 @@ func testAccCheckGatewayResponseDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Gateway Response still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/integration_response_test.go b/internal/service/apigateway/integration_response_test.go index e1985539cb7..5ca557def33 100644 --- a/internal/service/apigateway/integration_response_test.go +++ b/internal/service/apigateway/integration_response_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -179,15 +179,9 @@ func testAccCheckIntegrationResponseDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Method still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/integration_test.go b/internal/service/apigateway/integration_test.go index f48cfe7ef58..12e5b820042 100644 --- a/internal/service/apigateway/integration_test.go +++ b/internal/service/apigateway/integration_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -404,15 +404,9 @@ func testAccCheckIntegrationDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Method still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/method_response_test.go b/internal/service/apigateway/method_response_test.go index e81cf1482d9..29436533bac 100644 --- a/internal/service/apigateway/method_response_test.go +++ b/internal/service/apigateway/method_response_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -174,15 +174,9 @@ func testAccCheckMethodResponseDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Method still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/method_test.go b/internal/service/apigateway/method_test.go index dec757a472c..a9e81dcdab4 100644 --- a/internal/service/apigateway/method_test.go +++ b/internal/service/apigateway/method_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -337,15 +337,9 @@ func testAccCheckMethodDestroy(s *terraform.State) error { return fmt.Errorf("API Gateway Method still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/model_test.go b/internal/service/apigateway/model_test.go index 1de2fb21aa2..59f7afd8b47 100644 --- a/internal/service/apigateway/model_test.go +++ b/internal/service/apigateway/model_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -143,15 +143,9 @@ func testAccCheckModelDestroy(s *terraform.State) error { } } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/request_validator_test.go b/internal/service/apigateway/request_validator_test.go index 79f817ac874..4a923d07b64 100644 --- a/internal/service/apigateway/request_validator_test.go +++ b/internal/service/apigateway/request_validator_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -165,15 +165,9 @@ func testAccCheckRequestValidatorDestroy(s *terraform.State) error { return fmt.Errorf("API Request Validator still exists") } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/resource_test.go b/internal/service/apigateway/resource_test.go index efddd8f3933..96853d26af9 100644 --- a/internal/service/apigateway/resource_test.go +++ b/internal/service/apigateway/resource_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -176,15 +176,9 @@ func testAccCheckResourceDestroy(s *terraform.State) error { } } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/apigateway/usage_plan_key_test.go b/internal/service/apigateway/usage_plan_key_test.go index 99fc2ffa1e4..1dd8a4c16b9 100644 --- a/internal/service/apigateway/usage_plan_key_test.go +++ b/internal/service/apigateway/usage_plan_key_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -156,15 +156,9 @@ func testAccCheckUsagePlanKeyDestroy(s *terraform.State) error { } } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if !tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return err } - - return nil } return nil diff --git a/internal/service/efs/mount_target_test.go b/internal/service/efs/mount_target_test.go index 56319b4cdac..5cf2686cddd 100644 --- a/internal/service/efs/mount_target_test.go +++ b/internal/service/efs/mount_target_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/efs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" @@ -439,12 +438,7 @@ func testAccCheckVPNGatewayDestroy(s *terraform.State) error { return nil } - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "InvalidVpnGatewayID.NotFound" { + if !tfawserr.ErrCodeEquals(err, "InvalidVpnGatewayID.NotFound") { return err } } diff --git a/internal/service/elasticbeanstalk/application_version_test.go b/internal/service/elasticbeanstalk/application_version_test.go index 0c102beec89..cf4806c5a6a 100644 --- a/internal/service/elasticbeanstalk/application_version_test.go +++ b/internal/service/elasticbeanstalk/application_version_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -126,11 +126,8 @@ func testAccCheckApplicationVersionDestroy(s *terraform.State) error { return nil } - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "InvalidParameterValue" { + + if !tfawserr.ErrCodeEquals(err, "InvalidParameterValue") { return err } } diff --git a/internal/service/elasticsearch/domain_test.go b/internal/service/elasticsearch/domain_test.go index 6a2e18127ad..773c2682f92 100644 --- a/internal/service/elasticsearch/domain_test.go +++ b/internal/service/elasticsearch/domain_test.go @@ -7,10 +7,10 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -3021,15 +3021,9 @@ func testAccCheckELBDestroy(s *terraform.State) error { } } - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { return err } - - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } } return nil diff --git a/internal/service/elb/lb_ssl_negotiation_policy_test.go b/internal/service/elb/lb_ssl_negotiation_policy_test.go index 4ed2aafa9fc..4e2935effb9 100644 --- a/internal/service/elb/lb_ssl_negotiation_policy_test.go +++ b/internal/service/elb/lb_ssl_negotiation_policy_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -90,15 +90,9 @@ func testAccCheckLBSSLNegotiationPolicyDestroy(s *terraform.State) error { } } - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { return err } - - if providerErr.Code() != "LoadBalancerNotFound" { - return fmt.Errorf("Unexpected error: %s", err) - } } else { // Check that the SSL Negotiation Policy is destroyed elbName, _, policyName, err := tfelb.SSLNegotiationPolicyParseID(rs.Primary.ID) diff --git a/internal/service/elb/load_balancer_test.go b/internal/service/elb/load_balancer_test.go index 90fa370c247..68c8326a59c 100644 --- a/internal/service/elb/load_balancer_test.go +++ b/internal/service/elb/load_balancer_test.go @@ -9,8 +9,8 @@ import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -956,15 +956,9 @@ func testAccCheckLoadBalancerDestroy(s *terraform.State) error { } } - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { return err } - - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } } return nil diff --git a/internal/service/iam/account_password_policy_test.go b/internal/service/iam/account_password_policy_test.go index 8e74eae0c3f..221fb46f082 100644 --- a/internal/service/iam/account_password_policy_test.go +++ b/internal/service/iam/account_password_policy_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -59,12 +59,7 @@ func testAccCheckAccountPasswordPolicyDestroy(s *terraform.State) error { return fmt.Errorf("still exist.") } - // Verify the error is what we want - awsErr, ok := err.(awserr.Error) - if !ok { - return err - } - if awsErr.Code() != "NoSuchEntity" { + if !tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { return err } } diff --git a/internal/service/iam/group_test.go b/internal/service/iam/group_test.go index 6a665a33832..5d336c75b7c 100644 --- a/internal/service/iam/group_test.go +++ b/internal/service/iam/group_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -91,7 +91,6 @@ func testAccCheckGroupDestroy(s *terraform.State) error { continue } - // Try to get group _, err := conn.GetGroup(&iam.GetGroupInput{ GroupName: aws.String(rs.Primary.ID), }) @@ -99,12 +98,7 @@ func testAccCheckGroupDestroy(s *terraform.State) error { return errors.New("still exist.") } - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "NoSuchEntity" { + if !tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { return err } } diff --git a/internal/service/iam/user_ssh_key_test.go b/internal/service/iam/user_ssh_key_test.go index edbb52613bd..515d75fc853 100644 --- a/internal/service/iam/user_ssh_key_test.go +++ b/internal/service/iam/user_ssh_key_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -92,12 +92,7 @@ func testAccCheckUserSSHKeyDestroy(s *terraform.State) error { return fmt.Errorf("still exist.") } - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "NoSuchEntity" { + if !tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { return err } } diff --git a/internal/service/neptune/subnet_group_test.go b/internal/service/neptune/subnet_group_test.go index 606ecbe44e5..2d487f274e5 100644 --- a/internal/service/neptune/subnet_group_test.go +++ b/internal/service/neptune/subnet_group_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/neptune" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -156,12 +156,7 @@ func testAccCheckSubnetGroupDestroy(s *terraform.State) error { return nil } - // Verify the error is what we want - neptuneerr, ok := err.(awserr.Error) - if !ok { - return err - } - if neptuneerr.Code() != "DBSubnetGroupNotFoundFault" { + if !tfawserr.ErrCodeEquals(err, neptune.ErrCodeDBSubnetGroupNotFoundFault) { return err } } diff --git a/internal/service/opensearch/domain_test.go b/internal/service/opensearch/domain_test.go index 75065529770..4a3153784d9 100644 --- a/internal/service/opensearch/domain_test.go +++ b/internal/service/opensearch/domain_test.go @@ -7,10 +7,10 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" "github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/opensearchservice" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -1905,15 +1905,9 @@ func testAccCheckELBDestroy(s *terraform.State) error { } } - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { return err } - - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } } return nil diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index e0b938e469c..58355200ddb 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -327,12 +327,7 @@ func resourceClusterParameterGroupDeleteRefreshFunc( } if _, err := conn.DeleteDBClusterParameterGroup(&deleteOpts); err != nil { - rdserr, ok := err.(awserr.Error) - if !ok { - return d, "error", err - } - - if rdserr.Code() != "DBParameterGroupNotFound" { + if !tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { return d, "error", err } } diff --git a/internal/service/rds/cluster_parameter_group_test.go b/internal/service/rds/cluster_parameter_group_test.go index b54edb46bfa..add18aff410 100644 --- a/internal/service/rds/cluster_parameter_group_test.go +++ b/internal/service/rds/cluster_parameter_group_test.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -435,12 +436,7 @@ func testAccCheckClusterParameterGroupDestroy(s *terraform.State) error { } } - // Verify the error - newerr, ok := err.(awserr.Error) - if !ok { - return err - } - if newerr.Code() != "DBParameterGroupNotFound" { + if !tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { return err } } diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index bc0bb1b320c..4a32f0c4b9a 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -1017,12 +1017,7 @@ func testAccCheckParameterGroupDestroy(s *terraform.State) error { } } - // Verify the error - newerr, ok := err.(awserr.Error) - if !ok { - return err - } - if newerr.Code() != "DBParameterGroupNotFound" { + if !tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { return err } } diff --git a/internal/service/redshift/parameter_group_test.go b/internal/service/redshift/parameter_group_test.go index 50ff6286cd5..573fc63a5eb 100644 --- a/internal/service/redshift/parameter_group_test.go +++ b/internal/service/redshift/parameter_group_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/redshift" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -186,12 +186,7 @@ func testAccCheckParameterGroupDestroy(s *terraform.State) error { } } - // Verify the error - newerr, ok := err.(awserr.Error) - if !ok { - return err - } - if newerr.Code() != "ClusterParameterGroupNotFound" { + if !tfawserr.ErrCodeEquals(err, redshift.ErrCodeClusterParameterGroupNotFoundFault) { return err } } diff --git a/internal/service/sagemaker/model_test.go b/internal/service/sagemaker/model_test.go index 964a6c40f70..3b04d8255bc 100644 --- a/internal/service/sagemaker/model_test.go +++ b/internal/service/sagemaker/model_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -404,11 +404,7 @@ func testAccCheckModelDestroy(s *terraform.State) error { return nil } - sagemakerErr, ok := err.(awserr.Error) - if !ok { - return err - } - if sagemakerErr.Code() != "ResourceNotFound" { + if !tfawserr.ErrCodeEquals(err, sagemaker.ErrCodeResourceNotFound) { return err } } From 14df84e734cf82ce26589ea5dab22eebff1b41aa Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 23 Nov 2022 14:00:02 -0800 Subject: [PATCH 02/68] Update existing rule to expand conditional cases --- .ci/.semgrep.yml | 2 +- internal/service/cognitoidp/user_test.go | 6 +++--- internal/service/elb/app_cookie_stickiness_policy_test.go | 4 ++-- internal/service/elb/backend_server_policy_test.go | 3 +-- internal/service/elb/lb_cookie_stickiness_policy_test.go | 4 ++-- internal/service/elb/listener_policy_test.go | 3 +-- internal/service/elb/policy_test.go | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index e5873c5a1fd..8c39a0487fe 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -806,7 +806,7 @@ rules: - internal/ patterns: - pattern-either: - - pattern: if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK && $AWSERR.Code() == $CODE { $BODY } + - pattern: if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK && ... { $BODY } - pattern: | if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK { if $AWSERR.Code() == $CODE { $BODY } diff --git a/internal/service/cognitoidp/user_test.go b/internal/service/cognitoidp/user_test.go index 0258ccfc9c8..54518190b9b 100644 --- a/internal/service/cognitoidp/user_test.go +++ b/internal/service/cognitoidp/user_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -333,8 +333,8 @@ func testAccCheckUserDestroy(s *terraform.State) error { _, err := conn.AdminGetUser(params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == cognitoidentityprovider.ErrCodeUserNotFoundException || awsErr.Code() == cognitoidentityprovider.ErrCodeResourceNotFoundException) { - return nil + if !tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeUserNotFoundException, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + continue } return err } diff --git a/internal/service/elb/app_cookie_stickiness_policy_test.go b/internal/service/elb/app_cookie_stickiness_policy_test.go index ca5031863a5..9921c79c5b0 100644 --- a/internal/service/elb/app_cookie_stickiness_policy_test.go +++ b/internal/service/elb/app_cookie_stickiness_policy_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -90,7 +90,7 @@ func testAccCheckAppCookieStickinessPolicyDestroy(s *terraform.State) error { PolicyNames: []*string{aws.String(policyName)}, }) if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { continue } return err diff --git a/internal/service/elb/backend_server_policy_test.go b/internal/service/elb/backend_server_policy_test.go index 4732ba7349d..3efcaa35fdf 100644 --- a/internal/service/elb/backend_server_policy_test.go +++ b/internal/service/elb/backend_server_policy_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -80,7 +79,7 @@ func testAccCheckBackendServerPolicyDestroy(s *terraform.State) error { PolicyNames: []*string{aws.String(policyName)}, }) if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { continue } return err diff --git a/internal/service/elb/lb_cookie_stickiness_policy_test.go b/internal/service/elb/lb_cookie_stickiness_policy_test.go index 0e8b44188fc..fa207638fd3 100644 --- a/internal/service/elb/lb_cookie_stickiness_policy_test.go +++ b/internal/service/elb/lb_cookie_stickiness_policy_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -63,7 +63,7 @@ func testAccCheckLBCookieStickinessPolicyDestroy(s *terraform.State) error { PolicyNames: []*string{aws.String(policyName)}, }) if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { continue } return err diff --git a/internal/service/elb/listener_policy_test.go b/internal/service/elb/listener_policy_test.go index 1c7c9f20ab3..928fe105a83 100644 --- a/internal/service/elb/listener_policy_test.go +++ b/internal/service/elb/listener_policy_test.go @@ -6,7 +6,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -73,7 +72,7 @@ func testAccCheckListenerPolicyDestroy(s *terraform.State) error { PolicyNames: []*string{aws.String(policyName)}, }) if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { continue } return err diff --git a/internal/service/elb/policy_test.go b/internal/service/elb/policy_test.go index 58ddb1c3ffd..45565dea0f0 100644 --- a/internal/service/elb/policy_test.go +++ b/internal/service/elb/policy_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -294,7 +294,7 @@ func testAccCheckPolicyDestroy(s *terraform.State) error { PolicyNames: []*string{aws.String(policyName)}, }) if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { + if !tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { continue } return err From 52fc0049028b2c2fcb5ef5f7c9239528a82bc8d1 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 23 Nov 2022 15:25:38 -0800 Subject: [PATCH 03/68] Adds another rule --- .ci/.semgrep.yml | 5 +++++ internal/service/docdb/cluster_instance_test.go | 5 ++--- internal/service/elasticache/parameter_group.go | 6 ++---- internal/service/lambda/function.go | 3 +-- internal/service/rds/cluster_parameter_group.go | 3 +-- internal/service/rds/cluster_parameter_group_test.go | 4 +--- internal/service/redshift/security_group.go | 5 ++--- internal/service/servicecatalog/portfolio.go | 4 ++-- internal/service/waf/rate_based_rule.go | 4 ++-- internal/service/waf/size_constraint_set.go | 4 ++-- 10 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index 8c39a0487fe..751b4d83b9e 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -819,6 +819,11 @@ rules: if ... { ... } + - pattern: | + $AWSERR, $OK := $ORIGINALERR.(awserr.Error) + if $OK && ... { + ... + } severity: WARNING - id: fmt-Errorf-awserr-Error-Code diff --git a/internal/service/docdb/cluster_instance_test.go b/internal/service/docdb/cluster_instance_test.go index dde593e45ce..c26a5d2a113 100644 --- a/internal/service/docdb/cluster_instance_test.go +++ b/internal/service/docdb/cluster_instance_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/docdb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -289,8 +289,7 @@ func testAccClusterInstanceDisappears(v *docdb.DBInstance) resource.TestCheckFun } _, err := conn.DescribeDBInstances(opts) if err != nil { - dbinstanceerr, ok := err.(awserr.Error) - if ok && dbinstanceerr.Code() == "DBInstanceNotFound" { + if tfawserr.ErrCodeEquals(err, docdb.ErrCodeDBInstanceNotFoundFault) { return nil } return resource.NonRetryableError( diff --git a/internal/service/elasticache/parameter_group.go b/internal/service/elasticache/parameter_group.go index 6410a9ed6ef..b080540f2a2 100644 --- a/internal/service/elasticache/parameter_group.go +++ b/internal/service/elasticache/parameter_group.go @@ -8,7 +8,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -323,11 +322,10 @@ func deleteParameterGroup(conn *elasticache.ElastiCache, name string) error { err := resource.Retry(3*time.Minute, func() *resource.RetryError { _, err := conn.DeleteCacheParameterGroup(&deleteOpts) if err != nil { - awsErr, ok := err.(awserr.Error) - if ok && awsErr.Code() == "CacheParameterGroupNotFoundFault" { + if tfawserr.ErrCodeEquals(err, elasticache.ErrCodeCacheParameterGroupNotFoundFault) { return nil } - if ok && awsErr.Code() == "InvalidCacheParameterGroupState" { + if tfawserr.ErrCodeEquals(err, elasticache.ErrCodeInvalidCacheParameterGroupStateFault) { return resource.RetryableError(err) } return resource.NonRetryableError(err) diff --git a/internal/service/lambda/function.go b/internal/service/lambda/function.go index 2f001c21536..1d51dbf28df 100644 --- a/internal/service/lambda/function.go +++ b/internal/service/lambda/function.go @@ -11,7 +11,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/endpoints" "github.com/aws/aws-sdk-go/service/lambda" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" @@ -692,7 +691,7 @@ func resourceFunctionRead(d *schema.ResourceData, meta interface{}) error { getFunctionOutput, err := conn.GetFunction(params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == lambda.ErrCodeResourceNotFoundException && !d.IsNewResource() { + if tfawserr.ErrCodeEquals(err, lambda.ErrCodeResourceNotFoundException) && !d.IsNewResource() { d.SetId("") return nil } diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index 58355200ddb..a7fde9cb0be 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -6,7 +6,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -132,7 +131,7 @@ func resourceClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) describeResp, err := conn.DescribeDBClusterParameterGroups(&describeOpts) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DBParameterGroupNotFound" { + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { log.Printf("[WARN] DB Cluster Parameter Group (%s) not found, error code (404)", d.Id()) d.SetId("") return nil diff --git a/internal/service/rds/cluster_parameter_group_test.go b/internal/service/rds/cluster_parameter_group_test.go index add18aff410..66adf600d8f 100644 --- a/internal/service/rds/cluster_parameter_group_test.go +++ b/internal/service/rds/cluster_parameter_group_test.go @@ -7,7 +7,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -506,8 +505,7 @@ func testAccClusterParameterGroupDisappears(v *rds.DBClusterParameterGroup) reso } _, err := conn.DescribeDBClusterParameterGroups(opts) if err != nil { - dbparamgrouperr, ok := err.(awserr.Error) - if ok && dbparamgrouperr.Code() == "DBParameterGroupNotFound" { + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { return nil } return resource.NonRetryableError( diff --git a/internal/service/redshift/security_group.go b/internal/service/redshift/security_group.go index effbde4fe0c..81372a2dcb8 100644 --- a/internal/service/redshift/security_group.go +++ b/internal/service/redshift/security_group.go @@ -8,8 +8,8 @@ import ( "regexp" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/redshift" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -164,8 +164,7 @@ func resourceSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error _, err := conn.DeleteClusterSecurityGroup(&opts) if err != nil { - newerr, ok := err.(awserr.Error) - if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" { + if tfawserr.ErrCodeEquals(err, "InvalidRedshiftSecurityGroup.NotFound") { return nil } return err diff --git a/internal/service/servicecatalog/portfolio.go b/internal/service/servicecatalog/portfolio.go index b79157939c6..2cc65e8347d 100644 --- a/internal/service/servicecatalog/portfolio.go +++ b/internal/service/servicecatalog/portfolio.go @@ -6,8 +6,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/servicecatalog" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -108,7 +108,7 @@ func resourcePortfolioRead(d *schema.ResourceData, meta interface{}) error { log.Printf("[DEBUG] Reading Service Catalog Portfolio: %#v", input) resp, err := conn.DescribePortfolio(&input) if err != nil { - if scErr, ok := err.(awserr.Error); ok && scErr.Code() == "ResourceNotFoundException" { + if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) { log.Printf("[WARN] Service Catalog Portfolio %q not found, removing from state", d.Id()) d.SetId("") return nil diff --git a/internal/service/waf/rate_based_rule.go b/internal/service/waf/rate_based_rule.go index 5f61054b9d4..a7da8399d14 100644 --- a/internal/service/waf/rate_based_rule.go +++ b/internal/service/waf/rate_based_rule.go @@ -6,8 +6,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/waf" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -130,7 +130,7 @@ func resourceRateBasedRuleRead(d *schema.ResourceData, meta interface{}) error { resp, err := conn.GetRateBasedRule(params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == waf.ErrCodeNonexistentItemException { + if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { log.Printf("[WARN] WAF Rate Based Rule (%s) not found, removing from state", d.Id()) d.SetId("") return nil diff --git a/internal/service/waf/size_constraint_set.go b/internal/service/waf/size_constraint_set.go index 1aeb4d43156..4920a9fd638 100644 --- a/internal/service/waf/size_constraint_set.go +++ b/internal/service/waf/size_constraint_set.go @@ -6,8 +6,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/waf" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -59,7 +59,7 @@ func resourceSizeConstraintSetRead(d *schema.ResourceData, meta interface{}) err resp, err := conn.GetSizeConstraintSet(params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == waf.ErrCodeNonexistentItemException { + if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { log.Printf("[WARN] WAF SizeConstraintSet (%s) not found, removing from state", d.Id()) d.SetId("") return nil From c5625e6dbd4fdd4d2ad1515649e6c278337c8760 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 23 Nov 2022 16:21:57 -0800 Subject: [PATCH 04/68] Updates another existing rule --- .ci/.semgrep.yml | 2 +- .../configuration_template.go | 22 +++++++++---------- internal/service/elb/backend_server_policy.go | 10 ++++----- internal/service/elb/listener_policy.go | 10 ++++----- internal/service/iam/server_certificate.go | 21 +++++++++--------- internal/service/lambda/alias.go | 10 ++++----- internal/service/lightsail/domain.go | 13 +++++------ internal/service/lightsail/instance.go | 13 +++++------ internal/service/lightsail/key_pair.go | 14 +++++------- internal/service/lightsail/static_ip.go | 13 +++++------ .../service/lightsail/static_ip_attachment.go | 12 +++++----- internal/service/opsworks/permission.go | 13 +++++------ internal/service/opsworks/permission_test.go | 8 +++---- .../service/opsworks/user_profile_test.go | 8 +++---- 14 files changed, 70 insertions(+), 99 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index 751b4d83b9e..e248201df81 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -809,7 +809,7 @@ rules: - pattern: if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK && ... { $BODY } - pattern: | if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK { - if $AWSERR.Code() == $CODE { $BODY } + ... } - pattern: | $AWSERR, $OK := $ORIGINALERR.(awserr.Error) diff --git a/internal/service/elasticbeanstalk/configuration_template.go b/internal/service/elasticbeanstalk/configuration_template.go index b0b9e070a96..36913f56bad 100644 --- a/internal/service/elasticbeanstalk/configuration_template.go +++ b/internal/service/elasticbeanstalk/configuration_template.go @@ -3,11 +3,10 @@ package elasticbeanstalk import ( "fmt" "log" - "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -103,16 +102,15 @@ func resourceConfigurationTemplateRead(d *schema.ResourceData, meta interface{}) }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "No Configuration Template named") { - log.Printf("[WARN] No Configuration Template named (%s) found", d.Id()) - d.SetId("") - return nil - } else if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "No Platform named") { - log.Printf("[WARN] No Platform named (%s) found", d.Get("solution_stack_name").(string)) - d.SetId("") - return nil - } + if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "No Configuration Template named") { + log.Printf("[WARN] No Configuration Template named (%s) found", d.Id()) + d.SetId("") + return nil + } + if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "No Platform named") { + log.Printf("[WARN] No Platform named (%s) found", d.Get("solution_stack_name").(string)) + d.SetId("") + return nil } return err } diff --git a/internal/service/elb/backend_server_policy.go b/internal/service/elb/backend_server_policy.go index 8ee7a702e79..f0c39b6533c 100644 --- a/internal/service/elb/backend_server_policy.go +++ b/internal/service/elb/backend_server_policy.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/flex" @@ -77,11 +77,9 @@ func resourceBackendServerPolicyRead(d *schema.ResourceData, meta interface{}) e describeResp, err := conn.DescribeLoadBalancers(describeElbOpts) if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "LoadBalancerNotFound" { - d.SetId("") - return fmt.Errorf("LoadBalancerNotFound: %s", err) - } + if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { + d.SetId("") + return fmt.Errorf("LoadBalancerNotFound: %s", err) } return fmt.Errorf("Error retrieving ELB description: %s", err) } diff --git a/internal/service/elb/listener_policy.go b/internal/service/elb/listener_policy.go index e7a597773a8..7425f4e4cff 100644 --- a/internal/service/elb/listener_policy.go +++ b/internal/service/elb/listener_policy.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/flex" @@ -77,11 +77,9 @@ func resourceListenerPolicyRead(d *schema.ResourceData, meta interface{}) error describeResp, err := conn.DescribeLoadBalancers(describeElbOpts) if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "LoadBalancerNotFound" { - d.SetId("") - return fmt.Errorf("LoadBalancerNotFound: %s", err) - } + if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { + d.SetId("") + return fmt.Errorf("LoadBalancerNotFound: %s", err) } return fmt.Errorf("Error retrieving ELB description: %s", err) } diff --git a/internal/service/iam/server_certificate.go b/internal/service/iam/server_certificate.go index 5bd2c6d46ac..aa4bb8fae71 100644 --- a/internal/service/iam/server_certificate.go +++ b/internal/service/iam/server_certificate.go @@ -3,6 +3,7 @@ package iam import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports "crypto/sha1" "encoding/hex" + "errors" "fmt" "log" "regexp" @@ -249,21 +250,23 @@ func resourceServerCertificateUpdate(d *schema.ResourceData, meta interface{}) e func resourceServerCertificateDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).IAMConn log.Printf("[INFO] Deleting IAM Server Certificate: %s", d.Id()) + input := &iam.DeleteServerCertificateInput{ + ServerCertificateName: aws.String(d.Get("name").(string)), + } err := resource.Retry(15*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{ - ServerCertificateName: aws.String(d.Get("name").(string)), - }) + _, err := conn.DeleteServerCertificate(input) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil + } + var awsErr awserr.Error + if errors.As(err, &awsErr) { if awsErr.Code() == iam.ErrCodeDeleteConflictException && strings.Contains(awsErr.Message(), "currently in use by arn") { currentlyInUseBy(awsErr.Message(), meta.(*conns.AWSClient).ELBConn) log.Printf("[WARN] Conflict deleting server certificate: %s, retrying", awsErr.Message()) return resource.RetryableError(err) } - if awsErr.Code() == iam.ErrCodeNoSuchEntityException { - return nil - } } return resource.NonRetryableError(err) } @@ -271,9 +274,7 @@ func resourceServerCertificateDelete(d *schema.ResourceData, meta interface{}) e }) if tfresource.TimedOut(err) { - _, err = conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{ - ServerCertificateName: aws.String(d.Get("name").(string)), - }) + _, err = conn.DeleteServerCertificate(input) } return err diff --git a/internal/service/lambda/alias.go b/internal/service/lambda/alias.go index 2654a07ec45..8fa407608ba 100644 --- a/internal/service/lambda/alias.go +++ b/internal/service/lambda/alias.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lambda" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -116,11 +116,9 @@ func resourceAliasRead(d *schema.ResourceData, meta interface{}) error { aliasConfiguration, err := conn.GetAlias(params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "ResourceNotFoundException" && strings.Contains(awsErr.Message(), "Cannot find alias arn") { - d.SetId("") - return nil - } + if tfawserr.ErrMessageContains(err, lambda.ErrCodeResourceNotFoundException, "Cannot find alias arn") { + d.SetId("") + return nil } return err } diff --git a/internal/service/lightsail/domain.go b/internal/service/lightsail/domain.go index e14c0304dd0..9dc2f9c9ccd 100644 --- a/internal/service/lightsail/domain.go +++ b/internal/service/lightsail/domain.go @@ -4,8 +4,8 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -52,13 +52,10 @@ func resourceDomainRead(d *schema.ResourceData, meta interface{}) error { }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NotFoundException" { - log.Printf("[WARN] Lightsail Domain (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + if tfawserr.ErrCodeEquals(err, lightsail.ErrCodeNotFoundException) { + log.Printf("[WARN] Lightsail Domain (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } return err } diff --git a/internal/service/lightsail/instance.go b/internal/service/lightsail/instance.go index ab963120b6d..2418fb36f5e 100644 --- a/internal/service/lightsail/instance.go +++ b/internal/service/lightsail/instance.go @@ -7,8 +7,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -193,13 +193,10 @@ func resourceInstanceRead(d *schema.ResourceData, meta interface{}) error { }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NotFoundException" { - log.Printf("[WARN] Lightsail Instance (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - return err + if tfawserr.ErrCodeEquals(err, lightsail.ErrCodeNotFoundException) { + log.Printf("[WARN] Lightsail Instance (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } return err } diff --git a/internal/service/lightsail/key_pair.go b/internal/service/lightsail/key_pair.go index 84940baec10..ab397b7d272 100644 --- a/internal/service/lightsail/key_pair.go +++ b/internal/service/lightsail/key_pair.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -169,14 +169,10 @@ func resourceKeyPairRead(d *schema.ResourceData, meta interface{}) error { }) if err != nil { - log.Printf("[WARN] Error getting KeyPair (%s): %s", d.Id(), err) - // check for known not found error - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NotFoundException" { - log.Printf("[WARN] Lightsail KeyPair (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + if tfawserr.ErrCodeEquals(err, lightsail.ErrCodeNotFoundException) { + log.Printf("[WARN] Lightsail KeyPair (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } return err } diff --git a/internal/service/lightsail/static_ip.go b/internal/service/lightsail/static_ip.go index f13883d3c40..75beaf0c625 100644 --- a/internal/service/lightsail/static_ip.go +++ b/internal/service/lightsail/static_ip.go @@ -4,8 +4,8 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -65,16 +65,13 @@ func resourceStaticIPRead(d *schema.ResourceData, meta interface{}) error { StaticIpName: aws.String(name), }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NotFoundException" { - log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + if tfawserr.ErrCodeEquals(err, lightsail.ErrCodeNotFoundException) { + log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } return err } - log.Printf("[INFO] Received Lightsail Static IP: %s", *out) d.Set("arn", out.StaticIp.Arn) d.Set("ip_address", out.StaticIp.IpAddress) diff --git a/internal/service/lightsail/static_ip_attachment.go b/internal/service/lightsail/static_ip_attachment.go index b4bf5302c66..0f2924fe6eb 100644 --- a/internal/service/lightsail/static_ip_attachment.go +++ b/internal/service/lightsail/static_ip_attachment.go @@ -4,8 +4,8 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" ) @@ -63,12 +63,10 @@ func resourceStaticIPAttachmentRead(d *schema.ResourceData, meta interface{}) er StaticIpName: aws.String(staticIpName), }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "NotFoundException" { - log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + if tfawserr.ErrCodeEquals(err, lightsail.ErrCodeNotFoundException) { + log.Printf("[WARN] Lightsail Static IP (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } return err } diff --git a/internal/service/opsworks/permission.go b/internal/service/opsworks/permission.go index 38466643dd2..af55c092dde 100644 --- a/internal/service/opsworks/permission.go +++ b/internal/service/opsworks/permission.go @@ -4,7 +4,6 @@ import ( "log" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -69,16 +68,14 @@ func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { StackId: aws.String(d.Get("stack_id").(string)), } - log.Printf("[DEBUG] Reading OpsWorks prermissions for: %s on stack: %s", d.Get("user_arn"), d.Get("stack_id")) + log.Printf("[DEBUG] Reading OpsWorks permissions for: %s on stack: %s", d.Get("user_arn"), d.Get("stack_id")) resp, err := client.DescribePermissions(req) if err != nil { - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() == "ResourceNotFoundException" { - log.Printf("[INFO] Permission not found") - d.SetId("") - return nil - } + if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + log.Printf("[INFO] OpsWorks Permissions (%s, %s) not found, removing from state", d.Get("user_arn"), d.Get("stack_id")) + d.SetId("") + return nil } return err } diff --git a/internal/service/opsworks/permission_test.go b/internal/service/opsworks/permission_test.go index 3bae51e3c1b..85431b123c9 100644 --- a/internal/service/opsworks/permission_test.go +++ b/internal/service/opsworks/permission_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/opsworks" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -172,10 +172,8 @@ func testAccCheckPermissionDestroy(s *terraform.State) error { } } - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() != "ResourceNotFoundException" { - return err - } + if !tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + continue } } return nil diff --git a/internal/service/opsworks/user_profile_test.go b/internal/service/opsworks/user_profile_test.go index 01e0c950a81..17c02cd82ff 100644 --- a/internal/service/opsworks/user_profile_test.go +++ b/internal/service/opsworks/user_profile_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/opsworks" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -111,10 +111,8 @@ func testAccCheckUserProfileDestroy(s *terraform.State) error { } } - if awserr, ok := err.(awserr.Error); ok { - if awserr.Code() != "ResourceNotFoundException" { - return err - } + if !tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + continue } } return nil From 62914aba227cfb98dbcd33bcd39774a6b0aa04c9 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Wed, 23 Nov 2022 16:35:52 -0800 Subject: [PATCH 05/68] Cleans up error handling --- .../configuration_template_test.go | 16 ++-------------- internal/service/iot/certificate_test.go | 6 ++---- internal/service/iot/policy_test.go | 5 ++--- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/internal/service/elasticbeanstalk/configuration_template_test.go b/internal/service/elasticbeanstalk/configuration_template_test.go index de7236f8d27..9bb9ca2ca7c 100644 --- a/internal/service/elasticbeanstalk/configuration_template_test.go +++ b/internal/service/elasticbeanstalk/configuration_template_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -98,19 +98,7 @@ func testAccCheckConfigurationTemplateDestroy(s *terraform.State) error { return nil } - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - - switch { - case ec2err.Code() == "InvalidBeanstalkConfigurationTemplateID.NotFound": - return nil - // This error can be returned when the beanstalk application no longer exists. - case ec2err.Code() == "InvalidParameterValue": - return nil - default: + if !tfawserr.ErrCodeEquals(err, "InvalidBeanstalkConfigurationTemplateID.NotFound", "InvalidParameterValue") { return err } } diff --git a/internal/service/iot/certificate_test.go b/internal/service/iot/certificate_test.go index 19da3f073a2..79233ddf21d 100644 --- a/internal/service/iot/certificate_test.go +++ b/internal/service/iot/certificate_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iot" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -103,10 +103,8 @@ func testAccCheckCertificateDestroy_basic(s *terraform.State) error { } } - // Verify the error is what we want if err != nil { - iotErr, ok := err.(awserr.Error) - if !ok || iotErr.Code() != "ResourceNotFoundException" { + if !tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { return err } } diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 78469a2fd30..46185b9cc21 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iot" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -91,8 +91,7 @@ func testAccCheckPolicyDestroy_basic(s *terraform.State) error { // Verify the error is what we want if err != nil { - iotErr, ok := err.(awserr.Error) - if !ok || iotErr.Code() != "ResourceNotFoundException" { + if !tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { return err } } From 24ddeaa0626b151598f5b2d5c0e92967fbf3975d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Jan 2023 12:55:08 -0500 Subject: [PATCH 06/68] Redshift: Fix semgrep 'aws-go-sdk-error-code-helper'. --- internal/service/redshift/security_group.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/service/redshift/security_group.go b/internal/service/redshift/security_group.go index 0a67665b604..6bc3a708720 100644 --- a/internal/service/redshift/security_group.go +++ b/internal/service/redshift/security_group.go @@ -8,8 +8,8 @@ import ( "regexp" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/redshift" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -164,11 +164,11 @@ func resourceSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error _, err := conn.DeleteClusterSecurityGroup(&opts) + if tfawserr.ErrCodeEquals(err, "InvalidRedshiftSecurityGroup.NotFound") { + return nil + } + if err != nil { - newerr, ok := err.(awserr.Error) - if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" { - return nil - } return fmt.Errorf("deleting Redshift Security Group (%s): %w", d.Id(), err) } From 0bb8654b8b345b5de4dd20fa1588c0989c97839e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Jan 2023 14:01:01 -0500 Subject: [PATCH 07/68] r/aws_rds_cluster_parameter_group: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccRDSClusterParameterGroup_' PKG=rds ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/rds/... -v -count 1 -parallel 3 -run=TestAccRDSClusterParameterGroup_ -timeout 180m === RUN TestAccRDSClusterParameterGroup_basic === PAUSE TestAccRDSClusterParameterGroup_basic === RUN TestAccRDSClusterParameterGroup_disappears === PAUSE TestAccRDSClusterParameterGroup_disappears === RUN TestAccRDSClusterParameterGroup_tags === PAUSE TestAccRDSClusterParameterGroup_tags === RUN TestAccRDSClusterParameterGroup_withApplyMethod === PAUSE TestAccRDSClusterParameterGroup_withApplyMethod === RUN TestAccRDSClusterParameterGroup_namePrefix === PAUSE TestAccRDSClusterParameterGroup_namePrefix === RUN TestAccRDSClusterParameterGroup_NamePrefix_parameter === PAUSE TestAccRDSClusterParameterGroup_NamePrefix_parameter === RUN TestAccRDSClusterParameterGroup_generatedName === PAUSE TestAccRDSClusterParameterGroup_generatedName === RUN TestAccRDSClusterParameterGroup_GeneratedName_parameter === PAUSE TestAccRDSClusterParameterGroup_GeneratedName_parameter === RUN TestAccRDSClusterParameterGroup_only === PAUSE TestAccRDSClusterParameterGroup_only === RUN TestAccRDSClusterParameterGroup_updateParameters === PAUSE TestAccRDSClusterParameterGroup_updateParameters === RUN TestAccRDSClusterParameterGroup_caseParameters === PAUSE TestAccRDSClusterParameterGroup_caseParameters === CONT TestAccRDSClusterParameterGroup_basic === CONT TestAccRDSClusterParameterGroup_generatedName === CONT TestAccRDSClusterParameterGroup_withApplyMethod --- PASS: TestAccRDSClusterParameterGroup_generatedName (20.59s) === CONT TestAccRDSClusterParameterGroup_tags --- PASS: TestAccRDSClusterParameterGroup_withApplyMethod (20.84s) === CONT TestAccRDSClusterParameterGroup_updateParameters --- PASS: TestAccRDSClusterParameterGroup_updateParameters (31.07s) === CONT TestAccRDSClusterParameterGroup_caseParameters --- PASS: TestAccRDSClusterParameterGroup_basic (63.34s) === CONT TestAccRDSClusterParameterGroup_NamePrefix_parameter --- PASS: TestAccRDSClusterParameterGroup_tags (43.01s) === CONT TestAccRDSClusterParameterGroup_namePrefix --- PASS: TestAccRDSClusterParameterGroup_caseParameters (28.44s) === CONT TestAccRDSClusterParameterGroup_only --- PASS: TestAccRDSClusterParameterGroup_NamePrefix_parameter (18.56s) === CONT TestAccRDSClusterParameterGroup_GeneratedName_parameter --- PASS: TestAccRDSClusterParameterGroup_namePrefix (18.32s) === CONT TestAccRDSClusterParameterGroup_disappears --- PASS: TestAccRDSClusterParameterGroup_disappears (13.11s) --- PASS: TestAccRDSClusterParameterGroup_only (17.18s) --- PASS: TestAccRDSClusterParameterGroup_GeneratedName_parameter (17.47s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 104.624s --- .../service/rds/cluster_parameter_group.go | 116 ++++--- .../rds/cluster_parameter_group_test.go | 294 +++++++++--------- 2 files changed, 228 insertions(+), 182 deletions(-) diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index 9bbd1f698ae..d851c1269a5 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -9,7 +9,6 @@ import ( rds_sdkv2 "github.com/aws/aws-sdk-go-v2/service/rds" "github.com/aws/aws-sdk-go-v2/service/rds/types" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -31,6 +30,7 @@ func ResourceClusterParameterGroup() *schema.Resource { Read: resourceClusterParameterGroupRead, Update: resourceClusterParameterGroupUpdate, DeleteWithoutTimeout: resourceClusterParameterGroupDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -131,69 +131,69 @@ func resourceClusterParameterGroupRead(d *schema.ResourceData, meta interface{}) defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - describeOpts := rds.DescribeDBClusterParameterGroupsInput{ - DBClusterParameterGroupName: aws.String(d.Id()), - } - - describeResp, err := conn.DescribeDBClusterParameterGroups(&describeOpts) - if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "DBParameterGroupNotFound" { - log.Printf("[WARN] DB Cluster Parameter Group (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + dbClusterParameterGroup, err := FindDBClusterParameterGroupByName(conn, d.Id()) - return fmt.Errorf("reading RDS Cluster Parameter Group (%s): %s", d.Id(), err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] RDS DB Cluster Parameter Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - if len(describeResp.DBClusterParameterGroups) != 1 || - aws.StringValue(describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName) != d.Id() { - return fmt.Errorf("Unable to find Cluster Parameter Group: %#v", describeResp.DBClusterParameterGroups) + if err != nil { + return fmt.Errorf("reading RDS DB Cluster Parameter Group (%s): %w", d.Id(), err) } - arn := aws.StringValue(describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupArn) + arn := aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupArn) d.Set("arn", arn) - d.Set("description", describeResp.DBClusterParameterGroups[0].Description) - d.Set("family", describeResp.DBClusterParameterGroups[0].DBParameterGroupFamily) - d.Set("name", describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName) - d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(describeResp.DBClusterParameterGroups[0].DBClusterParameterGroupName))) + d.Set("description", dbClusterParameterGroup.Description) + d.Set("family", dbClusterParameterGroup.DBParameterGroupFamily) + d.Set("name", dbClusterParameterGroup.DBClusterParameterGroupName) + d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupName))) // Only include user customized parameters as there's hundreds of system/default ones - describeParametersOpts := rds.DescribeDBClusterParametersInput{ + input := &rds.DescribeDBClusterParametersInput{ DBClusterParameterGroupName: aws.String(d.Id()), Source: aws.String("user"), } - var parameters []*rds.Parameter - err = conn.DescribeDBClusterParametersPages(&describeParametersOpts, - func(describeParametersResp *rds.DescribeDBClusterParametersOutput, lastPage bool) bool { - parameters = append(parameters, describeParametersResp.Parameters...) + + err = conn.DescribeDBClusterParametersPages(input, func(page *rds.DescribeDBClusterParametersOutput, lastPage bool) bool { + if page == nil { return !lastPage - }) + } + + for _, v := range page.Parameters { + if v != nil { + parameters = append(parameters, v) + } + } + + return !lastPage + }) + if err != nil { - return fmt.Errorf("reading RDS Cluster Parameter Group (%s) parameters: %s", d.Id(), err) + return fmt.Errorf("reading RDS Cluster Parameter Group (%s) parameters: %w", d.Id(), err) } if err := d.Set("parameter", flattenParameters(parameters)); err != nil { - return fmt.Errorf("setting parameter: %s", err) + return fmt.Errorf("setting parameter: %w", err) } - resp, err := conn.ListTagsForResource(&rds.ListTagsForResourceInput{ - ResourceName: aws.String(arn), - }) - if err != nil { - log.Printf("[WARN] Error retrieving tags for DB Cluster Parameter Group (%s). Ignoring: %s", d.Id(), err) - } + tags, err := ListTags(conn, arn) - tags := KeyValueTags(resp.TagList).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + if err != nil { + log.Printf("[WARN] listing tags for RDS DB Cluster Parameter Group (%s): %s", d.Id(), err) + } else { + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) - //lintignore:AWSR002 - if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("setting tags: %w", err) - } + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return fmt.Errorf("setting tags: %w", err) + } - if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("setting tags_all: %w", err) + if err := d.Set("tags_all", tags.Map()); err != nil { + return fmt.Errorf("setting tags_all: %w", err) + } } return nil @@ -315,7 +315,7 @@ func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.Resource DBClusterParameterGroupName: aws.String(d.Id()), } - log.Printf("[DEBUG] Deleting RDS Cluster Parameter Group: %s", d.Id()) + log.Printf("[DEBUG] Deleting RDS DB Cluster Parameter Group: %s", d.Id()) err := resource.Retry(3*time.Minute, func() *resource.RetryError { _, err := conn.DeleteDBClusterParameterGroup(ctx, &input) if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { @@ -336,3 +336,33 @@ func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.Resource } return nil } + +func FindDBClusterParameterGroupByName(conn *rds.RDS, name string) (*rds.DBClusterParameterGroup, error) { + input := &rds.DescribeDBClusterParameterGroupsInput{ + DBClusterParameterGroupName: aws.String(name), + } + + output, err := conn.DescribeDBClusterParameterGroups(input) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil || len(output.DBClusterParameterGroups) == 0 || output.DBClusterParameterGroups[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + dbClusterParameterGroup := output.DBClusterParameterGroups[0] + + // Eventual consistency check. + if aws.StringValue(dbClusterParameterGroup.DBClusterParameterGroupName) != name { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return dbClusterParameterGroup, nil +} diff --git a/internal/service/rds/cluster_parameter_group_test.go b/internal/service/rds/cluster_parameter_group_test.go index c6b36856c7a..bb1e44d98f5 100644 --- a/internal/service/rds/cluster_parameter_group_test.go +++ b/internal/service/rds/cluster_parameter_group_test.go @@ -4,22 +4,22 @@ import ( "errors" "fmt" "testing" - "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/rds" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccRDSClusterParameterGroup_basic(t *testing.T) { var v rds.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" - parameterGroupName := fmt.Sprintf("cluster-parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -28,12 +28,12 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { CheckDestroy: testAccCheckClusterParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccClusterParameterGroupConfig_basic(parameterGroupName), + Config: testAccClusterParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, parameterGroupName), - acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "rds", fmt.Sprintf("cluster-pg:%s", parameterGroupName)), - resource.TestCheckResourceAttr(resourceName, "name", parameterGroupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "rds", fmt.Sprintf("cluster-pg:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "aurora5.6"), resource.TestCheckResourceAttr(resourceName, "description", "Test cluster parameter group for terraform"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -48,7 +48,7 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { "name": "character_set_client", "value": "utf8", }), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -57,11 +57,11 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccClusterParameterGroupConfig_addParameters(parameterGroupName), + Config: testAccClusterParameterGroupConfig_addParameters(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, parameterGroupName), - resource.TestCheckResourceAttr(resourceName, "name", parameterGroupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "aurora5.6"), resource.TestCheckResourceAttr(resourceName, "description", "Test cluster parameter group for terraform"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -84,14 +84,14 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { "name": "character_set_client", "value": "utf8", }), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - Config: testAccClusterParameterGroupConfig_basic(parameterGroupName), + Config: testAccClusterParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, parameterGroupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), testAccCheckClusterParameterNotUserDefined(resourceName, "collation_connection"), testAccCheckClusterParameterNotUserDefined(resourceName, "collation_server"), resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"), @@ -107,6 +107,75 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { "name": "character_set_client", "value": "utf8", }), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + +func TestAccRDSClusterParameterGroup_disappears(t *testing.T) { + var v rds.DBClusterParameterGroup + resourceName := "aws_rds_cluster_parameter_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterParameterGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterParameterGroupConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterParameterGroupExists(resourceName, &v), + acctest.CheckResourceDisappears(acctest.Provider, tfrds.ResourceClusterParameterGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccRDSClusterParameterGroup_tags(t *testing.T) { + var v rds.DBClusterParameterGroup + resourceName := "aws_rds_cluster_parameter_group.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterParameterGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccClusterParameterGroupConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterParameterGroupExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccClusterParameterGroupConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterParameterGroupExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccClusterParameterGroupConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterParameterGroupExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, }, @@ -115,7 +184,7 @@ func TestAccRDSClusterParameterGroup_basic(t *testing.T) { func TestAccRDSClusterParameterGroup_withApplyMethod(t *testing.T) { var v rds.DBClusterParameterGroup - parameterGroupName := fmt.Sprintf("cluster-parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rds_cluster_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -125,12 +194,12 @@ func TestAccRDSClusterParameterGroup_withApplyMethod(t *testing.T) { CheckDestroy: testAccCheckClusterParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccClusterParameterGroupConfig_applyMethod(parameterGroupName), + Config: testAccClusterParameterGroupConfig_applyMethod(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, parameterGroupName), - acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "rds", fmt.Sprintf("cluster-pg:%s", parameterGroupName)), - resource.TestCheckResourceAttr(resourceName, "name", parameterGroupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "rds", fmt.Sprintf("cluster-pg:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "aurora5.6"), resource.TestCheckResourceAttr(resourceName, "description", "Test cluster parameter group for terraform"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -262,33 +331,10 @@ func TestAccRDSClusterParameterGroup_GeneratedName_parameter(t *testing.T) { }) } -func TestAccRDSClusterParameterGroup_disappears(t *testing.T) { - var v rds.DBClusterParameterGroup - resourceName := "aws_rds_cluster_parameter_group.test" - parameterGroupName := fmt.Sprintf("cluster-parameter-group-test-terraform-%d", sdkacctest.RandInt()) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, rds.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckClusterParameterGroupDestroy, - Steps: []resource.TestStep{ - { - Config: testAccClusterParameterGroupConfig_basic(parameterGroupName), - Check: resource.ComposeTestCheckFunc( - testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccClusterParameterGroupDisappears(&v), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - func TestAccRDSClusterParameterGroup_only(t *testing.T) { var v rds.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" - parameterGroupName := fmt.Sprintf("cluster-parameter-group-test-tf-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -297,16 +343,13 @@ func TestAccRDSClusterParameterGroup_only(t *testing.T) { CheckDestroy: testAccCheckClusterParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccClusterParameterGroupConfig_only(parameterGroupName), + Config: testAccClusterParameterGroupConfig_only(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, parameterGroupName), - resource.TestCheckResourceAttr( - resourceName, "name", parameterGroupName), - resource.TestCheckResourceAttr( - resourceName, "family", "aurora5.6"), - resource.TestCheckResourceAttr( - resourceName, "description", "Managed by Terraform"), + testAccCheckClusterParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "family", "aurora5.6"), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), ), }, { @@ -321,7 +364,7 @@ func TestAccRDSClusterParameterGroup_only(t *testing.T) { func TestAccRDSClusterParameterGroup_updateParameters(t *testing.T) { var v rds.DBClusterParameterGroup resourceName := "aws_rds_cluster_parameter_group.test" - groupName := fmt.Sprintf("cluster-parameter-group-test-tf-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -330,11 +373,11 @@ func TestAccRDSClusterParameterGroup_updateParameters(t *testing.T) { CheckDestroy: testAccCheckClusterParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccClusterParameterGroupConfig_updateParametersInitial(groupName), + Config: testAccClusterParameterGroupConfig_updateParametersInitial(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "aurora5.6"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "character_set_results", @@ -356,10 +399,10 @@ func TestAccRDSClusterParameterGroup_updateParameters(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccClusterParameterGroupConfig_updateParametersUpdated(groupName), + Config: testAccClusterParameterGroupConfig_updateParametersUpdated(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterParameterGroupExists(resourceName, &v), - testAccCheckClusterParameterGroupAttributes(&v, groupName), + testAccCheckClusterParameterGroupAttributes(&v, rName), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "character_set_results", "value": "ascii", @@ -422,22 +465,17 @@ func testAccCheckClusterParameterGroupDestroy(s *terraform.State) error { continue } - // Try to find the Group - resp, err := conn.DescribeDBClusterParameterGroups( - &rds.DescribeDBClusterParameterGroupsInput{ - DBClusterParameterGroupName: aws.String(rs.Primary.ID), - }) + _, err := tfrds.FindDBClusterParameterGroupByName(conn, rs.Primary.ID) - if err == nil { - if len(resp.DBClusterParameterGroups) != 0 && - *resp.DBClusterParameterGroups[0].DBClusterParameterGroupName == rs.Primary.ID { - return errors.New("DB Cluster Parameter Group still exists") - } + if tfresource.NotFound(err) { + continue } - if !tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { + if err != nil { return err } + + return fmt.Errorf("RDS DB Cluster Parameter Group %s still exists", rs.Primary.ID) } return nil @@ -490,33 +528,6 @@ func testAccCheckClusterParameterGroupAttributes(v *rds.DBClusterParameterGroup, } } -func testAccClusterParameterGroupDisappears(v *rds.DBClusterParameterGroup) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() - opts := &rds.DeleteDBClusterParameterGroupInput{ - DBClusterParameterGroupName: v.DBClusterParameterGroupName, - } - if _, err := conn.DeleteDBClusterParameterGroup(opts); err != nil { - return err - } - return resource.Retry(40*time.Minute, func() *resource.RetryError { - opts := &rds.DescribeDBClusterParameterGroupsInput{ - DBClusterParameterGroupName: v.DBClusterParameterGroupName, - } - _, err := conn.DescribeDBClusterParameterGroups(opts) - if err != nil { - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { - return nil - } - return resource.NonRetryableError( - fmt.Errorf("Error retrieving DB Cluster Parameter Groups: %s", err)) - } - return resource.RetryableError(fmt.Errorf( - "Waiting for cluster parameter group to be deleted: %v", v.DBClusterParameterGroupName)) - }) - } -} - func testAccCheckClusterParameterGroupExists(n string, v *rds.DBClusterParameterGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -525,36 +536,27 @@ func testAccCheckClusterParameterGroupExists(n string, v *rds.DBClusterParameter } if rs.Primary.ID == "" { - return errors.New("No DB Cluster Parameter Group ID is set") + return errors.New("No RDS DB Cluster Parameter Group ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() - opts := rds.DescribeDBClusterParameterGroupsInput{ - DBClusterParameterGroupName: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeDBClusterParameterGroups(&opts) + output, err := tfrds.FindDBClusterParameterGroupByName(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.DBClusterParameterGroups) != 1 || - *resp.DBClusterParameterGroups[0].DBClusterParameterGroupName != rs.Primary.ID { - return errors.New("DB Cluster Parameter Group not found") - } - - *v = *resp.DBClusterParameterGroups[0] + *v = *output return nil } } -func testAccClusterParameterGroupConfig_basic(name string) string { +func testAccClusterParameterGroupConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" description = "Test cluster parameter group for terraform" @@ -572,18 +574,14 @@ resource "aws_rds_cluster_parameter_group" "test" { name = "character_set_results" value = "utf8" } - - tags = { - foo = "bar" - } } -`, name) +`, rName) } -func testAccClusterParameterGroupConfig_applyMethod(name string) string { +func testAccClusterParameterGroupConfig_applyMethod(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" description = "Test cluster parameter group for terraform" @@ -597,18 +595,14 @@ resource "aws_rds_cluster_parameter_group" "test" { value = "utf8" apply_method = "pending-reboot" } - - tags = { - foo = "bar" - } } -`, name) +`, rName) } -func testAccClusterParameterGroupConfig_addParameters(name string) string { +func testAccClusterParameterGroupConfig_addParameters(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" description = "Test cluster parameter group for terraform" @@ -636,28 +630,23 @@ resource "aws_rds_cluster_parameter_group" "test" { name = "collation_connection" value = "utf8_unicode_ci" } - - tags = { - foo = "bar" - baz = "foo" - } } -`, name) +`, rName) } -func testAccClusterParameterGroupConfig_only(name string) string { +func testAccClusterParameterGroupConfig_only(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" } -`, name) +`, rName) } -func testAccClusterParameterGroupConfig_updateParametersInitial(name string) string { +func testAccClusterParameterGroupConfig_updateParametersInitial(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" parameter { @@ -675,13 +664,13 @@ resource "aws_rds_cluster_parameter_group" "test" { value = "utf8" } } -`, name) +`, rName) } -func testAccClusterParameterGroupConfig_updateParametersUpdated(name string) string { +func testAccClusterParameterGroupConfig_updateParametersUpdated(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" parameter { @@ -699,13 +688,13 @@ resource "aws_rds_cluster_parameter_group" "test" { value = "ascii" } } -`, name) +`, rName) } func testAccClusterParameterGroupConfig_upperCase(rName string) string { return fmt.Sprintf(` resource "aws_rds_cluster_parameter_group" "test" { - name = "%s" + name = %[1]q family = "aurora5.6" parameter { @@ -756,3 +745,30 @@ resource "aws_rds_cluster_parameter_group" "test" { } } ` + +func testAccClusterParameterGroupConfig_tags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_rds_cluster_parameter_group" "test" { + name = %[1]q + family = "aurora5.6" + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccClusterParameterGroupConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_rds_cluster_parameter_group" "test" { + name = %[1]q + family = "aurora5.6" + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} From feb76ea43820768a544b33982969e0804e2b97f8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Jan 2023 15:01:58 -0500 Subject: [PATCH 08/68] r/aws_redshift_parameter_group: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccRedshiftParameterGroup_' PKG=redshift ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/redshift/... -v -count 1 -parallel 3 -run=TestAccRedshiftParameterGroup_ -timeout 180m === RUN TestAccRedshiftParameterGroup_basic === PAUSE TestAccRedshiftParameterGroup_basic === RUN TestAccRedshiftParameterGroup_disappears === PAUSE TestAccRedshiftParameterGroup_disappears === RUN TestAccRedshiftParameterGroup_update === PAUSE TestAccRedshiftParameterGroup_update === RUN TestAccRedshiftParameterGroup_tags === PAUSE TestAccRedshiftParameterGroup_tags === CONT TestAccRedshiftParameterGroup_basic === CONT TestAccRedshiftParameterGroup_update === CONT TestAccRedshiftParameterGroup_tags --- PASS: TestAccRedshiftParameterGroup_basic (19.77s) === CONT TestAccRedshiftParameterGroup_disappears --- PASS: TestAccRedshiftParameterGroup_update (31.38s) --- PASS: TestAccRedshiftParameterGroup_disappears (12.45s) --- PASS: TestAccRedshiftParameterGroup_tags (41.92s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/redshift 49.596s --- internal/service/redshift/flex.go | 35 --- internal/service/redshift/flex_test.go | 64 ------ internal/service/redshift/parameter_group.go | 187 +++++++++++----- .../service/redshift/parameter_group_test.go | 208 +++++++++--------- 4 files changed, 233 insertions(+), 261 deletions(-) delete mode 100644 internal/service/redshift/flex_test.go diff --git a/internal/service/redshift/flex.go b/internal/service/redshift/flex.go index 4fb913452ac..0ee0347f974 100644 --- a/internal/service/redshift/flex.go +++ b/internal/service/redshift/flex.go @@ -6,29 +6,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/flex" ) -func ExpandParameters(configured []interface{}) []*redshift.Parameter { - var parameters []*redshift.Parameter - - // Loop over our configured parameters and create - // an array of aws-sdk-go compatible objects - for _, pRaw := range configured { - data := pRaw.(map[string]interface{}) - - if data["name"].(string) == "" { - continue - } - - p := &redshift.Parameter{ - ParameterName: aws.String(data["name"].(string)), - ParameterValue: aws.String(data["value"].(string)), - } - - parameters = append(parameters, p) - } - - return parameters -} - func flattenLogging(ls *redshift.LoggingStatus) []interface{} { if ls == nil { return []interface{}{} @@ -51,18 +28,6 @@ func flattenLogging(ls *redshift.LoggingStatus) []interface{} { return []interface{}{cfg} } -// Flattens an array of Redshift Parameters into a []map[string]interface{} -func FlattenParameters(list []*redshift.Parameter) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(list)) - for _, i := range list { - result = append(result, map[string]interface{}{ - "name": aws.StringValue(i.ParameterName), - "value": aws.StringValue(i.ParameterValue), - }) - } - return result -} - func flattenSnapshotCopy(scs *redshift.ClusterSnapshotCopyStatus) []interface{} { if scs == nil { return []interface{}{} diff --git a/internal/service/redshift/flex_test.go b/internal/service/redshift/flex_test.go deleted file mode 100644 index fc35a664a69..00000000000 --- a/internal/service/redshift/flex_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package redshift - -import ( - "reflect" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/redshift" -) - -func TestExpandParameters(t *testing.T) { - t.Parallel() - - expanded := []interface{}{ - map[string]interface{}{ - "name": "character_set_client", - "value": "utf8", - }, - } - parameters := ExpandParameters(expanded) - - expected := &redshift.Parameter{ - ParameterName: aws.String("character_set_client"), - ParameterValue: aws.String("utf8"), - } - - if !reflect.DeepEqual(parameters[0], expected) { - t.Fatalf( - "Got:\n\n%#v\n\nExpected:\n\n%#v\n", - parameters[0], - expected) - } -} - -func TestFlattenParameters(t *testing.T) { - t.Parallel() - - cases := []struct { - Input []*redshift.Parameter - Output []map[string]interface{} - }{ - { - Input: []*redshift.Parameter{ - { - ParameterName: aws.String("character_set_client"), - ParameterValue: aws.String("utf8"), - }, - }, - Output: []map[string]interface{}{ - { - "name": "character_set_client", - "value": "utf8", - }, - }, - }, - } - - for _, tc := range cases { - output := FlattenParameters(tc.Input) - if !reflect.DeepEqual(output, tc.Output) { - t.Fatalf("Got:\n\n%#v\n\nExpected:\n\n%#v", output, tc.Output) - } - } -} diff --git a/internal/service/redshift/parameter_group.go b/internal/service/redshift/parameter_group.go index 5110fadd08a..7350f1af6a5 100644 --- a/internal/service/redshift/parameter_group.go +++ b/internal/service/redshift/parameter_group.go @@ -11,11 +11,13 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/redshift" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -25,6 +27,7 @@ func ResourceParameterGroup() *schema.Resource { Read: resourceParameterGroupRead, Update: resourceParameterGroupUpdate, Delete: resourceParameterGroupDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -34,7 +37,17 @@ func ResourceParameterGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, - + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "Managed by Terraform", + }, + "family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "name": { Type: schema.TypeString, ForceNew: true, @@ -47,20 +60,6 @@ func ResourceParameterGroup() *schema.Resource { validation.StringDoesNotMatch(regexp.MustCompile(`-$`), "cannot end with a hyphen"), ), }, - - "family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "description": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: "Managed by Terraform", - }, - "parameter": { Type: schema.TypeSet, Optional: true, @@ -78,7 +77,6 @@ func ResourceParameterGroup() *schema.Resource { }, Set: resourceParameterHash, }, - "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -92,31 +90,32 @@ func resourceParameterGroupCreate(d *schema.ResourceData, meta interface{}) erro defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - createOpts := redshift.CreateClusterParameterGroupInput{ - ParameterGroupName: aws.String(d.Get("name").(string)), - ParameterGroupFamily: aws.String(d.Get("family").(string)), + name := d.Get("name").(string) + input := &redshift.CreateClusterParameterGroupInput{ Description: aws.String(d.Get("description").(string)), + ParameterGroupFamily: aws.String(d.Get("family").(string)), + ParameterGroupName: aws.String(name), Tags: Tags(tags.IgnoreAWS()), } - log.Printf("[DEBUG] Create Redshift Parameter Group: %#v", createOpts) - _, err := conn.CreateClusterParameterGroup(&createOpts) + _, err := conn.CreateClusterParameterGroup(input) + if err != nil { - return fmt.Errorf("Error creating Redshift Parameter Group: %s", err) + return fmt.Errorf("creating Redshift Parameter Group (%s): %w", name, err) } - d.SetId(aws.StringValue(createOpts.ParameterGroupName)) + d.SetId(name) if v := d.Get("parameter").(*schema.Set); v.Len() > 0 { - parameters := ExpandParameters(v.List()) - - modifyOpts := redshift.ModifyClusterParameterGroupInput{ + input := &redshift.ModifyClusterParameterGroupInput{ ParameterGroupName: aws.String(d.Id()), - Parameters: parameters, + Parameters: expandParameters(v.List()), } - if _, err := conn.ModifyClusterParameterGroup(&modifyOpts); err != nil { - return fmt.Errorf("adding Redshift Parameter Group (%s) parameters: %s", d.Id(), err) + _, err := conn.ModifyClusterParameterGroup(input) + + if err != nil { + return fmt.Errorf("setting Redshift Parameter Group (%s) parameters: %w", d.Id(), err) } } @@ -128,21 +127,18 @@ func resourceParameterGroupRead(d *schema.ResourceData, meta interface{}) error defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - describeOpts := redshift.DescribeClusterParameterGroupsInput{ - ParameterGroupName: aws.String(d.Id()), + parameterGroup, err := FindParameterGroupByName(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Redshift Parameter Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - describeResp, err := conn.DescribeClusterParameterGroups(&describeOpts) if err != nil { return fmt.Errorf("reading Redshift Parameter Group (%s): %w", d.Id(), err) } - if len(describeResp.ParameterGroups) != 1 || - aws.StringValue(describeResp.ParameterGroups[0].ParameterGroupName) != d.Id() { - d.SetId("") - return fmt.Errorf("Unable to find Parameter Group: %#v", describeResp.ParameterGroups) - } - arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Service: "redshift", @@ -150,13 +146,12 @@ func resourceParameterGroupRead(d *schema.ResourceData, meta interface{}) error AccountID: meta.(*conns.AWSClient).AccountID, Resource: fmt.Sprintf("parametergroup:%s", d.Id()), }.String() - d.Set("arn", arn) + d.Set("description", parameterGroup.Description) + d.Set("family", parameterGroup.ParameterGroupFamily) + d.Set("name", parameterGroup.ParameterGroupName) - d.Set("name", describeResp.ParameterGroups[0].ParameterGroupName) - d.Set("family", describeResp.ParameterGroups[0].ParameterGroupFamily) - d.Set("description", describeResp.ParameterGroups[0].Description) - tags := KeyValueTags(describeResp.ParameterGroups[0].Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + tags := KeyValueTags(parameterGroup.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { @@ -167,17 +162,19 @@ func resourceParameterGroupRead(d *schema.ResourceData, meta interface{}) error return fmt.Errorf("setting tags_all: %w", err) } - describeParametersOpts := redshift.DescribeClusterParametersInput{ + input := &redshift.DescribeClusterParametersInput{ ParameterGroupName: aws.String(d.Id()), Source: aws.String("user"), } - describeParametersResp, err := conn.DescribeClusterParameters(&describeParametersOpts) + output, err := conn.DescribeClusterParameters(input) + if err != nil { - return fmt.Errorf("reading Redshift Parameter Group (%s): %w", d.Id(), err) + return fmt.Errorf("reading Redshift Parameter Group (%s) parameters: %w", d.Id(), err) } - d.Set("parameter", FlattenParameters(describeParametersResp.Parameters)) + d.Set("parameter", flattenParameters(output.Parameters)) + return nil } @@ -192,23 +189,20 @@ func resourceParameterGroupUpdate(d *schema.ResourceData, meta interface{}) erro if n == nil { n = new(schema.Set) } - os := o.(*schema.Set) ns := n.(*schema.Set) - // Expand the "parameter" set to aws-sdk-go compat []redshift.Parameter - parameters := ExpandParameters(ns.Difference(os).List()) - + parameters := expandParameters(ns.Difference(os).List()) if len(parameters) > 0 { - modifyOpts := redshift.ModifyClusterParameterGroupInput{ - ParameterGroupName: aws.String(d.Get("name").(string)), + input := &redshift.ModifyClusterParameterGroupInput{ + ParameterGroupName: aws.String(d.Id()), Parameters: parameters, } - log.Printf("[DEBUG] Modify Redshift Parameter Group: %s", modifyOpts) - _, err := conn.ModifyClusterParameterGroup(&modifyOpts) + _, err := conn.ModifyClusterParameterGroup(input) + if err != nil { - return fmt.Errorf("Error modifying Redshift Parameter Group: %s", err) + return fmt.Errorf("setting Redshift Parameter Group (%s) parameters: %w", d.Id(), err) } } } @@ -217,7 +211,7 @@ func resourceParameterGroupUpdate(d *schema.ResourceData, meta interface{}) erro o, n := d.GetChange("tags_all") if err := UpdateTags(conn, d.Get("arn").(string), o, n); err != nil { - return fmt.Errorf("updating Redshift Parameter Group (%s) tags: %s", d.Get("arn").(string), err) + return fmt.Errorf("updating Redshift Parameter Group (%s) tags: %s", d.Id(), err) } } @@ -227,13 +221,54 @@ func resourceParameterGroupUpdate(d *schema.ResourceData, meta interface{}) erro func resourceParameterGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).RedshiftConn() + log.Printf("[DEBUG] Deleting Redshift Parameter Group: %s", d.Id()) _, err := conn.DeleteClusterParameterGroup(&redshift.DeleteClusterParameterGroupInput{ ParameterGroupName: aws.String(d.Id()), }) - if err != nil && tfawserr.ErrCodeEquals(err, "RedshiftParameterGroupNotFoundFault") { + + if tfawserr.ErrCodeEquals(err, redshift.ErrCodeClusterParameterGroupNotFoundFault) { return nil } - return fmt.Errorf("deleting Redshift Parameter Group (%s): %w", d.Id(), err) + + if err != nil { + return fmt.Errorf("deleting Redshift Parameter Group (%s): %w", d.Id(), err) + } + + return nil +} + +func FindParameterGroupByName(conn *redshift.Redshift, name string) (*redshift.ClusterParameterGroup, error) { + input := &redshift.DescribeClusterParameterGroupsInput{ + ParameterGroupName: aws.String(name), + } + + output, err := conn.DescribeClusterParameterGroups(input) + + if tfawserr.ErrCodeEquals(err, redshift.ErrCodeClusterParameterGroupNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.ParameterGroups) == 0 || output.ParameterGroups[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + parameterGroup := output.ParameterGroups[0] + + // Eventual consistency check. + if aws.StringValue(parameterGroup.ParameterGroupName) != name { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return parameterGroup, nil } func resourceParameterHash(v interface{}) int { @@ -245,3 +280,37 @@ func resourceParameterHash(v interface{}) int { return create.StringHashcode(buf.String()) } + +func expandParameters(configured []interface{}) []*redshift.Parameter { + var parameters []*redshift.Parameter + + // Loop over our configured parameters and create + // an array of aws-sdk-go compatible objects + for _, pRaw := range configured { + data := pRaw.(map[string]interface{}) + + if data["name"].(string) == "" { + continue + } + + p := &redshift.Parameter{ + ParameterName: aws.String(data["name"].(string)), + ParameterValue: aws.String(data["value"].(string)), + } + + parameters = append(parameters, p) + } + + return parameters +} + +func flattenParameters(list []*redshift.Parameter) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(list)) + for _, i := range list { + result = append(result, map[string]interface{}{ + "name": aws.StringValue(i.ParameterName), + "value": aws.StringValue(i.ParameterValue), + }) + } + return result +} diff --git a/internal/service/redshift/parameter_group_test.go b/internal/service/redshift/parameter_group_test.go index 6b872847d9c..0a4d0ae1093 100644 --- a/internal/service/redshift/parameter_group_test.go +++ b/internal/service/redshift/parameter_group_test.go @@ -4,20 +4,20 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/redshift" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfredshift "github.com/hashicorp/terraform-provider-aws/internal/service/redshift" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccRedshiftParameterGroup_basic(t *testing.T) { var v redshift.ClusterParameterGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_redshift_parameter_group.test" - rInt := sdkacctest.RandInt() resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -26,9 +26,27 @@ func TestAccRedshiftParameterGroup_basic(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(rInt), - Check: resource.ComposeTestCheckFunc( + Config: testAccParameterGroupConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "redshift", fmt.Sprintf("parametergroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "family", "redshift-1.0"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "require_ssl", + "value": "true", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "query_group", + "value": "example", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "enable_user_activity_logging", + "value": "true", + }), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -40,9 +58,9 @@ func TestAccRedshiftParameterGroup_basic(t *testing.T) { }) } -func TestAccRedshiftParameterGroup_withParameters(t *testing.T) { +func TestAccRedshiftParameterGroup_disappears(t *testing.T) { var v redshift.ClusterParameterGroup - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_redshift_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -52,41 +70,20 @@ func TestAccRedshiftParameterGroup_withParameters(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(rInt), + Config: testAccParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), - resource.TestCheckResourceAttr( - resourceName, "name", fmt.Sprintf("test-terraform-%d", rInt)), - resource.TestCheckResourceAttr( - resourceName, "family", "redshift-1.0"), - resource.TestCheckResourceAttr( - resourceName, "description", "Managed by Terraform"), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ - "name": "require_ssl", - "value": "true", - }), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ - "name": "query_group", - "value": "example", - }), - resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ - "name": "enable_user_activity_logging", - "value": "true", - }), + acctest.CheckResourceDisappears(acctest.Provider, tfredshift.ResourceParameterGroup(), resourceName), ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ExpectNonEmptyPlan: true, }, }, }) } -func TestAccRedshiftParameterGroup_withoutParameters(t *testing.T) { +func TestAccRedshiftParameterGroup_update(t *testing.T) { var v redshift.ClusterParameterGroup - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_redshift_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -96,15 +93,15 @@ func TestAccRedshiftParameterGroup_withoutParameters(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_only(rInt), - Check: resource.ComposeTestCheckFunc( + Config: testAccParameterGroupConfig_noParameters(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), - resource.TestCheckResourceAttr( - resourceName, "name", fmt.Sprintf("test-terraform-%d", rInt)), - resource.TestCheckResourceAttr( - resourceName, "family", "redshift-1.0"), - resource.TestCheckResourceAttr( - resourceName, "description", "Test parameter group for terraform"), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "redshift", fmt.Sprintf("parametergroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "family", "redshift-1.0"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "parameter.#", "0"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -112,13 +109,37 @@ func TestAccRedshiftParameterGroup_withoutParameters(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccParameterGroupConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckParameterGroupExists(resourceName, &v), + acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "redshift", fmt.Sprintf("parametergroup:%s", rName)), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "family", "redshift-1.0"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "require_ssl", + "value": "true", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "query_group", + "value": "example", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ + "name": "enable_user_activity_logging", + "value": "true", + }), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, }, }) } -func TestAccRedshiftParameterGroup_withTags(t *testing.T) { +func TestAccRedshiftParameterGroup_tags(t *testing.T) { var v redshift.ClusterParameterGroup - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_redshift_parameter_group.test" resource.ParallelTest(t, resource.TestCase{ @@ -128,14 +149,12 @@ func TestAccRedshiftParameterGroup_withTags(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_tags(rInt, "aaa"), + Config: testAccParameterGroupConfig_tags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "3"), - resource.TestCheckResourceAttr(resourceName, "tags.name", fmt.Sprintf("test-terraform-%d", rInt)), - resource.TestCheckResourceAttr(resourceName, "tags.environment", "Production"), - resource.TestCheckResourceAttr(resourceName, "tags.description", fmt.Sprintf("Test parameter group for terraform %s", "aaa")), + resource.TestCheckResourceAttr(resourceName, "description", "Test parameter group for terraform"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { @@ -144,21 +163,20 @@ func TestAccRedshiftParameterGroup_withTags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccParameterGroupConfig_tags(rInt, "bbb"), + Config: testAccParameterGroupConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "3"), - resource.TestCheckResourceAttr(resourceName, "tags.description", fmt.Sprintf("Test parameter group for terraform %s", "bbb")), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { - Config: testAccParameterGroupConfig_tagsUpdate(rInt), + Config: testAccParameterGroupConfig_tags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(resourceName, &v), - resource.TestCheckResourceAttr( - resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.name", fmt.Sprintf("test-terraform-%d", rInt)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, }, @@ -173,22 +191,17 @@ func testAccCheckParameterGroupDestroy(s *terraform.State) error { continue } - // Try to find the Group - resp, err := conn.DescribeClusterParameterGroups( - &redshift.DescribeClusterParameterGroupsInput{ - ParameterGroupName: aws.String(rs.Primary.ID), - }) - - if err == nil { - if len(resp.ParameterGroups) != 0 && - *resp.ParameterGroups[0].ParameterGroupName == rs.Primary.ID { - return fmt.Errorf("Redshift Parameter Group still exists") - } + _, err := tfredshift.FindParameterGroupByName(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - if !tfawserr.ErrCodeEquals(err, redshift.ErrCodeClusterParameterGroupNotFoundFault) { + if err != nil { return err } + + return fmt.Errorf("Redshift Parameter Group %s still exists", rs.Primary.ID) } return nil @@ -207,41 +220,22 @@ func testAccCheckParameterGroupExists(n string, v *redshift.ClusterParameterGrou conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftConn() - opts := redshift.DescribeClusterParameterGroupsInput{ - ParameterGroupName: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeClusterParameterGroups(&opts) + output, err := tfredshift.FindParameterGroupByName(conn, rs.Primary.ID) if err != nil { return err } - if len(resp.ParameterGroups) != 1 || - *resp.ParameterGroups[0].ParameterGroupName != rs.Primary.ID { - return fmt.Errorf("Redshift Parameter Group not found") - } - - *v = *resp.ParameterGroups[0] + *v = *output return nil } } -func testAccParameterGroupConfig_only(rInt int) string { +func testAccParameterGroupConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_redshift_parameter_group" "test" { - name = "test-terraform-%d" - family = "redshift-1.0" - description = "Test parameter group for terraform" -} -`, rInt) -} - -func testAccParameterGroupConfig_basic(rInt int) string { - return fmt.Sprintf(` -resource "aws_redshift_parameter_group" "test" { - name = "test-terraform-%d" + name = %[1]q family = "redshift-1.0" parameter { @@ -259,35 +253,43 @@ resource "aws_redshift_parameter_group" "test" { value = "true" } } -`, rInt) +`, rName) +} + +func testAccParameterGroupConfig_noParameters(rName string) string { + return fmt.Sprintf(` +resource "aws_redshift_parameter_group" "test" { + name = %[1]q + family = "redshift-1.0" +} +`, rName) } -func testAccParameterGroupConfig_tags(rInt int, rString string) string { +func testAccParameterGroupConfig_tags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_redshift_parameter_group" "test" { - name = "test-terraform-%[1]d" + name = %[1]q family = "redshift-1.0" description = "Test parameter group for terraform" tags = { - environment = "Production" - name = "test-terraform-%[1]d" - description = "Test parameter group for terraform %[2]s" + %[2]q = %[3]q } } -`, rInt, rString) +`, rName, tagKey1, tagValue1) } -func testAccParameterGroupConfig_tagsUpdate(rInt int) string { +func testAccParameterGroupConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { return fmt.Sprintf(` resource "aws_redshift_parameter_group" "test" { - name = "test-terraform-%[1]d" + name = %[1]q family = "redshift-1.0" description = "Test parameter group for terraform" tags = { - name = "test-terraform-%[1]d" + %[2]q = %[3]q + %[4]q = %[5]q } } -`, rInt) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) } From 199471f749e2938458b4744ff807fb04d95552a9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Jan 2023 16:49:21 -0500 Subject: [PATCH 09/68] r/aws_opsworks_permission: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccOpsWorksPermission_' PKG=opsworks ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/opsworks/... -v -count 1 -parallel 3 -run=TestAccOpsWorksPermission_ -timeout 180m === RUN TestAccOpsWorksPermission_basic === PAUSE TestAccOpsWorksPermission_basic === RUN TestAccOpsWorksPermission_self === PAUSE TestAccOpsWorksPermission_self === CONT TestAccOpsWorksPermission_basic === CONT TestAccOpsWorksPermission_self --- PASS: TestAccOpsWorksPermission_self (49.26s) --- PASS: TestAccOpsWorksPermission_basic (80.01s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/opsworks 85.320s --- .changelog/27991.txt | 3 + internal/service/opsworks/instance.go | 70 +++++++ internal/service/opsworks/permission.go | 137 +++++++------- internal/service/opsworks/permission_test.go | 189 +------------------ internal/service/opsworks/status.go | 33 ---- internal/service/opsworks/wait.go | 52 ----- 6 files changed, 154 insertions(+), 330 deletions(-) create mode 100644 .changelog/27991.txt delete mode 100644 internal/service/opsworks/status.go delete mode 100644 internal/service/opsworks/wait.go diff --git a/.changelog/27991.txt b/.changelog/27991.txt new file mode 100644 index 00000000000..3096ff97767 --- /dev/null +++ b/.changelog/27991.txt @@ -0,0 +1,3 @@ +```release-note:bug +resource/aws_opsworks_permission: `stack_id` and `user_arn` are both Required and ForceNew +``` \ No newline at end of file diff --git a/internal/service/opsworks/instance.go b/internal/service/opsworks/instance.go index ad2efbc4201..dcfcd570d9d 100644 --- a/internal/service/opsworks/instance.go +++ b/internal/service/opsworks/instance.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -898,6 +899,75 @@ func stopInstance(d *schema.ResourceData, meta interface{}, timeout time.Duratio return nil } +func InstanceStatus(conn *opsworks.OpsWorks, instanceID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeInstances(&opsworks.DescribeInstancesInput{ + InstanceIds: []*string{aws.String(instanceID)}, + }) + + if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + if resp == nil || len(resp.Instances) == 0 || resp.Instances[0] == nil { + // Sometimes AWS just has consistency issues and doesn't see + // our instance yet. Return an empty state. + return nil, "", nil + } + + i := resp.Instances[0] + return i, aws.StringValue(i.Status), nil + } +} + +const ( + InstanceDeleteTimeout = 2 * time.Minute +) + +func waitInstanceDeleted(conn *opsworks.OpsWorks, instanceId string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{instanceStatusStopped, instanceStatusTerminating, instanceStatusTerminated}, + Target: []string{}, + Refresh: InstanceStatus(conn, instanceId), + Timeout: InstanceDeleteTimeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err := stateConf.WaitForState() + return err +} + +func waitInstanceStarted(conn *opsworks.OpsWorks, instanceId string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{instanceStatusRequested, instanceStatusPending, instanceStatusBooting, instanceStatusRunningSetup}, + Target: []string{instanceStatusOnline}, + Refresh: InstanceStatus(conn, instanceId), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + _, err := stateConf.WaitForState() + return err +} + +func waitInstanceStopped(conn *opsworks.OpsWorks, instanceId string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{instanceStatusStopping, instanceStatusTerminating, instanceStatusShuttingDown, instanceStatusTerminated}, + Target: []string{instanceStatusStopped}, + Refresh: InstanceStatus(conn, instanceId), + Timeout: timeout, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + _, err := stateConf.WaitForState() + return err +} + func readBlockDevices(instance *opsworks.Instance) map[string]interface{} { blockDevices := make(map[string]interface{}) blockDevices["ebs"] = make([]map[string]interface{}, 0) diff --git a/internal/service/opsworks/permission.go b/internal/service/opsworks/permission.go index f5b4fc17a2b..5364e2ac609 100644 --- a/internal/service/opsworks/permission.go +++ b/internal/service/opsworks/permission.go @@ -17,9 +17,9 @@ import ( func ResourcePermission() *schema.Resource { return &schema.Resource{ Create: resourceSetPermission, - Update: resourceSetPermission, - Delete: resourcePermissionDelete, Read: resourcePermissionRead, + Update: resourceSetPermission, + Delete: schema.Noop, Schema: map[string]*schema.Schema{ "allow_ssh": { @@ -32,10 +32,6 @@ func ResourcePermission() *schema.Resource { Computed: true, Optional: true, }, - "user_arn": { - Type: schema.TypeString, - Required: true, - }, "level": { Type: schema.TypeString, Computed: true, @@ -50,93 +46,104 @@ func ResourcePermission() *schema.Resource { }, "stack_id": { Type: schema.TypeString, - Computed: true, - Optional: true, + Required: true, + ForceNew: true, + }, + "user_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, }, } } -func resourcePermissionDelete(d *schema.ResourceData, meta interface{}) error { - return nil -} +func resourceSetPermission(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).OpsWorksConn() -func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() + iamUserARN := d.Get("user_arn").(string) + stackID := d.Get("stack_id").(string) + id := iamUserARN + stackID + input := &opsworks.SetPermissionInput{ + AllowSudo: aws.Bool(d.Get("allow_sudo").(bool)), + AllowSsh: aws.Bool(d.Get("allow_ssh").(bool)), + IamUserArn: aws.String(iamUserARN), + StackId: aws.String(stackID), + } - req := &opsworks.DescribePermissionsInput{ - IamUserArn: aws.String(d.Get("user_arn").(string)), - StackId: aws.String(d.Get("stack_id").(string)), + if d.IsNewResource() { + if v, ok := d.GetOk("level"); ok { + input.Level = aws.String(v.(string)) + } + } else if d.HasChange("level") { + input.Level = aws.String(d.Get("level").(string)) } - log.Printf("[DEBUG] Reading OpsWorks permissions for: %s on stack: %s", d.Get("user_arn"), d.Get("stack_id")) + _, err := tfresource.RetryWhenAWSErrMessageContains(propagationTimeout, func() (interface{}, error) { + return conn.SetPermission(input) + }, opsworks.ErrCodeResourceNotFoundException, "Unable to find user with ARN") - resp, err := client.DescribePermissions(req) if err != nil { - if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - log.Printf("[INFO] OpsWorks Permissions (%s, %s) not found, removing from state", d.Get("user_arn"), d.Get("stack_id")) - d.SetId("") - return nil - } - return fmt.Errorf("reading OpsWorks Permissions (%s): %w", d.Id(), err) + return fmt.Errorf("setting OpsWorks Permission (%s): %w", id, err) } - found := false - id := "" - for _, permission := range resp.Permissions { - id = *permission.IamUserArn + *permission.StackId - - if d.Get("user_arn").(string)+d.Get("stack_id").(string) == id { - found = true - d.SetId(id) - d.Set("allow_ssh", permission.AllowSsh) - d.Set("allow_sudo", permission.AllowSudo) - d.Set("user_arn", permission.IamUserArn) - d.Set("stack_id", permission.StackId) - d.Set("level", permission.Level) - } + if d.IsNewResource() { + d.SetId(id) } - if !found { + return resourcePermissionRead(d, meta) +} + +func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).OpsWorksConn() + + permission, err := FindPermissionByTwoPartKey(conn, d.Get("user_arn").(string), d.Get("stack_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] OpsWorks Permission %s not found, removing from state", d.Id()) d.SetId("") - log.Printf("[INFO] The correct permission could not be found for: %s on stack: %s", d.Get("user_arn"), d.Get("stack_id")) + return nil + } + + if err != nil { + return fmt.Errorf("reading OpsWorks Permission (%s): %w", d.Id(), err) } + d.Set("allow_ssh", permission.AllowSsh) + d.Set("allow_sudo", permission.AllowSudo) + d.Set("level", permission.Level) + d.Set("stack_id", permission.StackId) + d.Set("user_arn", permission.IamUserArn) + return nil } -func resourceSetPermission(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() - - req := &opsworks.SetPermissionInput{ - AllowSudo: aws.Bool(d.Get("allow_sudo").(bool)), - AllowSsh: aws.Bool(d.Get("allow_ssh").(bool)), - IamUserArn: aws.String(d.Get("user_arn").(string)), - StackId: aws.String(d.Get("stack_id").(string)), +func FindPermissionByTwoPartKey(conn *opsworks.OpsWorks, iamUserARN, stackID string) (*opsworks.Permission, error) { + input := &opsworks.DescribePermissionsInput{ + IamUserArn: aws.String(iamUserARN), + StackId: aws.String(stackID), } - if d.HasChange("level") { - req.Level = aws.String(d.Get("level").(string)) - } + output, err := conn.DescribePermissions(input) - err := resource.Retry(propagationTimeout, func() *resource.RetryError { - _, err := client.SetPermission(req) - if err != nil { - if tfawserr.ErrMessageContains(err, opsworks.ErrCodeResourceNotFoundException, "Unable to find user with ARN") { - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) + if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, } - return nil - }) - - if tfresource.TimedOut(err) { - _, err = client.SetPermission(req) } if err != nil { - return fmt.Errorf("setting OpsWorks Permissions (%s): %w", d.Id(), err) + return nil, err } - return resourcePermissionRead(d, meta) + if output == nil || len(output.Permissions) == 0 || output.Permissions[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.Permissions); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.Permissions[0], nil } diff --git a/internal/service/opsworks/permission_test.go b/internal/service/opsworks/permission_test.go index 99529a0512c..d80ccfc1bf8 100644 --- a/internal/service/opsworks/permission_test.go +++ b/internal/service/opsworks/permission_test.go @@ -4,31 +4,30 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfopsworks "github.com/hashicorp/terraform-provider-aws/internal/service/opsworks" ) func TestAccOpsWorksPermission_basic(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_permission.test" var opsperm opsworks.Permission + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckPermissionDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccPermissionConfig_create(rName, true, true, "iam_only"), Check: resource.ComposeTestCheckFunc( testAccCheckPermissionExists(resourceName, &opsperm), - testAccCheckCreatePermissionAttributes(&opsperm, true, true, "iam_only"), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "true"), resource.TestCheckResourceAttr(resourceName, "level", "iam_only"), @@ -38,7 +37,6 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { Config: testAccPermissionConfig_create(rName, true, false, "iam_only"), Check: resource.ComposeTestCheckFunc( testAccCheckPermissionExists(resourceName, &opsperm), - testAccCheckCreatePermissionAttributes(&opsperm, true, false, "iam_only"), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "iam_only"), @@ -48,7 +46,6 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { Config: testAccPermissionConfig_create(rName, false, false, "deny"), Check: resource.ComposeTestCheckFunc( testAccCheckPermissionExists(resourceName, &opsperm), - testAccCheckCreatePermissionAttributes(&opsperm, false, false, "deny"), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "false"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "deny"), @@ -58,7 +55,6 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { Config: testAccPermissionConfig_create(rName, false, false, "show"), Check: resource.ComposeTestCheckFunc( testAccCheckPermissionExists(resourceName, &opsperm), - testAccCheckCreatePermissionAttributes(&opsperm, false, false, "show"), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "false"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "show"), @@ -78,7 +74,7 @@ func TestAccOpsWorksPermission_self(t *testing.T) { PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: nil, // Cannot delete own OpsWorks Permission + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccPermissionConfig_self(rName, true, true), @@ -100,8 +96,7 @@ func TestAccOpsWorksPermission_self(t *testing.T) { }) } -func testAccCheckPermissionExists( - n string, opsperm *opsworks.Permission) resource.TestCheckFunc { +func testAccCheckPermissionExists(n string, v *opsworks.Permission) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -109,189 +104,23 @@ func testAccCheckPermissionExists( } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No OpsWorks Layer ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - params := &opsworks.DescribePermissionsInput{ - StackId: aws.String(rs.Primary.Attributes["stack_id"]), - IamUserArn: aws.String(rs.Primary.Attributes["user_arn"]), - } - resp, err := conn.DescribePermissions(params) + output, err := tfopsworks.FindPermissionByTwoPartKey(conn, rs.Primary.Attributes["user_arn"], rs.Primary.Attributes["stack_id"]) if err != nil { return err } - if v := len(resp.Permissions); v != 1 { - return fmt.Errorf("Expected 1 response returned, got %d", v) - } - - *opsperm = *resp.Permissions[0] + *v = *output return nil } } -func testAccCheckCreatePermissionAttributes( - opsperm *opsworks.Permission, allowSSH bool, allowSudo bool, level string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *opsperm.AllowSsh != allowSSH { - return fmt.Errorf("Unnexpected allowSSH: %t", *opsperm.AllowSsh) - } - - if *opsperm.AllowSudo != allowSudo { - return fmt.Errorf("Unnexpected allowSudo: %t", *opsperm.AllowSudo) - } - - if *opsperm.Level != level { - return fmt.Errorf("Unnexpected level: %s", *opsperm.Level) - } - - return nil - } -} - -func testAccCheckPermissionDestroy(s *terraform.State) error { - client := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_opsworks_permission" { - continue - } - - req := &opsworks.DescribePermissionsInput{ - IamUserArn: aws.String(rs.Primary.Attributes["user_arn"]), - } - - resp, err := client.DescribePermissions(req) - if err == nil { - if len(resp.Permissions) > 0 { - return fmt.Errorf("OpsWorks Permissions still exist.") - } - } - - if !tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - continue - } - } - return nil -} - -func testAccPermissionBase(rName string) string { - return fmt.Sprintf(` -data "aws_region" "current" {} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/24" - - tags = { - Name = %[1]q - } -} - -resource "aws_subnet" "test" { - cidr_block = aws_vpc.test.cidr_block - vpc_id = aws_vpc.test.id - - tags = { - Name = %[1]q - } -} - -resource "aws_opsworks_stack" "test" { - name = %[1]q - region = data.aws_region.current.name - vpc_id = aws_vpc.test.id - default_subnet_id = aws_subnet.test.id - service_role_arn = aws_iam_role.service.arn - default_instance_profile_arn = aws_iam_instance_profile.test.arn - default_os = "Amazon Linux 2016.09" - default_root_device_type = "ebs" - - custom_json = < Date: Thu, 19 Jan 2023 17:06:56 -0500 Subject: [PATCH 10/68] r/aws_opsworks_user_profile: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccOpsWorksUserProfile_' PKG=opsworks ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/opsworks/... -v -count 1 -parallel 3 -run=TestAccOpsWorksUserProfile_ -timeout 180m === RUN TestAccOpsWorksUserProfile_basic === PAUSE TestAccOpsWorksUserProfile_basic === RUN TestAccOpsWorksUserProfile_disappears === PAUSE TestAccOpsWorksUserProfile_disappears === CONT TestAccOpsWorksUserProfile_basic === CONT TestAccOpsWorksUserProfile_disappears --- PASS: TestAccOpsWorksUserProfile_disappears (16.25s) --- PASS: TestAccOpsWorksUserProfile_basic (28.09s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/opsworks 33.334s --- internal/service/opsworks/user_profile.go | 131 ++++++++++-------- .../service/opsworks/user_profile_test.go | 112 +++++++-------- 2 files changed, 128 insertions(+), 115 deletions(-) diff --git a/internal/service/opsworks/user_profile.go b/internal/service/opsworks/user_profile.go index d4a39745b23..1e2b21989c1 100644 --- a/internal/service/opsworks/user_profile.go +++ b/internal/service/opsworks/user_profile.go @@ -7,8 +7,10 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceUserProfile() *schema.Resource { @@ -19,122 +21,139 @@ func ResourceUserProfile() *schema.Resource { Delete: resourceUserProfileDelete, Schema: map[string]*schema.Schema{ - "user_arn": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "allow_self_management": { Type: schema.TypeBool, Optional: true, Default: false, }, - + "ssh_public_key": { + Type: schema.TypeString, + Optional: true, + }, "ssh_username": { Type: schema.TypeString, Required: true, }, - - "ssh_public_key": { + "user_arn": { Type: schema.TypeString, - Optional: true, + Required: true, + ForceNew: true, }, }, } } -func resourceUserProfileRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() +func resourceUserProfileCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).OpsWorksConn() - req := &opsworks.DescribeUserProfilesInput{ - IamUserArns: []*string{ - aws.String(d.Id()), - }, + iamUserARN := d.Get("user_arn").(string) + input := &opsworks.CreateUserProfileInput{ + AllowSelfManagement: aws.Bool(d.Get("allow_self_management").(bool)), + IamUserArn: aws.String(iamUserARN), + SshUsername: aws.String(d.Get("ssh_username").(string)), } - log.Printf("[DEBUG] Reading OpsWorks user profile: %s", d.Id()) - - resp, err := client.DescribeUserProfiles(req) - if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - log.Printf("[DEBUG] OpsWorks user profile (%s) not found", d.Id()) - d.SetId("") - return nil + if v, ok := d.GetOk("ssh_public_key"); ok { + input.SshPublicKey = aws.String(v.(string)) } + + _, err := conn.CreateUserProfile(input) + if err != nil { - return fmt.Errorf("error reading OpsWorks User Profile (%s): %w", d.Id(), err) + return fmt.Errorf("creating OpsWorks User Profile (%s): %w", iamUserARN, err) } - for _, profile := range resp.UserProfiles { - d.Set("allow_self_management", profile.AllowSelfManagement) - d.Set("user_arn", profile.IamUserArn) - d.Set("ssh_public_key", profile.SshPublicKey) - d.Set("ssh_username", profile.SshUsername) - break - } + d.SetId(iamUserARN) - return nil + return resourceUserProfileUpdate(d, meta) } -func resourceUserProfileCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() +func resourceUserProfileRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).OpsWorksConn() - req := &opsworks.CreateUserProfileInput{ - AllowSelfManagement: aws.Bool(d.Get("allow_self_management").(bool)), - IamUserArn: aws.String(d.Get("user_arn").(string)), - SshPublicKey: aws.String(d.Get("ssh_public_key").(string)), - SshUsername: aws.String(d.Get("ssh_username").(string)), + profile, err := FindUserProfileByARN(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] OpsWorks User Profile %s not found, removing from state", d.Id()) + d.SetId("") + return nil } - resp, err := client.CreateUserProfile(req) if err != nil { - return fmt.Errorf("error creating OpsWorks User Profile (%s): %w", d.Id(), err) + return fmt.Errorf("reading OpsWorks User Profile (%s): %w", d.Id(), err) } - d.SetId(aws.StringValue(resp.IamUserArn)) + d.Set("allow_self_management", profile.AllowSelfManagement) + d.Set("ssh_public_key", profile.SshPublicKey) + d.Set("ssh_username", profile.SshUsername) + d.Set("user_arn", profile.IamUserArn) - return resourceUserProfileUpdate(d, meta) + return nil } func resourceUserProfileUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() + conn := meta.(*conns.AWSClient).OpsWorksConn() - req := &opsworks.UpdateUserProfileInput{ + input := &opsworks.UpdateUserProfileInput{ AllowSelfManagement: aws.Bool(d.Get("allow_self_management").(bool)), IamUserArn: aws.String(d.Get("user_arn").(string)), SshPublicKey: aws.String(d.Get("ssh_public_key").(string)), SshUsername: aws.String(d.Get("ssh_username").(string)), } - log.Printf("[DEBUG] Updating OpsWorks user profile: %s", req) + _, err := conn.UpdateUserProfile(input) - _, err := client.UpdateUserProfile(req) if err != nil { - return fmt.Errorf("error updating OpsWorks User Profile (%s): %w", d.Id(), err) + return fmt.Errorf("updating OpsWorks User Profile (%s): %w", d.Id(), err) } return resourceUserProfileRead(d, meta) } func resourceUserProfileDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*conns.AWSClient).OpsWorksConn() + conn := meta.(*conns.AWSClient).OpsWorksConn() - req := &opsworks.DeleteUserProfileInput{ + log.Printf("[DEBUG] Deleting OpsWorks User Profile: %s", d.Id()) + _, err := conn.DeleteUserProfile(&opsworks.DeleteUserProfileInput{ IamUserArn: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { + return nil + } + + if err != nil { + return fmt.Errorf("deleting OpsWorks User Profile (%s): %w", d.Id(), err) } - log.Printf("[DEBUG] Deleting OpsWorks user profile: %s", d.Id()) + return nil +} + +func FindUserProfileByARN(conn *opsworks.OpsWorks, arn string) (*opsworks.UserProfile, error) { + input := &opsworks.DescribeUserProfilesInput{ + IamUserArns: aws.StringSlice([]string{arn}), + } - _, err := client.DeleteUserProfile(req) + output, err := conn.DescribeUserProfiles(input) if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - log.Printf("[DEBUG] OpsWorks User Profile (%s) not found to delete; removed from state", d.Id()) - return nil + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } } if err != nil { - return fmt.Errorf("error deleting OpsWorks User Profile (%s): %w", d.Id(), err) + return nil, err } - return nil + if output == nil || len(output.UserProfiles) == 0 || output.UserProfiles[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.UserProfiles); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.UserProfiles[0], nil } diff --git a/internal/service/opsworks/user_profile_test.go b/internal/service/opsworks/user_profile_test.go index 15f7d4e78ff..61dec443a87 100644 --- a/internal/service/opsworks/user_profile_test.go +++ b/internal/service/opsworks/user_profile_test.go @@ -4,20 +4,21 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfopsworks "github.com/hashicorp/terraform-provider-aws/internal/service/opsworks" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccOpsWorksUserProfile_basic(t *testing.T) { - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_user_profile.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), @@ -25,18 +26,18 @@ func TestAccOpsWorksUserProfile_basic(t *testing.T) { CheckDestroy: testAccCheckUserProfileDestroy, Steps: []resource.TestStep{ { - Config: testAccUserProfileConfig_create(rName), + Config: testAccUserProfileConfig_create(rName1), Check: resource.ComposeTestCheckFunc( - testAccCheckUserProfileExists(resourceName, rName), + testAccCheckUserProfileExists(resourceName), resource.TestCheckResourceAttr(resourceName, "ssh_public_key", ""), - resource.TestCheckResourceAttr(resourceName, "ssh_username", rName), + resource.TestCheckResourceAttr(resourceName, "ssh_username", rName1), resource.TestCheckResourceAttr(resourceName, "allow_self_management", "false"), ), }, { - Config: testAccUserProfileConfig_update(rName, rName2), + Config: testAccUserProfileConfig_update(rName1, rName2), Check: resource.ComposeTestCheckFunc( - testAccCheckUserProfileExists(resourceName, rName2), + testAccCheckUserProfileExists(resourceName), resource.TestCheckResourceAttr(resourceName, "ssh_public_key", ""), resource.TestCheckResourceAttr(resourceName, "ssh_username", rName2), resource.TestCheckResourceAttr(resourceName, "allow_self_management", "false"), @@ -46,8 +47,29 @@ func TestAccOpsWorksUserProfile_basic(t *testing.T) { }) } -func testAccCheckUserProfileExists( - n, username string) resource.TestCheckFunc { +func TestAccOpsWorksUserProfile_disappears(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_opsworks_user_profile.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, + ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserProfileDestroy, + Steps: []resource.TestStep{ + { + Config: testAccUserProfileConfig_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserProfileExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfopsworks.ResourceUserProfile(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckUserProfileExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -55,98 +77,70 @@ func testAccCheckUserProfileExists( } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - if _, ok := rs.Primary.Attributes["user_arn"]; !ok { - return fmt.Errorf("User Profile user arn is missing, should be set.") + return fmt.Errorf("No OpsWorks User Profile ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - params := &opsworks.DescribeUserProfilesInput{ - IamUserArns: []*string{aws.String(rs.Primary.Attributes["user_arn"])}, - } - resp, err := conn.DescribeUserProfiles(params) + _, err := tfopsworks.FindUserProfileByARN(conn, rs.Primary.ID) - if err != nil { - return err - } - - if v := len(resp.UserProfiles); v != 1 { - return fmt.Errorf("Expected 1 response returned, got %d", v) - } - - opsuserprofile := *resp.UserProfiles[0] - - if *opsuserprofile.AllowSelfManagement { - return fmt.Errorf("Unnexpected allowSelfManagement: %t", - *opsuserprofile.AllowSelfManagement) - } - - if *opsuserprofile.Name != username { - return fmt.Errorf("Unnexpected name: %s", *opsuserprofile.Name) - } - - return nil + return err } } func testAccCheckUserProfileDestroy(s *terraform.State) error { - client := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() + conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() for _, rs := range s.RootModule().Resources { if rs.Type != "aws_opsworks_user_profile" { continue } - req := &opsworks.DescribeUserProfilesInput{ - IamUserArns: []*string{aws.String(rs.Primary.Attributes["user_arn"])}, - } - resp, err := client.DescribeUserProfiles(req) + _, err := tfopsworks.FindUserProfileByARN(conn, rs.Primary.ID) - if err == nil { - if len(resp.UserProfiles) > 0 { - return fmt.Errorf("OpsWorks User Profiles still exist.") - } + if tfresource.NotFound(err) { + continue } - if !tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - continue + if err != nil { + return err } + + return fmt.Errorf("OpsWorks User Profile %s still exists", rs.Primary.ID) } + return nil } func testAccUserProfileConfig_create(rName string) string { return fmt.Sprintf(` resource "aws_opsworks_user_profile" "test" { - user_arn = aws_iam_user.test.arn - ssh_username = aws_iam_user.test.name + user_arn = aws_iam_user.test1.arn + ssh_username = aws_iam_user.test1.name } -resource "aws_iam_user" "test" { +resource "aws_iam_user" "test1" { name = %[1]q path = "/" } `, rName) } -func testAccUserProfileConfig_update(rName, rName2 string) string { +func testAccUserProfileConfig_update(rName1, rName2 string) string { return fmt.Sprintf(` resource "aws_opsworks_user_profile" "test" { - user_arn = aws_iam_user.new-test.arn - ssh_username = aws_iam_user.new-test.name + user_arn = aws_iam_user.test2.arn + ssh_username = aws_iam_user.test2.name } -resource "aws_iam_user" "test" { +resource "aws_iam_user" "test1" { name = %[1]q path = "/" } -resource "aws_iam_user" "new-test" { +resource "aws_iam_user" "test2" { name = %[2]q path = "/" } -`, rName, rName2) +`, rName1, rName2) } From 353df87173dff88fb42666b6cdd7ba3f813ab31c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 19 Jan 2023 17:13:30 -0500 Subject: [PATCH 11/68] r/aws_elasticache_security_group: Use 'tfawserr.ErrCodeEquals'. --- .../service/elasticache/security_group.go | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/internal/service/elasticache/security_group.go b/internal/service/elasticache/security_group.go index 9c120ffb3c9..f3ff5026cb7 100644 --- a/internal/service/elasticache/security_group.go +++ b/internal/service/elasticache/security_group.go @@ -7,8 +7,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -98,22 +98,15 @@ func resourceSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error _, err := conn.DeleteCacheSecurityGroup(&elasticache.DeleteCacheSecurityGroupInput{ CacheSecurityGroupName: aws.String(d.Id()), }) + + if tfawserr.ErrCodeEquals(err, "InvalidCacheSecurityGroupState", "DependencyViolation") { + return resource.RetryableError(err) + } + if err != nil { - apierr, ok := err.(awserr.Error) - if !ok { - return resource.RetryableError(err) - } - log.Printf("[DEBUG] APIError.Code: %v", apierr.Code()) - switch apierr.Code() { - case "InvalidCacheSecurityGroupState": - return resource.RetryableError(err) - case "DependencyViolation": - // If it is a dependency violation, we want to retry - return resource.RetryableError(err) - default: - return resource.NonRetryableError(err) - } + return resource.NonRetryableError(err) } + return nil }) From 97d08aba0491bde17ec1610e26612be319373659 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 08:51:47 -0500 Subject: [PATCH 12/68] r/aws_elasticache_subnet_group: Remove explicit cast to 'awserr.Error'. Acceptance test output: % make testacc TESTARGS='-run=TestAccElastiCacheSubnetGroup_' PKG=elasticache ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elasticache/... -v -count 1 -parallel 3 -run=TestAccElastiCacheSubnetGroup_ -timeout 180m === RUN TestAccElastiCacheSubnetGroup_basic === PAUSE TestAccElastiCacheSubnetGroup_basic === RUN TestAccElastiCacheSubnetGroup_disappears === PAUSE TestAccElastiCacheSubnetGroup_disappears === RUN TestAccElastiCacheSubnetGroup_tags === PAUSE TestAccElastiCacheSubnetGroup_tags === RUN TestAccElastiCacheSubnetGroup_update === PAUSE TestAccElastiCacheSubnetGroup_update === CONT TestAccElastiCacheSubnetGroup_basic === CONT TestAccElastiCacheSubnetGroup_tags === CONT TestAccElastiCacheSubnetGroup_update --- PASS: TestAccElastiCacheSubnetGroup_basic (28.45s) === CONT TestAccElastiCacheSubnetGroup_disappears --- PASS: TestAccElastiCacheSubnetGroup_update (46.61s) --- PASS: TestAccElastiCacheSubnetGroup_disappears (23.50s) --- PASS: TestAccElastiCacheSubnetGroup_tags (63.55s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elasticache 68.569s --- internal/service/elasticache/subnet_group.go | 94 ++++++-------------- 1 file changed, 29 insertions(+), 65 deletions(-) diff --git a/internal/service/elasticache/subnet_group.go b/internal/service/elasticache/subnet_group.go index fa449d75055..83a010b485a 100644 --- a/internal/service/elasticache/subnet_group.go +++ b/internal/service/elasticache/subnet_group.go @@ -8,10 +8,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticache" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/flex" @@ -26,6 +24,7 @@ func ResourceSubnetGroup() *schema.Resource { Read: resourceSubnetGroupRead, Update: resourceSubnetGroupUpdate, Delete: resourceSubnetGroupDelete, + Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -55,7 +54,6 @@ func ResourceSubnetGroup() *schema.Resource { Type: schema.TypeSet, Required: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), @@ -82,32 +80,24 @@ func resourceSubnetGroupCreate(d *schema.ResourceData, meta interface{}) error { defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - // Get the group properties name := d.Get("name").(string) - desc := d.Get("description").(string) - subnetIdsSet := d.Get("subnet_ids").(*schema.Set) - - log.Printf("[DEBUG] Cache subnet group create: name: %s, description: %s", name, desc) - - subnetIds := flex.ExpandStringSet(subnetIdsSet) - - req := &elasticache.CreateCacheSubnetGroupInput{ - CacheSubnetGroupDescription: aws.String(desc), + input := &elasticache.CreateCacheSubnetGroupInput{ + CacheSubnetGroupDescription: aws.String(d.Get("description").(string)), CacheSubnetGroupName: aws.String(name), - SubnetIds: subnetIds, + SubnetIds: flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set)), } if len(tags) > 0 { - req.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - output, err := conn.CreateCacheSubnetGroup(req) + output, err := conn.CreateCacheSubnetGroup(input) - if req.Tags != nil && verify.ErrorISOUnsupported(conn.PartitionID, err) { + if input.Tags != nil && verify.ErrorISOUnsupported(conn.PartitionID, err) { log.Printf("[WARN] failed creating ElastiCache Subnet Group with tags: %s. Trying create without tags.", err) - req.Tags = nil - output, err = conn.CreateCacheSubnetGroup(req) + input.Tags = nil + output, err = conn.CreateCacheSubnetGroup(input) } if err != nil { @@ -121,13 +111,13 @@ func resourceSubnetGroupCreate(d *schema.ResourceData, meta interface{}) error { d.SetId(strings.ToLower(name)) // In some partitions, only post-create tagging supported - if req.Tags == nil && len(tags) > 0 { + if input.Tags == nil && len(tags) > 0 { err := UpdateTags(conn, aws.StringValue(output.CacheSubnetGroup.ARN), nil, tags) if err != nil { if v, ok := d.GetOk("tags"); (ok && len(v.(map[string]interface{})) > 0) || !verify.ErrorISOUnsupported(conn.PartitionID, err) { // explicitly setting tags or not an iso-unsupported error - return fmt.Errorf("failed adding tags after create for ElastiCache Subnet Group (%s): %w", d.Id(), err) + return fmt.Errorf("adding tags after create for ElastiCache Subnet Group (%s): %w", d.Id(), err) } log.Printf("[WARN] failed adding tags after create for ElastiCache Subnet Group (%s): %s", d.Id(), err) @@ -154,15 +144,15 @@ func resourceSubnetGroupRead(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("reading ElastiCache Subnet Group (%s): %w", d.Id(), err) } - var subnetIds []*string + var subnetIDs []*string for _, subnet := range group.Subnets { - subnetIds = append(subnetIds, subnet.SubnetIdentifier) + subnetIDs = append(subnetIDs, subnet.SubnetIdentifier) } d.Set("arn", group.ARN) d.Set("name", group.CacheSubnetGroupName) d.Set("description", group.CacheSubnetGroupDescription) - d.Set("subnet_ids", subnetIds) + d.Set("subnet_ids", aws.StringValueSlice(subnetIDs)) tags, err := ListTags(conn, d.Get("arn").(string)) @@ -180,11 +170,11 @@ func resourceSubnetGroupRead(d *schema.ResourceData, meta interface{}) error { //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return fmt.Errorf("setting tags: %w", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return fmt.Errorf("setting tags_all: %w", err) } } @@ -195,22 +185,16 @@ func resourceSubnetGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).ElastiCacheConn() if d.HasChanges("subnet_ids", "description") { - var subnets []*string - if v := d.Get("subnet_ids"); v != nil { - for _, v := range v.(*schema.Set).List() { - subnets = append(subnets, aws.String(v.(string))) - } + input := &elasticache.ModifyCacheSubnetGroupInput{ + CacheSubnetGroupDescription: aws.String(d.Get("description").(string)), + CacheSubnetGroupName: aws.String(d.Get("name").(string)), + SubnetIds: flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set)), } - log.Printf("[DEBUG] Updating ElastiCache Subnet Group") - _, err := conn.ModifyCacheSubnetGroup(&elasticache.ModifyCacheSubnetGroupInput{ - CacheSubnetGroupName: aws.String(d.Get("name").(string)), - CacheSubnetGroupDescription: aws.String(d.Get("description").(string)), - SubnetIds: subnets, - }) + _, err := conn.ModifyCacheSubnetGroup(input) if err != nil { - return fmt.Errorf("error updating ElastiCache Subnet Group (%s): %w", d.Id(), err) + return fmt.Errorf("updating ElastiCache Subnet Group (%s): %w", d.Id(), err) } } @@ -218,10 +202,11 @@ func resourceSubnetGroupUpdate(d *schema.ResourceData, meta interface{}) error { o, n := d.GetChange("tags_all") err := UpdateTags(conn, d.Get("arn").(string), o, n) + if err != nil { if v, ok := d.GetOk("tags"); (ok && len(v.(map[string]interface{})) > 0) || !verify.ErrorISOUnsupported(conn.PartitionID, err) { // explicitly setting tags or not an iso-unsupported error - return fmt.Errorf("failed updating ElastiCache Subnet Group (%s) tags: %w", d.Id(), err) + return fmt.Errorf("updating ElastiCache Subnet Group (%s) tags: %w", d.Id(), err) } log.Printf("[WARN] failed updating tags for ElastiCache Subnet Group (%s): %s", d.Id(), err) @@ -233,40 +218,19 @@ func resourceSubnetGroupUpdate(d *schema.ResourceData, meta interface{}) error { func resourceSubnetGroupDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).ElastiCacheConn() - log.Printf("[DEBUG] Cache subnet group delete: %s", d.Id()) - - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteCacheSubnetGroup(&elasticache.DeleteCacheSubnetGroupInput{ - CacheSubnetGroupName: aws.String(d.Id()), - }) - if err != nil { - apierr, ok := err.(awserr.Error) - if !ok { - return resource.RetryableError(err) - } - log.Printf("[DEBUG] APIError.Code: %v", apierr.Code()) - switch apierr.Code() { - case "DependencyViolation": - // If it is a dependency violation, we want to retry - return resource.RetryableError(err) - default: - return resource.NonRetryableError(err) - } - } - return nil - }) - if tfresource.TimedOut(err) { - _, err = conn.DeleteCacheSubnetGroup(&elasticache.DeleteCacheSubnetGroupInput{ + log.Printf("[DEBUG] Deleting ElastiCache Subnet Group: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrCodeEquals(5*time.Minute, func() (interface{}, error) { + return conn.DeleteCacheSubnetGroup(&elasticache.DeleteCacheSubnetGroupInput{ CacheSubnetGroupName: aws.String(d.Id()), }) - } + }, "DependencyViolation") if tfawserr.ErrCodeEquals(err, elasticache.ErrCodeCacheSubnetGroupNotFoundFault) { return nil } if err != nil { - return fmt.Errorf("error deleting ElastiCache Subnet Group (%s): %w", d.Id(), err) + return fmt.Errorf("deleting ElastiCache Subnet Group (%s): %w", d.Id(), err) } return nil From 02044f1f3d4bbd20d2d709831088f415bc08be2f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 09:15:17 -0500 Subject: [PATCH 13/68] r/aws_ses_receipt_rule: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccSESReceiptRule_' PKG=ses ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ses/... -v -count 1 -parallel 3 -run=TestAccSESReceiptRule_ -timeout 180m === RUN TestAccSESReceiptRule_basic === PAUSE TestAccSESReceiptRule_basic === RUN TestAccSESReceiptRule_s3Action === PAUSE TestAccSESReceiptRule_s3Action === RUN TestAccSESReceiptRule_snsAction === PAUSE TestAccSESReceiptRule_snsAction === RUN TestAccSESReceiptRule_snsActionEncoding === PAUSE TestAccSESReceiptRule_snsActionEncoding === RUN TestAccSESReceiptRule_lambdaAction === PAUSE TestAccSESReceiptRule_lambdaAction === RUN TestAccSESReceiptRule_stopAction === PAUSE TestAccSESReceiptRule_stopAction === RUN TestAccSESReceiptRule_order === PAUSE TestAccSESReceiptRule_order === RUN TestAccSESReceiptRule_actions === PAUSE TestAccSESReceiptRule_actions === RUN TestAccSESReceiptRule_disappears === PAUSE TestAccSESReceiptRule_disappears === CONT TestAccSESReceiptRule_basic === CONT TestAccSESReceiptRule_stopAction === CONT TestAccSESReceiptRule_actions --- PASS: TestAccSESReceiptRule_basic (19.94s) === CONT TestAccSESReceiptRule_order --- PASS: TestAccSESReceiptRule_actions (19.95s) === CONT TestAccSESReceiptRule_disappears --- PASS: TestAccSESReceiptRule_stopAction (20.74s) === CONT TestAccSESReceiptRule_snsActionEncoding --- PASS: TestAccSESReceiptRule_order (27.24s) === CONT TestAccSESReceiptRule_lambdaAction --- PASS: TestAccSESReceiptRule_snsActionEncoding (28.18s) === CONT TestAccSESReceiptRule_snsAction --- PASS: TestAccSESReceiptRule_disappears (37.14s) === CONT TestAccSESReceiptRule_s3Action --- PASS: TestAccSESReceiptRule_snsAction (20.28s) --- PASS: TestAccSESReceiptRule_s3Action (22.22s) --- PASS: TestAccSESReceiptRule_lambdaAction (43.83s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ses 96.636s --- internal/service/ses/receipt_rule.go | 446 ++++++++-------------- internal/service/ses/receipt_rule_test.go | 32 +- 2 files changed, 180 insertions(+), 298 deletions(-) diff --git a/internal/service/ses/receipt_rule.go b/internal/service/ses/receipt_rule.go index 5c9238ffebb..c8a729fc115 100644 --- a/internal/service/ses/receipt_rule.go +++ b/internal/service/ses/receipt_rule.go @@ -1,7 +1,6 @@ package ses import ( - "bytes" "fmt" "log" "regexp" @@ -12,11 +11,12 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ses" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -26,63 +26,12 @@ func ResourceReceiptRule() *schema.Resource { Update: resourceReceiptRuleUpdate, Read: resourceReceiptRuleRead, Delete: resourceReceiptRuleDelete, + Importer: &schema.ResourceImporter{ State: resourceReceiptRuleImport, }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.All( - validation.StringLenBetween(1, 64), - validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z._-]+$`), "must contain only alphanumeric, period, underscore, and hyphen characters"), - validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z]`), "must begin with a alphanumeric character"), - validation.StringMatch(regexp.MustCompile(`[0-9a-zA-Z]$`), "must end with a alphanumeric character"), - ), - }, - - "rule_set_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "after": { - Type: schema.TypeString, - Optional: true, - }, - - "enabled": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - - "recipients": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - - "scan_enabled": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - - "tls_policy": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validation.StringInSlice(ses.TlsPolicy_Values(), false), - }, - "add_header_action": { Type: schema.TypeSet, Optional: true, @@ -96,30 +45,26 @@ func ResourceReceiptRule() *schema.Resource { validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z-]+$`), "must contain only alphanumeric and dash characters"), ), }, - "header_value": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(0, 2048), }, - "position": { Type: schema.TypeInt, Required: true, }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["header_name"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["header_value"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - + "after": { + Type: schema.TypeString, + Optional: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, "bounce_action": { Type: schema.TypeSet, Optional: true, @@ -129,55 +74,35 @@ func ResourceReceiptRule() *schema.Resource { Type: schema.TypeString, Required: true, }, - + "position": { + Type: schema.TypeInt, + Required: true, + }, "sender": { Type: schema.TypeString, Required: true, }, - "smtp_reply_code": { Type: schema.TypeString, Required: true, }, - "status_code": { Type: schema.TypeString, Optional: true, }, - "topic_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - - "position": { - Type: schema.TypeInt, - Required: true, - }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["message"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["sender"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["smtp_reply_code"].(string))) - - if _, ok := m["status_code"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["status_code"].(string))) - } - - if _, ok := m["topic_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - } - - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "lambda_action": { Type: schema.TypeSet, Optional: true, @@ -188,45 +113,45 @@ func ResourceReceiptRule() *schema.Resource { Required: true, ValidateFunc: verify.ValidARN, }, - "invocation_type": { Type: schema.TypeString, Optional: true, Default: ses.InvocationTypeEvent, ValidateFunc: validation.StringInSlice(ses.InvocationType_Values(), false), }, - + "position": { + Type: schema.TypeInt, + Required: true, + }, "topic_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - - "position": { - Type: schema.TypeInt, - Required: true, - }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["function_arn"].(string))) - - if _, ok := m["invocation_type"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["invocation_type"].(string))) - } - - if _, ok := m["topic_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - } - - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.All( + validation.StringLenBetween(1, 64), + validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z._-]+$`), "must contain only alphanumeric, period, underscore, and hyphen characters"), + validation.StringMatch(regexp.MustCompile(`^[0-9a-zA-Z]`), "must begin with a alphanumeric character"), + validation.StringMatch(regexp.MustCompile(`[0-9a-zA-Z]$`), "must end with a alphanumeric character"), + ), + }, + "recipients": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "rule_set_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "s3_action": { Type: schema.TypeSet, Optional: true, @@ -236,54 +161,33 @@ func ResourceReceiptRule() *schema.Resource { Type: schema.TypeString, Required: true, }, - "kms_key_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - "object_key_prefix": { Type: schema.TypeString, Optional: true, }, - - "topic_arn": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: verify.ValidARN, - }, - "position": { Type: schema.TypeInt, Required: true, ValidateFunc: validation.IntAtLeast(1), }, + "topic_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, + }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["bucket_name"].(string))) - - if _, ok := m["kms_key_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["kms_key_arn"].(string))) - } - - if _, ok := m["object_key_prefix"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["object_key_prefix"].(string))) - } - - if _, ok := m["topic_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - } - - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - + "scan_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, "sns_action": { Type: schema.TypeSet, Optional: true, @@ -295,29 +199,18 @@ func ResourceReceiptRule() *schema.Resource { Optional: true, ValidateFunc: validation.StringInSlice(ses.SNSActionEncoding_Values(), false), }, + "position": { + Type: schema.TypeInt, + Required: true, + }, "topic_arn": { Type: schema.TypeString, Required: true, ValidateFunc: verify.ValidARN, }, - - "position": { - Type: schema.TypeInt, - Required: true, - }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["encoding"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - "stop_action": { Type: schema.TypeSet, Optional: true, @@ -328,34 +221,24 @@ func ResourceReceiptRule() *schema.Resource { Required: true, ValidateFunc: validation.StringInSlice(ses.StopScope_Values(), false), }, - + "position": { + Type: schema.TypeInt, + Required: true, + }, "topic_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - - "position": { - Type: schema.TypeInt, - Required: true, - }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["scope"].(string))) - - if _, ok := m["topic_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - } - - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, - + "tls_policy": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validation.StringInSlice(ses.TlsPolicy_Values(), false), + }, "workmail_action": { Type: schema.TypeSet, Optional: true, @@ -366,100 +249,42 @@ func ResourceReceiptRule() *schema.Resource { Required: true, ValidateFunc: verify.ValidARN, }, - + "position": { + Type: schema.TypeInt, + Required: true, + }, "topic_arn": { Type: schema.TypeString, Optional: true, ValidateFunc: verify.ValidARN, }, - - "position": { - Type: schema.TypeInt, - Required: true, - }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["organization_arn"].(string))) - - if _, ok := m["topic_arn"]; ok { - buf.WriteString(fmt.Sprintf("%s-", m["topic_arn"].(string))) - } - - buf.WriteString(fmt.Sprintf("%d-", m["position"].(int))) - - return create.StringHashcode(buf.String()) - }, }, }, } } -func resourceReceiptRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - idParts := strings.Split(d.Id(), ":") - if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { - return nil, fmt.Errorf("unexpected format of ID (%q), expected :", d.Id()) - } - - ruleSetName := idParts[0] - ruleName := idParts[1] - - d.Set("rule_set_name", ruleSetName) - d.Set("name", ruleName) - d.SetId(ruleName) - - return []*schema.ResourceData{d}, nil -} - func resourceReceiptRuleCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).SESConn() - createOpts := &ses.CreateReceiptRuleInput{ + name := d.Get("name").(string) + input := &ses.CreateReceiptRuleInput{ Rule: buildReceiptRule(d), RuleSetName: aws.String(d.Get("rule_set_name").(string)), } if v, ok := d.GetOk("after"); ok { - createOpts.After = aws.String(v.(string)) + input.After = aws.String(v.(string)) } - _, err := conn.CreateReceiptRule(createOpts) - if err != nil { - return fmt.Errorf("Error creating SES rule: %s", err) - } - - d.SetId(d.Get("name").(string)) - - return resourceReceiptRuleRead(d, meta) -} - -func resourceReceiptRuleUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).SESConn() + _, err := conn.CreateReceiptRule(input) - updateOpts := &ses.UpdateReceiptRuleInput{ - Rule: buildReceiptRule(d), - RuleSetName: aws.String(d.Get("rule_set_name").(string)), - } - - _, err := conn.UpdateReceiptRule(updateOpts) if err != nil { - return fmt.Errorf("Error updating SES rule: %s", err) + return fmt.Errorf("creating SES Receipt Rule (%s): %w", name, err) } - if d.HasChange("after") { - changePosOpts := &ses.SetReceiptRulePositionInput{ - After: aws.String(d.Get("after").(string)), - RuleName: aws.String(d.Get("name").(string)), - RuleSetName: aws.String(d.Get("rule_set_name").(string)), - } - - _, err := conn.SetReceiptRulePosition(changePosOpts) - if err != nil { - return fmt.Errorf("Error updating SES rule: %s", err) - } - } + d.SetId(name) return resourceReceiptRuleRead(d, meta) } @@ -468,30 +293,22 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).SESConn() ruleSetName := d.Get("rule_set_name").(string) - describeOpts := &ses.DescribeReceiptRuleInput{ - RuleName: aws.String(d.Id()), - RuleSetName: aws.String(ruleSetName), + rule, err := FindReceiptRuleByTwoPartKey(conn, d.Id(), ruleSetName) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] SES Receipt Rule (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - response, err := conn.DescribeReceiptRule(describeOpts) if err != nil { - if tfawserr.ErrCodeEquals(err, ses.ErrCodeRuleDoesNotExistException) { - log.Printf("[WARN] SES Receipt Rule (%s) not found", d.Id()) - d.SetId("") - return nil - } - if tfawserr.ErrCodeEquals(err, ses.ErrCodeRuleSetDoesNotExistException) { - log.Printf("[WARN] SES Receipt Rule Set (%s) belonging to SES Receipt Rule (%s) not found, removing from state", aws.StringValue(describeOpts.RuleSetName), d.Id()) - d.SetId("") - return nil - } - return fmt.Errorf("reading SES Receipt Rule (%s): %s", d.Id(), err) + return fmt.Errorf("reading SES Receipt Rule (%s): %w", d.Id(), err) } - d.Set("enabled", response.Rule.Enabled) - d.Set("recipients", flex.FlattenStringSet(response.Rule.Recipients)) - d.Set("scan_enabled", response.Rule.ScanEnabled) - d.Set("tls_policy", response.Rule.TlsPolicy) + d.Set("enabled", rule.Enabled) + d.Set("recipients", flex.FlattenStringSet(rule.Recipients)) + d.Set("scan_enabled", rule.ScanEnabled) + d.Set("tls_policy", rule.TlsPolicy) addHeaderActionList := []map[string]interface{}{} bounceActionList := []map[string]interface{}{} @@ -501,7 +318,7 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { stopActionList := []map[string]interface{}{} workmailActionList := []map[string]interface{}{} - for i, element := range response.Rule.Actions { + for i, element := range rule.Actions { if element.AddHeaderAction != nil { addHeaderAction := map[string]interface{}{ "header_name": aws.StringValue(element.AddHeaderAction.HeaderName), @@ -652,22 +469,95 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { return nil } +func resourceReceiptRuleUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).SESConn() + + input := &ses.UpdateReceiptRuleInput{ + Rule: buildReceiptRule(d), + RuleSetName: aws.String(d.Get("rule_set_name").(string)), + } + + _, err := conn.UpdateReceiptRule(input) + + if err != nil { + return fmt.Errorf("updating SES Receipt Rule (%s): %w", d.Id(), err) + } + + if d.HasChange("after") { + input := &ses.SetReceiptRulePositionInput{ + After: aws.String(d.Get("after").(string)), + RuleName: aws.String(d.Get("name").(string)), + RuleSetName: aws.String(d.Get("rule_set_name").(string)), + } + + _, err := conn.SetReceiptRulePosition(input) + + if err != nil { + return fmt.Errorf("setting SES Receipt Rule (%s) position: %w", d.Id(), err) + } + } + + return resourceReceiptRuleRead(d, meta) +} + func resourceReceiptRuleDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).SESConn() - deleteOpts := &ses.DeleteReceiptRuleInput{ + log.Printf("[DEBUG] Deleting SES Receipt Rule: %s", d.Id()) + _, err := conn.DeleteReceiptRule(&ses.DeleteReceiptRuleInput{ RuleName: aws.String(d.Id()), RuleSetName: aws.String(d.Get("rule_set_name").(string)), - } + }) - _, err := conn.DeleteReceiptRule(deleteOpts) if err != nil { - return fmt.Errorf("Error deleting SES receipt rule: %s", err) + return fmt.Errorf("deleting SES Receipt Rule (%s): %w", d.Id(), err) } return nil } +func resourceReceiptRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + idParts := strings.Split(d.Id(), ":") + if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { + return nil, fmt.Errorf("unexpected format of ID (%q), expected :", d.Id()) + } + + ruleSetName := idParts[0] + ruleName := idParts[1] + + d.Set("rule_set_name", ruleSetName) + d.Set("name", ruleName) + d.SetId(ruleName) + + return []*schema.ResourceData{d}, nil +} + +func FindReceiptRuleByTwoPartKey(conn *ses.SES, ruleName, ruleSetName string) (*ses.ReceiptRule, error) { + input := &ses.DescribeReceiptRuleInput{ + RuleName: aws.String(ruleName), + RuleSetName: aws.String(ruleSetName), + } + + output, err := conn.DescribeReceiptRule(input) + + if tfawserr.ErrCodeEquals(err, ses.ErrCodeRuleDoesNotExistException, ses.ErrCodeRuleSetDoesNotExistException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Rule == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Rule, nil +} + func buildReceiptRule(d *schema.ResourceData) *ses.ReceiptRule { receiptRule := &ses.ReceiptRule{ Name: aws.String(d.Get("name").(string)), diff --git a/internal/service/ses/receipt_rule_test.go b/internal/service/ses/receipt_rule_test.go index 9863212e225..e4be52ebd25 100644 --- a/internal/service/ses/receipt_rule_test.go +++ b/internal/service/ses/receipt_rule_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/ses" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -14,6 +13,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfses "github.com/hashicorp/terraform-provider-aws/internal/service/ses" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccSESReceiptRule_basic(t *testing.T) { @@ -366,50 +366,42 @@ func testAccCheckReceiptRuleDestroy(s *terraform.State) error { continue } - params := &ses.DescribeReceiptRuleInput{ - RuleName: aws.String(rs.Primary.Attributes["name"]), - RuleSetName: aws.String(rs.Primary.Attributes["rule_set_name"]), - } + _, err := tfses.FindReceiptRuleByTwoPartKey(conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) - _, err := conn.DescribeReceiptRule(params) - if err == nil { - return fmt.Errorf("Receipt rule %s still exists. Failing!", rs.Primary.ID) + if tfresource.NotFound(err) { + continue } - // Verify the error is what we want - _, ok := err.(awserr.Error) - if !ok { + if err != nil { return err } + + return fmt.Errorf("SES Receipt Rule %s still exists", rs.Primary.ID) } return nil } -func testAccCheckReceiptRuleExists(n string, rule *ses.ReceiptRule) resource.TestCheckFunc { +func testAccCheckReceiptRuleExists(n string, v *ses.ReceiptRule) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("SES Receipt Rule not found: %s", n) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("SES Receipt Rule name not set") + return fmt.Errorf("No SES Receipt Rule ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).SESConn() - params := &ses.DescribeReceiptRuleInput{ - RuleName: aws.String(rs.Primary.Attributes["name"]), - RuleSetName: aws.String(rs.Primary.Attributes["rule_set_name"]), - } + output, err := tfses.FindReceiptRuleByTwoPartKey(conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) - resp, err := conn.DescribeReceiptRule(params) if err != nil { return err } - *rule = *resp.Rule + *v = *output return nil } From 99b8b713772212b063f812ea9c441195b984695c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 11:42:50 -0500 Subject: [PATCH 14/68] r/aws_emr_fleet: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccEMRInstanceFleet_' PKG=emr ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/emr/... -v -count 1 -parallel 3 -run=TestAccEMRInstanceFleet_ -timeout 180m === RUN TestAccEMRInstanceFleet_basic === PAUSE TestAccEMRInstanceFleet_basic === RUN TestAccEMRInstanceFleet_Zero_count === PAUSE TestAccEMRInstanceFleet_Zero_count === RUN TestAccEMRInstanceFleet_ebsBasic === PAUSE TestAccEMRInstanceFleet_ebsBasic === RUN TestAccEMRInstanceFleet_full === PAUSE TestAccEMRInstanceFleet_full === RUN TestAccEMRInstanceFleet_disappears === PAUSE TestAccEMRInstanceFleet_disappears === CONT TestAccEMRInstanceFleet_basic === CONT TestAccEMRInstanceFleet_full === CONT TestAccEMRInstanceFleet_disappears --- PASS: TestAccEMRInstanceFleet_full (548.98s) === CONT TestAccEMRInstanceFleet_ebsBasic --- PASS: TestAccEMRInstanceFleet_disappears (557.13s) === CONT TestAccEMRInstanceFleet_Zero_count --- PASS: TestAccEMRInstanceFleet_basic (569.82s) --- PASS: TestAccEMRInstanceFleet_ebsBasic (494.20s) --- PASS: TestAccEMRInstanceFleet_Zero_count (680.26s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/emr 1242.428s --- internal/service/emr/instance_fleet.go | 170 +++++++++++--------- internal/service/emr/instance_fleet_test.go | 123 +++++--------- 2 files changed, 135 insertions(+), 158 deletions(-) diff --git a/internal/service/emr/instance_fleet.go b/internal/service/emr/instance_fleet.go index ac04f4eecd9..07630df5fb5 100644 --- a/internal/service/emr/instance_fleet.go +++ b/internal/service/emr/instance_fleet.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceInstanceFleet() *schema.Resource { @@ -21,6 +22,7 @@ func ResourceInstanceFleet() *schema.Resource { Read: resourceInstanceFleetRead, Update: resourceInstanceFleetUpdate, Delete: resourceInstanceFleetDelete, + Importer: &schema.ResourceImporter{ State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -34,6 +36,7 @@ func ResourceInstanceFleet() *schema.Resource { return []*schema.ResourceData{d}, nil }, }, + Schema: map[string]*schema.Schema{ "cluster_id": { Type: schema.TypeString, @@ -189,6 +192,14 @@ func ResourceInstanceFleet() *schema.Resource { Optional: true, ForceNew: true, }, + "provisioned_on_demand_capacity": { + Type: schema.TypeInt, + Computed: true, + }, + "provisioned_spot_capacity": { + Type: schema.TypeInt, + Computed: true, + }, "target_on_demand_capacity": { Type: schema.TypeInt, Optional: true, @@ -199,14 +210,6 @@ func ResourceInstanceFleet() *schema.Resource { Optional: true, Default: 0, }, - "provisioned_on_demand_capacity": { - Type: schema.TypeInt, - Computed: true, - }, - "provisioned_spot_capacity": { - Type: schema.TypeInt, - Computed: true, - }, }, } } @@ -214,10 +217,6 @@ func ResourceInstanceFleet() *schema.Resource { func resourceInstanceFleetCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EMRConn() - addInstanceFleetInput := &emr.AddInstanceFleetInput{ - ClusterId: aws.String(d.Get("cluster_id").(string)), - } - taskFleet := map[string]interface{}{ "name": d.Get("name"), "target_on_demand_capacity": d.Get("target_on_demand_capacity"), @@ -225,63 +224,49 @@ func resourceInstanceFleetCreate(d *schema.ResourceData, meta interface{}) error "instance_type_configs": d.Get("instance_type_configs"), "launch_specifications": d.Get("launch_specifications"), } - addInstanceFleetInput.InstanceFleet = readInstanceFleetConfig(taskFleet, emr.InstanceFleetTypeTask) + input := &emr.AddInstanceFleetInput{ + ClusterId: aws.String(d.Get("cluster_id").(string)), + InstanceFleet: readInstanceFleetConfig(taskFleet, emr.InstanceFleetTypeTask), + } + + output, err := conn.AddInstanceFleet(input) - log.Printf("[DEBUG] Creating EMR instance fleet params: %s", addInstanceFleetInput) - resp, err := conn.AddInstanceFleet(addInstanceFleetInput) if err != nil { - return fmt.Errorf("error adding EMR Instance Fleet: %w", err) + return fmt.Errorf("creating EMR Instance Fleet: %w", err) } - log.Printf("[DEBUG] Created EMR instance fleet finished: %#v", resp) - if resp == nil { - return fmt.Errorf("error creating instance fleet: no instance fleet returned") - } - d.SetId(aws.StringValue(resp.InstanceFleetId)) + d.SetId(aws.StringValue(output.InstanceFleetId)) - return nil + return resourceInstanceFleetRead(d, meta) } func resourceInstanceFleetRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EMRConn() - instanceFleets, err := FetchAllInstanceFleets(conn, d.Get("cluster_id").(string)) - if err != nil { - return fmt.Errorf("error listing EMR Instance Fleets for Cluster (%s): %w", d.Get("cluster_id").(string), err) - } - - fleet := FindInstanceFleetByID(instanceFleets, d.Id()) - if fleet == nil { - if d.IsNewResource() { - return fmt.Errorf("error finding EMR Instance Fleet (%s): not found after creation", d.Id()) - } + fleet, err := FindInstanceFleetByTwoPartKey(conn, d.Get("cluster_id").(string), d.Id()) - log.Printf("[DEBUG] EMR Instance Fleet (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EMR Instance Fleet (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - if err := d.Set("instance_type_configs", flatteninstanceTypeConfigs(fleet.InstanceTypeSpecifications)); err != nil { - return fmt.Errorf("error setting instance_type_configs: %w", err) + if err != nil { + return fmt.Errorf("reading EMR Instance Fleet (%s): %w", d.Id(), err) } + if err := d.Set("instance_type_configs", flatteninstanceTypeConfigs(fleet.InstanceTypeSpecifications)); err != nil { + return fmt.Errorf("setting instance_type_configs: %w", err) + } if err := d.Set("launch_specifications", flattenLaunchSpecifications(fleet.LaunchSpecifications)); err != nil { - return fmt.Errorf("error setting launch_specifications: %w", err) + return fmt.Errorf("setting launch_specifications: %w", err) } d.Set("name", fleet.Name) d.Set("provisioned_on_demand_capacity", fleet.ProvisionedOnDemandCapacity) d.Set("provisioned_spot_capacity", fleet.ProvisionedSpotCapacity) d.Set("target_on_demand_capacity", fleet.TargetOnDemandCapacity) d.Set("target_spot_capacity", fleet.TargetSpotCapacity) - return nil -} -func FindInstanceFleetByID(instanceFleets []*emr.InstanceFleet, fleetId string) *emr.InstanceFleet { - for _, fleet := range instanceFleets { - if fleet != nil && aws.StringValue(fleet.Id) == fleetId { - return fleet - } - } return nil } @@ -296,78 +281,105 @@ func resourceInstanceFleetUpdate(d *schema.ResourceData, meta interface{}) error TargetSpotCapacity: aws.Int64(int64(d.Get("target_spot_capacity").(int))), } - modifyInstanceFleetInput := &emr.ModifyInstanceFleetInput{ + input := &emr.ModifyInstanceFleetInput{ ClusterId: aws.String(d.Get("cluster_id").(string)), InstanceFleet: modifyConfig, } - _, err := conn.ModifyInstanceFleet(modifyInstanceFleetInput) + _, err := conn.ModifyInstanceFleet(input) + if err != nil { - return fmt.Errorf("error modifying EMR Instance Fleet (%s): %w", d.Id(), err) + return fmt.Errorf("updating EMR Instance Fleet (%s): %w", d.Id(), err) } stateConf := &resource.StateChangeConf{ Pending: []string{emr.InstanceFleetStateProvisioning, emr.InstanceFleetStateBootstrapping, emr.InstanceFleetStateResizing}, Target: []string{emr.InstanceFleetStateRunning}, - Refresh: instanceFleetStateRefresh(conn, d.Get("cluster_id").(string), d.Id()), + Refresh: statusInstanceFleet(conn, d.Get("cluster_id").(string), d.Id()), Timeout: 75 * time.Minute, Delay: 10 * time.Second, MinTimeout: 30 * time.Second, } _, err = stateConf.WaitForState() + if err != nil { - return fmt.Errorf("error waiting for instance (%s) to terminate: %s", d.Id(), err) + return fmt.Errorf("waiting for EMR Instance Fleet (%s) update: %s", d.Id(), err) } return resourceInstanceFleetRead(d, meta) } -func instanceFleetStateRefresh(conn *emr.EMR, clusterID, ifID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - instanceFleets, err := FetchAllInstanceFleets(conn, clusterID) - if err != nil { - return nil, "Not Found", err - } - - fleet := FindInstanceFleetByID(instanceFleets, ifID) - if fleet == nil { - return nil, "Not Found", err - } - - if fleet.Status == nil || fleet.Status.State == nil { - log.Printf("[WARN] ERM Instance Fleet found, but without state") - return nil, "Undefined", fmt.Errorf("undefined EMR Cluster Instance Fleet state") - } - - return fleet, *fleet.Status.State, nil - } -} - func resourceInstanceFleetDelete(d *schema.ResourceData, meta interface{}) error { - log.Printf("[WARN] AWS EMR Instance Fleet does not support DELETE; resizing cluster to zero before removing from state") conn := meta.(*conns.AWSClient).EMRConn() - clusterId := d.Get("cluster_id").(string) - - modifyInstanceFleetInput := &emr.ModifyInstanceFleetInput{ - ClusterId: aws.String(clusterId), + // AWS EMR Instance Fleet does not support DELETE; resizing cluster to zero before removing from state. + log.Printf("[DEBUG] Deleting EMR Instance Fleet: %s", d.Id()) + _, err := conn.ModifyInstanceFleet(&emr.ModifyInstanceFleetInput{ + ClusterId: aws.String(d.Get("cluster_id").(string)), InstanceFleet: &emr.InstanceFleetModifyConfig{ InstanceFleetId: aws.String(d.Id()), TargetOnDemandCapacity: aws.Int64(0), TargetSpotCapacity: aws.Int64(0), }, - } - - _, err := conn.ModifyInstanceFleet(modifyInstanceFleetInput) + }) if tfawserr.ErrMessageContains(err, emr.ErrCodeInvalidRequestException, "instance fleet may only be modified when the cluster is running or waiting") { return nil } if err != nil { - return fmt.Errorf("error deleting/modifying EMR Instance Fleet (%s): %w", d.Id(), err) + return fmt.Errorf("deleting EMR Instance Fleet (%s): %w", d.Id(), err) } return nil } + +func FindInstanceFleetByTwoPartKey(conn *emr.EMR, clusterID, fleetID string) (*emr.InstanceFleet, error) { + input := &emr.ListInstanceFleetsInput{ + ClusterId: aws.String(clusterID), + } + var fleets []*emr.InstanceFleet + + err := conn.ListInstanceFleetsPages(input, func(page *emr.ListInstanceFleetsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.InstanceFleets { + if v != nil && v.Status != nil { + fleets = append(fleets, v) + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + for _, fleet := range fleets { + if aws.StringValue(fleet.Id) == fleetID { + return fleet, nil + } + } + + return nil, &resource.NotFoundError{} +} + +func statusInstanceFleet(conn *emr.EMR, clusterID, fleetID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindInstanceFleetByTwoPartKey(conn, clusterID, fleetID) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status.State), nil + } +} diff --git a/internal/service/emr/instance_fleet_test.go b/internal/service/emr/instance_fleet_test.go index d179deaa1fb..0902becd620 100644 --- a/internal/service/emr/instance_fleet_test.go +++ b/internal/service/emr/instance_fleet_test.go @@ -2,11 +2,8 @@ package emr_test import ( "fmt" - "log" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/emr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -20,11 +17,12 @@ func TestAccEMRInstanceFleet_basic(t *testing.T) { var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceFleetDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_basic(rName), @@ -48,11 +46,12 @@ func TestAccEMRInstanceFleet_Zero_count(t *testing.T) { var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceFleetDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_basic(rName), @@ -84,11 +83,12 @@ func TestAccEMRInstanceFleet_ebsBasic(t *testing.T) { var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceFleetDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_ebsBasic(rName), @@ -112,11 +112,12 @@ func TestAccEMRInstanceFleet_full(t *testing.T) { var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceFleetDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_full(rName), @@ -146,7 +147,7 @@ func TestAccEMRInstanceFleet_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceFleetDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_basic(rName), @@ -162,59 +163,27 @@ func TestAccEMRInstanceFleet_disappears(t *testing.T) { }) } -func testAccCheckInstanceFleetDestroy(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_emr_cluster" { - continue - } - - params := &emr.DescribeClusterInput{ - ClusterId: aws.String(rs.Primary.ID), - } - - describe, err := conn.DescribeCluster(params) - - if err == nil { - if describe.Cluster != nil && - *describe.Cluster.Status.State == "WAITING" { - return fmt.Errorf("EMR Cluster still exists") - } - } - - providerErr, ok := err.(awserr.Error) - if !ok { - return err - } - - log.Printf("[ERROR] %v", providerErr) - } - - return nil -} - func testAccCheckInstanceFleetExists(n string, v *emr.InstanceFleet) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } + if rs.Primary.ID == "" { - return fmt.Errorf("No task fleet id set") + return fmt.Errorf("No EMR Instance Fleet ID is set") } - meta := acctest.Provider.Meta() - conn := meta.(*conns.AWSClient).EMRConn() - instanceFleets, err := tfemr.FetchAllInstanceFleets(conn, rs.Primary.Attributes["cluster_id"]) + + conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn() + + output, err := tfemr.FindInstanceFleetByTwoPartKey(conn, rs.Primary.Attributes["cluster_id"], rs.Primary.ID) + if err != nil { - return fmt.Errorf("EMR error: %v", err) + return err } - fleet := tfemr.FindInstanceFleetByID(instanceFleets, rs.Primary.ID) - if fleet == nil { - return fmt.Errorf("No match found for (%s)", n) - } - v = fleet + *v = *output + return nil } } @@ -230,24 +199,14 @@ func testAccInstanceFleetResourceImportStateIdFunc(resourceName string) resource } } -const testAccInstanceFleetBase = ` -data "aws_availability_zones" "available" { - # Many instance types are not available in this availability zone - exclude_zone_ids = ["usw2-az4"] - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - +func testAccInstanceFleetConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags = { - Name = "tf-acc-test-emr-cluster" + Name = %[1]q } } @@ -255,12 +214,13 @@ resource "aws_internet_gateway" "test" { vpc_id = aws_vpc.test.id tags = { - Name = "tf-acc-test-emr-cluster" + Name = %[1]q } } resource "aws_security_group" "test" { vpc_id = aws_vpc.test.id + name = %[1]q ingress { from_port = 0 @@ -277,7 +237,7 @@ resource "aws_security_group" "test" { } tags = { - Name = "tf-acc-test-emr-cluster" + Name = %[1]q } # EMR will modify ingress rules @@ -293,7 +253,7 @@ resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id tags = { - Name = "tf-acc-test-emr-cluster" + Name = %[1]q } } @@ -304,6 +264,10 @@ resource "aws_route_table" "test" { cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.test.id } + + tags = { + Name = %[1]q + } } resource "aws_route_table_association" "test" { @@ -449,10 +413,11 @@ resource "aws_emr_cluster" "test" { instance_profile = aws_iam_instance_profile.emr_instance_profile.arn } } -` +`, rName)) +} -func testAccInstanceFleetConfig_basic(r string) string { - return fmt.Sprintf(testAccInstanceFleetBase+` +func testAccInstanceFleetConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccInstanceFleetConfig_base(rName), fmt.Sprintf(` resource "aws_emr_instance_fleet" "task" { cluster_id = aws_emr_cluster.test.id @@ -471,11 +436,11 @@ resource "aws_emr_instance_fleet" "task" { target_on_demand_capacity = 1 target_spot_capacity = 0 } -`, r) +`, rName)) } -func testAccInstanceFleetConfig_zeroCount(r string) string { - return fmt.Sprintf(testAccInstanceFleetBase+` +func testAccInstanceFleetConfig_zeroCount(rName string) string { + return acctest.ConfigCompose(testAccInstanceFleetConfig_base(rName), fmt.Sprintf(` resource "aws_emr_instance_fleet" "task" { cluster_id = aws_emr_cluster.test.id @@ -494,11 +459,11 @@ resource "aws_emr_instance_fleet" "task" { target_on_demand_capacity = 0 target_spot_capacity = 0 } -`, r) +`, rName)) } -func testAccInstanceFleetConfig_ebsBasic(r string) string { - return fmt.Sprintf(testAccInstanceFleetBase+` +func testAccInstanceFleetConfig_ebsBasic(rName string) string { + return acctest.ConfigCompose(testAccInstanceFleetConfig_base(rName), fmt.Sprintf(` resource "aws_emr_instance_fleet" "task" { cluster_id = aws_emr_cluster.test.id @@ -526,11 +491,11 @@ resource "aws_emr_instance_fleet" "task" { target_on_demand_capacity = 0 target_spot_capacity = 1 } -`, r) +`, rName)) } -func testAccInstanceFleetConfig_full(r string) string { - return fmt.Sprintf(testAccInstanceFleetBase+` +func testAccInstanceFleetConfig_full(rName string) string { + return acctest.ConfigCompose(testAccInstanceFleetConfig_base(rName), fmt.Sprintf(` resource "aws_emr_instance_fleet" "task" { cluster_id = aws_emr_cluster.test.id @@ -578,5 +543,5 @@ resource "aws_emr_instance_fleet" "task" { target_on_demand_capacity = 2 target_spot_capacity = 2 } -`, r) +`, rName)) } From 74092f513d9f0b633afa0cbc1fad0004bd376d4d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 11:48:19 -0500 Subject: [PATCH 15/68] Remove 'testAccCheckInstanceGroupDestroy'. --- internal/service/emr/instance_group_test.go | 48 ++++----------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/internal/service/emr/instance_group_test.go b/internal/service/emr/instance_group_test.go index 45a6adcd4f3..e06b5768273 100644 --- a/internal/service/emr/instance_group_test.go +++ b/internal/service/emr/instance_group_test.go @@ -2,11 +2,9 @@ package emr_test import ( "fmt" - "log" "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/emr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -25,7 +23,7 @@ func TestAccEMRInstanceGroup_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -56,7 +54,7 @@ func TestAccEMRInstanceGroup_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -83,7 +81,7 @@ func TestAccEMRInstanceGroup_Disappears_emrCluster(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -107,7 +105,7 @@ func TestAccEMRInstanceGroup_bidPrice(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -159,7 +157,7 @@ func TestAccEMRInstanceGroup_sJSON(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_configurationsJSON(rName, "partitionName1"), @@ -202,7 +200,7 @@ func TestAccEMRInstanceGroup_autoScalingPolicy(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_autoScalingPolicy(rName, 1, 3), @@ -247,7 +245,7 @@ func TestAccEMRInstanceGroup_instanceCount(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -277,7 +275,7 @@ func TestAccEMRInstanceGroup_EBS_ebsOptimized(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy, + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_ebs(rName, true), @@ -306,36 +304,6 @@ func TestAccEMRInstanceGroup_EBS_ebsOptimized(t *testing.T) { }) } -func testAccCheckInstanceGroupDestroy(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_emr_cluster" { - continue - } - - params := &emr.DescribeClusterInput{ - ClusterId: aws.String(rs.Primary.ID), - } - - describe, err := conn.DescribeCluster(params) - - if err == nil { - if describe.Cluster != nil && - *describe.Cluster.Status.State == "WAITING" { - return fmt.Errorf("EMR Cluster still exists") - } - } - - if providerErr, ok := err.(awserr.Error); !ok { - log.Printf("[ERROR] %v", providerErr) - return err - } - } - - return nil -} - func testAccCheckInstanceGroupExists(name string, ig *emr.InstanceGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] From 9722d21d820100ce5a413ac7474a3d1b29f86885 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 14:56:58 -0500 Subject: [PATCH 16/68] r/aws_iam_server_certificate: Modernize. Acceptance test output: % make testacc TESTARGS='-run=TestAccIAMServerCertificate_' PKG=iam ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iam/... -v -count 1 -parallel 3 -run=TestAccIAMServerCertificate_ -timeout 180m === RUN TestAccIAMServerCertificate_basic === PAUSE TestAccIAMServerCertificate_basic === RUN TestAccIAMServerCertificate_nameGenerated === PAUSE TestAccIAMServerCertificate_nameGenerated === RUN TestAccIAMServerCertificate_namePrefix === PAUSE TestAccIAMServerCertificate_namePrefix === RUN TestAccIAMServerCertificate_disappears === PAUSE TestAccIAMServerCertificate_disappears === RUN TestAccIAMServerCertificate_tags === PAUSE TestAccIAMServerCertificate_tags === RUN TestAccIAMServerCertificate_file === PAUSE TestAccIAMServerCertificate_file === RUN TestAccIAMServerCertificate_path === PAUSE TestAccIAMServerCertificate_path === CONT TestAccIAMServerCertificate_basic === CONT TestAccIAMServerCertificate_tags === CONT TestAccIAMServerCertificate_namePrefix --- PASS: TestAccIAMServerCertificate_namePrefix (14.84s) === CONT TestAccIAMServerCertificate_disappears --- PASS: TestAccIAMServerCertificate_basic (17.51s) === CONT TestAccIAMServerCertificate_nameGenerated --- PASS: TestAccIAMServerCertificate_disappears (12.16s) === CONT TestAccIAMServerCertificate_path --- PASS: TestAccIAMServerCertificate_nameGenerated (14.11s) === CONT TestAccIAMServerCertificate_file --- PASS: TestAccIAMServerCertificate_tags (39.81s) --- PASS: TestAccIAMServerCertificate_path (16.35s) --- PASS: TestAccIAMServerCertificate_file (23.43s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 60.962s --- internal/service/iam/server_certificate.go | 184 ++++++++---------- .../service/iam/server_certificate_test.go | 170 +++++++++------- 2 files changed, 176 insertions(+), 178 deletions(-) diff --git a/internal/service/iam/server_certificate.go b/internal/service/iam/server_certificate.go index 8f4a2a3b413..dc2a894d476 100644 --- a/internal/service/iam/server_certificate.go +++ b/internal/service/iam/server_certificate.go @@ -1,24 +1,21 @@ package iam -import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports +import ( "crypto/sha1" "encoding/hex" - "errors" "fmt" "log" - "regexp" "strings" "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -30,11 +27,16 @@ func ResourceServerCertificate() *schema.Resource { Read: resourceServerCertificateRead, Update: resourceServerCertificateUpdate, Delete: resourceServerCertificateDelete, + Importer: &schema.ResourceImporter{ State: resourceServerCertificateImport, }, Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "certificate_body": { Type: schema.TypeString, Required: true, @@ -42,7 +44,6 @@ func ResourceServerCertificate() *schema.Resource { DiffSuppressFunc: suppressNormalizeCertRemoval, StateFunc: StateTrimSpace, }, - "certificate_chain": { Type: schema.TypeString, Optional: true, @@ -50,23 +51,10 @@ func ResourceServerCertificate() *schema.Resource { DiffSuppressFunc: suppressNormalizeCertRemoval, StateFunc: StateTrimSpace, }, - - "path": { + "expiration": { Type: schema.TypeString, - Optional: true, - Default: "/", - ForceNew: true, - }, - - "private_key": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - Sensitive: true, - DiffSuppressFunc: suppressNormalizeCertRemoval, - StateFunc: StateTrimSpace, + Computed: true, }, - "name": { Type: schema.TypeString, Optional: true, @@ -75,29 +63,34 @@ func ResourceServerCertificate() *schema.Resource { ConflictsWith: []string{"name_prefix"}, ValidateFunc: validation.StringLenBetween(0, 128), }, - "name_prefix": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ConflictsWith: []string{"name"}, ValidateFunc: validation.StringLenBetween(0, 128-resource.UniqueIDSuffixLength), }, - - "arn": { + "path": { Type: schema.TypeString, - Computed: true, + Optional: true, + Default: "/", + ForceNew: true, }, - "expiration": { - Type: schema.TypeString, - Computed: true, + "private_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + DiffSuppressFunc: suppressNormalizeCertRemoval, + StateFunc: StateTrimSpace, }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), "upload_date": { Type: schema.TypeString, Computed: true, }, - "tags": tftags.TagsSchema(), - "tags_all": tftags.TagsSchemaComputed(), }, CustomizeDiff: verify.SetTagsDiff, @@ -109,54 +102,45 @@ func resourceServerCertificateCreate(d *schema.ResourceData, meta interface{}) e defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - var sslCertName string - if v, ok := d.GetOk("name"); ok { - sslCertName = v.(string) - } else if v, ok := d.GetOk("name_prefix"); ok { - sslCertName = resource.PrefixedUniqueId(v.(string)) - } else { - sslCertName = resource.UniqueId() - } - - createOpts := &iam.UploadServerCertificateInput{ + sslCertName := create.Name(d.Get("name").(string), d.Get("name_prefix").(string)) + input := &iam.UploadServerCertificateInput{ CertificateBody: aws.String(d.Get("certificate_body").(string)), PrivateKey: aws.String(d.Get("private_key").(string)), ServerCertificateName: aws.String(sslCertName), } - if len(tags) > 0 { - createOpts.Tags = Tags(tags.IgnoreAWS()) - } - if v, ok := d.GetOk("certificate_chain"); ok { - createOpts.CertificateChain = aws.String(v.(string)) + input.CertificateChain = aws.String(v.(string)) } if v, ok := d.GetOk("path"); ok { - createOpts.Path = aws.String(v.(string)) + input.Path = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating IAM Server Certificate with opts: %s", createOpts) - resp, err := conn.UploadServerCertificate(createOpts) + if len(tags) > 0 { + input.Tags = Tags(tags.IgnoreAWS()) + } + + output, err := conn.UploadServerCertificate(input) // Some partitions (i.e., ISO) may not support tag-on-create - if createOpts.Tags != nil && verify.ErrorISOUnsupported(conn.PartitionID, err) { + if input.Tags != nil && verify.ErrorISOUnsupported(conn.PartitionID, err) { log.Printf("[WARN] failed creating IAM Server Certificate (%s) with tags: %s. Trying create without tags.", sslCertName, err) - createOpts.Tags = nil + input.Tags = nil - resp, err = conn.UploadServerCertificate(createOpts) + output, err = conn.UploadServerCertificate(input) } if err != nil { - return fmt.Errorf("error uploading server certificate: %w", err) + return fmt.Errorf("creating IAM Server Certificate (%s): %w", sslCertName, err) } - d.SetId(aws.StringValue(resp.ServerCertificateMetadata.ServerCertificateId)) - d.Set("name", sslCertName) + d.SetId(aws.StringValue(output.ServerCertificateMetadata.ServerCertificateId)) + d.Set("name", sslCertName) // Required for resource Read. // Some partitions (i.e., ISO) may not support tag-on-create, attempt tag after create - if createOpts.Tags == nil && len(tags) > 0 { - err := serverCertificateUpdateTags(conn, d.Get("name").(string), nil, tags) + if input.Tags == nil && len(tags) > 0 { + err := serverCertificateUpdateTags(conn, sslCertName, nil, tags) // If default tags only, log and continue. Otherwise, error. if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && verify.ErrorISOUnsupported(conn.PartitionID, err) { @@ -177,34 +161,31 @@ func resourceServerCertificateRead(d *schema.ResourceData, meta interface{}) err defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - resp, err := conn.GetServerCertificate(&iam.GetServerCertificateInput{ - ServerCertificateName: aws.String(d.Get("name").(string)), - }) + cert, err := FindServerCertificateByName(conn, d.Get("name").(string)) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] IAM Server Certificate (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error reading IAM Server Certificate (%s): %w", d.Id(), err) + return fmt.Errorf("reading IAM Server Certificate (%s): %w", d.Id(), err) } - cert := resp.ServerCertificate metadata := cert.ServerCertificateMetadata d.SetId(aws.StringValue(metadata.ServerCertificateId)) - + d.Set("arn", metadata.Arn) d.Set("certificate_body", cert.CertificateBody) d.Set("certificate_chain", cert.CertificateChain) - d.Set("path", metadata.Path) - d.Set("arn", metadata.Arn) if metadata.Expiration != nil { d.Set("expiration", aws.TimeValue(metadata.Expiration).Format(time.RFC3339)) } else { d.Set("expiration", nil) } - + d.Set("name", metadata.ServerCertificateName) + d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(metadata.ServerCertificateName))) + d.Set("path", metadata.Path) if metadata.UploadDate != nil { d.Set("upload_date", aws.TimeValue(metadata.UploadDate).Format(time.RFC3339)) } else { @@ -215,11 +196,11 @@ func resourceServerCertificateRead(d *schema.ResourceData, meta interface{}) err //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %w", err) + return fmt.Errorf("setting tags: %w", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return fmt.Errorf("error setting tags_all: %w", err) + return fmt.Errorf("setting tags_all: %w", err) } return nil @@ -240,7 +221,7 @@ func resourceServerCertificateUpdate(d *schema.ResourceData, meta interface{}) e } if err != nil { - return fmt.Errorf("failed updating tags for IAM Server Certificate (%s): %w", d.Id(), err) + return fmt.Errorf("updating tags for IAM Server Certificate (%s): %w", d.Id(), err) } } @@ -249,32 +230,16 @@ func resourceServerCertificateUpdate(d *schema.ResourceData, meta interface{}) e func resourceServerCertificateDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).IAMConn() - log.Printf("[INFO] Deleting IAM Server Certificate: %s", d.Id()) - input := &iam.DeleteServerCertificateInput{ - ServerCertificateName: aws.String(d.Get("name").(string)), - } - err := resource.Retry(15*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteServerCertificate(input) - if err != nil { - if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil - } - var awsErr awserr.Error - if errors.As(err, &awsErr) { - if awsErr.Code() == iam.ErrCodeDeleteConflictException && strings.Contains(awsErr.Message(), "currently in use by arn") { - currentlyInUseBy(awsErr.Message(), meta.(*conns.AWSClient).ELBConn()) - log.Printf("[WARN] Conflict deleting server certificate: %s, retrying", awsErr.Message()) - return resource.RetryableError(err) - } - } - return resource.NonRetryableError(err) - } - return nil - }) + log.Printf("[DEBUG] Deleting IAM Server Certificate: %s", d.Id()) + _, err := tfresource.RetryWhenAWSErrMessageContains(15*time.Minute, func() (interface{}, error) { + return conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{ + ServerCertificateName: aws.String(d.Get("name").(string)), + }) + }, iam.ErrCodeDeleteConflictException, "currently in use by arn") - if tfresource.TimedOut(err) { - _, err = conn.DeleteServerCertificate(input) + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil } if err != nil { @@ -291,20 +256,29 @@ func resourceServerCertificateImport( return []*schema.ResourceData{d}, nil } -func currentlyInUseBy(awsErr string, conn *elb.ELB) { - r := regexp.MustCompile(`currently in use by ([a-z0-9:-]+)\/([a-z0-9-]+)\.`) - matches := r.FindStringSubmatch(awsErr) - if len(matches) > 0 { - lbName := matches[2] - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(lbName)}, - } - if _, err := conn.DescribeLoadBalancers(describeElbOpts); err != nil { - if tfawserr.ErrCodeEquals(err, "LoadBalancerNotFound") { - log.Printf("[WARN] Load Balancer (%s) causing delete conflict not found", lbName) - } +func FindServerCertificateByName(conn *iam.IAM, name string) (*iam.ServerCertificate, error) { + input := &iam.GetServerCertificateInput{ + ServerCertificateName: aws.String(name), + } + + output, err := conn.GetServerCertificate(input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, } } + + if err != nil { + return nil, err + } + + if output == nil || output.ServerCertificate == nil || output.ServerCertificate.ServerCertificateMetadata == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.ServerCertificate, nil } func normalizeCert(cert interface{}) string { diff --git a/internal/service/iam/server_certificate_test.go b/internal/service/iam/server_certificate_test.go index 8355850bc78..687e5726831 100644 --- a/internal/service/iam/server_certificate_test.go +++ b/internal/service/iam/server_certificate_test.go @@ -5,7 +5,6 @@ import ( "strings" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -13,14 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIAMServerCertificate_basic(t *testing.T) { var cert iam.ServerCertificate - resourceName := "aws_iam_server_certificate.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") @@ -39,6 +37,7 @@ func TestAccIAMServerCertificate_basic(t *testing.T) { acctest.CheckResourceAttrRFC3339(resourceName, "upload_date"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), resource.TestCheckResourceAttr(resourceName, "path", "/"), resource.TestCheckResourceAttr(resourceName, "certificate_body", strings.TrimSpace(certificate)), ), @@ -54,12 +53,9 @@ func TestAccIAMServerCertificate_basic(t *testing.T) { }) } -func TestAccIAMServerCertificate_tags(t *testing.T) { +func TestAccIAMServerCertificate_nameGenerated(t *testing.T) { var cert iam.ServerCertificate - resourceName := "aws_iam_server_certificate.test" - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") @@ -70,46 +66,45 @@ func TestAccIAMServerCertificate_tags(t *testing.T) { CheckDestroy: testAccCheckServerCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccServerCertificateConfig_tags1(rName, key, certificate, "key1", "value1"), - Check: resource.ComposeTestCheckFunc( - testAccCheckCertExists(resourceName, &cert), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - ImportStateId: rName, - ImportStateVerifyIgnore: []string{"private_key"}, - }, - { - Config: testAccServerCertificateConfig_tags2(rName, key, certificate, "key1", "value1updated", "key2", "value2"), + Config: testAccServerCertificateConfig_nameGenerated(key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + acctest.CheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", resource.UniqueIdPrefix), ), }, + }, + }) +} + +func TestAccIAMServerCertificate_namePrefix(t *testing.T) { + var cert iam.ServerCertificate + resourceName := "aws_iam_server_certificate.test" + key := acctest.TLSRSAPrivateKeyPEM(t, 2048) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckServerCertificateDestroy, + Steps: []resource.TestStep{ { - Config: testAccServerCertificateConfig_tags1(rName, key, certificate, "key2", "value2"), + Config: testAccServerCertificateConfig_namePrefix("tf-acc-test-prefix-", key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), - resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", "tf-acc-test-prefix-"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "tf-acc-test-prefix-"), ), }, }, }) } -func TestAccIAMServerCertificate_Name_prefix(t *testing.T) { +func TestAccIAMServerCertificate_disappears(t *testing.T) { var cert iam.ServerCertificate - resourceName := "aws_iam_server_certificate.test" - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") @@ -120,19 +115,21 @@ func TestAccIAMServerCertificate_Name_prefix(t *testing.T) { CheckDestroy: testAccCheckServerCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccServerCertificateConfig_random(key, certificate), + Config: testAccServerCertificateConfig_basic(rName, key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), + acctest.CheckResourceDisappears(acctest.Provider, tfiam.ResourceServerCertificate(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) } -func TestAccIAMServerCertificate_disappears(t *testing.T) { +func TestAccIAMServerCertificate_tags(t *testing.T) { var cert iam.ServerCertificate resourceName := "aws_iam_server_certificate.test" - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") @@ -143,12 +140,36 @@ func TestAccIAMServerCertificate_disappears(t *testing.T) { CheckDestroy: testAccCheckServerCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccServerCertificateConfig_random(key, certificate), + Config: testAccServerCertificateConfig_tags1(rName, key, certificate, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), - acctest.CheckResourceDisappears(acctest.Provider, tfiam.ResourceServerCertificate(), resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateId: rName, + ImportStateVerifyIgnore: []string{"private_key"}, + }, + { + Config: testAccServerCertificateConfig_tags2(rName, key, certificate, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCertExists(resourceName, &cert), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccServerCertificateConfig_tags1(rName, key, certificate, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckCertExists(resourceName, &cert), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), - ExpectNonEmptyPlan: true, }, }, }) @@ -156,12 +177,10 @@ func TestAccIAMServerCertificate_disappears(t *testing.T) { func TestAccIAMServerCertificate_file(t *testing.T) { var cert iam.ServerCertificate - - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) unixFile := "test-fixtures/iam-ssl-unix-line-endings.pem" winFile := "test-fixtures/iam-ssl-windows-line-endings.pem.winfile" resourceName := "aws_iam_server_certificate.test" - resourceId := fmt.Sprintf("terraform-test-cert-%d", rInt) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -170,7 +189,7 @@ func TestAccIAMServerCertificate_file(t *testing.T) { CheckDestroy: testAccCheckServerCertificateDestroy, Steps: []resource.TestStep{ { - Config: testAccServerCertificateConfig_file(rInt, unixFile), + Config: testAccServerCertificateConfig_file(rName, unixFile), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), ), @@ -179,11 +198,11 @@ func TestAccIAMServerCertificate_file(t *testing.T) { ResourceName: resourceName, ImportState: true, ImportStateVerify: true, - ImportStateId: resourceId, + ImportStateId: rName, ImportStateVerifyIgnore: []string{"private_key"}, }, { - Config: testAccServerCertificateConfig_file(rInt, winFile), + Config: testAccServerCertificateConfig_file(rName, winFile), Check: resource.ComposeTestCheckFunc( testAccCheckCertExists(resourceName, &cert), ), @@ -194,10 +213,8 @@ func TestAccIAMServerCertificate_file(t *testing.T) { func TestAccIAMServerCertificate_path(t *testing.T) { var cert iam.ServerCertificate - resourceName := "aws_iam_server_certificate.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") @@ -225,7 +242,7 @@ func TestAccIAMServerCertificate_path(t *testing.T) { }) } -func testAccCheckCertExists(n string, cert *iam.ServerCertificate) resource.TestCheckFunc { +func testAccCheckCertExists(n string, v *iam.ServerCertificate) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -233,19 +250,18 @@ func testAccCheckCertExists(n string, cert *iam.ServerCertificate) resource.Test } if rs.Primary.ID == "" { - return fmt.Errorf("No Server Cert ID is set") + return fmt.Errorf("No IAM Server Certificate ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn() - describeOpts := &iam.GetServerCertificateInput{ - ServerCertificateName: aws.String(rs.Primary.Attributes["name"]), - } - resp, err := conn.GetServerCertificate(describeOpts) + + output, err := tfiam.FindServerCertificateByName(conn, rs.Primary.Attributes["name"]) + if err != nil { return err } - *cert = *resp.ServerCertificate + *v = *output return nil } @@ -259,18 +275,17 @@ func testAccCheckServerCertificateDestroy(s *terraform.State) error { continue } - // Try to find the Cert - opts := &iam.GetServerCertificateInput{ - ServerCertificateName: aws.String(rs.Primary.Attributes["name"]), + _, err := tfiam.FindServerCertificateByName(conn, rs.Primary.Attributes["name"]) + + if tfresource.NotFound(err) { + continue } - resp, err := conn.GetServerCertificate(opts) - if err == nil { - if resp.ServerCertificate != nil { - return fmt.Errorf("Error: Server Cert still exists") - } - return nil + if err != nil { + return err } + + return fmt.Errorf("IAM Server Certificate %s still exists", rs.Primary.ID) } return nil @@ -279,28 +294,37 @@ func testAccCheckServerCertificateDestroy(s *terraform.State) error { func testAccServerCertificateConfig_basic(rName, key, certificate string) string { return fmt.Sprintf(` resource "aws_iam_server_certificate" "test" { - name = "%[1]s" + name = %[1]q certificate_body = "%[2]s" private_key = "%[3]s" } `, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) } -func testAccServerCertificateConfig_random(key, certificate string) string { +func testAccServerCertificateConfig_nameGenerated(key, certificate string) string { return fmt.Sprintf(` resource "aws_iam_server_certificate" "test" { - name_prefix = "tf-acc-test" certificate_body = "%[1]s" private_key = "%[2]s" } `, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) } +func testAccServerCertificateConfig_namePrefix(namePrefix, key, certificate string) string { + return fmt.Sprintf(` +resource "aws_iam_server_certificate" "test" { + name_prefix = %[1]q + certificate_body = "%[2]s" + private_key = "%[3]s" +} +`, namePrefix, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) +} + func testAccServerCertificateConfig_path(rName, path, key, certificate string) string { return fmt.Sprintf(` resource "aws_iam_server_certificate" "test" { - name = "%[1]s" - path = "%[2]s" + name = %[1]q + path = %[2]q certificate_body = "%[3]s" private_key = "%[4]s" } @@ -308,11 +332,11 @@ resource "aws_iam_server_certificate" "test" { } // iam-ssl-unix-line-endings -func testAccServerCertificateConfig_file(rInt int, fName string) string { +func testAccServerCertificateConfig_file(rName, fName string) string { return fmt.Sprintf(` resource "aws_iam_server_certificate" "test" { - name = "terraform-test-cert-%d" - certificate_body = file("%s") + name = %[1]q + certificate_body = file(%[2]q) private_key = < Date: Fri, 20 Jan 2023 15:01:27 -0500 Subject: [PATCH 17/68] Simplify 'aws-go-sdk-error-code-helper'. --- .ci/.semgrep.yml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index c88470e1855..faafc0ff5c1 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -840,24 +840,8 @@ rules: - internal/ patterns: - pattern-either: - - pattern: if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK && ... { $BODY } - - pattern: | - if $AWSERR, $OK := $ORIGINALERR.(awserr.Error); $OK { - ... - } - - pattern: | - $AWSERR, $OK := $ORIGINALERR.(awserr.Error) - if !$OK { - ... - } - if ... { - ... - } - - pattern: | - $AWSERR, $OK := $ORIGINALERR.(awserr.Error) - if $OK && ... { - ... - } + - pattern: $AWSERR, $OK := $ORIGINALERR.(awserr.Error) + - pattern: var $AWSERR awserr.Error severity: WARNING - id: fmt-Errorf-awserr-Error-Code From b1aff115bd40019d5d7f7ae21a5adbe7658b1dda Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 17:03:36 -0500 Subject: [PATCH 18/68] r/aws_ses_receipt_rule: Add 'context.Context'. --- internal/service/ses/receipt_rule.go | 69 ++++++----- internal/service/ses/receipt_rule_test.go | 138 +++++++++++----------- 2 files changed, 108 insertions(+), 99 deletions(-) diff --git a/internal/service/ses/receipt_rule.go b/internal/service/ses/receipt_rule.go index c8a729fc115..ff3c44d8c3c 100644 --- a/internal/service/ses/receipt_rule.go +++ b/internal/service/ses/receipt_rule.go @@ -1,6 +1,7 @@ package ses import ( + "context" "fmt" "log" "regexp" @@ -11,10 +12,12 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/service/ses" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -22,13 +25,13 @@ import ( func ResourceReceiptRule() *schema.Resource { return &schema.Resource{ - Create: resourceReceiptRuleCreate, - Update: resourceReceiptRuleUpdate, - Read: resourceReceiptRuleRead, - Delete: resourceReceiptRuleDelete, + CreateWithoutTimeout: resourceReceiptRuleCreate, + UpdateWithoutTimeout: resourceReceiptRuleUpdate, + ReadWithoutTimeout: resourceReceiptRuleRead, + DeleteWithoutTimeout: resourceReceiptRuleDelete, Importer: &schema.ResourceImporter{ - State: resourceReceiptRuleImport, + StateContext: resourceReceiptRuleImport, }, Schema: map[string]*schema.Schema{ @@ -265,7 +268,8 @@ func ResourceReceiptRule() *schema.Resource { } } -func resourceReceiptRuleCreate(d *schema.ResourceData, meta interface{}) error { +func resourceReceiptRuleCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SESConn() name := d.Get("name").(string) @@ -278,22 +282,23 @@ func resourceReceiptRuleCreate(d *schema.ResourceData, meta interface{}) error { input.After = aws.String(v.(string)) } - _, err := conn.CreateReceiptRule(input) + _, err := conn.CreateReceiptRuleWithContext(ctx, input) if err != nil { - return fmt.Errorf("creating SES Receipt Rule (%s): %w", name, err) + return sdkdiag.AppendErrorf(diags, "creating SES Receipt Rule (%s): %s", name, err) } d.SetId(name) - return resourceReceiptRuleRead(d, meta) + return resourceReceiptRuleRead(ctx, d, meta) } -func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { +func resourceReceiptRuleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SESConn() ruleSetName := d.Get("rule_set_name").(string) - rule, err := FindReceiptRuleByTwoPartKey(conn, d.Id(), ruleSetName) + rule, err := FindReceiptRuleByTwoPartKey(ctx, conn, d.Id(), ruleSetName) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] SES Receipt Rule (%s) not found, removing from state", d.Id()) @@ -302,7 +307,7 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("reading SES Receipt Rule (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading SES Receipt Rule (%s): %s", d.Id(), err) } d.Set("enabled", rule.Enabled) @@ -424,37 +429,37 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { err = d.Set("add_header_action", addHeaderActionList) if err != nil { - return fmt.Errorf("setting add_header_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting add_header_action: %s", err) } err = d.Set("bounce_action", bounceActionList) if err != nil { - return fmt.Errorf("setting bounce_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting bounce_action: %s", err) } err = d.Set("lambda_action", lambdaActionList) if err != nil { - return fmt.Errorf("setting lambda_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting lambda_action: %s", err) } err = d.Set("s3_action", s3ActionList) if err != nil { - return fmt.Errorf("setting s3_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting s3_action: %s", err) } err = d.Set("sns_action", snsActionList) if err != nil { - return fmt.Errorf("setting sns_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting sns_action: %s", err) } err = d.Set("stop_action", stopActionList) if err != nil { - return fmt.Errorf("setting stop_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting stop_action: %s", err) } err = d.Set("workmail_action", workmailActionList) if err != nil { - return fmt.Errorf("setting workmail_action: %w", err) + return sdkdiag.AppendErrorf(diags, "setting workmail_action: %s", err) } arn := arn.ARN{ @@ -469,7 +474,8 @@ func resourceReceiptRuleRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceReceiptRuleUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceReceiptRuleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SESConn() input := &ses.UpdateReceiptRuleInput{ @@ -477,10 +483,10 @@ func resourceReceiptRuleUpdate(d *schema.ResourceData, meta interface{}) error { RuleSetName: aws.String(d.Get("rule_set_name").(string)), } - _, err := conn.UpdateReceiptRule(input) + _, err := conn.UpdateReceiptRuleWithContext(ctx, input) if err != nil { - return fmt.Errorf("updating SES Receipt Rule (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating SES Receipt Rule (%s): %s", d.Id(), err) } if d.HasChange("after") { @@ -490,33 +496,34 @@ func resourceReceiptRuleUpdate(d *schema.ResourceData, meta interface{}) error { RuleSetName: aws.String(d.Get("rule_set_name").(string)), } - _, err := conn.SetReceiptRulePosition(input) + _, err := conn.SetReceiptRulePositionWithContext(ctx, input) if err != nil { - return fmt.Errorf("setting SES Receipt Rule (%s) position: %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "setting SES Receipt Rule (%s) position: %s", d.Id(), err) } } - return resourceReceiptRuleRead(d, meta) + return resourceReceiptRuleRead(ctx, d, meta) } -func resourceReceiptRuleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceReceiptRuleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).SESConn() log.Printf("[DEBUG] Deleting SES Receipt Rule: %s", d.Id()) - _, err := conn.DeleteReceiptRule(&ses.DeleteReceiptRuleInput{ + _, err := conn.DeleteReceiptRuleWithContext(ctx, &ses.DeleteReceiptRuleInput{ RuleName: aws.String(d.Id()), RuleSetName: aws.String(d.Get("rule_set_name").(string)), }) if err != nil { - return fmt.Errorf("deleting SES Receipt Rule (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting SES Receipt Rule (%s): %s", d.Id(), err) } return nil } -func resourceReceiptRuleImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { +func resourceReceiptRuleImport(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), ":") if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { return nil, fmt.Errorf("unexpected format of ID (%q), expected :", d.Id()) @@ -532,13 +539,13 @@ func resourceReceiptRuleImport(d *schema.ResourceData, meta interface{}) ([]*sch return []*schema.ResourceData{d}, nil } -func FindReceiptRuleByTwoPartKey(conn *ses.SES, ruleName, ruleSetName string) (*ses.ReceiptRule, error) { +func FindReceiptRuleByTwoPartKey(ctx context.Context, conn *ses.SES, ruleName, ruleSetName string) (*ses.ReceiptRule, error) { input := &ses.DescribeReceiptRuleInput{ RuleName: aws.String(ruleName), RuleSetName: aws.String(ruleSetName), } - output, err := conn.DescribeReceiptRule(input) + output, err := conn.DescribeReceiptRuleWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, ses.ErrCodeRuleDoesNotExistException, ses.ErrCodeRuleSetDoesNotExistException) { return nil, &resource.NotFoundError{ diff --git a/internal/service/ses/receipt_rule_test.go b/internal/service/ses/receipt_rule_test.go index e4be52ebd25..8e538c45a55 100644 --- a/internal/service/ses/receipt_rule_test.go +++ b/internal/service/ses/receipt_rule_test.go @@ -1,6 +1,7 @@ package ses_test import ( + "context" "fmt" "testing" @@ -17,25 +18,25 @@ import ( ) func TestAccSESReceiptRule_basic(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_basic(rName, acctest.DefaultEmailAddress), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "rule_set_name", rName), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "ses", fmt.Sprintf("receipt-rule-set/%s:receipt-rule/%s", rName, rName)), @@ -63,25 +64,25 @@ func TestAccSESReceiptRule_basic(t *testing.T) { } func TestAccSESReceiptRule_s3Action(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_s3Action(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "s3_action.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "s3_action.*.bucket_name", "aws_s3_bucket.test", "id"), @@ -100,25 +101,25 @@ func TestAccSESReceiptRule_s3Action(t *testing.T) { } func TestAccSESReceiptRule_snsAction(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_snsAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "sns_action.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "sns_action.*.topic_arn", "aws_sns_topic.test", "arn"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sns_action.*", map[string]string{ @@ -137,25 +138,25 @@ func TestAccSESReceiptRule_snsAction(t *testing.T) { } func TestAccSESReceiptRule_snsActionEncoding(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_snsActionEncoding(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "sns_action.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "sns_action.*.topic_arn", "aws_sns_topic.test", "arn"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "sns_action.*", map[string]string{ @@ -174,25 +175,25 @@ func TestAccSESReceiptRule_snsActionEncoding(t *testing.T) { } func TestAccSESReceiptRule_lambdaAction(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_lambdaAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "lambda_action.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "lambda_action.*.function_arn", "aws_lambda_function.test", "arn"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "lambda_action.*", map[string]string{ @@ -211,25 +212,25 @@ func TestAccSESReceiptRule_lambdaAction(t *testing.T) { } func TestAccSESReceiptRule_stopAction(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_stopAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "stop_action.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "stop_action.*", map[string]string{ "scope": "RuleSet", @@ -247,25 +248,25 @@ func TestAccSESReceiptRule_stopAction(t *testing.T) { } func TestAccSESReceiptRule_order(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_order(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckResourceAttr(resourceName, "name", "second"), resource.TestCheckResourceAttrPair(resourceName, "after", "aws_ses_receipt_rule.test1", "name"), ), @@ -280,25 +281,25 @@ func TestAccSESReceiptRule_order(t *testing.T) { } func TestAccSESReceiptRule_actions(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_actions(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "add_header_action.*", map[string]string{ "header_name": "Added-By", "header_value": "Terraform", @@ -321,36 +322,35 @@ func TestAccSESReceiptRule_actions(t *testing.T) { } func TestAccSESReceiptRule_disappears(t *testing.T) { + ctx := acctest.Context(t) var rule ses.ReceiptRule - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ses_receipt_rule.test" - ruleSetResourceName := "aws_ses_receipt_rule_set.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) - testAccPreCheck(t) - testAccPreCheckReceiptRule(t) + testAccPreCheck(ctx, t) + testAccPreCheckReceiptRule(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, ses.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckReceiptRuleDestroy, + CheckDestroy: testAccCheckReceiptRuleDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccReceiptRuleConfig_basic(rName, acctest.DefaultEmailAddress), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), - acctest.CheckResourceDisappears(acctest.Provider, tfses.ResourceReceiptRuleSet(), ruleSetResourceName), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfses.ResourceReceiptRuleSet(), ruleSetResourceName), ), ExpectNonEmptyPlan: true, }, { Config: testAccReceiptRuleConfig_basic(rName, acctest.DefaultEmailAddress), Check: resource.ComposeTestCheckFunc( - testAccCheckReceiptRuleExists(resourceName, &rule), - acctest.CheckResourceDisappears(acctest.Provider, tfses.ResourceReceiptRule(), resourceName), + testAccCheckReceiptRuleExists(ctx, resourceName, &rule), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfses.ResourceReceiptRule(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -358,31 +358,33 @@ func TestAccSESReceiptRule_disappears(t *testing.T) { }) } -func testAccCheckReceiptRuleDestroy(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).SESConn() +func testAccCheckReceiptRuleDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).SESConn() - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_ses_receipt_rule" { - continue - } + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ses_receipt_rule" { + continue + } - _, err := tfses.FindReceiptRuleByTwoPartKey(conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) + _, err := tfses.FindReceiptRuleByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) - if tfresource.NotFound(err) { - continue - } + if tfresource.NotFound(err) { + continue + } - if err != nil { - return err + if err != nil { + return err + } + + return fmt.Errorf("SES Receipt Rule %s still exists", rs.Primary.ID) } - return fmt.Errorf("SES Receipt Rule %s still exists", rs.Primary.ID) + return nil } - - return nil } -func testAccCheckReceiptRuleExists(n string, v *ses.ReceiptRule) resource.TestCheckFunc { +func testAccCheckReceiptRuleExists(ctx context.Context, n string, v *ses.ReceiptRule) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -395,7 +397,7 @@ func testAccCheckReceiptRuleExists(n string, v *ses.ReceiptRule) resource.TestCh conn := acctest.Provider.Meta().(*conns.AWSClient).SESConn() - output, err := tfses.FindReceiptRuleByTwoPartKey(conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) + output, err := tfses.FindReceiptRuleByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rule_set_name"]) if err != nil { return err @@ -418,7 +420,7 @@ func testAccReceiptRuleImportStateIdFunc(resourceName string) resource.ImportSta } } -func testAccPreCheckReceiptRule(t *testing.T) { +func testAccPreCheckReceiptRule(ctx context.Context, t *testing.T) { conn := acctest.Provider.Meta().(*conns.AWSClient).SESConn() input := &ses.DescribeReceiptRuleInput{ @@ -426,7 +428,7 @@ func testAccPreCheckReceiptRule(t *testing.T) { RuleSetName: aws.String("MyRuleSet"), } - _, err := conn.DescribeReceiptRule(input) + _, err := conn.DescribeReceiptRuleWithContext(ctx, input) if acctest.PreCheckSkipError(err) { t.Skipf("skipping acceptance testing: %s", err) From c7e9eaae24534092b2b4dea945c131a5f5136cd1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 17:10:22 -0500 Subject: [PATCH 19/68] r/aws_opsworks_user_profile: Add 'context.Context'. --- internal/service/opsworks/user_profile.go | 48 +++++++++-------- .../service/opsworks/user_profile_test.go | 51 ++++++++++--------- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/internal/service/opsworks/user_profile.go b/internal/service/opsworks/user_profile.go index 1e2b21989c1..3b9a34a700c 100644 --- a/internal/service/opsworks/user_profile.go +++ b/internal/service/opsworks/user_profile.go @@ -1,24 +1,26 @@ package opsworks import ( - "fmt" + "context" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceUserProfile() *schema.Resource { return &schema.Resource{ - Create: resourceUserProfileCreate, - Read: resourceUserProfileRead, - Update: resourceUserProfileUpdate, - Delete: resourceUserProfileDelete, + CreateWithoutTimeout: resourceUserProfileCreate, + ReadWithoutTimeout: resourceUserProfileRead, + UpdateWithoutTimeout: resourceUserProfileUpdate, + DeleteWithoutTimeout: resourceUserProfileDelete, Schema: map[string]*schema.Schema{ "allow_self_management": { @@ -43,7 +45,8 @@ func ResourceUserProfile() *schema.Resource { } } -func resourceUserProfileCreate(d *schema.ResourceData, meta interface{}) error { +func resourceUserProfileCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() iamUserARN := d.Get("user_arn").(string) @@ -57,21 +60,22 @@ func resourceUserProfileCreate(d *schema.ResourceData, meta interface{}) error { input.SshPublicKey = aws.String(v.(string)) } - _, err := conn.CreateUserProfile(input) + _, err := conn.CreateUserProfileWithContext(ctx, input) if err != nil { - return fmt.Errorf("creating OpsWorks User Profile (%s): %w", iamUserARN, err) + return sdkdiag.AppendErrorf(diags, "creating OpsWorks User Profile (%s): %s", iamUserARN, err) } d.SetId(iamUserARN) - return resourceUserProfileUpdate(d, meta) + return resourceUserProfileUpdate(ctx, d, meta) } -func resourceUserProfileRead(d *schema.ResourceData, meta interface{}) error { +func resourceUserProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() - profile, err := FindUserProfileByARN(conn, d.Id()) + profile, err := FindUserProfileByARN(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] OpsWorks User Profile %s not found, removing from state", d.Id()) @@ -80,7 +84,7 @@ func resourceUserProfileRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("reading OpsWorks User Profile (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading OpsWorks User Profile (%s): %s", d.Id(), err) } d.Set("allow_self_management", profile.AllowSelfManagement) @@ -91,7 +95,8 @@ func resourceUserProfileRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceUserProfileUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceUserProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() input := &opsworks.UpdateUserProfileInput{ @@ -101,20 +106,21 @@ func resourceUserProfileUpdate(d *schema.ResourceData, meta interface{}) error { SshUsername: aws.String(d.Get("ssh_username").(string)), } - _, err := conn.UpdateUserProfile(input) + _, err := conn.UpdateUserProfileWithContext(ctx, input) if err != nil { - return fmt.Errorf("updating OpsWorks User Profile (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating OpsWorks User Profile (%s): %s", d.Id(), err) } - return resourceUserProfileRead(d, meta) + return resourceUserProfileRead(ctx, d, meta) } -func resourceUserProfileDelete(d *schema.ResourceData, meta interface{}) error { +func resourceUserProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() log.Printf("[DEBUG] Deleting OpsWorks User Profile: %s", d.Id()) - _, err := conn.DeleteUserProfile(&opsworks.DeleteUserProfileInput{ + _, err := conn.DeleteUserProfileWithContext(ctx, &opsworks.DeleteUserProfileInput{ IamUserArn: aws.String(d.Id()), }) @@ -123,18 +129,18 @@ func resourceUserProfileDelete(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("deleting OpsWorks User Profile (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting OpsWorks User Profile (%s): %s", d.Id(), err) } return nil } -func FindUserProfileByARN(conn *opsworks.OpsWorks, arn string) (*opsworks.UserProfile, error) { +func FindUserProfileByARN(ctx context.Context, conn *opsworks.OpsWorks, arn string) (*opsworks.UserProfile, error) { input := &opsworks.DescribeUserProfilesInput{ IamUserArns: aws.StringSlice([]string{arn}), } - output, err := conn.DescribeUserProfiles(input) + output, err := conn.DescribeUserProfilesWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { return nil, &resource.NotFoundError{ diff --git a/internal/service/opsworks/user_profile_test.go b/internal/service/opsworks/user_profile_test.go index 61dec443a87..209420a2a40 100644 --- a/internal/service/opsworks/user_profile_test.go +++ b/internal/service/opsworks/user_profile_test.go @@ -1,6 +1,7 @@ package opsworks_test import ( + "context" "fmt" "testing" @@ -15,6 +16,7 @@ import ( ) func TestAccOpsWorksUserProfile_basic(t *testing.T) { + ctx := acctest.Context(t) rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_user_profile.test" @@ -23,12 +25,12 @@ func TestAccOpsWorksUserProfile_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckUserProfileDestroy, + CheckDestroy: testAccCheckUserProfileDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccUserProfileConfig_create(rName1), Check: resource.ComposeTestCheckFunc( - testAccCheckUserProfileExists(resourceName), + testAccCheckUserProfileExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "ssh_public_key", ""), resource.TestCheckResourceAttr(resourceName, "ssh_username", rName1), resource.TestCheckResourceAttr(resourceName, "allow_self_management", "false"), @@ -37,7 +39,7 @@ func TestAccOpsWorksUserProfile_basic(t *testing.T) { { Config: testAccUserProfileConfig_update(rName1, rName2), Check: resource.ComposeTestCheckFunc( - testAccCheckUserProfileExists(resourceName), + testAccCheckUserProfileExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "ssh_public_key", ""), resource.TestCheckResourceAttr(resourceName, "ssh_username", rName2), resource.TestCheckResourceAttr(resourceName, "allow_self_management", "false"), @@ -48,6 +50,7 @@ func TestAccOpsWorksUserProfile_basic(t *testing.T) { } func TestAccOpsWorksUserProfile_disappears(t *testing.T) { + ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_user_profile.test" @@ -55,13 +58,13 @@ func TestAccOpsWorksUserProfile_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckPartitionHasService(opsworks.EndpointsID, t) }, ErrorCheck: acctest.ErrorCheck(t, opsworks.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckUserProfileDestroy, + CheckDestroy: testAccCheckUserProfileDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccUserProfileConfig_create(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckUserProfileExists(resourceName), - acctest.CheckResourceDisappears(acctest.Provider, tfopsworks.ResourceUserProfile(), resourceName), + testAccCheckUserProfileExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfopsworks.ResourceUserProfile(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -69,7 +72,7 @@ func TestAccOpsWorksUserProfile_disappears(t *testing.T) { }) } -func testAccCheckUserProfileExists(n string) resource.TestCheckFunc { +func testAccCheckUserProfileExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -82,34 +85,36 @@ func testAccCheckUserProfileExists(n string) resource.TestCheckFunc { conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - _, err := tfopsworks.FindUserProfileByARN(conn, rs.Primary.ID) + _, err := tfopsworks.FindUserProfileByARN(ctx, conn, rs.Primary.ID) return err } } -func testAccCheckUserProfileDestroy(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() +func testAccCheckUserProfileDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_opsworks_user_profile" { - continue - } + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_opsworks_user_profile" { + continue + } - _, err := tfopsworks.FindUserProfileByARN(conn, rs.Primary.ID) + _, err := tfopsworks.FindUserProfileByARN(ctx, conn, rs.Primary.ID) - if tfresource.NotFound(err) { - continue - } + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } - if err != nil { - return err + return fmt.Errorf("OpsWorks User Profile %s still exists", rs.Primary.ID) } - return fmt.Errorf("OpsWorks User Profile %s still exists", rs.Primary.ID) + return nil } - - return nil } func testAccUserProfileConfig_create(rName string) string { From d4c7c02082f009a2886dbff7430dc377e66596c6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 17:16:12 -0500 Subject: [PATCH 20/68] r/aws_opsworks_permission: Add 'context.Context'. --- internal/service/opsworks/permission.go | 34 +++++++++++--------- internal/service/opsworks/permission_test.go | 19 ++++++----- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/internal/service/opsworks/permission.go b/internal/service/opsworks/permission.go index 5364e2ac609..6365c1f7eeb 100644 --- a/internal/service/opsworks/permission.go +++ b/internal/service/opsworks/permission.go @@ -1,25 +1,27 @@ package opsworks import ( - "fmt" + "context" "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourcePermission() *schema.Resource { return &schema.Resource{ - Create: resourceSetPermission, - Read: resourcePermissionRead, - Update: resourceSetPermission, - Delete: schema.Noop, + CreateWithoutTimeout: resourceSetPermission, + ReadWithoutTimeout: resourcePermissionRead, + UpdateWithoutTimeout: resourceSetPermission, + DeleteWithoutTimeout: schema.NoopContext, Schema: map[string]*schema.Schema{ "allow_ssh": { @@ -58,7 +60,8 @@ func ResourcePermission() *schema.Resource { } } -func resourceSetPermission(d *schema.ResourceData, meta interface{}) error { +func resourceSetPermission(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() iamUserARN := d.Get("user_arn").(string) @@ -79,25 +82,26 @@ func resourceSetPermission(d *schema.ResourceData, meta interface{}) error { input.Level = aws.String(d.Get("level").(string)) } - _, err := tfresource.RetryWhenAWSErrMessageContains(propagationTimeout, func() (interface{}, error) { - return conn.SetPermission(input) + _, err := tfresource.RetryWhenAWSErrMessageContainsContext(ctx, propagationTimeout, func() (interface{}, error) { + return conn.SetPermissionWithContext(ctx, input) }, opsworks.ErrCodeResourceNotFoundException, "Unable to find user with ARN") if err != nil { - return fmt.Errorf("setting OpsWorks Permission (%s): %w", id, err) + return sdkdiag.AppendErrorf(diags, "setting OpsWorks Permission (%s): %s", id, err) } if d.IsNewResource() { d.SetId(id) } - return resourcePermissionRead(d, meta) + return resourcePermissionRead(ctx, d, meta) } -func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { +func resourcePermissionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).OpsWorksConn() - permission, err := FindPermissionByTwoPartKey(conn, d.Get("user_arn").(string), d.Get("stack_id").(string)) + permission, err := FindPermissionByTwoPartKey(ctx, conn, d.Get("user_arn").(string), d.Get("stack_id").(string)) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] OpsWorks Permission %s not found, removing from state", d.Id()) @@ -106,7 +110,7 @@ func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("reading OpsWorks Permission (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading OpsWorks Permission (%s): %s", d.Id(), err) } d.Set("allow_ssh", permission.AllowSsh) @@ -118,13 +122,13 @@ func resourcePermissionRead(d *schema.ResourceData, meta interface{}) error { return nil } -func FindPermissionByTwoPartKey(conn *opsworks.OpsWorks, iamUserARN, stackID string) (*opsworks.Permission, error) { +func FindPermissionByTwoPartKey(ctx context.Context, conn *opsworks.OpsWorks, iamUserARN, stackID string) (*opsworks.Permission, error) { input := &opsworks.DescribePermissionsInput{ IamUserArn: aws.String(iamUserARN), StackId: aws.String(stackID), } - output, err := conn.DescribePermissions(input) + output, err := conn.DescribePermissionsWithContext(ctx, input) if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { return nil, &resource.NotFoundError{ diff --git a/internal/service/opsworks/permission_test.go b/internal/service/opsworks/permission_test.go index d80ccfc1bf8..08091f65002 100644 --- a/internal/service/opsworks/permission_test.go +++ b/internal/service/opsworks/permission_test.go @@ -1,6 +1,7 @@ package opsworks_test import ( + "context" "fmt" "testing" @@ -14,6 +15,7 @@ import ( ) func TestAccOpsWorksPermission_basic(t *testing.T) { + ctx := acctest.Context(t) rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_permission.test" var opsperm opsworks.Permission @@ -27,7 +29,7 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { { Config: testAccPermissionConfig_create(rName, true, true, "iam_only"), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "true"), resource.TestCheckResourceAttr(resourceName, "level", "iam_only"), @@ -36,7 +38,7 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { { Config: testAccPermissionConfig_create(rName, true, false, "iam_only"), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "iam_only"), @@ -45,7 +47,7 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { { Config: testAccPermissionConfig_create(rName, false, false, "deny"), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "false"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "deny"), @@ -54,7 +56,7 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { { Config: testAccPermissionConfig_create(rName, false, false, "show"), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "false"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), resource.TestCheckResourceAttr(resourceName, "level", "show"), @@ -66,6 +68,7 @@ func TestAccOpsWorksPermission_basic(t *testing.T) { // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/4804 func TestAccOpsWorksPermission_self(t *testing.T) { + ctx := acctest.Context(t) var opsperm opsworks.Permission rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_opsworks_permission.test" @@ -79,7 +82,7 @@ func TestAccOpsWorksPermission_self(t *testing.T) { { Config: testAccPermissionConfig_self(rName, true, true), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "true"), ), @@ -87,7 +90,7 @@ func TestAccOpsWorksPermission_self(t *testing.T) { { Config: testAccPermissionConfig_self(rName, true, false), Check: resource.ComposeTestCheckFunc( - testAccCheckPermissionExists(resourceName, &opsperm), + testAccCheckPermissionExists(ctx, resourceName, &opsperm), resource.TestCheckResourceAttr(resourceName, "allow_ssh", "true"), resource.TestCheckResourceAttr(resourceName, "allow_sudo", "false"), ), @@ -96,7 +99,7 @@ func TestAccOpsWorksPermission_self(t *testing.T) { }) } -func testAccCheckPermissionExists(n string, v *opsworks.Permission) resource.TestCheckFunc { +func testAccCheckPermissionExists(ctx context.Context, n string, v *opsworks.Permission) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -109,7 +112,7 @@ func testAccCheckPermissionExists(n string, v *opsworks.Permission) resource.Tes conn := acctest.Provider.Meta().(*conns.AWSClient).OpsWorksConn() - output, err := tfopsworks.FindPermissionByTwoPartKey(conn, rs.Primary.Attributes["user_arn"], rs.Primary.Attributes["stack_id"]) + output, err := tfopsworks.FindPermissionByTwoPartKey(ctx, conn, rs.Primary.Attributes["user_arn"], rs.Primary.Attributes["stack_id"]) if err != nil { return err From f16cf2e666b26f6b8e88627a159226fb0489365e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 20 Jan 2023 17:23:31 -0500 Subject: [PATCH 21/68] r/aws_emr_instance_fleet: Add 'context.Context'. --- internal/service/emr/instance_fleet.go | 66 +++++++++++---------- internal/service/emr/instance_fleet_test.go | 45 ++++---------- 2 files changed, 47 insertions(+), 64 deletions(-) diff --git a/internal/service/emr/instance_fleet.go b/internal/service/emr/instance_fleet.go index 07630df5fb5..6d3f36c5dc0 100644 --- a/internal/service/emr/instance_fleet.go +++ b/internal/service/emr/instance_fleet.go @@ -1,6 +1,7 @@ package emr import ( + "context" "fmt" "log" "strings" @@ -9,22 +10,24 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/emr" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceInstanceFleet() *schema.Resource { return &schema.Resource{ - Create: resourceInstanceFleetCreate, - Read: resourceInstanceFleetRead, - Update: resourceInstanceFleetUpdate, - Delete: resourceInstanceFleetDelete, + CreateWithoutTimeout: resourceInstanceFleetCreate, + ReadWithoutTimeout: resourceInstanceFleetRead, + UpdateWithoutTimeout: resourceInstanceFleetUpdate, + DeleteWithoutTimeout: resourceInstanceFleetDelete, Importer: &schema.ResourceImporter{ - State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + StateContext: func(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" { return nil, fmt.Errorf("Unexpected format of ID (%q), expected cluster-id/fleet-id", d.Id()) @@ -214,7 +217,8 @@ func ResourceInstanceFleet() *schema.Resource { } } -func resourceInstanceFleetCreate(d *schema.ResourceData, meta interface{}) error { +func resourceInstanceFleetCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EMRConn() taskFleet := map[string]interface{}{ @@ -229,21 +233,22 @@ func resourceInstanceFleetCreate(d *schema.ResourceData, meta interface{}) error InstanceFleet: readInstanceFleetConfig(taskFleet, emr.InstanceFleetTypeTask), } - output, err := conn.AddInstanceFleet(input) + output, err := conn.AddInstanceFleetWithContext(ctx, input) if err != nil { - return fmt.Errorf("creating EMR Instance Fleet: %w", err) + return sdkdiag.AppendErrorf(diags, "creating EMR Instance Fleet: %w", err) } d.SetId(aws.StringValue(output.InstanceFleetId)) - return resourceInstanceFleetRead(d, meta) + return resourceInstanceFleetRead(ctx, d, meta) } -func resourceInstanceFleetRead(d *schema.ResourceData, meta interface{}) error { +func resourceInstanceFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EMRConn() - fleet, err := FindInstanceFleetByTwoPartKey(conn, d.Get("cluster_id").(string), d.Id()) + fleet, err := FindInstanceFleetByTwoPartKey(ctx, conn, d.Get("cluster_id").(string), d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EMR Instance Fleet (%s) not found, removing from state", d.Id()) @@ -252,14 +257,14 @@ func resourceInstanceFleetRead(d *schema.ResourceData, meta interface{}) error { } if err != nil { - return fmt.Errorf("reading EMR Instance Fleet (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading EMR Instance Fleet (%s): %s", d.Id(), err) } if err := d.Set("instance_type_configs", flatteninstanceTypeConfigs(fleet.InstanceTypeSpecifications)); err != nil { - return fmt.Errorf("setting instance_type_configs: %w", err) + return sdkdiag.AppendErrorf(diags, "setting instance_type_configs: %s", err) } if err := d.Set("launch_specifications", flattenLaunchSpecifications(fleet.LaunchSpecifications)); err != nil { - return fmt.Errorf("setting launch_specifications: %w", err) + return sdkdiag.AppendErrorf(diags, "setting launch_specifications: %s", err) } d.Set("name", fleet.Name) d.Set("provisioned_on_demand_capacity", fleet.ProvisionedOnDemandCapacity) @@ -270,52 +275,51 @@ func resourceInstanceFleetRead(d *schema.ResourceData, meta interface{}) error { return nil } -func resourceInstanceFleetUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceInstanceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EMRConn() - log.Printf("[DEBUG] Modify EMR task fleet") - modifyConfig := &emr.InstanceFleetModifyConfig{ InstanceFleetId: aws.String(d.Id()), TargetOnDemandCapacity: aws.Int64(int64(d.Get("target_on_demand_capacity").(int))), TargetSpotCapacity: aws.Int64(int64(d.Get("target_spot_capacity").(int))), } - input := &emr.ModifyInstanceFleetInput{ ClusterId: aws.String(d.Get("cluster_id").(string)), InstanceFleet: modifyConfig, } - _, err := conn.ModifyInstanceFleet(input) + _, err := conn.ModifyInstanceFleetWithContext(ctx, input) if err != nil { - return fmt.Errorf("updating EMR Instance Fleet (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating EMR Instance Fleet (%s): %s", d.Id(), err) } stateConf := &resource.StateChangeConf{ Pending: []string{emr.InstanceFleetStateProvisioning, emr.InstanceFleetStateBootstrapping, emr.InstanceFleetStateResizing}, Target: []string{emr.InstanceFleetStateRunning}, - Refresh: statusInstanceFleet(conn, d.Get("cluster_id").(string), d.Id()), + Refresh: statusInstanceFleet(ctx, conn, d.Get("cluster_id").(string), d.Id()), Timeout: 75 * time.Minute, Delay: 10 * time.Second, MinTimeout: 30 * time.Second, } - _, err = stateConf.WaitForState() + _, err = stateConf.WaitForStateContext(ctx) if err != nil { - return fmt.Errorf("waiting for EMR Instance Fleet (%s) update: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for EMR Instance Fleet (%s) update: %s", d.Id(), err) } - return resourceInstanceFleetRead(d, meta) + return resourceInstanceFleetRead(ctx, d, meta) } -func resourceInstanceFleetDelete(d *schema.ResourceData, meta interface{}) error { +func resourceInstanceFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EMRConn() // AWS EMR Instance Fleet does not support DELETE; resizing cluster to zero before removing from state. log.Printf("[DEBUG] Deleting EMR Instance Fleet: %s", d.Id()) - _, err := conn.ModifyInstanceFleet(&emr.ModifyInstanceFleetInput{ + _, err := conn.ModifyInstanceFleetWithContext(ctx, &emr.ModifyInstanceFleetInput{ ClusterId: aws.String(d.Get("cluster_id").(string)), InstanceFleet: &emr.InstanceFleetModifyConfig{ InstanceFleetId: aws.String(d.Id()), @@ -329,19 +333,19 @@ func resourceInstanceFleetDelete(d *schema.ResourceData, meta interface{}) error } if err != nil { - return fmt.Errorf("deleting EMR Instance Fleet (%s): %w", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting EMR Instance Fleet (%s): %s", d.Id(), err) } return nil } -func FindInstanceFleetByTwoPartKey(conn *emr.EMR, clusterID, fleetID string) (*emr.InstanceFleet, error) { +func FindInstanceFleetByTwoPartKey(ctx context.Context, conn *emr.EMR, clusterID, fleetID string) (*emr.InstanceFleet, error) { input := &emr.ListInstanceFleetsInput{ ClusterId: aws.String(clusterID), } var fleets []*emr.InstanceFleet - err := conn.ListInstanceFleetsPages(input, func(page *emr.ListInstanceFleetsOutput, lastPage bool) bool { + err := conn.ListInstanceFleetsPagesWithContext(ctx, input, func(page *emr.ListInstanceFleetsOutput, lastPage bool) bool { if page == nil { return !lastPage } @@ -368,9 +372,9 @@ func FindInstanceFleetByTwoPartKey(conn *emr.EMR, clusterID, fleetID string) (*e return nil, &resource.NotFoundError{} } -func statusInstanceFleet(conn *emr.EMR, clusterID, fleetID string) resource.StateRefreshFunc { +func statusInstanceFleet(ctx context.Context, conn *emr.EMR, clusterID, fleetID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindInstanceFleetByTwoPartKey(conn, clusterID, fleetID) + output, err := FindInstanceFleetByTwoPartKey(ctx, conn, clusterID, fleetID) if tfresource.NotFound(err) { return nil, "", nil diff --git a/internal/service/emr/instance_fleet_test.go b/internal/service/emr/instance_fleet_test.go index 0902becd620..5b67de2b0b8 100644 --- a/internal/service/emr/instance_fleet_test.go +++ b/internal/service/emr/instance_fleet_test.go @@ -1,6 +1,7 @@ package emr_test import ( + "context" "fmt" "testing" @@ -14,6 +15,7 @@ import ( ) func TestAccEMRInstanceFleet_basic(t *testing.T) { + ctx := acctest.Context(t) var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" @@ -26,7 +28,7 @@ func TestAccEMRInstanceFleet_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_basic(rName), - Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(resourceName, &fleet), + Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(ctx, resourceName, &fleet), resource.TestCheckResourceAttr(resourceName, "instance_type_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "target_on_demand_capacity", "1"), resource.TestCheckResourceAttr(resourceName, "target_spot_capacity", "0"), @@ -43,6 +45,7 @@ func TestAccEMRInstanceFleet_basic(t *testing.T) { } func TestAccEMRInstanceFleet_Zero_count(t *testing.T) { + ctx := acctest.Context(t) var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" @@ -55,7 +58,7 @@ func TestAccEMRInstanceFleet_Zero_count(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_basic(rName), - Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(resourceName, &fleet), + Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(ctx, resourceName, &fleet), resource.TestCheckResourceAttr(resourceName, "instance_type_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "target_on_demand_capacity", "1"), resource.TestCheckResourceAttr(resourceName, "target_spot_capacity", "0"), @@ -63,7 +66,7 @@ func TestAccEMRInstanceFleet_Zero_count(t *testing.T) { }, { Config: testAccInstanceFleetConfig_zeroCount(rName), - Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(resourceName, &fleet), + Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(ctx, resourceName, &fleet), resource.TestCheckResourceAttr(resourceName, "instance_type_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "target_on_demand_capacity", "0"), resource.TestCheckResourceAttr(resourceName, "target_spot_capacity", "0"), @@ -80,6 +83,7 @@ func TestAccEMRInstanceFleet_Zero_count(t *testing.T) { } func TestAccEMRInstanceFleet_ebsBasic(t *testing.T) { + ctx := acctest.Context(t) var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" @@ -92,7 +96,7 @@ func TestAccEMRInstanceFleet_ebsBasic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_ebsBasic(rName), - Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(resourceName, &fleet), + Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(ctx, resourceName, &fleet), resource.TestCheckResourceAttr(resourceName, "instance_type_configs.#", "1"), resource.TestCheckResourceAttr(resourceName, "target_on_demand_capacity", "0"), resource.TestCheckResourceAttr(resourceName, "target_spot_capacity", "1"), @@ -109,6 +113,7 @@ func TestAccEMRInstanceFleet_ebsBasic(t *testing.T) { } func TestAccEMRInstanceFleet_full(t *testing.T) { + ctx := acctest.Context(t) var fleet emr.InstanceFleet rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_emr_instance_fleet.task" @@ -121,7 +126,7 @@ func TestAccEMRInstanceFleet_full(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccInstanceFleetConfig_full(rName), - Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(resourceName, &fleet), + Check: resource.ComposeTestCheckFunc(testAccCheckInstanceFleetExists(ctx, resourceName, &fleet), resource.TestCheckResourceAttr(resourceName, "instance_type_configs.#", "2"), resource.TestCheckResourceAttr(resourceName, "target_on_demand_capacity", "2"), resource.TestCheckResourceAttr(resourceName, "target_spot_capacity", "2"), @@ -137,33 +142,7 @@ func TestAccEMRInstanceFleet_full(t *testing.T) { }) } -func TestAccEMRInstanceFleet_disappears(t *testing.T) { - var fleet emr.InstanceFleet - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_emr_instance_fleet.task" - emrClusterResourceName := "aws_emr_cluster.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: acctest.CheckDestroyNoop, - Steps: []resource.TestStep{ - { - Config: testAccInstanceFleetConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckInstanceFleetExists(resourceName, &fleet), - // EMR Instance Fleet can only be scaled down and are not removed until the - // Cluster is removed. Verify EMR Cluster disappearance handling. - acctest.CheckResourceDisappears(acctest.Provider, tfemr.ResourceCluster(), emrClusterResourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -func testAccCheckInstanceFleetExists(n string, v *emr.InstanceFleet) resource.TestCheckFunc { +func testAccCheckInstanceFleetExists(ctx context.Context, n string, v *emr.InstanceFleet) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -176,7 +155,7 @@ func testAccCheckInstanceFleetExists(n string, v *emr.InstanceFleet) resource.Te conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn() - output, err := tfemr.FindInstanceFleetByTwoPartKey(conn, rs.Primary.Attributes["cluster_id"], rs.Primary.ID) + output, err := tfemr.FindInstanceFleetByTwoPartKey(ctx, conn, rs.Primary.Attributes["cluster_id"], rs.Primary.ID) if err != nil { return err From f8d852cb44123d157c9e54b0046fb3a4a8cd15f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 3 Feb 2023 15:42:16 -0500 Subject: [PATCH 22/68] Various Context-related enhancements. --- internal/service/elasticache/subnet_group.go | 14 +++++++------- internal/service/emr/instance_fleet.go | 12 ++++++------ internal/service/iam/server_certificate.go | 16 ++++++++-------- internal/service/opsworks/permission.go | 6 +++--- internal/service/opsworks/user_profile.go | 12 ++++++------ internal/service/rds/cluster_parameter_group.go | 16 +++++++++------- internal/service/redshift/parameter_group.go | 12 ++++++------ internal/service/ses/receipt_rule.go | 10 +++++----- 8 files changed, 50 insertions(+), 48 deletions(-) diff --git a/internal/service/elasticache/subnet_group.go b/internal/service/elasticache/subnet_group.go index 1f8243a5da9..c04626d8dcf 100644 --- a/internal/service/elasticache/subnet_group.go +++ b/internal/service/elasticache/subnet_group.go @@ -99,7 +99,7 @@ func resourceSubnetGroupCreate(ctx context.Context, d *schema.ResourceData, meta log.Printf("[WARN] failed creating ElastiCache Subnet Group with tags: %s. Trying create without tags.", err) input.Tags = nil - output, err = conn.CreateCacheSubnetGroup(input) + output, err = conn.CreateCacheSubnetGroupWithContext(ctx, input) } if err != nil { @@ -126,7 +126,7 @@ func resourceSubnetGroupCreate(ctx context.Context, d *schema.ResourceData, meta } } - return resourceSubnetGroupRead(ctx, d, meta) + return append(diags, resourceSubnetGroupRead(ctx, d, meta)...) } func resourceSubnetGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -140,7 +140,7 @@ func resourceSubnetGroupRead(ctx context.Context, d *schema.ResourceData, meta i if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] ElastiCache Subnet Group (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -181,7 +181,7 @@ func resourceSubnetGroupRead(ctx context.Context, d *schema.ResourceData, meta i } } - return nil + return diags } func resourceSubnetGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -217,7 +217,7 @@ func resourceSubnetGroupUpdate(ctx context.Context, d *schema.ResourceData, meta } } - return resourceSubnetGroupRead(ctx, d, meta) + return append(diags, resourceSubnetGroupRead(ctx, d, meta)...) } func resourceSubnetGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -232,12 +232,12 @@ func resourceSubnetGroupDelete(ctx context.Context, d *schema.ResourceData, meta }, "DependencyViolation") if tfawserr.ErrCodeEquals(err, elasticache.ErrCodeCacheSubnetGroupNotFoundFault) { - return nil + return diags } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ElastiCache Subnet Group (%s): %s", d.Id(), err) } - return nil + return diags } diff --git a/internal/service/emr/instance_fleet.go b/internal/service/emr/instance_fleet.go index 24f2f5a267d..e24cd73a0ca 100644 --- a/internal/service/emr/instance_fleet.go +++ b/internal/service/emr/instance_fleet.go @@ -241,7 +241,7 @@ func resourceInstanceFleetCreate(ctx context.Context, d *schema.ResourceData, me d.SetId(aws.StringValue(output.InstanceFleetId)) - return resourceInstanceFleetRead(ctx, d, meta) + return append(diags, resourceInstanceFleetRead(ctx, d, meta)...) } func resourceInstanceFleetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -253,7 +253,7 @@ func resourceInstanceFleetRead(ctx context.Context, d *schema.ResourceData, meta if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EMR Instance Fleet (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -272,7 +272,7 @@ func resourceInstanceFleetRead(ctx context.Context, d *schema.ResourceData, meta d.Set("target_on_demand_capacity", fleet.TargetOnDemandCapacity) d.Set("target_spot_capacity", fleet.TargetSpotCapacity) - return nil + return diags } func resourceInstanceFleetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -310,7 +310,7 @@ func resourceInstanceFleetUpdate(ctx context.Context, d *schema.ResourceData, me return sdkdiag.AppendErrorf(diags, "waiting for EMR Instance Fleet (%s) update: %s", d.Id(), err) } - return resourceInstanceFleetRead(ctx, d, meta) + return append(diags, resourceInstanceFleetRead(ctx, d, meta)...) } func resourceInstanceFleetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -329,14 +329,14 @@ func resourceInstanceFleetDelete(ctx context.Context, d *schema.ResourceData, me }) if tfawserr.ErrMessageContains(err, emr.ErrCodeInvalidRequestException, "instance fleet may only be modified when the cluster is running or waiting") { - return nil + return diags } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting EMR Instance Fleet (%s): %s", d.Id(), err) } - return nil + return diags } func FindInstanceFleetByTwoPartKey(ctx context.Context, conn *emr.EMR, clusterID, fleetID string) (*emr.InstanceFleet, error) { diff --git a/internal/service/iam/server_certificate.go b/internal/service/iam/server_certificate.go index 607658e80bc..ddd70299bcb 100644 --- a/internal/service/iam/server_certificate.go +++ b/internal/service/iam/server_certificate.go @@ -148,7 +148,7 @@ func resourceServerCertificateCreate(ctx context.Context, d *schema.ResourceData // If default tags only, log and continue. Otherwise, error. if v, ok := d.GetOk("tags"); (!ok || len(v.(map[string]interface{})) == 0) && verify.ErrorISOUnsupported(conn.PartitionID, err) { log.Printf("[WARN] failed adding tags after create for IAM Server Certificate (%s): %s", d.Id(), err) - return resourceServerCertificateRead(ctx, d, meta) + return append(diags, resourceServerCertificateRead(ctx, d, meta)...) } if err != nil { @@ -156,7 +156,7 @@ func resourceServerCertificateCreate(ctx context.Context, d *schema.ResourceData } } - return resourceServerCertificateRead(ctx, d, meta) + return append(diags, resourceServerCertificateRead(ctx, d, meta)...) } func resourceServerCertificateRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -170,7 +170,7 @@ func resourceServerCertificateRead(ctx context.Context, d *schema.ResourceData, if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] IAM Server Certificate (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -207,7 +207,7 @@ func resourceServerCertificateRead(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "setting tags_all: %s", err) } - return nil + return diags } func resourceServerCertificateUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -222,7 +222,7 @@ func resourceServerCertificateUpdate(ctx context.Context, d *schema.ResourceData // Some partitions (i.e., ISO) may not support tagging, giving error if verify.ErrorISOUnsupported(conn.PartitionID, err) { log.Printf("[WARN] failed updating tags for IAM Server Certificate (%s): %s", d.Id(), err) - return resourceServerCertificateRead(ctx, d, meta) + return append(diags, resourceServerCertificateRead(ctx, d, meta)...) } if err != nil { @@ -230,7 +230,7 @@ func resourceServerCertificateUpdate(ctx context.Context, d *schema.ResourceData } } - return resourceServerCertificateRead(ctx, d, meta) + return append(diags, resourceServerCertificateRead(ctx, d, meta)...) } func resourceServerCertificateDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -245,14 +245,14 @@ func resourceServerCertificateDelete(ctx context.Context, d *schema.ResourceData }, iam.ErrCodeDeleteConflictException, "currently in use by arn") if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return nil + return diags } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting IAM Server Certificate (%s): %s", d.Id(), err) } - return nil + return diags } func resourceServerCertificateImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { diff --git a/internal/service/opsworks/permission.go b/internal/service/opsworks/permission.go index a5953ae68d8..822e362c8a6 100644 --- a/internal/service/opsworks/permission.go +++ b/internal/service/opsworks/permission.go @@ -94,7 +94,7 @@ func resourceSetPermission(ctx context.Context, d *schema.ResourceData, meta int d.SetId(id) } - return resourcePermissionRead(ctx, d, meta) + return append(diags, resourcePermissionRead(ctx, d, meta)...) } func resourcePermissionRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -106,7 +106,7 @@ func resourcePermissionRead(ctx context.Context, d *schema.ResourceData, meta in if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] OpsWorks Permission %s not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -119,7 +119,7 @@ func resourcePermissionRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("stack_id", permission.StackId) d.Set("user_arn", permission.IamUserArn) - return nil + return diags } func FindPermissionByTwoPartKey(ctx context.Context, conn *opsworks.OpsWorks, iamUserARN, stackID string) (*opsworks.Permission, error) { diff --git a/internal/service/opsworks/user_profile.go b/internal/service/opsworks/user_profile.go index 3b9a34a700c..141cbd13a95 100644 --- a/internal/service/opsworks/user_profile.go +++ b/internal/service/opsworks/user_profile.go @@ -68,7 +68,7 @@ func resourceUserProfileCreate(ctx context.Context, d *schema.ResourceData, meta d.SetId(iamUserARN) - return resourceUserProfileUpdate(ctx, d, meta) + return append(diags, resourceUserProfileUpdate(ctx, d, meta)...) } func resourceUserProfileRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -80,7 +80,7 @@ func resourceUserProfileRead(ctx context.Context, d *schema.ResourceData, meta i if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] OpsWorks User Profile %s not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -92,7 +92,7 @@ func resourceUserProfileRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("ssh_username", profile.SshUsername) d.Set("user_arn", profile.IamUserArn) - return nil + return diags } func resourceUserProfileUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -112,7 +112,7 @@ func resourceUserProfileUpdate(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "updating OpsWorks User Profile (%s): %s", d.Id(), err) } - return resourceUserProfileRead(ctx, d, meta) + return append(diags, resourceUserProfileRead(ctx, d, meta)...) } func resourceUserProfileDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -125,14 +125,14 @@ func resourceUserProfileDelete(ctx context.Context, d *schema.ResourceData, meta }) if tfawserr.ErrCodeEquals(err, opsworks.ErrCodeResourceNotFoundException) { - return nil + return diags } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting OpsWorks User Profile (%s): %s", d.Id(), err) } - return nil + return diags } func FindUserProfileByARN(ctx context.Context, conn *opsworks.OpsWorks, arn string) (*opsworks.UserProfile, error) { diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index f0aedb1eb5f..e97cbbcf2c9 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -122,7 +122,7 @@ func resourceClusterParameterGroupCreate(ctx context.Context, d *schema.Resource // Set for update d.Set("arn", output.DBClusterParameterGroup.DBClusterParameterGroupArn) - return resourceClusterParameterGroupUpdate(ctx, d, meta) + return append(diags, resourceClusterParameterGroupUpdate(ctx, d, meta)...) } func resourceClusterParameterGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -136,7 +136,7 @@ func resourceClusterParameterGroupRead(ctx context.Context, d *schema.ResourceDa if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RDS DB Cluster Parameter Group (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -196,7 +196,7 @@ func resourceClusterParameterGroupRead(ctx context.Context, d *schema.ResourceDa } } - return nil + return diags } func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -233,7 +233,7 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource Parameters: paramsToModify, } - _, err := conn.ModifyDBClusterParameterGroup(input) + _, err := conn.ModifyDBClusterParameterGroupWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "modifying DB Cluster Parameter Group (%s): %s", d.Id(), err) @@ -287,7 +287,7 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource }) if tfresource.TimedOut(err) { - _, err = conn.ResetDBClusterParameterGroup(input) + _, err = conn.ResetDBClusterParameterGroupWithContext(ctx, input) } if err != nil { @@ -305,7 +305,7 @@ func resourceClusterParameterGroupUpdate(ctx context.Context, d *schema.Resource } } - return resourceClusterParameterGroupRead(ctx, d, meta) + return append(diags, resourceClusterParameterGroupRead(ctx, d, meta)...) } func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -332,10 +332,12 @@ func resourceClusterParameterGroupDelete(ctx context.Context, d *schema.Resource if tfresource.TimedOut(err) { _, err = conn.DeleteDBClusterParameterGroup(ctx, input) } + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting RDS Cluster Parameter Group (%s): %s", d.Id(), err) } - return nil + + return diags } func FindDBClusterParameterGroupByName(ctx context.Context, conn *rds.RDS, name string) (*rds.DBClusterParameterGroup, error) { diff --git a/internal/service/redshift/parameter_group.go b/internal/service/redshift/parameter_group.go index 88d95334b0f..1f815b1dabb 100644 --- a/internal/service/redshift/parameter_group.go +++ b/internal/service/redshift/parameter_group.go @@ -123,7 +123,7 @@ func resourceParameterGroupCreate(ctx context.Context, d *schema.ResourceData, m } } - return resourceParameterGroupRead(ctx, d, meta) + return append(diags, resourceParameterGroupRead(ctx, d, meta)...) } func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -137,7 +137,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] Redshift Parameter Group (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -180,7 +180,7 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met d.Set("parameter", flattenParameters(output.Parameters)) - return nil + return diags } func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -221,7 +221,7 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m } } - return resourceParameterGroupRead(ctx, d, meta) + return append(diags, resourceParameterGroupRead(ctx, d, meta)...) } func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -234,14 +234,14 @@ func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, m }) if tfawserr.ErrCodeEquals(err, redshift.ErrCodeClusterParameterGroupNotFoundFault) { - return nil + return diags } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting Redshift Parameter Group (%s): %s", d.Id(), err) } - return nil + return diags } func FindParameterGroupByName(ctx context.Context, conn *redshift.Redshift, name string) (*redshift.ClusterParameterGroup, error) { diff --git a/internal/service/ses/receipt_rule.go b/internal/service/ses/receipt_rule.go index ff3c44d8c3c..4f334b590ae 100644 --- a/internal/service/ses/receipt_rule.go +++ b/internal/service/ses/receipt_rule.go @@ -290,7 +290,7 @@ func resourceReceiptRuleCreate(ctx context.Context, d *schema.ResourceData, meta d.SetId(name) - return resourceReceiptRuleRead(ctx, d, meta) + return append(diags, resourceReceiptRuleRead(ctx, d, meta)...) } func resourceReceiptRuleRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -303,7 +303,7 @@ func resourceReceiptRuleRead(ctx context.Context, d *schema.ResourceData, meta i if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] SES Receipt Rule (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { @@ -471,7 +471,7 @@ func resourceReceiptRuleRead(ctx context.Context, d *schema.ResourceData, meta i }.String() d.Set("arn", arn) - return nil + return diags } func resourceReceiptRuleUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -503,7 +503,7 @@ func resourceReceiptRuleUpdate(ctx context.Context, d *schema.ResourceData, meta } } - return resourceReceiptRuleRead(ctx, d, meta) + return append(diags, resourceReceiptRuleRead(ctx, d, meta)...) } func resourceReceiptRuleDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -520,7 +520,7 @@ func resourceReceiptRuleDelete(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "deleting SES Receipt Rule (%s): %s", d.Id(), err) } - return nil + return diags } func resourceReceiptRuleImport(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { From 798bca7ff57c5aa2a71a2dc356d02f1e8eb96246 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 3 Feb 2023 15:44:58 -0500 Subject: [PATCH 23/68] Simplify 'aws-go-sdk-error-code-helper' semgrep rule. --- .ci/.semgrep.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index 661af8b714d..eb4e8e37954 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -841,10 +841,7 @@ rules: paths: include: - internal/ - patterns: - - pattern-either: - - pattern: $AWSERR, $OK := $ORIGINALERR.(awserr.Error) - - pattern: var $AWSERR awserr.Error + pattern: $AWSERR, $OK := $ORIGINALERR.(awserr.Error) severity: WARNING - id: fmt-Errorf-awserr-Error-Code From d29e670aa853d332b5a816a7a31b6201e045734a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 3 Feb 2023 16:35:39 -0500 Subject: [PATCH 24/68] r/aws_api_gateway_api_key: Add 'FindAPIKeyByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayAPIKey_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayAPIKey_ -timeout 180m === RUN TestAccAPIGatewayAPIKey_basic === PAUSE TestAccAPIGatewayAPIKey_basic === RUN TestAccAPIGatewayAPIKey_tags === PAUSE TestAccAPIGatewayAPIKey_tags === RUN TestAccAPIGatewayAPIKey_description === PAUSE TestAccAPIGatewayAPIKey_description === RUN TestAccAPIGatewayAPIKey_enabled === PAUSE TestAccAPIGatewayAPIKey_enabled === RUN TestAccAPIGatewayAPIKey_value === PAUSE TestAccAPIGatewayAPIKey_value === RUN TestAccAPIGatewayAPIKey_disappears === PAUSE TestAccAPIGatewayAPIKey_disappears === CONT TestAccAPIGatewayAPIKey_basic === CONT TestAccAPIGatewayAPIKey_enabled === CONT TestAccAPIGatewayAPIKey_description --- PASS: TestAccAPIGatewayAPIKey_basic (20.17s) === CONT TestAccAPIGatewayAPIKey_disappears --- PASS: TestAccAPIGatewayAPIKey_description (32.84s) === CONT TestAccAPIGatewayAPIKey_value --- PASS: TestAccAPIGatewayAPIKey_enabled (33.11s) === CONT TestAccAPIGatewayAPIKey_tags --- PASS: TestAccAPIGatewayAPIKey_disappears (14.31s) --- PASS: TestAccAPIGatewayAPIKey_value (17.39s) --- PASS: TestAccAPIGatewayAPIKey_tags (38.25s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 76.557s --- internal/service/apigateway/api_key.go | 83 ++++++++++++++------- internal/service/apigateway/api_key_test.go | 35 +++------ 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/internal/service/apigateway/api_key.go b/internal/service/apigateway/api_key.go index 0579f9cd6c1..3431b89b5de 100644 --- a/internal/service/apigateway/api_key.go +++ b/internal/service/apigateway/api_key.go @@ -11,11 +11,13 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -83,17 +85,20 @@ func resourceAPIKeyCreate(ctx context.Context, d *schema.ResourceData, meta inte conn := meta.(*conns.AWSClient).APIGatewayConn() defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - log.Printf("[DEBUG] Creating API Gateway API Key") - apiKey, err := conn.CreateApiKeyWithContext(ctx, &apigateway.CreateApiKeyInput{ - Name: aws.String(d.Get("name").(string)), + name := d.Get("name").(string) + input := &apigateway.CreateApiKeyInput{ Description: aws.String(d.Get("description").(string)), Enabled: aws.Bool(d.Get("enabled").(bool)), - Value: aws.String(d.Get("value").(string)), + Name: aws.String(name), Tags: Tags(tags.IgnoreAWS()), - }) + Value: aws.String(d.Get("value").(string)), + } + + apiKey, err := conn.CreateApiKeyWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating API Gateway API Key: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway API Key (%s): %s", name, err) } d.SetId(aws.StringValue(apiKey.Id)) @@ -107,19 +112,15 @@ func resourceAPIKeyRead(ctx context.Context, d *schema.ResourceData, meta interf defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - log.Printf("[DEBUG] Reading API Gateway API Key: %s", d.Id()) + apiKey, err := FindAPIKeyByID(ctx, conn, d.Id()) - apiKey, err := conn.GetApiKeyWithContext(ctx, &apigateway.GetApiKeyInput{ - ApiKey: aws.String(d.Id()), - IncludeValue: aws.Bool(true), - }) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway API Key (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway API Key (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { return sdkdiag.AppendErrorf(diags, "reading API Gateway API Key (%s): %s", d.Id(), err) } @@ -187,31 +188,33 @@ func resourceAPIKeyUpdate(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating API Gateway API Key: %s", d.Id()) + if d.HasChangesExcept("tags", "tags_all") { + _, err := conn.UpdateApiKeyWithContext(ctx, &apigateway.UpdateApiKeyInput{ + ApiKey: aws.String(d.Id()), + PatchOperations: resourceAPIKeyUpdateOperations(d), + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating API Gateway API Key (%s): %s", d.Id(), err) + } + } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") + if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { return sdkdiag.AppendErrorf(diags, "updating tags: %s", err) } } - _, err := conn.UpdateApiKeyWithContext(ctx, &apigateway.UpdateApiKeyInput{ - ApiKey: aws.String(d.Id()), - PatchOperations: resourceAPIKeyUpdateOperations(d), - }) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating API Gateway API Key (%s): %s", d.Id(), err) - } - return append(diags, resourceAPIKeyRead(ctx, d, meta)...) } func resourceAPIKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway API Key: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway API Key: %s", d.Id()) _, err := conn.DeleteApiKeyWithContext(ctx, &apigateway.DeleteApiKeyInput{ ApiKey: aws.String(d.Id()), }) @@ -226,3 +229,29 @@ func resourceAPIKeyDelete(ctx context.Context, d *schema.ResourceData, meta inte return diags } + +func FindAPIKeyByID(ctx context.Context, conn *apigateway.APIGateway, id string) (*apigateway.ApiKey, error) { + input := &apigateway.GetApiKeyInput{ + ApiKey: aws.String(id), + IncludeValue: aws.Bool(true), + } + + output, err := conn.GetApiKeyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/api_key_test.go b/internal/service/apigateway/api_key_test.go index 473ba36d878..3b3d406f450 100644 --- a/internal/service/apigateway/api_key_test.go +++ b/internal/service/apigateway/api_key_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -15,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayAPIKey_basic(t *testing.T) { @@ -221,7 +221,7 @@ func TestAccAPIGatewayAPIKey_disappears(t *testing.T) { }) } -func testAccCheckAPIKeyExists(ctx context.Context, n string, res *apigateway.ApiKey) resource.TestCheckFunc { +func testAccCheckAPIKeyExists(ctx context.Context, n string, v *apigateway.ApiKey) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -229,24 +229,18 @@ func testAccCheckAPIKeyExists(ctx context.Context, n string, res *apigateway.Api } if rs.Primary.ID == "" { - return fmt.Errorf("No API Gateway ApiKey ID is set") + return fmt.Errorf("No API Gateway API Key ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetApiKeyInput{ - ApiKey: aws.String(rs.Primary.ID), - } - describe, err := conn.GetApiKeyWithContext(ctx, req) + output, err := tfapigateway.FindAPIKeyByID(ctx, conn, rs.Primary.ID) + if err != nil { return err } - if *describe.Id != rs.Primary.ID { - return fmt.Errorf("APIGateway ApiKey not found") - } - - *res = *describe + *v = *output return nil } @@ -261,24 +255,17 @@ func testAccCheckAPIKeyDestroy(ctx context.Context) resource.TestCheckFunc { continue } - describe, err := conn.GetApiKeysWithContext(ctx, &apigateway.GetApiKeysInput{}) + _, err := tfapigateway.FindAPIKeyByID(ctx, conn, rs.Primary.ID) - if err == nil { - if len(describe.Items) != 0 && - *describe.Items[0].Id == rs.Primary.ID { - return fmt.Errorf("API Gateway ApiKey still exists") - } + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway API Key %s still exists", rs.Primary.ID) } return nil From 1d25b771186570dce6ba9dffe9a9054cbbb1d6c4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 3 Feb 2023 16:39:13 -0500 Subject: [PATCH 25/68] d/aws_api_gateway_api_key: Use 'FindAPIKeyByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayAPIKeyDataSource_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayAPIKeyDataSource_ -timeout 180m === RUN TestAccAPIGatewayAPIKeyDataSource_basic === PAUSE TestAccAPIGatewayAPIKeyDataSource_basic === CONT TestAccAPIGatewayAPIKeyDataSource_basic --- PASS: TestAccAPIGatewayAPIKeyDataSource_basic (13.91s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 19.021s --- internal/service/apigateway/api_key_data_source.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/service/apigateway/api_key_data_source.go b/internal/service/apigateway/api_key_data_source.go index e7b03fa4747..bfa5247fff0 100644 --- a/internal/service/apigateway/api_key_data_source.go +++ b/internal/service/apigateway/api_key_data_source.go @@ -5,7 +5,6 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -56,13 +55,11 @@ func dataSourceAPIKeyRead(ctx context.Context, d *schema.ResourceData, meta inte conn := meta.(*conns.AWSClient).APIGatewayConn() ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - apiKey, err := conn.GetApiKeyWithContext(ctx, &apigateway.GetApiKeyInput{ - ApiKey: aws.String(d.Get("id").(string)), - IncludeValue: aws.Bool(true), - }) + id := d.Get("id").(string) + apiKey, err := FindAPIKeyByID(ctx, conn, id) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading API Gateway API Key (%s): %s", d.Get("id").(string), err) + return sdkdiag.AppendErrorf(diags, "reading API Gateway API Key (%s): %s", id, err) } d.SetId(aws.StringValue(apiKey.Id)) @@ -76,5 +73,6 @@ func dataSourceAPIKeyRead(ctx context.Context, d *schema.ResourceData, meta inte if err := d.Set("tags", KeyValueTags(apiKey.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return sdkdiag.AppendErrorf(diags, "setting tags: %s", err) } + return diags } From e09cd1aba2fa154f5ee9baee6db5c527c1c0607f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 15:22:16 -0500 Subject: [PATCH 26/68] d/aws_api_gateway_api_key: Tidy up acceptance test configuration. --- .../apigateway/api_key_data_source_test.go | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/internal/service/apigateway/api_key_data_source_test.go b/internal/service/apigateway/api_key_data_source_test.go index b14e37271dc..fdd87c43cb9 100644 --- a/internal/service/apigateway/api_key_data_source_test.go +++ b/internal/service/apigateway/api_key_data_source_test.go @@ -11,9 +11,9 @@ import ( ) func TestAccAPIGatewayAPIKeyDataSource_basic(t *testing.T) { - rName := sdkacctest.RandString(8) - resourceName1 := "aws_api_gateway_api_key.example_key" - dataSourceName1 := "data.aws_api_gateway_api_key.test_key" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_api_gateway_api_key.test" + dataSourceName := "data.aws_api_gateway_api_key.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -23,14 +23,14 @@ func TestAccAPIGatewayAPIKeyDataSource_basic(t *testing.T) { { Config: testAccAPIKeyDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName1, "id", dataSourceName1, "id"), - resource.TestCheckResourceAttrPair(resourceName1, "name", dataSourceName1, "name"), - resource.TestCheckResourceAttrPair(resourceName1, "value", dataSourceName1, "value"), - resource.TestCheckResourceAttrPair(resourceName1, "enabled", dataSourceName1, "enabled"), - resource.TestCheckResourceAttrPair(resourceName1, "description", dataSourceName1, "description"), - resource.TestCheckResourceAttrSet(dataSourceName1, "last_updated_date"), - resource.TestCheckResourceAttrSet(dataSourceName1, "created_date"), - resource.TestCheckResourceAttr(dataSourceName1, "tags.%", "0"), + resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceName, "name"), + resource.TestCheckResourceAttrPair(resourceName, "value", dataSourceName, "value"), + resource.TestCheckResourceAttrPair(resourceName, "enabled", dataSourceName, "enabled"), + resource.TestCheckResourceAttrPair(resourceName, "description", dataSourceName, "description"), + resource.TestCheckResourceAttrSet(dataSourceName, "last_updated_date"), + resource.TestCheckResourceAttrSet(dataSourceName, "created_date"), + resource.TestCheckResourceAttr(dataSourceName, "tags.%", "0"), ), }, }, @@ -39,12 +39,12 @@ func TestAccAPIGatewayAPIKeyDataSource_basic(t *testing.T) { func testAccAPIKeyDataSourceConfig_basic(r string) string { return fmt.Sprintf(` -resource "aws_api_gateway_api_key" "example_key" { - name = "%s" +resource "aws_api_gateway_api_key" "test" { + name = %[1]q } -data "aws_api_gateway_api_key" "test_key" { - id = aws_api_gateway_api_key.example_key.id +data "aws_api_gateway_api_key" "test" { + id = aws_api_gateway_api_key.test.id } `, r) } From f10dc669f4979ffb8fb7058e235582f91403ea0d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 15:44:24 -0500 Subject: [PATCH 27/68] r/aws_api_gateway_authorizer: Add 'FindAuthorizerByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayAuthorizer_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayAuthorizer_ -timeout 180m === RUN TestAccAPIGatewayAuthorizer_basic === PAUSE TestAccAPIGatewayAuthorizer_basic === RUN TestAccAPIGatewayAuthorizer_cognito === PAUSE TestAccAPIGatewayAuthorizer_cognito === RUN TestAccAPIGatewayAuthorizer_Cognito_authorizerCredentials === PAUSE TestAccAPIGatewayAuthorizer_Cognito_authorizerCredentials === RUN TestAccAPIGatewayAuthorizer_switchAuthType === PAUSE TestAccAPIGatewayAuthorizer_switchAuthType === RUN TestAccAPIGatewayAuthorizer_switchAuthorizerTTL === PAUSE TestAccAPIGatewayAuthorizer_switchAuthorizerTTL === RUN TestAccAPIGatewayAuthorizer_authTypeValidation === PAUSE TestAccAPIGatewayAuthorizer_authTypeValidation === RUN TestAccAPIGatewayAuthorizer_Zero_ttl === PAUSE TestAccAPIGatewayAuthorizer_Zero_ttl === RUN TestAccAPIGatewayAuthorizer_disappears === PAUSE TestAccAPIGatewayAuthorizer_disappears === CONT TestAccAPIGatewayAuthorizer_basic === CONT TestAccAPIGatewayAuthorizer_switchAuthorizerTTL === CONT TestAccAPIGatewayAuthorizer_Zero_ttl --- PASS: TestAccAPIGatewayAuthorizer_Zero_ttl (40.43s) === CONT TestAccAPIGatewayAuthorizer_disappears --- PASS: TestAccAPIGatewayAuthorizer_disappears (29.82s) === CONT TestAccAPIGatewayAuthorizer_authTypeValidation --- PASS: TestAccAPIGatewayAuthorizer_basic (102.47s) === CONT TestAccAPIGatewayAuthorizer_Cognito_authorizerCredentials --- PASS: TestAccAPIGatewayAuthorizer_Cognito_authorizerCredentials (27.22s) === CONT TestAccAPIGatewayAuthorizer_switchAuthType --- PASS: TestAccAPIGatewayAuthorizer_switchAuthorizerTTL (172.63s) === CONT TestAccAPIGatewayAuthorizer_cognito --- PASS: TestAccAPIGatewayAuthorizer_switchAuthType (70.49s) --- PASS: TestAccAPIGatewayAuthorizer_cognito (48.89s) --- PASS: TestAccAPIGatewayAuthorizer_authTypeValidation (259.02s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 334.375s --- internal/service/apigateway/authorizer.go | 121 +++++++++++------- .../service/apigateway/authorizer_test.go | 58 ++++----- 2 files changed, 95 insertions(+), 84 deletions(-) diff --git a/internal/service/apigateway/authorizer.go b/internal/service/apigateway/authorizer.go index 7695ced063e..b39a3f7fc31 100644 --- a/internal/service/apigateway/authorizer.go +++ b/internal/service/apigateway/authorizer.go @@ -11,11 +11,13 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -102,22 +104,25 @@ func ResourceAuthorizer() *schema.Resource { func resourceAuthorizerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - var postCreateOps []*apigateway.PatchOperation - input := apigateway.CreateAuthorizerInput{ + if err := validateAuthorizerType(d); err != nil { + return sdkdiag.AppendErrorf(diags, "creating API Gateway Authorizer: %s", err) + } + + var postCreateOps []*apigateway.PatchOperation + name := d.Get("name").(string) + input := &apigateway.CreateAuthorizerInput{ IdentitySource: aws.String(d.Get("identity_source").(string)), - Name: aws.String(d.Get("name").(string)), + Name: aws.String(name), RestApiId: aws.String(d.Get("rest_api_id").(string)), Type: aws.String(d.Get("type").(string)), AuthorizerResultTtlInSeconds: aws.Int64(int64(d.Get("authorizer_result_ttl_in_seconds").(int))), } - if err := validateAuthorizerType(d); err != nil { - return sdkdiag.AppendErrorf(diags, "creating API Gateway Authorizer: %s", err) - } if v, ok := d.GetOk("authorizer_uri"); ok { input.AuthorizerUri = aws.String(v.(string)) } + if v, ok := d.GetOk("authorizer_credentials"); ok { // While the CreateAuthorizer method allows one to pass AuthorizerCredentials // regardless of authorizer Type, the API ignores this setting if the authorizer @@ -137,29 +142,30 @@ func resourceAuthorizerCreate(ctx context.Context, d *schema.ResourceData, meta if v, ok := d.GetOk("identity_validation_expression"); ok { input.IdentityValidationExpression = aws.String(v.(string)) } + if v, ok := d.GetOk("provider_arns"); ok { input.ProviderARNs = flex.ExpandStringSet(v.(*schema.Set)) } - log.Printf("[INFO] Creating API Gateway Authorizer: %s", input) - out, err := conn.CreateAuthorizerWithContext(ctx, &input) + output, err := conn.CreateAuthorizerWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating API Gateway Authorizer: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway Authorizer (%s): %s", name, err) } - d.SetId(aws.StringValue(out.Id)) + d.SetId(aws.StringValue(output.Id)) if postCreateOps != nil { - input := apigateway.UpdateAuthorizerInput{ + input := &apigateway.UpdateAuthorizerInput{ AuthorizerId: aws.String(d.Id()), PatchOperations: postCreateOps, RestApiId: input.RestApiId, } - log.Printf("[INFO] Applying update operations to API Gateway Authorizer: %s", d.Id()) - _, err := conn.UpdateAuthorizerWithContext(ctx, &input) + _, err := conn.UpdateAuthorizerWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "applying update operations to API Gateway Authorizer (%s) failed: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating API Gateway Authorizer (%s): %s", d.Id(), err) } } @@ -170,33 +176,32 @@ func resourceAuthorizerRead(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[INFO] Reading API Gateway Authorizer %s", d.Id()) + apiID := d.Get("rest_api_id").(string) + authorizer, err := FindAuthorizerByTwoPartKey(ctx, conn, d.Id(), apiID) - restApiId := d.Get("rest_api_id").(string) - input := apigateway.GetAuthorizerInput{ - AuthorizerId: aws.String(d.Id()), - RestApiId: aws.String(restApiId), + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Authorizer (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - authorizer, err := conn.GetAuthorizerWithContext(ctx, &input) if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Authorizer (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Authorizer (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Received API Gateway Authorizer: %s", authorizer) + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: "apigateway", + Region: meta.(*conns.AWSClient).Region, + Resource: fmt.Sprintf("/restapis/%s/authorizers/%s", apiID, d.Id()), + }.String() + d.Set("arn", arn) d.Set("authorizer_credentials", authorizer.AuthorizerCredentials) - if authorizer.AuthorizerResultTtlInSeconds != nil { // nosemgrep:ci.helper-schema-ResourceData-Set-extraneous-nil-check d.Set("authorizer_result_ttl_in_seconds", authorizer.AuthorizerResultTtlInSeconds) } else { d.Set("authorizer_result_ttl_in_seconds", DefaultAuthorizerTTL) } - d.Set("authorizer_uri", authorizer.AuthorizerUri) d.Set("identity_source", authorizer.IdentitySource) d.Set("identity_validation_expression", authorizer.IdentityValidationExpression) @@ -204,14 +209,6 @@ func resourceAuthorizerRead(ctx context.Context, d *schema.ResourceData, meta in d.Set("type", authorizer.Type) d.Set("provider_arns", flex.FlattenStringSet(authorizer.ProviderARNs)) - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: "apigateway", - Region: meta.(*conns.AWSClient).Region, - Resource: fmt.Sprintf("/restapis/%s/authorizers/%s", restApiId, d.Id()), - }.String() - d.Set("arn", arn) - return diags } @@ -219,11 +216,6 @@ func resourceAuthorizerUpdate(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - input := apigateway.UpdateAuthorizerInput{ - AuthorizerId: aws.String(d.Id()), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - } - operations := make([]*apigateway.PatchOperation, 0) if d.HasChange("authorizer_uri") { @@ -298,12 +290,16 @@ func resourceAuthorizerUpdate(ctx context.Context, d *schema.ResourceData, meta } } - input.PatchOperations = operations + input := &apigateway.UpdateAuthorizerInput{ + AuthorizerId: aws.String(d.Id()), + PatchOperations: operations, + RestApiId: aws.String(d.Get("rest_api_id").(string)), + } + + _, err := conn.UpdateAuthorizerWithContext(ctx, input) - log.Printf("[INFO] Updating API Gateway Authorizer: %s", input) - _, err := conn.UpdateAuthorizerWithContext(ctx, &input) if err != nil { - return sdkdiag.AppendErrorf(diags, "updating API Gateway Authorizer failed: %s", err) + return sdkdiag.AppendErrorf(diags, "updating API Gateway Authorizer (%s): %s", d.Id(), err) } return append(diags, resourceAuthorizerRead(ctx, d, meta)...) @@ -312,12 +308,13 @@ func resourceAuthorizerUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceAuthorizerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - input := apigateway.DeleteAuthorizerInput{ + + log.Printf("[INFO] Deleting API Gateway Authorizer: %s", d.Id()) + _, err := conn.DeleteAuthorizerWithContext(ctx, &apigateway.DeleteAuthorizerInput{ AuthorizerId: aws.String(d.Id()), RestApiId: aws.String(d.Get("rest_api_id").(string)), - } - log.Printf("[INFO] Deleting API Gateway Authorizer: %s", input) - _, err := conn.DeleteAuthorizerWithContext(ctx, &input) + }) + if err != nil { // XXX: Figure out a way to delete the method that depends on the authorizer first // otherwise the authorizer will be dangling until the API is deleted @@ -360,3 +357,29 @@ func validateAuthorizerType(d *schema.ResourceData) error { return nil } + +func FindAuthorizerByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, authorizerID, apiID string) (*apigateway.Authorizer, error) { + input := &apigateway.GetAuthorizerInput{ + AuthorizerId: aws.String(authorizerID), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetAuthorizerWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/authorizer_test.go b/internal/service/apigateway/authorizer_test.go index 02080547565..3430a06f2dc 100644 --- a/internal/service/apigateway/authorizer_test.go +++ b/internal/service/apigateway/authorizer_test.go @@ -7,8 +7,6 @@ import ( "strconv" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -16,6 +14,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayAuthorizer_basic(t *testing.T) { @@ -316,7 +315,7 @@ func TestAccAPIGatewayAuthorizer_disappears(t *testing.T) { }) } -func testAccCheckAuthorizerExists(ctx context.Context, n string, res *apigateway.Authorizer) resource.TestCheckFunc { +func testAccCheckAuthorizerExists(ctx context.Context, n string, v *apigateway.Authorizer) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -329,16 +328,13 @@ func testAccCheckAuthorizerExists(ctx context.Context, n string, res *apigateway conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetAuthorizerInput{ - AuthorizerId: aws.String(rs.Primary.ID), - RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), - } - describe, err := conn.GetAuthorizerWithContext(ctx, req) + output, err := tfapigateway.FindAuthorizerByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -353,25 +349,17 @@ func testAccCheckAuthorizerDestroy(ctx context.Context) resource.TestCheckFunc { continue } - req := &apigateway.GetAuthorizerInput{ - AuthorizerId: aws.String(rs.Primary.ID), - RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), - } - _, err := conn.GetAuthorizerWithContext(ctx, req) + _, err := tfapigateway.FindAuthorizerByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) - if err == nil { - return fmt.Errorf("API Gateway Authorizer still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Authorizer %s still exists", rs.Primary.ID) } return nil @@ -389,7 +377,7 @@ func testAccAuthorizerImportStateIdFunc(resourceName string) resource.ImportStat } } -func testAccAuthorizerBaseConfig(rName string) string { +func testAccAuthorizerConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { name = %[1]q @@ -466,18 +454,18 @@ resource "aws_lambda_function" "test" { } func testAccAuthorizerConfig_lambda(rName string) string { - return testAccAuthorizerBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_authorizer" "test" { name = %[1]q rest_api_id = aws_api_gateway_rest_api.test.id authorizer_uri = aws_lambda_function.test.invoke_arn authorizer_credentials = aws_iam_role.test.arn } -`, rName) +`, rName)) } func testAccAuthorizerConfig_lambdaUpdate(rName string) string { - return testAccAuthorizerBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_authorizer" "test" { name = "%[1]s_modified" rest_api_id = aws_api_gateway_rest_api.test.id @@ -486,11 +474,11 @@ resource "aws_api_gateway_authorizer" "test" { authorizer_result_ttl_in_seconds = 360 identity_validation_expression = ".*" } -`, rName) +`, rName)) } func testAccAuthorizerConfig_lambdaNoCache(rName string) string { - return testAccAuthorizerBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_authorizer" "test" { name = "%[1]s_modified" rest_api_id = aws_api_gateway_rest_api.test.id @@ -499,7 +487,7 @@ resource "aws_api_gateway_authorizer" "test" { authorizer_result_ttl_in_seconds = 0 identity_validation_expression = ".*" } -`, rName) +`, rName)) } func testAccAuthorizerConfig_cognito(rName string) string { @@ -586,24 +574,24 @@ resource "aws_api_gateway_authorizer" "test" { } func testAccAuthorizerConfig_authTypeValidationDefaultToken(rName string) string { - return testAccAuthorizerBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_authorizer" "test" { - name = "%s" + name = %[1]q rest_api_id = aws_api_gateway_rest_api.test.id authorizer_credentials = aws_iam_role.test.arn } -`, rName) +`, rName)) } func testAccAuthorizerConfig_authTypeValidationRequest(rName string) string { - return testAccAuthorizerBaseConfig(rName) + fmt.Sprintf(` + return acctest.ConfigCompose(testAccAuthorizerConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_authorizer" "test" { - name = "%s" + name = %[1]q type = "REQUEST" rest_api_id = aws_api_gateway_rest_api.test.id authorizer_credentials = aws_iam_role.test.arn } -`, rName) +`, rName)) } func testAccAuthorizerConfig_authTypeValidationCognito(rName string) string { From b55bf04e104637595f3b2084b6e0324263af0431 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 15:58:23 -0500 Subject: [PATCH 28/68] r/aws_api_gateway_client_certificate: Add 'FindClientCertificateByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayClientCertificate_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayClientCertificate_ -timeout 180m === RUN TestAccAPIGatewayClientCertificate_basic === PAUSE TestAccAPIGatewayClientCertificate_basic === RUN TestAccAPIGatewayClientCertificate_tags === PAUSE TestAccAPIGatewayClientCertificate_tags === RUN TestAccAPIGatewayClientCertificate_disappears === PAUSE TestAccAPIGatewayClientCertificate_disappears === CONT TestAccAPIGatewayClientCertificate_basic === CONT TestAccAPIGatewayClientCertificate_disappears === CONT TestAccAPIGatewayClientCertificate_tags --- PASS: TestAccAPIGatewayClientCertificate_disappears (16.33s) --- PASS: TestAccAPIGatewayClientCertificate_basic (30.98s) --- PASS: TestAccAPIGatewayClientCertificate_tags (40.83s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 46.363s --- .../service/apigateway/client_certificate.go | 136 +++++++++++------- .../apigateway/client_certificate_test.go | 37 ++--- 2 files changed, 97 insertions(+), 76 deletions(-) diff --git a/internal/service/apigateway/client_certificate.go b/internal/service/apigateway/client_certificate.go index d42aa1c8093..654cf6a694d 100644 --- a/internal/service/apigateway/client_certificate.go +++ b/internal/service/apigateway/client_certificate.go @@ -10,10 +10,12 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -23,28 +25,29 @@ func ResourceClientCertificate() *schema.Resource { ReadWithoutTimeout: resourceClientCertificateRead, UpdateWithoutTimeout: resourceClientCertificateUpdate, DeleteWithoutTimeout: resourceClientCertificateDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ - "description": { + "arn": { Type: schema.TypeString, - Optional: true, + Computed: true, }, "created_date": { Type: schema.TypeString, Computed: true, }, - "expiration_date": { + "description": { Type: schema.TypeString, - Computed: true, + Optional: true, }, - "pem_encoded_certificate": { + "expiration_date": { Type: schema.TypeString, Computed: true, }, - "arn": { + "pem_encoded_certificate": { Type: schema.TypeString, Computed: true, }, @@ -62,20 +65,23 @@ func resourceClientCertificateCreate(ctx context.Context, d *schema.ResourceData defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - input := apigateway.GenerateClientCertificateInput{} + input := &apigateway.GenerateClientCertificateInput{} + if v, ok := d.GetOk("description"); ok { input.Description = aws.String(v.(string)) } + if len(tags) > 0 { input.Tags = Tags(tags.IgnoreAWS()) } - log.Printf("[DEBUG] Generating API Gateway Client Certificate: %s", input) - out, err := conn.GenerateClientCertificateWithContext(ctx, &input) + + output, err := conn.GenerateClientCertificateWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Failed to generate client certificate: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway Client Certificate: %s", err) } - d.SetId(aws.StringValue(out.ClientCertificateId)) + d.SetId(aws.StringValue(output.ClientCertificateId)) return append(diags, resourceClientCertificateRead(ctx, d, meta)...) } @@ -86,20 +92,31 @@ func resourceClientCertificateRead(ctx context.Context, d *schema.ResourceData, defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - input := apigateway.GetClientCertificateInput{ - ClientCertificateId: aws.String(d.Id()), + cert, err := FindClientCertificateByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Client Certificate (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - out, err := conn.GetClientCertificateWithContext(ctx, &input) + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Client Certificate (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Client Certificate (%s): %s", d.Id(), err) } - tags := KeyValueTags(out.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: "apigateway", + Region: meta.(*conns.AWSClient).Region, + Resource: fmt.Sprintf("/clientcertificates/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("created_date", cert.CreatedDate.String()) + d.Set("description", cert.Description) + d.Set("expiration_date", cert.ExpirationDate.String()) + d.Set("pem_encoded_certificate", cert.PemEncodedCertificate) + + tags := KeyValueTags(cert.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { @@ -110,19 +127,6 @@ func resourceClientCertificateRead(ctx context.Context, d *schema.ResourceData, return sdkdiag.AppendErrorf(diags, "setting tags_all: %s", err) } - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: "apigateway", - Region: meta.(*conns.AWSClient).Region, - Resource: fmt.Sprintf("/clientcertificates/%s", d.Id()), - }.String() - d.Set("arn", arn) - - d.Set("description", out.Description) - d.Set("created_date", out.CreatedDate.String()) - d.Set("expiration_date", out.ExpirationDate.String()) - d.Set("pem_encoded_certificate", out.PemEncodedCertificate) - return diags } @@ -130,28 +134,28 @@ func resourceClientCertificateUpdate(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - operations := make([]*apigateway.PatchOperation, 0) - if d.HasChange("description") { - operations = append(operations, &apigateway.PatchOperation{ - Op: aws.String(apigateway.OpReplace), - Path: aws.String("/description"), - Value: aws.String(d.Get("description").(string)), - }) - } + if d.HasChangesExcept("tags", "tags_all") { + input := &apigateway.UpdateClientCertificateInput{ + ClientCertificateId: aws.String(d.Id()), + PatchOperations: []*apigateway.PatchOperation{ + { + Op: aws.String(apigateway.OpReplace), + Path: aws.String("/description"), + Value: aws.String(d.Get("description").(string)), + }, + }, + } - input := apigateway.UpdateClientCertificateInput{ - ClientCertificateId: aws.String(d.Id()), - PatchOperations: operations, - } + _, err := conn.UpdateClientCertificateWithContext(ctx, input) - log.Printf("[DEBUG] Updating API Gateway Client Certificate: %s", input) - _, err := conn.UpdateClientCertificateWithContext(ctx, &input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "Updating API Gateway Client Certificate failed: %s", err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating API Gateway Client Certificate (%s): %s", d.Id(), err) + } } if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") + if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { return sdkdiag.AppendErrorf(diags, "updating tags: %s", err) } @@ -163,14 +167,40 @@ func resourceClientCertificateUpdate(ctx context.Context, d *schema.ResourceData func resourceClientCertificateDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() + log.Printf("[DEBUG] Deleting API Gateway Client Certificate: %s", d.Id()) - input := apigateway.DeleteClientCertificateInput{ + _, err := conn.DeleteClientCertificateWithContext(ctx, &apigateway.DeleteClientCertificateInput{ ClientCertificateId: aws.String(d.Id()), - } - _, err := conn.DeleteClientCertificateWithContext(ctx, &input) + }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Deleting API Gateway Client Certificate failed: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting API Gateway Client Certificate (%s): %s", d.Id(), err) } return diags } + +func FindClientCertificateByID(ctx context.Context, conn *apigateway.APIGateway, id string) (*apigateway.ClientCertificate, error) { + input := &apigateway.GetClientCertificateInput{ + ClientCertificateId: aws.String(id), + } + + output, err := conn.GetClientCertificateWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/client_certificate_test.go b/internal/service/apigateway/client_certificate_test.go index 6b4469e2596..c2f502d6f75 100644 --- a/internal/service/apigateway/client_certificate_test.go +++ b/internal/service/apigateway/client_certificate_test.go @@ -6,14 +6,13 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayClientCertificate_basic(t *testing.T) { @@ -120,7 +119,7 @@ func TestAccAPIGatewayClientCertificate_disappears(t *testing.T) { }) } -func testAccCheckClientCertificateExists(ctx context.Context, n string, res *apigateway.ClientCertificate) resource.TestCheckFunc { +func testAccCheckClientCertificateExists(ctx context.Context, n string, v *apigateway.ClientCertificate) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -133,15 +132,13 @@ func testAccCheckClientCertificateExists(ctx context.Context, n string, res *api conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetClientCertificateInput{ - ClientCertificateId: aws.String(rs.Primary.ID), - } - out, err := conn.GetClientCertificateWithContext(ctx, req) + output, err := tfapigateway.FindClientCertificateByID(ctx, conn, rs.Primary.ID) + if err != nil { return err } - *res = *out + *v = *output return nil } @@ -156,23 +153,17 @@ func testAccCheckClientCertificateDestroy(ctx context.Context) resource.TestChec continue } - req := &apigateway.GetClientCertificateInput{ - ClientCertificateId: aws.String(rs.Primary.ID), - } - out, err := conn.GetClientCertificateWithContext(ctx, req) - if err == nil { - return fmt.Errorf("API Gateway Client Certificate still exists: %s", out) - } + _, err := tfapigateway.FindClientCertificateByID(ctx, conn, rs.Primary.ID) - awsErr, ok := err.(awserr.Error) - if !ok { - return err + if tfresource.NotFound(err) { + continue } - if awsErr.Code() != apigateway.ErrCodeNotFoundException { + + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Client Certificate %s still exists", rs.Primary.ID) } return nil @@ -197,7 +188,7 @@ resource "aws_api_gateway_client_certificate" "test" { description = "Hello from TF acceptance test" tags = { - %q = %q + %[1]q = %[2]q } } `, tagKey1, tagValue1) @@ -209,8 +200,8 @@ resource "aws_api_gateway_client_certificate" "test" { description = "Hello from TF acceptance test" tags = { - %q = %q - %q = %q + %[1]q = %[2]q + %[3]q = %[4]q } } `, tagKey1, tagValue1, tagKey2, tagValue2) From cd0c218bc1741685f1fabc7ecb8d1e6fb19830ad Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 16:15:15 -0500 Subject: [PATCH 29/68] r/aws_api_gateway_gateway_response: Add 'FindGatewayResponseByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayGatewayResponse_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayGatewayResponse_ -timeout 180m === RUN TestAccAPIGatewayGatewayResponse_basic === PAUSE TestAccAPIGatewayGatewayResponse_basic === RUN TestAccAPIGatewayGatewayResponse_disappears === PAUSE TestAccAPIGatewayGatewayResponse_disappears === CONT TestAccAPIGatewayGatewayResponse_basic === CONT TestAccAPIGatewayGatewayResponse_disappears --- PASS: TestAccAPIGatewayGatewayResponse_disappears (16.25s) --- PASS: TestAccAPIGatewayGatewayResponse_basic (68.01s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 73.704s --- .../service/apigateway/gateway_response.go | 123 ++++++++++-------- .../apigateway/gateway_response_test.go | 55 +++----- 2 files changed, 89 insertions(+), 89 deletions(-) diff --git a/internal/service/apigateway/gateway_response.go b/internal/service/apigateway/gateway_response.go index b5fc1aa0091..8f7fbca8ed5 100644 --- a/internal/service/apigateway/gateway_response.go +++ b/internal/service/apigateway/gateway_response.go @@ -10,9 +10,12 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceGatewayResponse() *schema.Resource { @@ -21,6 +24,7 @@ func ResourceGatewayResponse() *schema.Resource { ReadWithoutTimeout: resourceGatewayResponseRead, UpdateWithoutTimeout: resourceGatewayResponsePut, DeleteWithoutTimeout: resourceGatewayResponseDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -37,34 +41,30 @@ func ResourceGatewayResponse() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { + "response_parameters": { + Type: schema.TypeMap, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "response_templates": { + Type: schema.TypeMap, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "response_type": { Type: schema.TypeString, Required: true, ForceNew: true, }, - - "response_type": { + "rest_api_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "status_code": { Type: schema.TypeString, Optional: true, }, - - "response_templates": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - - "response_parameters": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, }, } } @@ -73,40 +73,32 @@ func resourceGatewayResponsePut(ctx context.Context, d *schema.ResourceData, met var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - templates := make(map[string]string) - if kv, ok := d.GetOk("response_templates"); ok { - for k, v := range kv.(map[string]interface{}) { - templates[k] = v.(string) - } + input := &apigateway.PutGatewayResponseInput{ + ResponseType: aws.String(d.Get("response_type").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), } - parameters := make(map[string]string) - if kv, ok := d.GetOk("response_parameters"); ok { - for k, v := range kv.(map[string]interface{}) { - parameters[k] = v.(string) - } + if v, ok := d.GetOk("response_parameters"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseParameters = flex.ExpandStringMap(v.(map[string]interface{})) } - input := apigateway.PutGatewayResponseInput{ - RestApiId: aws.String(d.Get("rest_api_id").(string)), - ResponseType: aws.String(d.Get("response_type").(string)), - ResponseTemplates: aws.StringMap(templates), - ResponseParameters: aws.StringMap(parameters), + if v, ok := d.GetOk("response_templates"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseTemplates = flex.ExpandStringMap(v.(map[string]interface{})) } if v, ok := d.GetOk("status_code"); ok { input.StatusCode = aws.String(v.(string)) } - log.Printf("[DEBUG] Putting API Gateway Gateway Response: %s", input) + _, err := conn.PutGatewayResponseWithContext(ctx, input) - _, err := conn.PutGatewayResponseWithContext(ctx, &input) if err != nil { - return sdkdiag.AppendErrorf(diags, "Error putting API Gateway Gateway Response: %s", err) + return sdkdiag.AppendErrorf(diags, "putting API Gateway Gateway Response: %s", err) } - d.SetId(fmt.Sprintf("aggr-%s-%s", d.Get("rest_api_id").(string), d.Get("response_type").(string))) - log.Printf("[DEBUG] API Gateway Gateway Response put (%q)", d.Id()) + if d.IsNewResource() { + d.SetId(fmt.Sprintf("aggr-%s-%s", d.Get("rest_api_id").(string), d.Get("response_type").(string))) + } return append(diags, resourceGatewayResponseRead(ctx, d, meta)...) } @@ -115,26 +107,22 @@ func resourceGatewayResponseRead(ctx context.Context, d *schema.ResourceData, me var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Gateway Response %s", d.Id()) - gatewayResponse, err := conn.GetGatewayResponseWithContext(ctx, &apigateway.GetGatewayResponseInput{ - RestApiId: aws.String(d.Get("rest_api_id").(string)), - ResponseType: aws.String(d.Get("response_type").(string)), - }) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Gateway Response (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading API Gateway Response (%s): %s", d.Id(), err) + gatewayResponse, err := FindGatewayResponseByTwoPartKey(ctx, conn, d.Get("response_type").(string), d.Get("rest_api_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Gateway Response (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - log.Printf("[DEBUG] Received API Gateway Gateway Response: %s", gatewayResponse) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading API Gateway Gateway Response (%s): %s", d.Id(), err) + } + d.Set("response_parameters", aws.StringValueMap(gatewayResponse.ResponseParameters)) + d.Set("response_templates", aws.StringValueMap(gatewayResponse.ResponseTemplates)) d.Set("response_type", gatewayResponse.ResponseType) d.Set("status_code", gatewayResponse.StatusCode) - d.Set("response_templates", aws.StringValueMap(gatewayResponse.ResponseTemplates)) - d.Set("response_parameters", aws.StringValueMap(gatewayResponse.ResponseParameters)) return diags } @@ -142,11 +130,11 @@ func resourceGatewayResponseRead(ctx context.Context, d *schema.ResourceData, me func resourceGatewayResponseDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Gateway Response: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Gateway Response: %s", d.Id()) _, err := conn.DeleteGatewayResponseWithContext(ctx, &apigateway.DeleteGatewayResponseInput{ - RestApiId: aws.String(d.Get("rest_api_id").(string)), ResponseType: aws.String(d.Get("response_type").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), }) if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { @@ -154,7 +142,34 @@ func resourceGatewayResponseDelete(ctx context.Context, d *schema.ResourceData, } if err != nil { - return sdkdiag.AppendErrorf(diags, "Error deleting API Gateway gateway response: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting API Gateway Gateway Response (%s): %s", d.Id(), err) } + return diags } + +func FindGatewayResponseByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, responseType, apiID string) (*apigateway.UpdateGatewayResponseOutput, error) { + input := &apigateway.GetGatewayResponseInput{ + ResponseType: aws.String(responseType), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetGatewayResponseWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/gateway_response_test.go b/internal/service/apigateway/gateway_response_test.go index 89193626bd6..d01f8de8fff 100644 --- a/internal/service/apigateway/gateway_response_test.go +++ b/internal/service/apigateway/gateway_response_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,13 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayGatewayResponse_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.UpdateGatewayResponseOutput - - rName := sdkacctest.RandString(10) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_gateway_response.test" resource.ParallelTest(t, resource.TestCase{ @@ -39,7 +37,12 @@ func TestAccAPIGatewayGatewayResponse_basic(t *testing.T) { resource.TestCheckNoResourceAttr(resourceName, "response_templates.application/json"), ), }, - + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccGatewayResponseImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, { Config: testAccGatewayResponseConfig_update(rName), Check: resource.ComposeTestCheckFunc( @@ -50,12 +53,6 @@ func TestAccAPIGatewayGatewayResponse_basic(t *testing.T) { resource.TestCheckNoResourceAttr(resourceName, "response_parameters.gatewayresponse.header.Authorization"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccGatewayResponseImportStateIdFunc(resourceName), - ImportStateVerify: true, - }, }, }) } @@ -63,8 +60,7 @@ func TestAccAPIGatewayGatewayResponse_basic(t *testing.T) { func TestAccAPIGatewayGatewayResponse_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.UpdateGatewayResponseOutput - - rName := sdkacctest.RandString(10) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_gateway_response.test" resource.ParallelTest(t, resource.TestCase{ @@ -85,7 +81,7 @@ func TestAccAPIGatewayGatewayResponse_disappears(t *testing.T) { }) } -func testAccCheckGatewayResponseExists(ctx context.Context, n string, res *apigateway.UpdateGatewayResponseOutput) resource.TestCheckFunc { +func testAccCheckGatewayResponseExists(ctx context.Context, n string, v *apigateway.UpdateGatewayResponseOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -98,16 +94,13 @@ func testAccCheckGatewayResponseExists(ctx context.Context, n string, res *apiga conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetGatewayResponseInput{ - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - ResponseType: aws.String(rs.Primary.Attributes["response_type"]), - } - describe, err := conn.GetGatewayResponseWithContext(ctx, req) + output, err := tfapigateway.FindGatewayResponseByTwoPartKey(ctx, conn, rs.Primary.Attributes["response_type"], rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -122,25 +115,17 @@ func testAccCheckGatewayResponseDestroy(ctx context.Context) resource.TestCheckF continue } - req := &apigateway.GetGatewayResponseInput{ - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - ResponseType: aws.String(rs.Primary.Attributes["response_type"]), - } - _, err := conn.GetGatewayResponseWithContext(ctx, req) + _, err := tfapigateway.FindGatewayResponseByTwoPartKey(ctx, conn, rs.Primary.Attributes["response_type"], rs.Primary.Attributes["rest_api_id"]) - if err == nil { - return fmt.Errorf("API Gateway Gateway Response still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Gateway Response %s still exists", rs.Primary.ID) } return nil @@ -161,7 +146,7 @@ func testAccGatewayResponseImportStateIdFunc(resourceName string) resource.Impor func testAccGatewayResponseConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_gateway_response" "test" { @@ -183,7 +168,7 @@ resource "aws_api_gateway_gateway_response" "test" { func testAccGatewayResponseConfig_update(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_gateway_response" "test" { From 643d68090c00b74feae42db810624076f7a67efa Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 16:37:41 -0500 Subject: [PATCH 30/68] r/aws_api_gateway_integration_response: Add 'FindIntegrationResponseByFourPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayIntegrationResponse_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayIntegrationResponse_ -timeout 180m === RUN TestAccAPIGatewayIntegrationResponse_basic === PAUSE TestAccAPIGatewayIntegrationResponse_basic === RUN TestAccAPIGatewayIntegrationResponse_disappears === PAUSE TestAccAPIGatewayIntegrationResponse_disappears === CONT TestAccAPIGatewayIntegrationResponse_basic === CONT TestAccAPIGatewayIntegrationResponse_disappears --- PASS: TestAccAPIGatewayIntegrationResponse_disappears (19.03s) --- PASS: TestAccAPIGatewayIntegrationResponse_basic (62.88s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 68.595s --- .../apigateway/integration_response.go | 167 +++++++++--------- .../apigateway/integration_response_test.go | 133 ++++---------- 2 files changed, 124 insertions(+), 176 deletions(-) diff --git a/internal/service/apigateway/integration_response.go b/internal/service/apigateway/integration_response.go index 468617d22b9..73c60fc9431 100644 --- a/internal/service/apigateway/integration_response.go +++ b/internal/service/apigateway/integration_response.go @@ -10,17 +10,21 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceIntegrationResponse() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceIntegrationResponseCreate, + CreateWithoutTimeout: resourceIntegrationResponsePut, ReadWithoutTimeout: resourceIntegrationResponseRead, - UpdateWithoutTimeout: resourceIntegrationResponseCreate, + UpdateWithoutTimeout: resourceIntegrationResponsePut, DeleteWithoutTimeout: resourceIntegrationResponseDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -41,97 +45,85 @@ func ResourceIntegrationResponse() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "resource_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + "content_handling": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validIntegrationContentHandling(), }, - "http_method": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validHTTPMethod(), }, - - "status_code": { + "resource_id": { Type: schema.TypeString, Required: true, + ForceNew: true, }, - - "selection_pattern": { - Type: schema.TypeString, + "response_parameters": { + Type: schema.TypeMap, + Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, }, - "response_templates": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - - "response_parameters": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "selection_pattern": { + Type: schema.TypeString, Optional: true, }, - - "content_handling": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: validIntegrationContentHandling(), + "status_code": { + Type: schema.TypeString, + Required: true, }, }, } } -func resourceIntegrationResponseCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceIntegrationResponsePut(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - templates := make(map[string]string) - for k, v := range d.Get("response_templates").(map[string]interface{}) { - templates[k] = v.(string) + input := &apigateway.PutIntegrationResponseInput{ + HttpMethod: aws.String(d.Get("http_method").(string)), + ResourceId: aws.String(d.Get("resource_id").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + StatusCode: aws.String(d.Get("status_code").(string)), } - parameters := make(map[string]string) - if kv, ok := d.GetOk("response_parameters"); ok { - for k, v := range kv.(map[string]interface{}) { - parameters[k] = v.(string) - } + if v, ok := d.GetOk("content_handling"); ok { + input.ContentHandling = aws.String(v.(string)) } - var contentHandling *string - if val, ok := d.GetOk("content_handling"); ok { - contentHandling = aws.String(val.(string)) + if v, ok := d.GetOk("response_parameters"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseParameters = flex.ExpandStringMap(v.(map[string]interface{})) } - input := apigateway.PutIntegrationResponseInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - StatusCode: aws.String(d.Get("status_code").(string)), - ResponseTemplates: aws.StringMap(templates), - ResponseParameters: aws.StringMap(parameters), - ContentHandling: contentHandling, + if v, ok := d.GetOk("response_templates"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseTemplates = flex.ExpandStringMap(v.(map[string]interface{})) } + if v, ok := d.GetOk("selection_pattern"); ok { input.SelectionPattern = aws.String(v.(string)) } - _, err := conn.PutIntegrationResponseWithContext(ctx, &input) + _, err := conn.PutIntegrationResponseWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Error creating API Gateway Integration Response: %s", err) + return sdkdiag.AppendErrorf(diags, "putting API Gateway Integration Response: %s", err) } - d.SetId(fmt.Sprintf("agir-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string))) - log.Printf("[DEBUG] API Gateway Integration Response ID: %s", d.Id()) + if d.IsNewResource() { + d.SetId(fmt.Sprintf("agir-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string))) + } return append(diags, resourceIntegrationResponseRead(ctx, d, meta)...) } @@ -140,39 +132,26 @@ func resourceIntegrationResponseRead(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Integration Response %s", d.Id()) - integrationResponse, err := conn.GetIntegrationResponseWithContext(ctx, &apigateway.GetIntegrationResponseInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - StatusCode: aws.String(d.Get("status_code").(string)), - }) + integrationResponse, err := FindIntegrationResponseByFourPartKey(ctx, conn, d.Get("http_method").(string), d.Get("resource_id").(string), d.Get("rest_api_id").(string), d.Get("status_code").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Integration Response (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Integration Response (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Integration Response (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Received API Gateway Integration Response: %s", integrationResponse) - d.Set("content_handling", integrationResponse.ContentHandling) - - if err := d.Set("response_parameters", aws.StringValueMap(integrationResponse.ResponseParameters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting response_parameters: %s", err) - } - - // We need to explicitly convert key = nil values into key = "", which aws.StringValueMap() removes - responseTemplateMap := make(map[string]string) - for key, valuePointer := range integrationResponse.ResponseTemplates { - responseTemplateMap[key] = aws.StringValue(valuePointer) + d.Set("response_parameters", aws.StringValueMap(integrationResponse.ResponseParameters)) + // We need to explicitly convert key = nil values into key = "", which aws.StringValueMap() removes. + responseTemplates := make(map[string]string) + for k, v := range integrationResponse.ResponseTemplates { + responseTemplates[k] = aws.StringValue(v) } - if err := d.Set("response_templates", responseTemplateMap); err != nil { - return sdkdiag.AppendErrorf(diags, "setting response_templates: %s", err) - } - + d.Set("response_templates", responseTemplates) d.Set("selection_pattern", integrationResponse.SelectionPattern) return diags @@ -181,8 +160,8 @@ func resourceIntegrationResponseRead(ctx context.Context, d *schema.ResourceData func resourceIntegrationResponseDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Integration Response: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Integration Response: %s", d.Id()) _, err := conn.DeleteIntegrationResponseWithContext(ctx, &apigateway.DeleteIntegrationResponseInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -200,3 +179,31 @@ func resourceIntegrationResponseDelete(ctx context.Context, d *schema.ResourceDa return diags } + +func FindIntegrationResponseByFourPartKey(ctx context.Context, conn *apigateway.APIGateway, httpMethod, resourceID, apiID, statusCode string) (*apigateway.IntegrationResponse, error) { + input := &apigateway.GetIntegrationResponseInput{ + HttpMethod: aws.String(httpMethod), + ResourceId: aws.String(resourceID), + RestApiId: aws.String(apiID), + StatusCode: aws.String(statusCode), + } + + output, err := conn.GetIntegrationResponseWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/integration_response_test.go b/internal/service/apigateway/integration_response_test.go index da7b3a6c254..e6540c90d57 100644 --- a/internal/service/apigateway/integration_response_test.go +++ b/internal/service/apigateway/integration_response_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,12 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayIntegrationResponse_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.IntegrationResponse - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(10)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration_response.test" resource.ParallelTest(t, resource.TestCase{ @@ -30,29 +29,16 @@ func TestAccAPIGatewayIntegrationResponse_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccIntegrationResponseConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckIntegrationResponseExists(ctx, resourceName, &conf), - testAccCheckIntegrationResponseAttributes(&conf), - resource.TestCheckResourceAttr( - resourceName, "response_templates.application/json", ""), - resource.TestCheckResourceAttr( - resourceName, "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), - resource.TestCheckResourceAttr( - resourceName, "content_handling", ""), - ), - }, - - { - Config: testAccIntegrationResponseConfig_update(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckIntegrationResponseExists(ctx, resourceName, &conf), - testAccCheckIntegrationResponseAttributesUpdate(&conf), - resource.TestCheckResourceAttr( - resourceName, "response_templates.application/json", "$input.path('$')"), - resource.TestCheckResourceAttr( - resourceName, "response_templates.application/xml", ""), - resource.TestCheckResourceAttr( - resourceName, "content_handling", "CONVERT_TO_BINARY"), + resource.TestCheckResourceAttr(resourceName, "content_handling", ""), + resource.TestCheckResourceAttr(resourceName, "response_parameters.%", "1"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.method.response.header.Content-Type", "integration.response.body.type"), + resource.TestCheckResourceAttr(resourceName, "response_templates.%", "2"), + resource.TestCheckResourceAttr(resourceName, "response_templates.application/json", ""), + resource.TestCheckResourceAttr(resourceName, "response_templates.application/xml", "#set($inputRoot = $input.path('$'))\n{ }"), + resource.TestCheckResourceAttr(resourceName, "selection_pattern", ".*"), + resource.TestCheckResourceAttr(resourceName, "status_code", "400"), ), }, { @@ -61,6 +47,19 @@ func TestAccAPIGatewayIntegrationResponse_basic(t *testing.T) { ImportStateIdFunc: testAccIntegrationResponseImportStateIdFunc(resourceName), ImportStateVerify: true, }, + { + Config: testAccIntegrationResponseConfig_update(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIntegrationResponseExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "content_handling", "CONVERT_TO_BINARY"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.%", "0"), + resource.TestCheckResourceAttr(resourceName, "response_templates.%", "2"), + resource.TestCheckResourceAttr(resourceName, "response_templates.application/json", "$input.path('$')"), + resource.TestCheckResourceAttr(resourceName, "response_templates.application/xml", ""), + resource.TestCheckResourceAttr(resourceName, "selection_pattern", ""), + resource.TestCheckResourceAttr(resourceName, "status_code", "400"), + ), + }, }, }) } @@ -68,7 +67,7 @@ func TestAccAPIGatewayIntegrationResponse_basic(t *testing.T) { func TestAccAPIGatewayIntegrationResponse_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.IntegrationResponse - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(10)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration_response.test" resource.ParallelTest(t, resource.TestCase{ @@ -89,50 +88,7 @@ func TestAccAPIGatewayIntegrationResponse_disappears(t *testing.T) { }) } -func testAccCheckIntegrationResponseAttributes(conf *apigateway.IntegrationResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.StatusCode != "400" { - return fmt.Errorf("wrong StatusCode: %q", *conf.StatusCode) - } - if conf.ResponseTemplates["application/json"] != nil { - return fmt.Errorf("wrong ResponseTemplate for application/json") - } - if *conf.ResponseTemplates["application/xml"] != "#set($inputRoot = $input.path('$'))\n{ }" { - return fmt.Errorf("wrong ResponseTemplate for application/xml") - } - if conf.SelectionPattern == nil || *conf.SelectionPattern != ".*" { - return fmt.Errorf("wrong SelectionPattern (expected .*)") - } - if *conf.ResponseParameters["method.response.header.Content-Type"] != "integration.response.body.type" { - return fmt.Errorf("wrong ResponseParameters for header.Content-Type") - } - return nil - } -} - -func testAccCheckIntegrationResponseAttributesUpdate(conf *apigateway.IntegrationResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.StatusCode != "400" { - return fmt.Errorf("wrong StatusCode: %q", *conf.StatusCode) - } - if *conf.ResponseTemplates["application/json"] != "$input.path('$')" { - return fmt.Errorf("wrong ResponseTemplate for application/json") - } - if conf.ResponseTemplates["application/xml"] != nil { - return fmt.Errorf("wrong ResponseTemplate for application/xml") - } - if conf.SelectionPattern != nil { - return fmt.Errorf("wrong SelectionPattern (expected nil)") - } - if conf.ResponseParameters["method.response.header.Content-Type"] != nil { - return fmt.Errorf("ResponseParameters for header.Content-Type shouldnt exist") - } - - return nil - } -} - -func testAccCheckIntegrationResponseExists(ctx context.Context, n string, res *apigateway.IntegrationResponse) resource.TestCheckFunc { +func testAccCheckIntegrationResponseExists(ctx context.Context, n string, v *apigateway.IntegrationResponse) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -140,23 +96,18 @@ func testAccCheckIntegrationResponseExists(ctx context.Context, n string, res *a } if rs.Primary.ID == "" { - return fmt.Errorf("No API Gateway Method ID is set") + return fmt.Errorf("No API Gateway Integration Response ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetIntegrationResponseInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - StatusCode: aws.String(rs.Primary.Attributes["status_code"]), - } - describe, err := conn.GetIntegrationResponseWithContext(ctx, req) + output, err := tfapigateway.FindIntegrationResponseByFourPartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"], rs.Primary.Attributes["status_code"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -171,27 +122,17 @@ func testAccCheckIntegrationResponseDestroy(ctx context.Context) resource.TestCh continue } - req := &apigateway.GetIntegrationResponseInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - StatusCode: aws.String(rs.Primary.Attributes["status_code"]), - } - _, err := conn.GetIntegrationResponseWithContext(ctx, req) + _, err := tfapigateway.FindIntegrationResponseByFourPartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"], rs.Primary.Attributes["status_code"]) - if err == nil { - return fmt.Errorf("API Gateway Method still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Integration Response %s still exists", rs.Primary.ID) } return nil @@ -212,7 +153,7 @@ func testAccIntegrationResponseImportStateIdFunc(resourceName string) resource.I func testAccIntegrationResponseConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_resource" "test" { @@ -282,7 +223,7 @@ resource "aws_api_gateway_integration_response" "test" { func testAccIntegrationResponseConfig_update(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_resource" "test" { From ce80f53da5e3e970afd2f0fc5a9d1392eb1ed387 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 17:14:56 -0500 Subject: [PATCH 31/68] r/aws_api_gateway_integration: Add 'FindIntegrationByThreePartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayIntegration_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayIntegration_ -timeout 180m === RUN TestAccAPIGatewayIntegration_basic === PAUSE TestAccAPIGatewayIntegration_basic === RUN TestAccAPIGatewayIntegration_contentHandling === PAUSE TestAccAPIGatewayIntegration_contentHandling === RUN TestAccAPIGatewayIntegration_CacheKey_parameters === PAUSE TestAccAPIGatewayIntegration_CacheKey_parameters === RUN TestAccAPIGatewayIntegration_integrationType === PAUSE TestAccAPIGatewayIntegration_integrationType === RUN TestAccAPIGatewayIntegration_TLS_insecureSkipVerification === PAUSE TestAccAPIGatewayIntegration_TLS_insecureSkipVerification === RUN TestAccAPIGatewayIntegration_disappears === PAUSE TestAccAPIGatewayIntegration_disappears === CONT TestAccAPIGatewayIntegration_basic === CONT TestAccAPIGatewayIntegration_integrationType === CONT TestAccAPIGatewayIntegration_CacheKey_parameters --- PASS: TestAccAPIGatewayIntegration_CacheKey_parameters (20.14s) === CONT TestAccAPIGatewayIntegration_disappears --- PASS: TestAccAPIGatewayIntegration_disappears (37.26s) === CONT TestAccAPIGatewayIntegration_TLS_insecureSkipVerification --- PASS: TestAccAPIGatewayIntegration_TLS_insecureSkipVerification (29.47s) === CONT TestAccAPIGatewayIntegration_contentHandling --- PASS: TestAccAPIGatewayIntegration_basic (116.95s) --- PASS: TestAccAPIGatewayIntegration_contentHandling (40.68s) --- PASS: TestAccAPIGatewayIntegration_integrationType (675.99s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 681.215s --- internal/service/apigateway/integration.go | 208 ++++++++---------- .../service/apigateway/integration_test.go | 64 ++---- 2 files changed, 120 insertions(+), 152 deletions(-) diff --git a/internal/service/apigateway/integration.go b/internal/service/apigateway/integration.go index 46f572ef1d6..a4d5418bbb6 100644 --- a/internal/service/apigateway/integration.go +++ b/internal/service/apigateway/integration.go @@ -11,11 +11,13 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceIntegration() *schema.Resource { @@ -24,6 +26,7 @@ func ResourceIntegration() *schema.Resource { ReadWithoutTimeout: resourceIntegrationRead, UpdateWithoutTimeout: resourceIntegrationUpdate, DeleteWithoutTimeout: resourceIntegrationDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -42,89 +45,48 @@ func ResourceIntegration() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "resource_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "http_method": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validHTTPMethod(), - }, - - "type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - apigateway.IntegrationTypeHttp, - apigateway.IntegrationTypeAws, - apigateway.IntegrationTypeMock, - apigateway.IntegrationTypeHttpProxy, - apigateway.IntegrationTypeAwsProxy, - }, false), + "cache_key_parameters": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, }, - - "connection_type": { + "cache_namespace": { Type: schema.TypeString, Optional: true, - Default: apigateway.ConnectionTypeInternet, - ValidateFunc: validation.StringInSlice([]string{ - apigateway.ConnectionTypeInternet, - apigateway.ConnectionTypeVpcLink, - }, false), + Computed: true, }, - "connection_id": { Type: schema.TypeString, Optional: true, }, - - "uri": { - Type: schema.TypeString, - Optional: true, + "connection_type": { + Type: schema.TypeString, + Optional: true, + Default: apigateway.ConnectionTypeInternet, + ValidateFunc: validation.StringInSlice(apigateway.ConnectionType_Values(), false), + }, + "content_handling": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validIntegrationContentHandling(), }, - "credentials": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - - "integration_http_method": { + "http_method": { Type: schema.TypeString, - Optional: true, + Required: true, ForceNew: true, ValidateFunc: validHTTPMethod(), }, - - "request_templates": { - Type: schema.TypeMap, - Optional: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - - "request_parameters": { - Type: schema.TypeMap, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - }, - - "content_handling": { + "integration_http_method": { Type: schema.TypeString, Optional: true, - ValidateFunc: validIntegrationContentHandling(), + ForceNew: true, + ValidateFunc: validHTTPMethod(), }, - "passthrough_behavior": { Type: schema.TypeString, Optional: true, @@ -136,31 +98,35 @@ func ResourceIntegration() *schema.Resource { "NEVER", }, false), }, - - "cache_key_parameters": { - Type: schema.TypeSet, + "request_parameters": { + Type: schema.TypeMap, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, Optional: true, }, - - "cache_namespace": { - Type: schema.TypeString, + "request_templates": { + Type: schema.TypeMap, Optional: true, - Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "resource_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, - "timeout_milliseconds": { Type: schema.TypeInt, Optional: true, ValidateFunc: validation.IntBetween(50, 29000), Default: 29000, }, - "tls_config": { Type: schema.TypeList, Optional: true, - MinItems: 0, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -171,6 +137,16 @@ func ResourceIntegration() *schema.Resource { }, }, }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(apigateway.IntegrationType_Values(), false), + }, + "uri": { + Type: schema.TypeString, + Optional: true, + }, }, } } @@ -179,8 +155,6 @@ func resourceIntegrationCreate(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Print("[DEBUG] Creating API Gateway Integration") - input := &apigateway.PutIntegrationInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -245,7 +219,7 @@ func resourceIntegrationCreate(ctx context.Context, d *schema.ResourceData, meta _, err := conn.PutIntegrationWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "Error creating API Gateway Integration: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway Integration: %s", err) } d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string))) @@ -257,25 +231,19 @@ func resourceIntegrationRead(ctx context.Context, d *schema.ResourceData, meta i var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Integration: %s", d.Id()) - integration, err := conn.GetIntegrationWithContext(ctx, &apigateway.GetIntegrationInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - }) + integration, err := FindIntegrationByThreePartKey(ctx, conn, d.Get("http_method").(string), d.Get("resource_id").(string), d.Get("rest_api_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Integration (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Integration (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Integration (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Received API Gateway Integration: %s", integration) - if err := d.Set("cache_key_parameters", flex.FlattenStringList(integration.CacheKeyParameters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting cache_key_parameters: %s", err) - } + d.Set("cache_key_parameters", aws.StringValueSlice(integration.CacheKeyParameters)) d.Set("cache_namespace", integration.CacheNamespace) d.Set("connection_id", integration.ConnectionId) d.Set("connection_type", apigateway.ConnectionTypeInternet) @@ -286,20 +254,13 @@ func resourceIntegrationRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("credentials", integration.Credentials) d.Set("integration_http_method", integration.HttpMethod) d.Set("passthrough_behavior", integration.PassthroughBehavior) - - if err := d.Set("request_parameters", aws.StringValueMap(integration.RequestParameters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting request_parameters: %s", err) - } - + d.Set("request_parameters", aws.StringValueMap(integration.RequestParameters)) // We need to explicitly convert key = nil values into key = "", which aws.StringValueMap() removes - requestTemplateMap := make(map[string]string) - for key, valuePointer := range integration.RequestTemplates { - requestTemplateMap[key] = aws.StringValue(valuePointer) - } - if err := d.Set("request_templates", requestTemplateMap); err != nil { - return sdkdiag.AppendErrorf(diags, "setting request_templates: %s", err) + requestTemplates := make(map[string]string) + for k, v := range integration.RequestTemplates { + requestTemplates[k] = aws.StringValue(v) } - + d.Set("request_templates", requestTemplates) d.Set("timeout_milliseconds", integration.TimeoutInMillis) d.Set("type", integration.Type) d.Set("uri", integration.Uri) @@ -315,7 +276,6 @@ func resourceIntegrationUpdate(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating API Gateway Integration: %s", d.Id()) operations := make([]*apigateway.PatchOperation, 0) // https://docs.aws.amazon.com/apigateway/api-reference/link-relation/integration-update/#remarks @@ -484,28 +444,27 @@ func resourceIntegrationUpdate(ctx context.Context, d *schema.ResourceData, meta } } - params := &apigateway.UpdateIntegrationInput{ + input := &apigateway.UpdateIntegrationInput{ HttpMethod: aws.String(d.Get("http_method").(string)), + PatchOperations: operations, ResourceId: aws.String(d.Get("resource_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), - PatchOperations: operations, } - _, err := conn.UpdateIntegrationWithContext(ctx, params) + _, err := conn.UpdateIntegrationWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Error updating API Gateway Integration: %s", err) + return sdkdiag.AppendErrorf(diags, "updating API Gateway Integration (%s): %s", d.Id(), err) } - d.SetId(fmt.Sprintf("agi-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string))) - return append(diags, resourceIntegrationRead(ctx, d, meta)...) } func resourceIntegrationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Integration: %s", d.Id()) _, err := conn.DeleteIntegrationWithContext(ctx, &apigateway.DeleteIntegrationInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -523,6 +482,33 @@ func resourceIntegrationDelete(ctx context.Context, d *schema.ResourceData, meta return diags } +func FindIntegrationByThreePartKey(ctx context.Context, conn *apigateway.APIGateway, httpMethod, resourceID, apiID string) (*apigateway.Integration, error) { + input := &apigateway.GetIntegrationInput{ + HttpMethod: aws.String(httpMethod), + ResourceId: aws.String(resourceID), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetIntegrationWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + func expandTLSConfig(vConfig []interface{}) *apigateway.TlsConfig { config := &apigateway.TlsConfig{} diff --git a/internal/service/apigateway/integration_test.go b/internal/service/apigateway/integration_test.go index cc31bde42bb..f5490430875 100644 --- a/internal/service/apigateway/integration_test.go +++ b/internal/service/apigateway/integration_test.go @@ -6,8 +6,6 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -15,12 +13,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayIntegration_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -49,7 +48,12 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "tls_config.#", "0"), ), }, - + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccIntegrationImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, { Config: testAccIntegrationConfig_update(rName), Check: resource.ComposeTestCheckFunc( @@ -69,7 +73,6 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "2000"), ), }, - { Config: testAccIntegrationConfig_updateURI(rName), Check: resource.ComposeTestCheckFunc( @@ -89,7 +92,6 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "2000"), ), }, - { Config: testAccIntegrationConfig_updateNoTemplates(rName), Check: resource.ComposeTestCheckFunc( @@ -105,7 +107,6 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "2000"), ), }, - { Config: testAccIntegrationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( @@ -124,12 +125,6 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "timeout_milliseconds", "29000"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccIntegrationImportStateIdFunc(resourceName), - ImportStateVerify: true, - }, }, }) } @@ -137,7 +132,7 @@ func TestAccAPIGatewayIntegration_basic(t *testing.T) { func TestAccAPIGatewayIntegration_contentHandling(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -214,7 +209,7 @@ func TestAccAPIGatewayIntegration_contentHandling(t *testing.T) { func TestAccAPIGatewayIntegration_CacheKey_parameters(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -258,7 +253,7 @@ func TestAccAPIGatewayIntegration_CacheKey_parameters(t *testing.T) { func TestAccAPIGatewayIntegration_integrationType(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -304,7 +299,7 @@ func TestAccAPIGatewayIntegration_integrationType(t *testing.T) { func TestAccAPIGatewayIntegration_TLS_insecureSkipVerification(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -342,7 +337,7 @@ func TestAccAPIGatewayIntegration_TLS_insecureSkipVerification(t *testing.T) { func TestAccAPIGatewayIntegration_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Integration - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(7)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_integration.test" resource.ParallelTest(t, resource.TestCase{ @@ -363,7 +358,7 @@ func TestAccAPIGatewayIntegration_disappears(t *testing.T) { }) } -func testAccCheckIntegrationExists(ctx context.Context, n string, res *apigateway.Integration) resource.TestCheckFunc { +func testAccCheckIntegrationExists(ctx context.Context, n string, v *apigateway.Integration) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -371,22 +366,18 @@ func testAccCheckIntegrationExists(ctx context.Context, n string, res *apigatewa } if rs.Primary.ID == "" { - return fmt.Errorf("No API Gateway Method ID is set") + return fmt.Errorf("No API Gateway Integration ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetIntegrationInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetIntegrationWithContext(ctx, req) + output, err := tfapigateway.FindIntegrationByThreePartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -401,26 +392,17 @@ func testAccCheckIntegrationDestroy(ctx context.Context) resource.TestCheckFunc continue } - req := &apigateway.GetIntegrationInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - _, err := conn.GetIntegrationWithContext(ctx, req) + _, err := tfapigateway.FindIntegrationByThreePartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"]) - if err == nil { - return fmt.Errorf("API Gateway Method still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Integration %s still exists", rs.Primary.ID) } return nil From 1e314c8cb166bf92be0f8659a38e23f9b038e224 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 17:27:43 -0500 Subject: [PATCH 32/68] r/aws_api_gateway_method_response: Add 'FindMethodResponseByFourPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayMethodResponse_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayMethodResponse_ -timeout 180m === RUN TestAccAPIGatewayMethodResponse_basic === PAUSE TestAccAPIGatewayMethodResponse_basic === RUN TestAccAPIGatewayMethodResponse_disappears === PAUSE TestAccAPIGatewayMethodResponse_disappears === CONT TestAccAPIGatewayMethodResponse_basic === CONT TestAccAPIGatewayMethodResponse_disappears --- PASS: TestAccAPIGatewayMethodResponse_disappears (17.03s) --- PASS: TestAccAPIGatewayMethodResponse_basic (57.75s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 62.911s --- .../service/apigateway/method_response.go | 126 ++++++++-------- .../apigateway/method_response_test.go | 135 +++++------------- 2 files changed, 108 insertions(+), 153 deletions(-) diff --git a/internal/service/apigateway/method_response.go b/internal/service/apigateway/method_response.go index de5f0d28091..9d1b2af94da 100644 --- a/internal/service/apigateway/method_response.go +++ b/internal/service/apigateway/method_response.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "strconv" "strings" "sync" "time" @@ -13,9 +12,11 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) @@ -27,6 +28,7 @@ func ResourceMethodResponse() *schema.Resource { ReadWithoutTimeout: resourceMethodResponseRead, UpdateWithoutTimeout: resourceMethodResponseUpdate, DeleteWithoutTimeout: resourceMethodResponseDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -47,41 +49,36 @@ func ResourceMethodResponse() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "resource_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "http_method": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validHTTPMethod(), }, - - "status_code": { + "resource_id": { Type: schema.TypeString, Required: true, + ForceNew: true, }, - "response_models": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "response_parameters": { Type: schema.TypeMap, Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, }, + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "status_code": { + Type: schema.TypeString, + Required: true, + }, }, } } @@ -90,34 +87,26 @@ func resourceMethodResponseCreate(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - models := make(map[string]string) - for k, v := range d.Get("response_models").(map[string]interface{}) { - models[k] = v.(string) + input := &apigateway.PutMethodResponseInput{ + HttpMethod: aws.String(d.Get("http_method").(string)), + ResourceId: aws.String(d.Get("resource_id").(string)), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + StatusCode: aws.String(d.Get("status_code").(string)), } - parameters := make(map[string]bool) - if kv, ok := d.GetOk("response_parameters"); ok { - for k, v := range kv.(map[string]interface{}) { - parameters[k], ok = v.(bool) - if !ok { - value, _ := strconv.ParseBool(v.(string)) - parameters[k] = value - } - } + if v, ok := d.GetOk("response_models"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseModels = flex.ExpandStringMap(v.(map[string]interface{})) + } + + if v, ok := d.GetOk("response_parameters"); ok && len(v.(map[string]interface{})) > 0 { + input.ResponseParameters = flex.ExpandBoolMap(v.(map[string]interface{})) } resourceMethodResponseMutex.Lock() defer resourceMethodResponseMutex.Unlock() _, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, 2*time.Minute, func() (interface{}, error) { - return conn.PutMethodResponseWithContext(ctx, &apigateway.PutMethodResponseInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - StatusCode: aws.String(d.Get("status_code").(string)), - ResponseModels: aws.StringMap(models), - ResponseParameters: aws.BoolMap(parameters), - }) + return conn.PutMethodResponseWithContext(ctx, input) }, apigateway.ErrCodeConflictException) if err != nil { @@ -125,7 +114,6 @@ func resourceMethodResponseCreate(ctx context.Context, d *schema.ResourceData, m } d.SetId(fmt.Sprintf("agmr-%s-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string), d.Get("status_code").(string))) - log.Printf("[DEBUG] API Gateway Method ID: %s", d.Id()) return diags } @@ -134,19 +122,15 @@ func resourceMethodResponseRead(ctx context.Context, d *schema.ResourceData, met var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Method Response %s", d.Id()) - methodResponse, err := conn.GetMethodResponseWithContext(ctx, &apigateway.GetMethodResponseInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - StatusCode: aws.String(d.Get("status_code").(string)), - }) + methodResponse, err := FindMethodResponseByFourPartKey(ctx, conn, d.Get("http_method").(string), d.Get("resource_id").(string), d.Get("rest_api_id").(string), d.Get("status_code").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Method Response (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Method Response (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Method Response (%s): %s", d.Id(), err) } @@ -167,7 +151,6 @@ func resourceMethodResponseUpdate(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating API Gateway Method Response %s", d.Id()) operations := make([]*apigateway.PatchOperation, 0) if d.HasChange("response_models") { @@ -175,17 +158,18 @@ func resourceMethodResponseUpdate(ctx context.Context, d *schema.ResourceData, m } if d.HasChange("response_parameters") { - ops := expandMethodParametersOperations(d, "response_parameters", "responseParameters") - operations = append(operations, ops...) + operations = append(operations, expandMethodParametersOperations(d, "response_parameters", "responseParameters")...) } - _, err := conn.UpdateMethodResponseWithContext(ctx, &apigateway.UpdateMethodResponseInput{ + input := &apigateway.UpdateMethodResponseInput{ HttpMethod: aws.String(d.Get("http_method").(string)), + PatchOperations: operations, ResourceId: aws.String(d.Get("resource_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), StatusCode: aws.String(d.Get("status_code").(string)), - PatchOperations: operations, - }) + } + + _, err := conn.UpdateMethodResponseWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Method Response (%s): %s", d.Id(), err) @@ -197,8 +181,8 @@ func resourceMethodResponseUpdate(ctx context.Context, d *schema.ResourceData, m func resourceMethodResponseDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Method Response: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Method Response: %s", d.Id()) _, err := conn.DeleteMethodResponseWithContext(ctx, &apigateway.DeleteMethodResponseInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -216,3 +200,31 @@ func resourceMethodResponseDelete(ctx context.Context, d *schema.ResourceData, m return diags } + +func FindMethodResponseByFourPartKey(ctx context.Context, conn *apigateway.APIGateway, httpMethod, resourceID, apiID, statusCode string) (*apigateway.MethodResponse, error) { + input := &apigateway.GetMethodResponseInput{ + HttpMethod: aws.String(httpMethod), + ResourceId: aws.String(resourceID), + RestApiId: aws.String(apiID), + StatusCode: aws.String(statusCode), + } + + output, err := conn.GetMethodResponseWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/method_response_test.go b/internal/service/apigateway/method_response_test.go index 02bcca99a3d..836ffefe497 100644 --- a/internal/service/apigateway/method_response_test.go +++ b/internal/service/apigateway/method_response_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,13 +12,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayMethodResponse_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.MethodResponse - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(10)) - resourceName := "aws_api_gateway_method_response.error" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_api_gateway_method_response.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckAPIGatewayTypeEDGE(t) }, @@ -30,25 +29,14 @@ func TestAccAPIGatewayMethodResponse_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccMethodResponseConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckMethodResponseExists(ctx, resourceName, &conf), - testAccCheckMethodResponseAttributes(&conf), - resource.TestCheckResourceAttr( - resourceName, "status_code", "400"), - resource.TestCheckResourceAttr( - resourceName, "response_models.application/json", "Error"), - ), - }, - - { - Config: testAccMethodResponseConfig_update(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMethodResponseExists(ctx, resourceName, &conf), - testAccCheckMethodResponseAttributesUpdate(&conf), - resource.TestCheckResourceAttr( - resourceName, "status_code", "400"), - resource.TestCheckResourceAttr( - resourceName, "response_models.application/json", "Empty"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "response_models.%", "1"), + resource.TestCheckResourceAttr(resourceName, "response_models.application/json", "Error"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.%", "1"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.method.response.header.Content-Type", "true"), + resource.TestCheckResourceAttr(resourceName, "status_code", "400"), ), }, { @@ -57,6 +45,18 @@ func TestAccAPIGatewayMethodResponse_basic(t *testing.T) { ImportStateIdFunc: testAccMethodResponseImportStateIdFunc(resourceName), ImportStateVerify: true, }, + { + Config: testAccMethodResponseConfig_update(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckMethodResponseExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "response_models.%", "1"), + resource.TestCheckResourceAttr(resourceName, "response_models.application/json", "Empty"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.%", "1"), + resource.TestCheckResourceAttr(resourceName, "response_parameters.method.response.header.Host", "false"), + resource.TestCheckResourceAttr(resourceName, "status_code", "400"), + ), + }, }, }) } @@ -64,8 +64,8 @@ func TestAccAPIGatewayMethodResponse_basic(t *testing.T) { func TestAccAPIGatewayMethodResponse_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.MethodResponse - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(10)) - resourceName := "aws_api_gateway_method_response.error" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_api_gateway_method_response.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); acctest.PreCheckAPIGatewayTypeEDGE(t) }, @@ -85,49 +85,7 @@ func TestAccAPIGatewayMethodResponse_disappears(t *testing.T) { }) } -func testAccCheckMethodResponseAttributes(conf *apigateway.MethodResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.StatusCode == "" { - return fmt.Errorf("empty StatusCode") - } - if val, ok := conf.ResponseModels["application/json"]; !ok { - return fmt.Errorf("missing application/json ResponseModel") - } else { - if *val != "Error" { - return fmt.Errorf("wrong application/json ResponseModel") - } - } - if val, ok := conf.ResponseParameters["method.response.header.Content-Type"]; !ok { - return fmt.Errorf("missing Content-Type ResponseParameters") - } else { - if !*val { - return fmt.Errorf("wrong ResponseParameters value") - } - } - return nil - } -} - -func testAccCheckMethodResponseAttributesUpdate(conf *apigateway.MethodResponse) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.StatusCode == "" { - return fmt.Errorf("empty StatusCode") - } - if val, ok := conf.ResponseModels["application/json"]; !ok { - return fmt.Errorf("missing application/json ResponseModel") - } else { - if *val != "Empty" { - return fmt.Errorf("wrong application/json ResponseModel") - } - } - if conf.ResponseParameters["method.response.header.Content-Type"] != nil { - return fmt.Errorf("Content-Type ResponseParameters shouldn't exist") - } - return nil - } -} - -func testAccCheckMethodResponseExists(ctx context.Context, n string, res *apigateway.MethodResponse) resource.TestCheckFunc { +func testAccCheckMethodResponseExists(ctx context.Context, n string, v *apigateway.MethodResponse) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -135,23 +93,18 @@ func testAccCheckMethodResponseExists(ctx context.Context, n string, res *apigat } if rs.Primary.ID == "" { - return fmt.Errorf("No API Gateway Method ID is set") + return fmt.Errorf("No API Gateway Method Response ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetMethodResponseInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - StatusCode: aws.String(rs.Primary.Attributes["status_code"]), - } - describe, err := conn.GetMethodResponseWithContext(ctx, req) + output, err := tfapigateway.FindMethodResponseByFourPartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"], rs.Primary.Attributes["status_code"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -166,27 +119,17 @@ func testAccCheckMethodResponseDestroy(ctx context.Context) resource.TestCheckFu continue } - req := &apigateway.GetMethodResponseInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - StatusCode: aws.String(rs.Primary.Attributes["status_code"]), - } - _, err := conn.GetMethodResponseWithContext(ctx, req) + _, err := tfapigateway.FindMethodResponseByFourPartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"], rs.Primary.Attributes["status_code"]) - if err == nil { - return fmt.Errorf("API Gateway Method still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Method Response %s still exists", rs.Primary.ID) } return nil @@ -207,7 +150,7 @@ func testAccMethodResponseImportStateIdFunc(resourceName string) resource.Import func testAccMethodResponseConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_resource" "test" { @@ -227,7 +170,7 @@ resource "aws_api_gateway_method" "test" { } } -resource "aws_api_gateway_method_response" "error" { +resource "aws_api_gateway_method_response" "test" { rest_api_id = aws_api_gateway_rest_api.test.id resource_id = aws_api_gateway_resource.test.id http_method = aws_api_gateway_method.test.http_method @@ -247,7 +190,7 @@ resource "aws_api_gateway_method_response" "error" { func testAccMethodResponseConfig_update(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_resource" "test" { @@ -267,7 +210,7 @@ resource "aws_api_gateway_method" "test" { } } -resource "aws_api_gateway_method_response" "error" { +resource "aws_api_gateway_method_response" "test" { rest_api_id = aws_api_gateway_rest_api.test.id resource_id = aws_api_gateway_resource.test.id http_method = aws_api_gateway_method.test.http_method @@ -278,7 +221,7 @@ resource "aws_api_gateway_method_response" "error" { } response_parameters = { - "method.response.header.Host" = true + "method.response.header.Host" = false } } `, rName) From fca7bfd87e77f79d2edcd2c617b6ed5723f0636e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 17:35:37 -0500 Subject: [PATCH 33/68] Move 'aws-go-sdk-error-code-helper' semgrep rule to its own file. --- .ci/.semgrep.yml | 9 --------- .ci/semgrep/aws/awserr.yml | 9 +++++++++ .github/workflows/semgrep-ci.yml | 1 + GNUmakefile | 1 + 4 files changed, 11 insertions(+), 9 deletions(-) create mode 100644 .ci/semgrep/aws/awserr.yml diff --git a/.ci/.semgrep.yml b/.ci/.semgrep.yml index eb4e8e37954..bf75574e66d 100644 --- a/.ci/.semgrep.yml +++ b/.ci/.semgrep.yml @@ -835,15 +835,6 @@ rules: pattern: int(*$VALUE) severity: WARNING - - id: aws-go-sdk-error-code-helper - languages: [go] - message: 'Use `tfawserr` helpers for checking AWS Go SDK v1 errors (e.g. `tfawserr.ErrMessageContains(err, CODE, MESSAGE)`)' - paths: - include: - - internal/ - pattern: $AWSERR, $OK := $ORIGINALERR.(awserr.Error) - severity: WARNING - - id: fmt-Errorf-awserr-Error-Code languages: [go] message: Prefer `err` with `%w` format verb instead of `err.Code()` or `err.Message()` diff --git a/.ci/semgrep/aws/awserr.yml b/.ci/semgrep/aws/awserr.yml new file mode 100644 index 00000000000..dacb12f8164 --- /dev/null +++ b/.ci/semgrep/aws/awserr.yml @@ -0,0 +1,9 @@ +rules: + - id: aws-go-sdk-error-code-helper + languages: [go] + message: 'Use `tfawserr` helpers for checking AWS Go SDK v1 errors (e.g. `tfawserr.ErrMessageContains(err, CODE, MESSAGE)`)' + paths: + include: + - internal/ + pattern: $AWSERR, $OK := $ORIGINALERR.(awserr.Error) + severity: WARNING diff --git a/.github/workflows/semgrep-ci.yml b/.github/workflows/semgrep-ci.yml index 596e01c208c..6ec55ae6e6f 100644 --- a/.github/workflows/semgrep-ci.yml +++ b/.github/workflows/semgrep-ci.yml @@ -28,6 +28,7 @@ jobs: semgrep $COMMON_PARAMS \ --config .ci/.semgrep.yml \ --config .ci/semgrep/acctest/ \ + --config .ci/semgrep/aws/ \ --config 'r/dgryski.semgrep-go.badnilguard' \ --config 'r/dgryski.semgrep-go.errnilcheck' \ --config 'r/dgryski.semgrep-go.marshaljson' \ diff --git a/GNUmakefile b/GNUmakefile index 87a2b4f3f8d..2b93a2a4308 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -275,6 +275,7 @@ semall: --config .ci/.semgrep-service-name2.yml \ --config .ci/.semgrep-service-name3.yml \ --config .ci/semgrep/acctest/ \ + --config .ci/semgrep/aws/ \ --config .ci/semgrep/migrate/ \ --config 'r/dgryski.semgrep-go.badnilguard' \ --config 'r/dgryski.semgrep-go.errnilcheck' \ From 61c85264cf42a6b806ac77dcef8bbd6cc9fe36dc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sat, 4 Feb 2023 18:18:48 -0500 Subject: [PATCH 34/68] r/aws_api_gateway_method: Add 'FindMethodByThreePartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayMethod_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayMethod_ -timeout 180m === RUN TestAccAPIGatewayMethod_basic === PAUSE TestAccAPIGatewayMethod_basic === RUN TestAccAPIGatewayMethod_customAuthorizer === PAUSE TestAccAPIGatewayMethod_customAuthorizer === RUN TestAccAPIGatewayMethod_cognitoAuthorizer === PAUSE TestAccAPIGatewayMethod_cognitoAuthorizer === RUN TestAccAPIGatewayMethod_customRequestValidator === PAUSE TestAccAPIGatewayMethod_customRequestValidator === RUN TestAccAPIGatewayMethod_disappears === PAUSE TestAccAPIGatewayMethod_disappears === RUN TestAccAPIGatewayMethod_operationName === PAUSE TestAccAPIGatewayMethod_operationName === CONT TestAccAPIGatewayMethod_basic === CONT TestAccAPIGatewayMethod_customRequestValidator === CONT TestAccAPIGatewayMethod_cognitoAuthorizer --- PASS: TestAccAPIGatewayMethod_basic (27.82s) === CONT TestAccAPIGatewayMethod_customAuthorizer --- PASS: TestAccAPIGatewayMethod_customRequestValidator (43.34s) === CONT TestAccAPIGatewayMethod_operationName --- PASS: TestAccAPIGatewayMethod_operationName (30.74s) === CONT TestAccAPIGatewayMethod_disappears --- PASS: TestAccAPIGatewayMethod_customAuthorizer (94.84s) --- PASS: TestAccAPIGatewayMethod_disappears (71.16s) --- PASS: TestAccAPIGatewayMethod_cognitoAuthorizer (215.34s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 220.214s --- internal/service/apigateway/method.go | 188 ++++++------- internal/service/apigateway/method_test.go | 311 ++++++--------------- 2 files changed, 171 insertions(+), 328 deletions(-) diff --git a/internal/service/apigateway/method.go b/internal/service/apigateway/method.go index 58ab67567c0..bb471ab9220 100644 --- a/internal/service/apigateway/method.go +++ b/internal/service/apigateway/method.go @@ -4,17 +4,18 @@ import ( "context" "fmt" "log" - "strconv" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceMethod() *schema.Resource { @@ -23,6 +24,7 @@ func ResourceMethod() *schema.Resource { ReadWithoutTimeout: resourceMethodRead, UpdateWithoutTimeout: resourceMethodUpdate, DeleteWithoutTimeout: resourceMethodDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -41,68 +43,57 @@ func ResourceMethod() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { + "api_key_required": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "authorization": { Type: schema.TypeString, Required: true, - ForceNew: true, }, - - "resource_id": { + "authorization_scopes": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + }, + "authorizer_id": { Type: schema.TypeString, - Required: true, - ForceNew: true, + Optional: true, }, - "http_method": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validHTTPMethod(), }, - - "authorization": { - Type: schema.TypeString, - Required: true, - }, - - "authorizer_id": { + "operation_name": { Type: schema.TypeString, Optional: true, }, - - "authorization_scopes": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - Optional: true, - }, - - "api_key_required": { - Type: schema.TypeBool, - Optional: true, - Default: false, - }, - "request_models": { Type: schema.TypeMap, Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, - "request_parameters": { Type: schema.TypeMap, Elem: &schema.Schema{Type: schema.TypeBool}, Optional: true, }, - "request_validator_id": { Type: schema.TypeString, Optional: true, }, - - "operation_name": { + "resource_id": { Type: schema.TypeString, - Optional: true, + Required: true, + ForceNew: true, + }, + "rest_api_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, }, } @@ -112,32 +103,12 @@ func resourceMethodCreate(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - input := apigateway.PutMethodInput{ + input := &apigateway.PutMethodInput{ + ApiKeyRequired: aws.Bool(d.Get("api_key_required").(bool)), AuthorizationType: aws.String(d.Get("authorization").(string)), HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), - ApiKeyRequired: aws.Bool(d.Get("api_key_required").(bool)), - } - - models := make(map[string]string) - for k, v := range d.Get("request_models").(map[string]interface{}) { - models[k] = v.(string) - } - if len(models) > 0 { - input.RequestModels = aws.StringMap(models) - } - - parameters := make(map[string]bool) - if kv, ok := d.GetOk("request_parameters"); ok { - for k, v := range kv.(map[string]interface{}) { - parameters[k], ok = v.(bool) - if !ok { - value, _ := strconv.ParseBool(v.(string)) - parameters[k] = value - } - } - input.RequestParameters = aws.BoolMap(parameters) } if v, ok := d.GetOk("authorizer_id"); ok { @@ -152,17 +123,25 @@ func resourceMethodCreate(ctx context.Context, d *schema.ResourceData, meta inte input.OperationName = aws.String(v.(string)) } + if v, ok := d.GetOk("request_models"); ok && len(v.(map[string]interface{})) > 0 { + input.RequestModels = flex.ExpandStringMap(v.(map[string]interface{})) + } + + if v, ok := d.GetOk("request_parameters"); ok && len(v.(map[string]interface{})) > 0 { + input.RequestParameters = flex.ExpandBoolMap(v.(map[string]interface{})) + } + if v, ok := d.GetOk("request_validator_id"); ok { input.RequestValidatorId = aws.String(v.(string)) } - _, err := conn.PutMethodWithContext(ctx, &input) + _, err := conn.PutMethodWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating API Gateway Method: %s", err) } d.SetId(fmt.Sprintf("agm-%s-%s-%s", d.Get("rest_api_id").(string), d.Get("resource_id").(string), d.Get("http_method").(string))) - log.Printf("[DEBUG] API Gateway Method ID: %s", d.Id()) return diags } @@ -171,41 +150,26 @@ func resourceMethodRead(ctx context.Context, d *schema.ResourceData, meta interf var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Method %s", d.Id()) - out, err := conn.GetMethodWithContext(ctx, &apigateway.GetMethodInput{ - HttpMethod: aws.String(d.Get("http_method").(string)), - ResourceId: aws.String(d.Get("resource_id").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - }) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Method (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading API Gateway Method (%s): %s", d.Id(), err) - } - log.Printf("[DEBUG] Received API Gateway Method: %s", out) - - d.Set("api_key_required", out.ApiKeyRequired) - - if err := d.Set("authorization_scopes", flex.FlattenStringList(out.AuthorizationScopes)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting authorization_scopes: %s", err) - } - - d.Set("authorization", out.AuthorizationType) - d.Set("authorizer_id", out.AuthorizerId) - d.Set("operation_name", out.OperationName) + method, err := FindMethodByThreePartKey(ctx, conn, d.Get("http_method").(string), d.Get("resource_id").(string), d.Get("rest_api_id").(string)) - if err := d.Set("request_models", aws.StringValueMap(out.RequestModels)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting request_models: %s", err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Method (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - if err := d.Set("request_parameters", aws.BoolValueMap(out.RequestParameters)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting request_parameters: %s", err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading API Gateway Method (%s): %s", d.Id(), err) } - d.Set("request_validator_id", out.RequestValidatorId) + d.Set("api_key_required", method.ApiKeyRequired) + d.Set("authorization", method.AuthorizationType) + d.Set("authorization_scopes", aws.StringValueSlice(method.AuthorizationScopes)) + d.Set("authorizer_id", method.AuthorizerId) + d.Set("operation_name", method.OperationName) + d.Set("request_models", aws.StringValueMap(method.RequestModels)) + d.Set("request_parameters", aws.BoolValueMap(method.RequestParameters)) + d.Set("request_validator_id", method.RequestValidatorId) return diags } @@ -214,8 +178,8 @@ func resourceMethodUpdate(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating API Gateway Method %s", d.Id()) operations := make([]*apigateway.PatchOperation, 0) + if d.HasChange("resource_id") { operations = append(operations, &apigateway.PatchOperation{ Op: aws.String(apigateway.OpReplace), @@ -229,15 +193,6 @@ func resourceMethodUpdate(ctx context.Context, d *schema.ResourceData, meta inte } if d.HasChange("request_parameters") { - parameters := make(map[string]bool) - var ok bool - for k, v := range d.Get("request_parameters").(map[string]interface{}) { - parameters[k], ok = v.(bool) - if !ok { - value, _ := strconv.ParseBool(v.(string)) - parameters[k] = value - } - } ops := expandMethodParametersOperations(d, "request_parameters", "requestParameters") operations = append(operations, ops...) } @@ -322,12 +277,14 @@ func resourceMethodUpdate(ctx context.Context, d *schema.ResourceData, meta inte }) } - _, err := conn.UpdateMethodWithContext(ctx, &apigateway.UpdateMethodInput{ + input := &apigateway.UpdateMethodInput{ HttpMethod: aws.String(d.Get("http_method").(string)), + PatchOperations: operations, ResourceId: aws.String(d.Get("resource_id").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), - PatchOperations: operations, - }) + } + + _, err := conn.UpdateMethodWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Method (%s): %s", d.Id(), err) @@ -339,8 +296,8 @@ func resourceMethodUpdate(ctx context.Context, d *schema.ResourceData, meta inte func resourceMethodDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Method: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Method: %s", d.Id()) _, err := conn.DeleteMethodWithContext(ctx, &apigateway.DeleteMethodInput{ HttpMethod: aws.String(d.Get("http_method").(string)), ResourceId: aws.String(d.Get("resource_id").(string)), @@ -357,3 +314,30 @@ func resourceMethodDelete(ctx context.Context, d *schema.ResourceData, meta inte return diags } + +func FindMethodByThreePartKey(ctx context.Context, conn *apigateway.APIGateway, httpMethod, resourceID, apiID string) (*apigateway.Method, error) { + input := &apigateway.GetMethodInput{ + HttpMethod: aws.String(httpMethod), + ResourceId: aws.String(resourceID), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetMethodWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/method_test.go b/internal/service/apigateway/method_test.go index 59161ea6e83..8fba5aad4aa 100644 --- a/internal/service/apigateway/method_test.go +++ b/internal/service/apigateway/method_test.go @@ -3,11 +3,8 @@ package apigateway_test import ( "context" "fmt" - "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -15,12 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayMethod_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -30,13 +28,16 @@ func TestAccAPIGatewayMethod_basic(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_basic(rInt), - Check: resource.ComposeTestCheckFunc( + Config: testAccMethodConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributes(&conf), - resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), resource.TestCheckResourceAttr(resourceName, "authorization", "NONE"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "request_models.%", "1"), resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), + resource.TestCheckResourceAttr(resourceName, "request_parameters.%", "2"), + resource.TestCheckResourceAttr(resourceName, "request_parameters.method.request.header.Content-Type", "false"), + resource.TestCheckResourceAttr(resourceName, "request_parameters.method.request.querystring.page", "true"), ), }, { @@ -45,12 +46,16 @@ func TestAccAPIGatewayMethod_basic(t *testing.T) { ImportStateIdFunc: testAccMethodImportStateIdFunc(resourceName), ImportStateVerify: true, }, - { - Config: testAccMethodConfig_update(rInt), - Check: resource.ComposeTestCheckFunc( + Config: testAccMethodConfig_update(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributesUpdate(&conf), + resource.TestCheckResourceAttr(resourceName, "authorization", "NONE"), + resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), + resource.TestCheckResourceAttr(resourceName, "request_models.%", "1"), + resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), + resource.TestCheckResourceAttr(resourceName, "request_parameters.%", "1"), + resource.TestCheckResourceAttr(resourceName, "request_parameters.method.request.querystring.page", "false"), ), }, }, @@ -60,7 +65,7 @@ func TestAccAPIGatewayMethod_basic(t *testing.T) { func TestAccAPIGatewayMethod_customAuthorizer(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -70,14 +75,11 @@ func TestAccAPIGatewayMethod_customAuthorizer(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_customAuthorizer(rInt), + Config: testAccMethodConfig_customAuthorizer(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributes(&conf), - resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), resource.TestCheckResourceAttr(resourceName, "authorization", "CUSTOM"), - resource.TestMatchResourceAttr(resourceName, "authorizer_id", regexp.MustCompile("^[a-z0-9]{6}$")), - resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), + resource.TestCheckResourceAttrSet(resourceName, "authorizer_id"), ), }, { @@ -88,10 +90,9 @@ func TestAccAPIGatewayMethod_customAuthorizer(t *testing.T) { }, { - Config: testAccMethodConfig_update(rInt), + Config: testAccMethodConfig_update(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributesUpdate(&conf), resource.TestCheckResourceAttr(resourceName, "authorization", "NONE"), resource.TestCheckResourceAttr(resourceName, "authorizer_id", ""), ), @@ -103,7 +104,7 @@ func TestAccAPIGatewayMethod_customAuthorizer(t *testing.T) { func TestAccAPIGatewayMethod_cognitoAuthorizer(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -113,26 +114,21 @@ func TestAccAPIGatewayMethod_cognitoAuthorizer(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_cognitoAuthorizer(rInt), + Config: testAccMethodConfig_cognitoAuthorizer(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributes(&conf), - resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), resource.TestCheckResourceAttr(resourceName, "authorization", "COGNITO_USER_POOLS"), - resource.TestMatchResourceAttr(resourceName, "authorizer_id", regexp.MustCompile("^[a-z0-9]{6}$")), - resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), + resource.TestCheckResourceAttrSet(resourceName, "authorizer_id"), resource.TestCheckResourceAttr(resourceName, "authorization_scopes.#", "2"), ), }, { - Config: testAccMethodConfig_cognitoAuthorizerUpdate(rInt), + Config: testAccMethodConfig_cognitoAuthorizerUpdate(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributesUpdate(&conf), resource.TestCheckResourceAttr(resourceName, "authorization", "COGNITO_USER_POOLS"), - resource.TestMatchResourceAttr(resourceName, "authorizer_id", regexp.MustCompile("^[a-z0-9]{6}$")), - resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), + resource.TestCheckResourceAttrSet(resourceName, "authorizer_id"), resource.TestCheckResourceAttr(resourceName, "authorization_scopes.#", "3"), ), }, @@ -149,7 +145,7 @@ func TestAccAPIGatewayMethod_cognitoAuthorizer(t *testing.T) { func TestAccAPIGatewayMethod_customRequestValidator(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -159,14 +155,10 @@ func TestAccAPIGatewayMethod_customRequestValidator(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_customRequestValidator(rInt), + Config: testAccMethodConfig_customRequestValidator(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributes(&conf), - resource.TestCheckResourceAttr(resourceName, "http_method", "GET"), - resource.TestCheckResourceAttr(resourceName, "authorization", "NONE"), - resource.TestCheckResourceAttr(resourceName, "request_models.application/json", "Error"), - resource.TestMatchResourceAttr(resourceName, "request_validator_id", regexp.MustCompile("^[a-z0-9]{6}$")), + resource.TestCheckResourceAttrSet(resourceName, "request_validator_id"), ), }, { @@ -177,10 +169,9 @@ func TestAccAPIGatewayMethod_customRequestValidator(t *testing.T) { }, { - Config: testAccMethodConfig_customRequestValidatorUpdate(rInt), + Config: testAccMethodConfig_customRequestValidatorUpdate(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), - testAccCheckMethodAttributesUpdate(&conf), resource.TestCheckResourceAttr(resourceName, "request_validator_id", ""), ), }, @@ -191,7 +182,7 @@ func TestAccAPIGatewayMethod_customRequestValidator(t *testing.T) { func TestAccAPIGatewayMethod_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -201,7 +192,7 @@ func TestAccAPIGatewayMethod_disappears(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_basic(rInt), + Config: testAccMethodConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfapigateway.ResourceMethod(), resourceName), @@ -215,7 +206,7 @@ func TestAccAPIGatewayMethod_disappears(t *testing.T) { func TestAccAPIGatewayMethod_operationName(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Method - rInt := sdkacctest.RandInt() + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_method.test" resource.ParallelTest(t, resource.TestCase{ @@ -225,7 +216,7 @@ func TestAccAPIGatewayMethod_operationName(t *testing.T) { CheckDestroy: testAccCheckMethodDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMethodConfig_operationName(rInt, "getTest"), + Config: testAccMethodConfig_operationName(rName, "getTest"), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "operation_name", "getTest"), @@ -238,7 +229,7 @@ func TestAccAPIGatewayMethod_operationName(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccMethodConfig_operationName(rInt, "describeTest"), + Config: testAccMethodConfig_operationName(rName, "describeTest"), Check: resource.ComposeTestCheckFunc( testAccCheckMethodExists(ctx, resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "operation_name", "describeTest"), @@ -248,55 +239,7 @@ func TestAccAPIGatewayMethod_operationName(t *testing.T) { }) } -func testAccCheckMethodAttributes(conf *apigateway.Method) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.HttpMethod != "GET" { - return fmt.Errorf("Wrong HttpMethod: %q", *conf.HttpMethod) - } - if *conf.AuthorizationType != "NONE" && *conf.AuthorizationType != "CUSTOM" && *conf.AuthorizationType != "COGNITO_USER_POOLS" { - return fmt.Errorf("Wrong Authorization: %q", *conf.AuthorizationType) - } - - if val, ok := conf.RequestParameters["method.request.header.Content-Type"]; !ok { - return fmt.Errorf("missing Content-Type RequestParameters") - } else { - if *val { - return fmt.Errorf("wrong Content-Type RequestParameters value") - } - } - if val, ok := conf.RequestParameters["method.request.querystring.page"]; !ok { - return fmt.Errorf("missing page RequestParameters") - } else { - if !*val { - return fmt.Errorf("wrong query string RequestParameters value") - } - } - - return nil - } -} - -func testAccCheckMethodAttributesUpdate(conf *apigateway.Method) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.HttpMethod != "GET" { - return fmt.Errorf("Wrong HttpMethod: %q", *conf.HttpMethod) - } - if conf.RequestParameters["method.request.header.Content-Type"] != nil { - return fmt.Errorf("Content-Type RequestParameters shouldn't exist") - } - if val, ok := conf.RequestParameters["method.request.querystring.page"]; !ok { - return fmt.Errorf("missing updated page RequestParameters") - } else { - if *val { - return fmt.Errorf("wrong query string RequestParameters updated value") - } - } - - return nil - } -} - -func testAccCheckMethodExists(ctx context.Context, n string, res *apigateway.Method) resource.TestCheckFunc { +func testAccCheckMethodExists(ctx context.Context, n string, v *apigateway.Method) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -309,17 +252,13 @@ func testAccCheckMethodExists(ctx context.Context, n string, res *apigateway.Met conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetMethodInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetMethodWithContext(ctx, req) + output, err := tfapigateway.FindMethodByThreePartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -334,26 +273,17 @@ func testAccCheckMethodDestroy(ctx context.Context) resource.TestCheckFunc { continue } - req := &apigateway.GetMethodInput{ - HttpMethod: aws.String("GET"), - ResourceId: aws.String(s.RootModule().Resources["aws_api_gateway_resource.test"].Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - _, err := conn.GetMethodWithContext(ctx, req) + _, err := tfapigateway.FindMethodByThreePartKey(ctx, conn, rs.Primary.Attributes["http_method"], rs.Primary.Attributes["resource_id"], rs.Primary.Attributes["rest_api_id"]) - if err == nil { - return fmt.Errorf("API Gateway Method still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Method %s still exists", rs.Primary.ID) } return nil @@ -371,14 +301,14 @@ func testAccMethodImportStateIdFunc(resourceName string) resource.ImportStateIdF } } -func testAccMethodConfig_customAuthorizer(rInt int) string { +func testAccMethodConfig_customAuthorizer(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "tf-acc-test-custom-auth-%d" + name = %[1]q } resource "aws_iam_role" "invocation_role" { - name = "tf_acc_api_gateway_auth_invocation_role-%d" + name = "%[1]s-invocation" path = "/" assume_role_policy = < Date: Sat, 4 Feb 2023 18:34:47 -0500 Subject: [PATCH 35/68] r/aws_api_gateway_model: Add 'FindModelByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayModel_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayModel_ -timeout 180m === RUN TestAccAPIGatewayModel_basic === PAUSE TestAccAPIGatewayModel_basic === RUN TestAccAPIGatewayModel_disappears === PAUSE TestAccAPIGatewayModel_disappears === CONT TestAccAPIGatewayModel_basic === CONT TestAccAPIGatewayModel_disappears --- PASS: TestAccAPIGatewayModel_disappears (15.59s) --- PASS: TestAccAPIGatewayModel_basic (62.38s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 67.476s --- internal/service/apigateway/model.go | 123 +++++++++++++--------- internal/service/apigateway/model_test.go | 81 ++++---------- 2 files changed, 94 insertions(+), 110 deletions(-) diff --git a/internal/service/apigateway/model.go b/internal/service/apigateway/model.go index cd2d4fb14cb..bae2186ffb1 100644 --- a/internal/service/apigateway/model.go +++ b/internal/service/apigateway/model.go @@ -10,11 +10,13 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -24,6 +26,7 @@ func ResourceModel() *schema.Resource { ReadWithoutTimeout: resourceModelRead, UpdateWithoutTimeout: resourceModelUpdate, DeleteWithoutTimeout: resourceModelDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -53,23 +56,25 @@ func ResourceModel() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { + "content_type": { Type: schema.TypeString, Required: true, ForceNew: true, }, - + "description": { + Type: schema.TypeString, + Optional: true, + }, "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - - "description": { + "rest_api_id": { Type: schema.TypeString, - Optional: true, + Required: true, + ForceNew: true, }, - "schema": { Type: schema.TypeString, Optional: true, @@ -80,12 +85,6 @@ func ResourceModel() *schema.Resource { return json }, }, - - "content_type": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, }, } } @@ -93,32 +92,29 @@ func ResourceModel() *schema.Resource { func resourceModelCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Creating API Gateway Model") - var description *string + name := d.Get("name").(string) + input := &apigateway.CreateModelInput{ + ContentType: aws.String(d.Get("content_type").(string)), + Name: aws.String(name), + RestApiId: aws.String(d.Get("rest_api_id").(string)), + } + if v, ok := d.GetOk("description"); ok { - description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } - var schema *string + if v, ok := d.GetOk("schema"); ok { - schema = aws.String(v.(string)) + input.Schema = aws.String(v.(string)) } - var err error - model, err := conn.CreateModelWithContext(ctx, &apigateway.CreateModelInput{ - Name: aws.String(d.Get("name").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - ContentType: aws.String(d.Get("content_type").(string)), - - Description: description, - Schema: schema, - }) + output, err := conn.CreateModelWithContext(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "creating API Gateway Model: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway Model (%s): %s", name, err) } - d.SetId(aws.StringValue(model.Id)) + d.SetId(aws.StringValue(output.Id)) return diags } @@ -127,23 +123,21 @@ func resourceModelRead(ctx context.Context, d *schema.ResourceData, meta interfa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Model %s", d.Id()) - out, err := conn.GetModelWithContext(ctx, &apigateway.GetModelInput{ - ModelName: aws.String(d.Get("name").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - }) + model, err := FindModelByTwoPartKey(ctx, conn, d.Get("name").(string), d.Get("rest_api_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Model (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Model (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Model (%s): %s", d.Id(), err) } - d.Set("content_type", out.ContentType) - d.Set("description", out.Description) - d.Set("schema", out.Schema) + d.Set("content_type", model.ContentType) + d.Set("description", model.Description) + d.Set("schema", model.Schema) return diags } @@ -152,8 +146,8 @@ func resourceModelUpdate(ctx context.Context, d *schema.ResourceData, meta inter var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Model %s", d.Id()) operations := make([]*apigateway.PatchOperation, 0) + if d.HasChange("description") { operations = append(operations, &apigateway.PatchOperation{ Op: aws.String(apigateway.OpReplace), @@ -161,6 +155,7 @@ func resourceModelUpdate(ctx context.Context, d *schema.ResourceData, meta inter Value: aws.String(d.Get("description").(string)), }) } + if d.HasChange("schema") { operations = append(operations, &apigateway.PatchOperation{ Op: aws.String(apigateway.OpReplace), @@ -169,11 +164,14 @@ func resourceModelUpdate(ctx context.Context, d *schema.ResourceData, meta inter }) } - _, err := conn.UpdateModelWithContext(ctx, &apigateway.UpdateModelInput{ + input := &apigateway.UpdateModelInput{ ModelName: aws.String(d.Get("name").(string)), - RestApiId: aws.String(d.Get("rest_api_id").(string)), PatchOperations: operations, - }) + RestApiId: aws.String(d.Get("rest_api_id").(string)), + } + + _, err := conn.UpdateModelWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Model (%s): %s", d.Id(), err) } @@ -184,14 +182,12 @@ func resourceModelUpdate(ctx context.Context, d *schema.ResourceData, meta inter func resourceModelDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() + log.Printf("[DEBUG] Deleting API Gateway Model: %s", d.Id()) - input := &apigateway.DeleteModelInput{ + _, err := conn.DeleteModelWithContext(ctx, &apigateway.DeleteModelInput{ ModelName: aws.String(d.Get("name").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), - } - - log.Printf("[DEBUG] schema is %#v", d) - _, err := conn.DeleteModelWithContext(ctx, input) + }) if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return diags @@ -200,5 +196,32 @@ func resourceModelDelete(ctx context.Context, d *schema.ResourceData, meta inter if err != nil { return sdkdiag.AppendErrorf(diags, "deleting API Gateway Model (%s): %s", d.Id(), err) } + return diags } + +func FindModelByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, name, apiID string) (*apigateway.Model, error) { + input := &apigateway.GetModelInput{ + ModelName: aws.String(name), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetModelWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/model_test.go b/internal/service/apigateway/model_test.go index b226305e1df..79b9a07bc95 100644 --- a/internal/service/apigateway/model_test.go +++ b/internal/service/apigateway/model_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,14 +12,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayModel_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Model - rInt := sdkacctest.RandString(10) - rName := fmt.Sprintf("tf-acc-test-%s", rInt) - modelName := fmt.Sprintf("tfacctest%s", rInt) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + modelName := sdkacctest.RandString(16) resourceName := "aws_api_gateway_model.test" resource.ParallelTest(t, resource.TestCase{ @@ -33,14 +31,10 @@ func TestAccAPIGatewayModel_basic(t *testing.T) { { Config: testAccModelConfig_basic(rName, modelName), Check: resource.ComposeTestCheckFunc( - testAccCheckModelExists(ctx, resourceName, modelName, &conf), - testAccCheckModelAttributes(&conf, modelName), - resource.TestCheckResourceAttr( - resourceName, "name", modelName), - resource.TestCheckResourceAttr( - resourceName, "description", "a test schema"), - resource.TestCheckResourceAttr( - resourceName, "content_type", "application/json"), + testAccCheckModelExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "content_type", "application/json"), + resource.TestCheckResourceAttr(resourceName, "description", "a test schema"), + resource.TestCheckResourceAttr(resourceName, "name", modelName), ), }, { @@ -56,9 +50,8 @@ func TestAccAPIGatewayModel_basic(t *testing.T) { func TestAccAPIGatewayModel_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Model - rInt := sdkacctest.RandString(10) - rName := fmt.Sprintf("tf-acc-test-%s", rInt) - modelName := fmt.Sprintf("tfacctest%s", rInt) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + modelName := sdkacctest.RandString(16) resourceName := "aws_api_gateway_model.test" resource.ParallelTest(t, resource.TestCase{ @@ -70,7 +63,7 @@ func TestAccAPIGatewayModel_disappears(t *testing.T) { { Config: testAccModelConfig_basic(rName, modelName), Check: resource.ComposeTestCheckFunc( - testAccCheckModelExists(ctx, resourceName, modelName, &conf), + testAccCheckModelExists(ctx, resourceName, &conf), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfapigateway.ResourceModel(), resourceName), ), ExpectNonEmptyPlan: true, @@ -79,23 +72,7 @@ func TestAccAPIGatewayModel_disappears(t *testing.T) { }) } -func testAccCheckModelAttributes(conf *apigateway.Model, name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.Name != name { - return fmt.Errorf("Wrong Name: %q", *conf.Name) - } - if *conf.Description != "a test schema" { - return fmt.Errorf("Wrong Description: %q", *conf.Description) - } - if *conf.ContentType != "application/json" { - return fmt.Errorf("Wrong ContentType: %q", *conf.ContentType) - } - - return nil - } -} - -func testAccCheckModelExists(ctx context.Context, n, rName string, res *apigateway.Model) resource.TestCheckFunc { +func testAccCheckModelExists(ctx context.Context, n string, v *apigateway.Model) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -108,19 +85,13 @@ func testAccCheckModelExists(ctx context.Context, n, rName string, res *apigatew conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetModelInput{ - ModelName: aws.String(rName), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetModelWithContext(ctx, req) + output, err := tfapigateway.FindModelByTwoPartKey(ctx, conn, rs.Primary.Attributes["name"], rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - if *describe.Id != rs.Primary.ID { - return fmt.Errorf("APIGateway Model not found") - } - *res = *describe + *v = *output return nil } @@ -135,27 +106,17 @@ func testAccCheckModelDestroy(ctx context.Context) resource.TestCheckFunc { continue } - req := &apigateway.GetModelsInput{ - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetModelsWithContext(ctx, req) + _, err := tfapigateway.FindModelByTwoPartKey(ctx, conn, rs.Primary.Attributes["name"], rs.Primary.Attributes["rest_api_id"]) - if err == nil { - if len(describe.Items) != 0 && - *describe.Items[0].Id == rs.Primary.ID { - return fmt.Errorf("API Gateway Model still exists") - } + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Model %s still exists", rs.Primary.ID) } return nil @@ -176,12 +137,12 @@ func testAccModelImportStateIdFunc(resourceName string) resource.ImportStateIdFu func testAccModelConfig_basic(rName, modelName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } resource "aws_api_gateway_model" "test" { rest_api_id = aws_api_gateway_rest_api.test.id - name = "%s" + name = %[2]q description = "a test schema" content_type = "application/json" schema = < Date: Sun, 5 Feb 2023 15:24:18 -0500 Subject: [PATCH 36/68] r/aws_api_gateway_request_validator: Add 'FindRequestValidatorByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayRequestValidator_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayRequestValidator_ -timeout 180m === RUN TestAccAPIGatewayRequestValidator_basic === PAUSE TestAccAPIGatewayRequestValidator_basic === RUN TestAccAPIGatewayRequestValidator_disappears === PAUSE TestAccAPIGatewayRequestValidator_disappears === CONT TestAccAPIGatewayRequestValidator_basic === CONT TestAccAPIGatewayRequestValidator_disappears --- PASS: TestAccAPIGatewayRequestValidator_disappears (17.37s) --- PASS: TestAccAPIGatewayRequestValidator_basic (53.55s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 58.954s --- .../service/apigateway/request_validator.go | 82 ++++++++----- .../apigateway/request_validator_test.go | 110 +++++------------- 2 files changed, 81 insertions(+), 111 deletions(-) diff --git a/internal/service/apigateway/request_validator.go b/internal/service/apigateway/request_validator.go index fddaabc02d8..a7065436453 100644 --- a/internal/service/apigateway/request_validator.go +++ b/internal/service/apigateway/request_validator.go @@ -10,9 +10,11 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceRequestValidator() *schema.Resource { @@ -21,6 +23,7 @@ func ResourceRequestValidator() *schema.Resource { ReadWithoutTimeout: resourceRequestValidatorRead, UpdateWithoutTimeout: resourceRequestValidatorUpdate, DeleteWithoutTimeout: resourceRequestValidatorDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -36,23 +39,20 @@ func ResourceRequestValidator() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { + "name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, - - "name": { + "rest_api_id": { Type: schema.TypeString, Required: true, + ForceNew: true, }, - "validate_request_body": { Type: schema.TypeBool, Optional: true, Default: false, }, - "validate_request_parameters": { Type: schema.TypeBool, Optional: true, @@ -66,19 +66,21 @@ func resourceRequestValidatorCreate(ctx context.Context, d *schema.ResourceData, var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - input := apigateway.CreateRequestValidatorInput{ - Name: aws.String(d.Get("name").(string)), + name := d.Get("name").(string) + input := &apigateway.CreateRequestValidatorInput{ + Name: aws.String(name), RestApiId: aws.String(d.Get("rest_api_id").(string)), ValidateRequestBody: aws.Bool(d.Get("validate_request_body").(bool)), ValidateRequestParameters: aws.Bool(d.Get("validate_request_parameters").(bool)), } - out, err := conn.CreateRequestValidatorWithContext(ctx, &input) + output, err := conn.CreateRequestValidatorWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Error creating Request Validator: %s", err) + return sdkdiag.AppendErrorf(diags, "creating API Gateway Request Validator (%s): %s", name, err) } - d.SetId(aws.StringValue(out.Id)) + d.SetId(aws.StringValue(output.Id)) return diags } @@ -87,24 +89,21 @@ func resourceRequestValidatorRead(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - input := apigateway.GetRequestValidatorInput{ - RequestValidatorId: aws.String(d.Id()), - RestApiId: aws.String(d.Get("rest_api_id").(string)), + output, err := FindRequestValidatorByTwoPartKey(ctx, conn, d.Id(), d.Get("rest_api_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Request Validator (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - out, err := conn.GetRequestValidatorWithContext(ctx, &input) if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Request Validator (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Request Validator (%s): %s", d.Id(), err) } - d.Set("name", out.Name) - d.Set("validate_request_body", out.ValidateRequestBody) - d.Set("validate_request_parameters", out.ValidateRequestParameters) + d.Set("name", output.Name) + d.Set("validate_request_body", output.ValidateRequestBody) + d.Set("validate_request_parameters", output.ValidateRequestParameters) return diags } @@ -112,7 +111,6 @@ func resourceRequestValidatorRead(ctx context.Context, d *schema.ResourceData, m func resourceRequestValidatorUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating Request Validator %s", d.Id()) operations := make([]*apigateway.PatchOperation, 0) @@ -140,31 +138,31 @@ func resourceRequestValidatorUpdate(ctx context.Context, d *schema.ResourceData, }) } - input := apigateway.UpdateRequestValidatorInput{ + input := &apigateway.UpdateRequestValidatorInput{ RequestValidatorId: aws.String(d.Id()), RestApiId: aws.String(d.Get("rest_api_id").(string)), PatchOperations: operations, } - _, err := conn.UpdateRequestValidatorWithContext(ctx, &input) + _, err := conn.UpdateRequestValidatorWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Request Validator (%s): %s", d.Id(), err) } - log.Printf("[DEBUG] Updated Request Validator %s", d.Id()) - return append(diags, resourceRequestValidatorRead(ctx, d, meta)...) } func resourceRequestValidatorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting Request Validator %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Request Validator: %s", d.Id()) _, err := conn.DeleteRequestValidatorWithContext(ctx, &apigateway.DeleteRequestValidatorInput{ RequestValidatorId: aws.String(d.Id()), RestApiId: aws.String(d.Get("rest_api_id").(string)), }) + if err != nil { // XXX: Figure out a way to delete the method that depends on the request validator first // otherwise the validator will be dangling until the API is deleted @@ -175,3 +173,29 @@ func resourceRequestValidatorDelete(ctx context.Context, d *schema.ResourceData, return diags } + +func FindRequestValidatorByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, requestValidatorID, apiID string) (*apigateway.UpdateRequestValidatorOutput, error) { + input := &apigateway.GetRequestValidatorInput{ + RequestValidatorId: aws.String(requestValidatorID), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetRequestValidatorWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/request_validator_test.go b/internal/service/apigateway/request_validator_test.go index 410b5b2bdca..9b5fe77270f 100644 --- a/internal/service/apigateway/request_validator_test.go +++ b/internal/service/apigateway/request_validator_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,12 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayRequestValidator_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.UpdateRequestValidatorOutput - rName := fmt.Sprintf("tf-test-acc-%s", sdkacctest.RandString(8)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_request_validator.test" resource.ParallelTest(t, resource.TestCase{ @@ -32,32 +31,26 @@ func TestAccAPIGatewayRequestValidator_basic(t *testing.T) { Config: testAccRequestValidatorConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckRequestValidatorExists(ctx, resourceName, &conf), - testAccCheckRequestValidatorName(&conf, "tf-acc-test-request-validator"), - resource.TestCheckResourceAttr(resourceName, "name", "tf-acc-test-request-validator"), - testAccCheckRequestValidatorValidateRequestBody(&conf, false), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "validate_request_body", "false"), - testAccCheckRequestValidatorValidateRequestParameters(&conf, false), resource.TestCheckResourceAttr(resourceName, "validate_request_parameters", "false"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateIdFunc: testAccRequestValidatorImportStateIdFunc(resourceName), + ImportStateVerify: true, + }, { Config: testAccRequestValidatorConfig_updated(rName), Check: resource.ComposeTestCheckFunc( testAccCheckRequestValidatorExists(ctx, resourceName, &conf), - testAccCheckRequestValidatorName(&conf, "tf-acc-test-request-validator_modified"), - resource.TestCheckResourceAttr(resourceName, "name", "tf-acc-test-request-validator_modified"), - testAccCheckRequestValidatorValidateRequestBody(&conf, true), + resource.TestCheckResourceAttr(resourceName, "name", fmt.Sprintf("%s-modified", rName)), resource.TestCheckResourceAttr(resourceName, "validate_request_body", "true"), - testAccCheckRequestValidatorValidateRequestParameters(&conf, true), resource.TestCheckResourceAttr(resourceName, "validate_request_parameters", "true"), ), }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateIdFunc: testAccRequestValidatorImportStateIdFunc(resourceName), - ImportStateVerify: true, - }, }, }) } @@ -65,7 +58,7 @@ func TestAccAPIGatewayRequestValidator_basic(t *testing.T) { func TestAccAPIGatewayRequestValidator_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.UpdateRequestValidatorOutput - rName := fmt.Sprintf("tf-test-acc-%s", sdkacctest.RandString(8)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_request_validator.test" resource.ParallelTest(t, resource.TestCase{ @@ -86,43 +79,7 @@ func TestAccAPIGatewayRequestValidator_disappears(t *testing.T) { }) } -func testAccCheckRequestValidatorName(conf *apigateway.UpdateRequestValidatorOutput, expectedName string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if conf.Name == nil { - return fmt.Errorf("Empty Name, expected: %q", expectedName) - } - if *conf.Name != expectedName { - return fmt.Errorf("Name didn't match. Expected: %q, Given: %q", expectedName, *conf.Name) - } - return nil - } -} - -func testAccCheckRequestValidatorValidateRequestBody(conf *apigateway.UpdateRequestValidatorOutput, expectedValue bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - if conf.ValidateRequestBody == nil { - return fmt.Errorf("Empty ValidateRequestBody, expected: %t", expectedValue) - } - if *conf.ValidateRequestBody != expectedValue { - return fmt.Errorf("ValidateRequestBody didn't match. Expected: %t, Given: %t", expectedValue, *conf.ValidateRequestBody) - } - return nil - } -} - -func testAccCheckRequestValidatorValidateRequestParameters(conf *apigateway.UpdateRequestValidatorOutput, expectedValue bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - if conf.ValidateRequestParameters == nil { - return fmt.Errorf("Empty ValidateRequestParameters, expected: %t", expectedValue) - } - if *conf.ValidateRequestParameters != expectedValue { - return fmt.Errorf("ValidateRequestParameters didn't match. Expected: %t, Given: %t", expectedValue, *conf.ValidateRequestParameters) - } - return nil - } -} - -func testAccCheckRequestValidatorExists(ctx context.Context, n string, res *apigateway.UpdateRequestValidatorOutput) resource.TestCheckFunc { +func testAccCheckRequestValidatorExists(ctx context.Context, n string, v *apigateway.UpdateRequestValidatorOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -130,21 +87,18 @@ func testAccCheckRequestValidatorExists(ctx context.Context, n string, res *apig } if rs.Primary.ID == "" { - return fmt.Errorf("No API Request Validator ID is set") + return fmt.Errorf("No API Gateway Request Validator ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetRequestValidatorInput{ - RequestValidatorId: aws.String(rs.Primary.ID), - RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), - } - describe, err := conn.GetRequestValidatorWithContext(ctx, req) + output, err := tfapigateway.FindRequestValidatorByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - *res = *describe + *v = *output return nil } @@ -159,25 +113,17 @@ func testAccCheckRequestValidatorDestroy(ctx context.Context) resource.TestCheck continue } - req := &apigateway.GetRequestValidatorInput{ - RequestValidatorId: aws.String(rs.Primary.ID), - RestApiId: aws.String(rs.Primary.Attributes["rest_api_id"]), - } - _, err := conn.GetRequestValidatorWithContext(ctx, req) + _, err := tfapigateway.FindRequestValidatorByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) - if err == nil { - return fmt.Errorf("API Request Validator still exists") + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Request Validator %s still exists", rs.Primary.ID) } return nil @@ -198,27 +144,27 @@ func testAccRequestValidatorImportStateIdFunc(resourceName string) resource.Impo func testAccRequestValidatorConfig_base(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%s" + name = %[1]q } `, rName) } func testAccRequestValidatorConfig_basic(rName string) string { - return fmt.Sprintf(testAccRequestValidatorConfig_base(rName) + ` + return acctest.ConfigCompose(testAccRequestValidatorConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_request_validator" "test" { - name = "tf-acc-test-request-validator" + name = %[1]q rest_api_id = aws_api_gateway_rest_api.test.id } -`) +`, rName)) } func testAccRequestValidatorConfig_updated(rName string) string { - return fmt.Sprintf(testAccRequestValidatorConfig_base(rName) + ` + return acctest.ConfigCompose(testAccRequestValidatorConfig_base(rName), fmt.Sprintf(` resource "aws_api_gateway_request_validator" "test" { - name = "tf-acc-test-request-validator_modified" + name = "%[1]s-modified" rest_api_id = aws_api_gateway_rest_api.test.id validate_request_body = true validate_request_parameters = true } -`) +`, rName)) } From df9618c407c318b61826047c5cc37a523a5d7708 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 15:47:27 -0500 Subject: [PATCH 37/68] r/aws_api_gateway_usage_plan_key: Add 'FindUsagePlanKeyByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayUsagePlanKey_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayUsagePlanKey_ -timeout 180m === RUN TestAccAPIGatewayUsagePlanKey_basic === PAUSE TestAccAPIGatewayUsagePlanKey_basic === RUN TestAccAPIGatewayUsagePlanKey_disappears === PAUSE TestAccAPIGatewayUsagePlanKey_disappears === RUN TestAccAPIGatewayUsagePlanKey_KeyID_concurrency === PAUSE TestAccAPIGatewayUsagePlanKey_KeyID_concurrency === CONT TestAccAPIGatewayUsagePlanKey_basic === CONT TestAccAPIGatewayUsagePlanKey_KeyID_concurrency === CONT TestAccAPIGatewayUsagePlanKey_disappears --- PASS: TestAccAPIGatewayUsagePlanKey_disappears (21.19s) --- PASS: TestAccAPIGatewayUsagePlanKey_basic (59.09s) --- PASS: TestAccAPIGatewayUsagePlanKey_KeyID_concurrency (60.11s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 65.105s --- internal/service/apigateway/usage_plan_key.go | 78 ++++++++++++------- .../service/apigateway/usage_plan_key_test.go | 43 +++------- 2 files changed, 62 insertions(+), 59 deletions(-) diff --git a/internal/service/apigateway/usage_plan_key.go b/internal/service/apigateway/usage_plan_key.go index f9a544a8812..31e89d1b5ca 100644 --- a/internal/service/apigateway/usage_plan_key.go +++ b/internal/service/apigateway/usage_plan_key.go @@ -10,9 +10,11 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceUsagePlanKey() *schema.Resource { @@ -20,6 +22,7 @@ func ResourceUsagePlanKey() *schema.Resource { CreateWithoutTimeout: resourceUsagePlanKeyCreate, ReadWithoutTimeout: resourceUsagePlanKeyRead, DeleteWithoutTimeout: resourceUsagePlanKeyDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -41,24 +44,20 @@ func ResourceUsagePlanKey() *schema.Resource { Required: true, ForceNew: true, }, - "key_type": { Type: schema.TypeString, Required: true, ForceNew: true, }, - + "name": { + Type: schema.TypeString, + Computed: true, + }, "usage_plan_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - - "name": { - Type: schema.TypeString, - Computed: true, - }, - "value": { Type: schema.TypeString, Computed: true, @@ -70,21 +69,20 @@ func ResourceUsagePlanKey() *schema.Resource { func resourceUsagePlanKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Print("[DEBUG] Creating API Gateway Usage Plan Key") - params := &apigateway.CreateUsagePlanKeyInput{ + input := &apigateway.CreateUsagePlanKeyInput{ KeyId: aws.String(d.Get("key_id").(string)), KeyType: aws.String(d.Get("key_type").(string)), UsagePlanId: aws.String(d.Get("usage_plan_id").(string)), } - up, err := conn.CreateUsagePlanKeyWithContext(ctx, params) + output, err := conn.CreateUsagePlanKeyWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating API Gateway Usage Plan Key: %s", err) } - d.SetId(aws.StringValue(up.Id)) + d.SetId(aws.StringValue(output.Id)) return append(diags, resourceUsagePlanKeyRead(ctx, d, meta)...) } @@ -92,24 +90,22 @@ func resourceUsagePlanKeyCreate(ctx context.Context, d *schema.ResourceData, met func resourceUsagePlanKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Usage Plan Key: %s", d.Id()) - up, err := conn.GetUsagePlanKeyWithContext(ctx, &apigateway.GetUsagePlanKeyInput{ - UsagePlanId: aws.String(d.Get("usage_plan_id").(string)), - KeyId: aws.String(d.Get("key_id").(string)), - }) + upk, err := FindUsagePlanKeyByTwoPartKey(ctx, conn, d.Get("usage_plan_id").(string), d.Get("key_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Usage Plan Key (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Usage Plan Key (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Usage Plan Key (%s): %s", d.Id(), err) } - d.Set("name", up.Name) - d.Set("value", up.Value) - d.Set("key_type", up.Type) + d.Set("key_type", upk.Type) + d.Set("name", upk.Name) + d.Set("value", upk.Value) return diags } @@ -120,15 +116,43 @@ func resourceUsagePlanKeyDelete(ctx context.Context, d *schema.ResourceData, met log.Printf("[DEBUG] Deleting API Gateway Usage Plan Key: %s", d.Id()) _, err := conn.DeleteUsagePlanKeyWithContext(ctx, &apigateway.DeleteUsagePlanKeyInput{ - UsagePlanId: aws.String(d.Get("usage_plan_id").(string)), KeyId: aws.String(d.Get("key_id").(string)), + UsagePlanId: aws.String(d.Get("usage_plan_id").(string)), }) + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { return diags } + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting API Gateway usage plan key: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting API Gateway Usage Plan Key (%s): %s", d.Id(), err) } return diags } + +func FindUsagePlanKeyByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, usagePlanID, keyID string) (*apigateway.UsagePlanKey, error) { + input := &apigateway.GetUsagePlanKeyInput{ + KeyId: aws.String(keyID), + UsagePlanId: aws.String(usagePlanID), + } + + output, err := conn.GetUsagePlanKeyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/usage_plan_key_test.go b/internal/service/apigateway/usage_plan_key_test.go index ab604ea7f51..a3351c9516f 100644 --- a/internal/service/apigateway/usage_plan_key_test.go +++ b/internal/service/apigateway/usage_plan_key_test.go @@ -3,11 +3,8 @@ package apigateway_test import ( "context" "fmt" - "log" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -15,6 +12,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayUsagePlanKey_basic(t *testing.T) { @@ -106,7 +104,7 @@ func TestAccAPIGatewayUsagePlanKey_KeyID_concurrency(t *testing.T) { }) } -func testAccCheckUsagePlanKeyExists(ctx context.Context, n string, res *apigateway.UsagePlanKey) resource.TestCheckFunc { +func testAccCheckUsagePlanKeyExists(ctx context.Context, n string, v *apigateway.UsagePlanKey) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -119,22 +117,13 @@ func testAccCheckUsagePlanKeyExists(ctx context.Context, n string, res *apigatew conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetUsagePlanKeyInput{ - UsagePlanId: aws.String(rs.Primary.Attributes["usage_plan_id"]), - KeyId: aws.String(rs.Primary.Attributes["key_id"]), - } - up, err := conn.GetUsagePlanKeyWithContext(ctx, req) + output, err := tfapigateway.FindUsagePlanKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes["usage_plan_id"], rs.Primary.Attributes["key_id"]) + if err != nil { return err } - log.Printf("[DEBUG] Reading API Gateway Usage Plan Key: %#v", up) - - if *up.Id != rs.Primary.ID { - return fmt.Errorf("API Gateway Usage Plan Key not found") - } - - *res = *up + *v = *output return nil } @@ -149,27 +138,17 @@ func testAccCheckUsagePlanKeyDestroy(ctx context.Context) resource.TestCheckFunc continue } - req := &apigateway.GetUsagePlanKeyInput{ - UsagePlanId: aws.String(rs.Primary.ID), - KeyId: aws.String(rs.Primary.Attributes["key_id"]), - } - describe, err := conn.GetUsagePlanKeyWithContext(ctx, req) + _, err := tfapigateway.FindUsagePlanKeyByTwoPartKey(ctx, conn, rs.Primary.Attributes["usage_plan_id"], rs.Primary.Attributes["key_id"]) - if err == nil { - if describe.Id != nil && *describe.Id == rs.Primary.ID { - return fmt.Errorf("API Gateway Usage Plan Key still exists") - } + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != apigateway.ErrCodeNotFoundException { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Usage Plan Key %s still exists", rs.Primary.ID) } return nil @@ -190,7 +169,7 @@ func testAccCheckUsagePlanKeyImportStateIdFunc(resourceName string) resource.Imp func testAccUsagePlanKeyBaseConfig(rName string) string { return fmt.Sprintf(` resource "aws_api_gateway_rest_api" "test" { - name = "%[1]s" + name = %[1]q } resource "aws_api_gateway_resource" "test" { From 6833f650c9aa35dd3ac50f08f3d8135524498582 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 15:48:03 -0500 Subject: [PATCH 38/68] r/aws_api_gateway_resource: Add 'FindResourceByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccAPIGatewayResource_' PKG=apigateway ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/apigateway/... -v -count 1 -parallel 3 -run=TestAccAPIGatewayResource_ -timeout 180m === RUN TestAccAPIGatewayResource_basic === PAUSE TestAccAPIGatewayResource_basic === RUN TestAccAPIGatewayResource_update === PAUSE TestAccAPIGatewayResource_update === RUN TestAccAPIGatewayResource_disappears === PAUSE TestAccAPIGatewayResource_disappears === CONT TestAccAPIGatewayResource_basic === CONT TestAccAPIGatewayResource_update === CONT TestAccAPIGatewayResource_disappears --- PASS: TestAccAPIGatewayResource_disappears (18.64s) --- PASS: TestAccAPIGatewayResource_update (55.65s) --- PASS: TestAccAPIGatewayResource_basic (98.10s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/apigateway 103.305s --- internal/service/apigateway/resource.go | 81 +++++++++++------- internal/service/apigateway/resource_test.go | 88 ++++++-------------- 2 files changed, 78 insertions(+), 91 deletions(-) diff --git a/internal/service/apigateway/resource.go b/internal/service/apigateway/resource.go index 38b1f604d7f..97089b011aa 100644 --- a/internal/service/apigateway/resource.go +++ b/internal/service/apigateway/resource.go @@ -10,9 +10,11 @@ import ( "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceResource() *schema.Resource { @@ -21,6 +23,7 @@ func ResourceResource() *schema.Resource { ReadWithoutTimeout: resourceResourceRead, UpdateWithoutTimeout: resourceResourceUpdate, DeleteWithoutTimeout: resourceResourceDelete, + Importer: &schema.ResourceImporter{ StateContext: func(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { idParts := strings.Split(d.Id(), "/") @@ -36,25 +39,22 @@ func ResourceResource() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "rest_api_id": { + "parent_id": { Type: schema.TypeString, Required: true, - ForceNew: true, }, - - "parent_id": { + "path": { Type: schema.TypeString, - Required: true, + Computed: true, }, - "path_part": { Type: schema.TypeString, Required: true, }, - - "path": { + "rest_api_id": { Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, }, } @@ -63,20 +63,20 @@ func ResourceResource() *schema.Resource { func resourceResourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Creating API Gateway Resource for API %s", d.Get("rest_api_id").(string)) - var err error - resource, err := conn.CreateResourceWithContext(ctx, &apigateway.CreateResourceInput{ + input := &apigateway.CreateResourceInput{ ParentId: aws.String(d.Get("parent_id").(string)), PathPart: aws.String(d.Get("path_part").(string)), RestApiId: aws.String(d.Get("rest_api_id").(string)), - }) + } + + output, err := conn.CreateResourceWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating API Gateway Resource: %s", err) } - d.SetId(aws.StringValue(resource.Id)) + d.SetId(aws.StringValue(output.Id)) return append(diags, resourceResourceRead(ctx, d, meta)...) } @@ -85,18 +85,15 @@ func resourceResourceRead(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Reading API Gateway Resource %s", d.Id()) - resource, err := conn.GetResourceWithContext(ctx, &apigateway.GetResourceInput{ - ResourceId: aws.String(d.Id()), - RestApiId: aws.String(d.Get("rest_api_id").(string)), - }) + resource, err := FindResourceByTwoPartKey(ctx, conn, d.Id(), d.Get("rest_api_id").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] API Gateway Resource (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { - log.Printf("[WARN] API Gateway Resource (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading API Gateway Resource (%s): %s", d.Id(), err) } @@ -131,12 +128,13 @@ func resourceResourceUpdate(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Updating API Gateway Resource %s", d.Id()) - _, err := conn.UpdateResourceWithContext(ctx, &apigateway.UpdateResourceInput{ + input := &apigateway.UpdateResourceInput{ ResourceId: aws.String(d.Id()), RestApiId: aws.String(d.Get("rest_api_id").(string)), PatchOperations: resourceResourceUpdateOperations(d), - }) + } + + _, err := conn.UpdateResourceWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Resource (%s): %s", d.Id(), err) @@ -148,8 +146,8 @@ func resourceResourceUpdate(ctx context.Context, d *schema.ResourceData, meta in func resourceResourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayConn() - log.Printf("[DEBUG] Deleting API Gateway Resource: %s", d.Id()) + log.Printf("[DEBUG] Deleting API Gateway Resource: %s", d.Id()) _, err := conn.DeleteResourceWithContext(ctx, &apigateway.DeleteResourceInput{ ResourceId: aws.String(d.Id()), RestApiId: aws.String(d.Get("rest_api_id").(string)), @@ -162,5 +160,32 @@ func resourceResourceDelete(ctx context.Context, d *schema.ResourceData, meta in if err != nil { return sdkdiag.AppendErrorf(diags, "deleting API Gateway Resource (%s): %s", d.Id(), err) } + return diags } + +func FindResourceByTwoPartKey(ctx context.Context, conn *apigateway.APIGateway, resourceID, apiID string) (*apigateway.Resource, error) { + input := &apigateway.GetResourceInput{ + ResourceId: aws.String(resourceID), + RestApiId: aws.String(apiID), + } + + output, err := conn.GetResourceWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, apigateway.ErrCodeNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/apigateway/resource_test.go b/internal/service/apigateway/resource_test.go index aceab7ca926..a329a9df8fb 100644 --- a/internal/service/apigateway/resource_test.go +++ b/internal/service/apigateway/resource_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,12 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfapigateway "github.com/hashicorp/terraform-provider-aws/internal/service/apigateway" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccAPIGatewayResource_basic(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Resource - rName := fmt.Sprintf("tf-test-acc-%s", sdkacctest.RandString(8)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_resource.test" resource.ParallelTest(t, resource.TestCase{ @@ -32,11 +31,8 @@ func TestAccAPIGatewayResource_basic(t *testing.T) { Config: testAccResourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckResourceExists(ctx, resourceName, &conf), - testAccCheckResourceAttributes(&conf, "/test"), - resource.TestCheckResourceAttr( - resourceName, "path_part", "test"), - resource.TestCheckResourceAttr( - resourceName, "path", "/test"), + resource.TestCheckResourceAttr(resourceName, "path", "/test"), + resource.TestCheckResourceAttr(resourceName, "path_part", "test"), ), }, { @@ -52,7 +48,7 @@ func TestAccAPIGatewayResource_basic(t *testing.T) { func TestAccAPIGatewayResource_update(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Resource - rName := fmt.Sprintf("tf-test-acc-%s", sdkacctest.RandString(8)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_resource.test" resource.ParallelTest(t, resource.TestCase{ @@ -65,23 +61,8 @@ func TestAccAPIGatewayResource_update(t *testing.T) { Config: testAccResourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckResourceExists(ctx, resourceName, &conf), - testAccCheckResourceAttributes(&conf, "/test"), - resource.TestCheckResourceAttr( - resourceName, "path_part", "test"), - resource.TestCheckResourceAttr( - resourceName, "path", "/test"), - ), - }, - - { - Config: testAccResourceConfig_updatePathPart(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckResourceExists(ctx, resourceName, &conf), - testAccCheckResourceAttributes(&conf, "/test_changed"), - resource.TestCheckResourceAttr( - resourceName, "path_part", "test_changed"), - resource.TestCheckResourceAttr( - resourceName, "path", "/test_changed"), + resource.TestCheckResourceAttr(resourceName, "path", "/test"), + resource.TestCheckResourceAttr(resourceName, "path_part", "test"), ), }, { @@ -90,6 +71,14 @@ func TestAccAPIGatewayResource_update(t *testing.T) { ImportStateIdFunc: testAccResourceImportStateIdFunc(resourceName), ImportStateVerify: true, }, + { + Config: testAccResourceConfig_updatePathPart(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "path", "/test_changed"), + resource.TestCheckResourceAttr(resourceName, "path_part", "test_changed"), + ), + }, }, }) } @@ -97,7 +86,7 @@ func TestAccAPIGatewayResource_update(t *testing.T) { func TestAccAPIGatewayResource_disappears(t *testing.T) { ctx := acctest.Context(t) var conf apigateway.Resource - rName := fmt.Sprintf("tf-test-acc-%s", sdkacctest.RandString(8)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_api_gateway_resource.test" resource.ParallelTest(t, resource.TestCase{ @@ -118,17 +107,7 @@ func TestAccAPIGatewayResource_disappears(t *testing.T) { }) } -func testAccCheckResourceAttributes(conf *apigateway.Resource, path string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *conf.Path != path { - return fmt.Errorf("Wrong Path: %q", *conf.Path) - } - - return nil - } -} - -func testAccCheckResourceExists(ctx context.Context, n string, res *apigateway.Resource) resource.TestCheckFunc { +func testAccCheckResourceExists(ctx context.Context, n string, v *apigateway.Resource) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -141,20 +120,13 @@ func testAccCheckResourceExists(ctx context.Context, n string, res *apigateway.R conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayConn() - req := &apigateway.GetResourceInput{ - ResourceId: aws.String(rs.Primary.ID), - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetResourceWithContext(ctx, req) + output, err := tfapigateway.FindResourceByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) + if err != nil { return err } - if *describe.Id != rs.Primary.ID { - return fmt.Errorf("APIGateway Resource not found") - } - - *res = *describe + *v = *output return nil } @@ -169,27 +141,17 @@ func testAccCheckResourceDestroy(ctx context.Context) resource.TestCheckFunc { continue } - req := &apigateway.GetResourcesInput{ - RestApiId: aws.String(s.RootModule().Resources["aws_api_gateway_rest_api.test"].Primary.ID), - } - describe, err := conn.GetResourcesWithContext(ctx, req) + _, err := tfapigateway.FindResourceByTwoPartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["rest_api_id"]) - if err == nil { - if len(describe.Items) != 0 && - *describe.Items[0].Id == rs.Primary.ID { - return fmt.Errorf("API Gateway Resource still exists") - } + if tfresource.NotFound(err) { + continue } - aws2err, ok := err.(awserr.Error) - if !ok { - return err - } - if aws2err.Code() != "NotFoundException" { + if err != nil { return err } - return nil + return fmt.Errorf("API Gateway Resource %s still exists", rs.Primary.ID) } return nil From 88e01937c45bb5148e9ec16c904bf7258539c19b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 16:04:07 -0500 Subject: [PATCH 39/68] r/aws_cognito_user: Add 'FindUserByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccCognitoIDPUser_basic\|TestAccCognitoIDPUser_disappears' PKG=cognitoidp ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/cognitoidp/... -v -count 1 -parallel 3 -run=TestAccCognitoIDPUser_basic\|TestAccCognitoIDPUser_disappears -timeout 180m === RUN TestAccCognitoIDPUser_basic === PAUSE TestAccCognitoIDPUser_basic === RUN TestAccCognitoIDPUser_disappears === PAUSE TestAccCognitoIDPUser_disappears === CONT TestAccCognitoIDPUser_basic === CONT TestAccCognitoIDPUser_disappears --- PASS: TestAccCognitoIDPUser_disappears (16.16s) --- PASS: TestAccCognitoIDPUser_basic (19.16s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/cognitoidp 24.301s --- internal/service/cognitoidp/user.go | 46 ++++++++++++++++------- internal/service/cognitoidp/user_test.go | 48 ++++++++---------------- 2 files changed, 48 insertions(+), 46 deletions(-) diff --git a/internal/service/cognitoidp/user.go b/internal/service/cognitoidp/user.go index 8f02645d144..a599058822c 100644 --- a/internal/service/cognitoidp/user.go +++ b/internal/service/cognitoidp/user.go @@ -12,11 +12,13 @@ import ( "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -225,15 +227,9 @@ func resourceUserRead(ctx context.Context, d *schema.ResourceData, meta interfac var diags diag.Diagnostics conn := meta.(*conns.AWSClient).CognitoIDPConn() - log.Println("[DEBUG] Reading Cognito User") + user, err := FindUserByTwoPartKey(ctx, conn, d.Get("user_pool_id").(string), d.Get("username").(string)) - params := &cognitoidentityprovider.AdminGetUserInput{ - Username: aws.String(d.Get("username").(string)), - UserPoolId: aws.String(d.Get("user_pool_id").(string)), - } - - user, err := conn.AdminGetUserWithContext(ctx, params) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeUserNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { create.LogNotFoundRemoveState(names.CognitoIDP, create.ErrActionReading, ResNameUser, d.Get("username").(string)) d.SetId("") return diags @@ -373,14 +369,12 @@ func resourceUserDelete(ctx context.Context, d *schema.ResourceData, meta interf var diags diag.Diagnostics conn := meta.(*conns.AWSClient).CognitoIDPConn() - log.Print("[DEBUG] Deleting Cognito User") - - params := &cognitoidentityprovider.AdminDeleteUserInput{ + log.Printf("[DEBUG] Deleting Cognito User: %s", d.Id()) + _, err := conn.AdminDeleteUserWithContext(ctx, &cognitoidentityprovider.AdminDeleteUserInput{ Username: aws.String(d.Get("username").(string)), UserPoolId: aws.String(d.Get("user_pool_id").(string)), - } + }) - _, err := conn.AdminDeleteUserWithContext(ctx, params) if err != nil { return sdkdiag.AppendErrorf(diags, "deleting Cognito User (%s): %s", d.Id(), err) } @@ -400,6 +394,32 @@ func resourceUserImport(ctx context.Context, d *schema.ResourceData, meta interf return []*schema.ResourceData{d}, nil } +func FindUserByTwoPartKey(ctx context.Context, conn *cognitoidentityprovider.CognitoIdentityProvider, userPoolID, username string) (*cognitoidentityprovider.AdminGetUserOutput, error) { + input := &cognitoidentityprovider.AdminGetUserInput{ + Username: aws.String(username), + UserPoolId: aws.String(userPoolID), + } + + output, err := conn.AdminGetUserWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, cognitoidentityprovider.ErrCodeUserNotFoundException, cognitoidentityprovider.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} + func expandAttribute(tfMap map[string]interface{}) []*cognitoidentityprovider.AttributeType { if len(tfMap) == 0 { return nil diff --git a/internal/service/cognitoidp/user_test.go b/internal/service/cognitoidp/user_test.go index edf0f19519c..b2cdd1a992c 100644 --- a/internal/service/cognitoidp/user_test.go +++ b/internal/service/cognitoidp/user_test.go @@ -7,14 +7,14 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/service/cognitoidp" + tfcognitoidp "github.com/hashicorp/terraform-provider-aws/internal/service/cognitoidp" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccCognitoIDPUser_basic(t *testing.T) { @@ -75,7 +75,7 @@ func TestAccCognitoIDPUser_disappears(t *testing.T) { Config: testAccUserConfig_basic(rUserPoolName, rUserName), Check: resource.ComposeTestCheckFunc( testAccCheckUserExists(ctx, resourceName), - acctest.CheckResourceDisappears(ctx, acctest.Provider, cognitoidp.ResourceUser(), resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfcognitoidp.ResourceUser(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -289,37 +289,21 @@ func TestAccCognitoIDPUser_enabled(t *testing.T) { }) } -func testAccCheckUserExists(ctx context.Context, name string) resource.TestCheckFunc { +func testAccCheckUserExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - id := rs.Primary.ID - userName := rs.Primary.Attributes["username"] - userPoolId := rs.Primary.Attributes["user_pool_id"] - - if userName == "" { - return errors.New("No Cognito User Name set") - } - - if userPoolId == "" { - return errors.New("No Cognito User Pool Id set") - } - - if id != fmt.Sprintf("%s/%s", userPoolId, userName) { - return fmt.Errorf(fmt.Sprintf("ID should be user_pool_id/name. ID was %s. name was %s, user_pool_id was %s", id, userName, userPoolId)) + if rs.Primary.ID == "" { + return fmt.Errorf("No Cognito User ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).CognitoIDPConn() - params := &cognitoidentityprovider.AdminGetUserInput{ - Username: aws.String(rs.Primary.Attributes["username"]), - UserPoolId: aws.String(rs.Primary.Attributes["user_pool_id"]), - } + _, err := tfcognitoidp.FindUserByTwoPartKey(ctx, conn, rs.Primary.Attributes["user_pool_id"], rs.Primary.Attributes["username"]) - _, err := conn.AdminGetUserWithContext(ctx, params) return err } } @@ -333,19 +317,17 @@ func testAccCheckUserDestroy(ctx context.Context) resource.TestCheckFunc { continue } - params := &cognitoidentityprovider.AdminGetUserInput{ - Username: aws.String(rs.Primary.Attributes["username"]), - UserPoolId: aws.String(rs.Primary.Attributes["user_pool_id"]), - } + _, err := tfcognitoidp.FindUserByTwoPartKey(ctx, conn, rs.Primary.Attributes["user_pool_id"], rs.Primary.Attributes["username"]) - _, err := conn.AdminGetUserWithContext(ctx, params) + if tfresource.NotFound(err) { + continue + } if err != nil { - if awsErr, ok := err.(awserr.Error); ok && (awsErr.Code() == cognitoidentityprovider.ErrCodeUserNotFoundException || awsErr.Code() == cognitoidentityprovider.ErrCodeResourceNotFoundException) { - return nil - } return err } + + return fmt.Errorf("Cognito User %s still exists", rs.Primary.ID) } return nil From 142529a8726a7595356df663aa77e753b1d55f94 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:04:58 -0500 Subject: [PATCH 40/68] r/aws_efs_mount_target: Add 'FindMountTargetByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccEFSMountTarget_' PKG=efs ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/efs/... -v -count 1 -parallel 3 -run=TestAccEFSMountTarget_ -timeout 180m === RUN TestAccEFSMountTarget_basic === PAUSE TestAccEFSMountTarget_basic === RUN TestAccEFSMountTarget_disappears === PAUSE TestAccEFSMountTarget_disappears === RUN TestAccEFSMountTarget_ipAddress === PAUSE TestAccEFSMountTarget_ipAddress === RUN TestAccEFSMountTarget_IPAddress_emptyString === PAUSE TestAccEFSMountTarget_IPAddress_emptyString === CONT TestAccEFSMountTarget_basic === CONT TestAccEFSMountTarget_ipAddress === CONT TestAccEFSMountTarget_IPAddress_emptyString --- PASS: TestAccEFSMountTarget_IPAddress_emptyString (138.07s) === CONT TestAccEFSMountTarget_disappears --- PASS: TestAccEFSMountTarget_ipAddress (138.12s) --- PASS: TestAccEFSMountTarget_basic (239.87s) --- PASS: TestAccEFSMountTarget_disappears (132.15s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/efs 275.296s --- .changelog/27991.txt | 4 + internal/service/efs/mount_target.go | 333 +++++++++--------- internal/service/efs/mount_target_test.go | 324 +++-------------- website/docs/r/efs_mount_target.html.markdown | 7 + 4 files changed, 233 insertions(+), 435 deletions(-) diff --git a/.changelog/27991.txt b/.changelog/27991.txt index 3096ff97767..d863aa9b3fd 100644 --- a/.changelog/27991.txt +++ b/.changelog/27991.txt @@ -1,3 +1,7 @@ ```release-note:bug resource/aws_opsworks_permission: `stack_id` and `user_arn` are both Required and ForceNew +``` + +```release-note:enhancement +resource/aws_efs_mount_target: Add configurable timeouts for Create and Delete ``` \ No newline at end of file diff --git a/internal/service/efs/mount_target.go b/internal/service/efs/mount_target.go index 53c10d5a7b5..eb10a711a58 100644 --- a/internal/service/efs/mount_target.go +++ b/internal/service/efs/mount_target.go @@ -1,6 +1,6 @@ package efs -import ( +import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports "context" "fmt" "log" @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/efs" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -17,11 +18,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" - "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" -) - -const ( - mountTargetDeleteTimeout = 10 * time.Minute + tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceMountTarget() *schema.Resource { @@ -35,7 +33,24 @@ func ResourceMountTarget() *schema.Resource { StateContext: schema.ImportStatePassthroughContext, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "availability_zone_name": { + Type: schema.TypeString, + Computed: true, + }, + "availability_zone_id": { + Type: schema.TypeString, + Computed: true, + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, "file_system_arn": { Type: schema.TypeString, Computed: true, @@ -45,51 +60,35 @@ func ResourceMountTarget() *schema.Resource { Required: true, ForceNew: true, }, - "ip_address": { Type: schema.TypeString, - Computed: true, Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validation.IsIPv4Address, }, - - "security_groups": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Computed: true, - Optional: true, - }, - - "subnet_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "network_interface_id": { - Type: schema.TypeString, - Computed: true, - }, - "dns_name": { + "mount_target_dns_name": { Type: schema.TypeString, Computed: true, }, - "mount_target_dns_name": { + "network_interface_id": { Type: schema.TypeString, Computed: true, }, - "availability_zone_name": { + "owner_id": { Type: schema.TypeString, Computed: true, }, - "availability_zone_id": { - Type: schema.TypeString, + "security_groups": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, Computed: true, }, - "owner_id": { + "subnet_id": { Type: schema.TypeString, - Computed: true, + Required: true, + ForceNew: true, }, }, } @@ -99,220 +98,226 @@ func resourceMountTargetCreate(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EFSConn() - fsId := d.Get("file_system_id").(string) - subnetId := d.Get("subnet_id").(string) - // CreateMountTarget would return the same Mount Target ID // to parallel requests if they both include the same AZ // and we would end up managing the same MT as 2 resources. // So we make it fail by calling 1 request per AZ at a time. - az, err := getAzFromSubnetId(ctx, subnetId, meta) + subnetID := d.Get("subnet_id").(string) + az, err := getAZFromSubnetID(ctx, meta.(*conns.AWSClient).EC2Conn(), subnetID) + if err != nil { - return sdkdiag.AppendErrorf(diags, "getting Availability Zone from subnet ID (%s): %s", subnetId, err) + return sdkdiag.AppendErrorf(diags, "reading EC2 Subnet (%s): %s", subnetID, err) } - mtKey := "efs-mt-" + fsId + "-" + az + + fsID := d.Get("file_system_id").(string) + mtKey := "efs-mt-" + fsID + "-" + az conns.GlobalMutexKV.Lock(mtKey) defer conns.GlobalMutexKV.Unlock(mtKey) - input := efs.CreateMountTargetInput{ - FileSystemId: aws.String(fsId), - SubnetId: aws.String(subnetId), + input := &efs.CreateMountTargetInput{ + FileSystemId: aws.String(fsID), + SubnetId: aws.String(subnetID), } if v, ok := d.GetOk("ip_address"); ok { input.IpAddress = aws.String(v.(string)) } + if v, ok := d.GetOk("security_groups"); ok { input.SecurityGroups = flex.ExpandStringSet(v.(*schema.Set)) } - mt, err := conn.CreateMountTargetWithContext(ctx, &input) + mt, err := conn.CreateMountTargetWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating EFS Mount Target (%s): %s", fsId, err) + return sdkdiag.AppendErrorf(diags, "creating EFS Mount Target (%s): %s", fsID, err) } d.SetId(aws.StringValue(mt.MountTargetId)) - log.Printf("[INFO] EFS mount target ID: %s", d.Id()) - - stateConf := &resource.StateChangeConf{ - Pending: []string{efs.LifeCycleStateCreating}, - Target: []string{efs.LifeCycleStateAvailable}, - Refresh: func() (interface{}, string, error) { - resp, err := conn.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{ - MountTargetId: aws.String(d.Id()), - }) - if err != nil { - return nil, "error", err - } - - if HasEmptyMountTargets(resp) { - return nil, "error", fmt.Errorf("EFS mount target %q could not be found.", d.Id()) - } - - mt := resp.MountTargets[0] - - log.Printf("[DEBUG] Current status of %q: %q", aws.StringValue(mt.MountTargetId), aws.StringValue(mt.LifeCycleState)) - return mt, aws.StringValue(mt.LifeCycleState), nil - }, - Timeout: 30 * time.Minute, - Delay: 2 * time.Second, - MinTimeout: 3 * time.Second, - } - _, err = stateConf.WaitForStateContext(ctx) - if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for EFS mount target (%s) to create: %s", d.Id(), err) + if _, err := waitMountTargetCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for EFS Mount Target (%s) create: %s", d.Id(), err) } - log.Printf("[DEBUG] EFS mount target created: %s", aws.StringValue(mt.MountTargetId)) - return append(diags, resourceMountTargetRead(ctx, d, meta)...) } -func resourceMountTargetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceMountTargetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EFSConn() - if d.HasChange("security_groups") { - input := efs.ModifyMountTargetSecurityGroupsInput{ - MountTargetId: aws.String(d.Id()), - SecurityGroups: flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)), - } - _, err := conn.ModifyMountTargetSecurityGroupsWithContext(ctx, &input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating EFS Mount Target (%s): %s", d.Id(), err) - } - } - - return append(diags, resourceMountTargetRead(ctx, d, meta)...) -} + mt, err := FindMountTargetByID(ctx, conn, d.Id()) -func resourceMountTargetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EFSConn() - resp, err := conn.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{ - MountTargetId: aws.String(d.Id()), - }) - if err != nil { - if tfawserr.ErrCodeEquals(err, efs.ErrCodeMountTargetNotFound) { - // The EFS mount target could not be found, - // which would indicate that it might be - // already deleted. - log.Printf("[WARN] EFS mount target %q could not be found.", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading EFS mount target %s: %s", d.Id(), err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EFS Mount Target (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - if HasEmptyMountTargets(resp) { - return sdkdiag.AppendErrorf(diags, "EFS mount target %q could not be found.", d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EFS Mount Target (%s): %s", d.Id(), err) } - mt := resp.MountTargets[0] - - log.Printf("[DEBUG] Found EFS mount target: %#v", mt) - - fsARN := arn.ARN{ + arn := arn.ARN{ AccountID: meta.(*conns.AWSClient).AccountID, Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, Resource: fmt.Sprintf("file-system/%s", aws.StringValue(mt.FileSystemId)), Service: "elasticfilesystem", }.String() - - d.Set("file_system_arn", fsARN) + d.Set("availability_zone_id", mt.AvailabilityZoneId) + d.Set("availability_zone_name", mt.AvailabilityZoneName) + d.Set("dns_name", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s.efs", aws.StringValue(mt.FileSystemId)))) + d.Set("file_system_arn", arn) d.Set("file_system_id", mt.FileSystemId) d.Set("ip_address", mt.IpAddress) - d.Set("subnet_id", mt.SubnetId) + d.Set("mount_target_dns_name", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s.%s.efs", aws.StringValue(mt.AvailabilityZoneName), aws.StringValue(mt.FileSystemId)))) d.Set("network_interface_id", mt.NetworkInterfaceId) - d.Set("availability_zone_name", mt.AvailabilityZoneName) - d.Set("availability_zone_id", mt.AvailabilityZoneId) d.Set("owner_id", mt.OwnerId) + d.Set("subnet_id", mt.SubnetId) - sgResp, err := conn.DescribeMountTargetSecurityGroupsWithContext(ctx, &efs.DescribeMountTargetSecurityGroupsInput{ + output, err := conn.DescribeMountTargetSecurityGroupsWithContext(ctx, &efs.DescribeMountTargetSecurityGroupsInput{ MountTargetId: aws.String(d.Id()), }) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EFS Mount Target (%s): %s", d.Id(), err) - } - err = d.Set("security_groups", flex.FlattenStringSet(sgResp.SecurityGroups)) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EFS Mount Target (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading EFS Mount Target (%s) security groups: %s", d.Id(), err) } - d.Set("dns_name", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s.efs", aws.StringValue(mt.FileSystemId)))) - d.Set("mount_target_dns_name", meta.(*conns.AWSClient).RegionalHostname(fmt.Sprintf("%s.%s.efs", aws.StringValue(mt.AvailabilityZoneName), aws.StringValue(mt.FileSystemId)))) + d.Set("security_groups", aws.StringValueSlice(output.SecurityGroups)) return diags } -func getAzFromSubnetId(ctx context.Context, subnetId string, meta interface{}) (string, error) { - conn := meta.(*conns.AWSClient).EC2Conn() - subnet, err := ec2.FindSubnetByID(ctx, conn, subnetId) - if err != nil { - return "", err +func resourceMountTargetUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EFSConn() + + if d.HasChange("security_groups") { + input := &efs.ModifyMountTargetSecurityGroupsInput{ + MountTargetId: aws.String(d.Id()), + SecurityGroups: flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)), + } + + _, err := conn.ModifyMountTargetSecurityGroupsWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating EFS Mount Target (%s) security groups: %s", d.Id(), err) + } } - return aws.StringValue(subnet.AvailabilityZone), nil + return append(diags, resourceMountTargetRead(ctx, d, meta)...) } func resourceMountTargetDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EFSConn() + log.Printf("[DEBUG] Deleting EFS Mount Target: %s", d.Id()) _, err := conn.DeleteMountTargetWithContext(ctx, &efs.DeleteMountTargetInput{ MountTargetId: aws.String(d.Id()), }) + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting EFS Mount Target (%s): %s", d.Id(), err) } - err = WaitForDeleteMountTarget(ctx, conn, d.Id(), mountTargetDeleteTimeout) - if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting EFS Mount Target (%s): waiting for completion: %s", d.Id(), err) + if _, err := waitMountTargetDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for EFS Mount Target (%s) delete: %s", d.Id(), err) } return diags } -func WaitForDeleteMountTarget(ctx context.Context, conn *efs.EFS, id string, timeout time.Duration) error { +func getAZFromSubnetID(ctx context.Context, conn *ec2.EC2, subnetID string) (string, error) { + subnet, err := tfec2.FindSubnetByID(ctx, conn, subnetID) + + if err != nil { + return "", err + } + + return aws.StringValue(subnet.AvailabilityZone), nil +} + +func FindMountTargetByID(ctx context.Context, conn *efs.EFS, id string) (*efs.MountTargetDescription, error) { + input := &efs.DescribeMountTargetsInput{ + MountTargetId: aws.String(id), + } + + output, err := conn.DescribeMountTargetsWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, efs.ErrCodeMountTargetNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.MountTargets) == 0 || output.MountTargets[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.MountTargets); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.MountTargets[0], nil +} + +func statusMountTargetLifeCycleState(ctx context.Context, conn *efs.EFS, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindMountTargetByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.LifeCycleState), nil + } +} + +func waitMountTargetCreated(ctx context.Context, conn *efs.EFS, id string, timeout time.Duration) (*efs.MountTargetDescription, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{efs.LifeCycleStateAvailable, efs.LifeCycleStateDeleting, efs.LifeCycleStateDeleted}, - Target: []string{}, - Refresh: func() (interface{}, string, error) { - resp, err := conn.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{ - MountTargetId: aws.String(id), - }) - if err != nil { - if tfawserr.ErrCodeEquals(err, efs.ErrCodeMountTargetNotFound) { - return nil, "", nil - } - - return nil, "error", err - } - - if HasEmptyMountTargets(resp) { - return nil, "", nil - } - - mt := resp.MountTargets[0] - - log.Printf("[DEBUG] Current status of %q: %q", aws.StringValue(mt.MountTargetId), aws.StringValue(mt.LifeCycleState)) - return mt, aws.StringValue(mt.LifeCycleState), nil - }, + Pending: []string{efs.LifeCycleStateCreating}, + Target: []string{efs.LifeCycleStateAvailable}, + Refresh: statusMountTargetLifeCycleState(ctx, conn, id), Timeout: timeout, Delay: 2 * time.Second, MinTimeout: 3 * time.Second, } - _, err := stateConf.WaitForStateContext(ctx) - return err + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*efs.MountTargetDescription); ok { + return output, err + } + + return nil, err } -func HasEmptyMountTargets(mto *efs.DescribeMountTargetsOutput) bool { - if mto != nil && len(mto.MountTargets) > 0 { - return false +func waitMountTargetDeleted(ctx context.Context, conn *efs.EFS, id string, timeout time.Duration) (*efs.MountTargetDescription, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{efs.LifeCycleStateAvailable, efs.LifeCycleStateDeleting, efs.LifeCycleStateDeleted}, + Target: []string{}, + Refresh: statusMountTargetLifeCycleState(ctx, conn, id), + Timeout: timeout, + Delay: 2 * time.Second, + MinTimeout: 3 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*efs.MountTargetDescription); ok { + return output, err } - return true + + return nil, err } diff --git a/internal/service/efs/mount_target_test.go b/internal/service/efs/mount_target_test.go index f7e88c5d429..1df1660234c 100644 --- a/internal/service/efs/mount_target_test.go +++ b/internal/service/efs/mount_target_test.go @@ -6,23 +6,20 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/efs" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfefs "github.com/hashicorp/terraform-provider-aws/internal/service/efs" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccEFSMountTarget_basic(t *testing.T) { ctx := acctest.Context(t) var mount efs.MountTargetDescription - ct := fmt.Sprintf("createtoken-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_efs_mount_target.test" resourceName2 := "aws_efs_mount_target.test2" @@ -33,9 +30,9 @@ func TestAccEFSMountTarget_basic(t *testing.T) { CheckDestroy: testAccCheckMountTargetDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMountTargetConfig_basic(ct), + Config: testAccMountTargetConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckMountTarget(ctx, resourceName, &mount), + testAccCheckMountTargetExists(ctx, resourceName, &mount), resource.TestCheckResourceAttrSet(resourceName, "availability_zone_id"), resource.TestCheckResourceAttrSet(resourceName, "availability_zone_name"), acctest.MatchResourceAttrRegionalHostname(resourceName, "dns_name", "efs", regexp.MustCompile(`fs-[^.]+`)), @@ -52,10 +49,10 @@ func TestAccEFSMountTarget_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccMountTargetConfig_modified(ct), + Config: testAccMountTargetConfig_modified(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckMountTarget(ctx, resourceName, &mount), - testAccCheckMountTarget(ctx, resourceName2, &mount), + testAccCheckMountTargetExists(ctx, resourceName, &mount), + testAccCheckMountTargetExists(ctx, resourceName2, &mount), acctest.MatchResourceAttrRegionalHostname(resourceName, "dns_name", "efs", regexp.MustCompile(`fs-[^.]+`)), acctest.MatchResourceAttrRegionalHostname(resourceName2, "dns_name", "efs", regexp.MustCompile(`fs-[^.]+`)), ), @@ -67,19 +64,19 @@ func TestAccEFSMountTarget_basic(t *testing.T) { func TestAccEFSMountTarget_disappears(t *testing.T) { ctx := acctest.Context(t) var mount efs.MountTargetDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_efs_mount_target.test" - ct := fmt.Sprintf("createtoken-%d", sdkacctest.RandInt()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, efs.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckVPNGatewayDestroy(ctx), + CheckDestroy: testAccCheckMountTargetDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccMountTargetConfig_basic(ct), + Config: testAccMountTargetConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckMountTarget(ctx, resourceName, &mount), + testAccCheckMountTargetExists(ctx, resourceName, &mount), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfefs.ResourceMountTarget(), resourceName), ), ExpectNonEmptyPlan: true, @@ -91,8 +88,8 @@ func TestAccEFSMountTarget_disappears(t *testing.T) { func TestAccEFSMountTarget_ipAddress(t *testing.T) { ctx := acctest.Context(t) var mount efs.MountTargetDescription - resourceName := "aws_efs_mount_target.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_efs_mount_target.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -103,7 +100,7 @@ func TestAccEFSMountTarget_ipAddress(t *testing.T) { { Config: testAccMountTargetConfig_ipAddress(rName, "10.0.0.100"), Check: resource.ComposeTestCheckFunc( - testAccCheckMountTarget(ctx, resourceName, &mount), + testAccCheckMountTargetExists(ctx, resourceName, &mount), resource.TestCheckResourceAttr(resourceName, "ip_address", "10.0.0.100"), ), }, @@ -120,8 +117,8 @@ func TestAccEFSMountTarget_ipAddress(t *testing.T) { func TestAccEFSMountTarget_IPAddress_emptyString(t *testing.T) { ctx := acctest.Context(t) var mount efs.MountTargetDescription - resourceName := "aws_efs_mount_target.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_efs_mount_target.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -132,7 +129,7 @@ func TestAccEFSMountTarget_IPAddress_emptyString(t *testing.T) { { Config: testAccMountTargetConfig_ipAddressNullIP(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckMountTarget(ctx, resourceName, &mount), + testAccCheckMountTargetExists(ctx, resourceName, &mount), resource.TestMatchResourceAttr(resourceName, "ip_address", regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`)), ), }, @@ -145,27 +142,6 @@ func TestAccEFSMountTarget_IPAddress_emptyString(t *testing.T) { }) } -func TestMountTarget_hasEmptyMountTargets(t *testing.T) { - t.Parallel() - - mto := &efs.DescribeMountTargetsOutput{ - MountTargets: []*efs.MountTargetDescription{}, - } - - actual := tfefs.HasEmptyMountTargets(mto) - if !actual { - t.Fatalf("Expected return value to be true, got %t", actual) - } - - // Add an empty mount target. - mto.MountTargets = append(mto.MountTargets, &efs.MountTargetDescription{}) - - actual = tfefs.HasEmptyMountTargets(mto) - if actual { - t.Fatalf("Expected return value to be false, got %t", actual) - } -} - func testAccCheckMountTargetDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EFSConn() @@ -174,291 +150,97 @@ func testAccCheckMountTargetDestroy(ctx context.Context) resource.TestCheckFunc continue } - resp, err := conn.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{ - MountTargetId: aws.String(rs.Primary.ID), - }) - if err != nil { - if tfawserr.ErrCodeEquals(err, efs.ErrCodeMountTargetNotFound) { - // gone - return nil - } - return fmt.Errorf("Error describing EFS Mount in tests: %s", err) + _, err := tfefs.FindMountTargetByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - if len(resp.MountTargets) > 0 { - return fmt.Errorf("EFS Mount target %q still exists", rs.Primary.ID) + + if err != nil { + return err } + + return fmt.Errorf("EFS Mount Target %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckMountTarget(ctx context.Context, resourceID string, mount *efs.MountTargetDescription) resource.TestCheckFunc { +func testAccCheckMountTargetExists(ctx context.Context, n string, v *efs.MountTargetDescription) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceID] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", resourceID) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - fs, ok := s.RootModule().Resources[resourceID] - if !ok { - return fmt.Errorf("Not found: %s", resourceID) + return fmt.Errorf("No EFS Mount Target ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).EFSConn() - mt, err := conn.DescribeMountTargetsWithContext(ctx, &efs.DescribeMountTargetsInput{ - MountTargetId: aws.String(fs.Primary.ID), - }) + + output, err := tfefs.FindMountTargetByID(ctx, conn, rs.Primary.ID) + if err != nil { return err } - if aws.StringValue(mt.MountTargets[0].MountTargetId) != fs.Primary.ID { - return fmt.Errorf("Mount target ID mismatch: %q != %q", - *mt.MountTargets[0].MountTargetId, fs.Primary.ID) - } - - *mount = *mt.MountTargets[0] + *v = *output return nil } } -func testAccMountTargetConfig_basic(ct string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - +func testAccMountTargetConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` resource "aws_efs_file_system" "test" { - creation_token = "%s" - tags = { - Name = "tf-acc-efs-mount-target-test" + Name = %[1]q } } +`, rName)) +} +func testAccMountTargetConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccMountTargetConfig_base(rName), ` resource "aws_efs_mount_target" "test" { file_system_id = aws_efs_file_system.test.id - subnet_id = aws_subnet.test.id -} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = "tf-acc-efs-mount-target-test" - } -} - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "10.0.1.0/24" - - tags = { - Name = "tf-acc-efs-mount-target-test" - } -} -`, ct) -} - -func testAccMountTargetConfig_modified(ct string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } + subnet_id = aws_subnet.test[0].id } - -resource "aws_efs_file_system" "test" { - creation_token = "%s" - - tags = { - Name = "tf-acc-efs-mount-target-test" - } +`) } +func testAccMountTargetConfig_modified(rName string) string { + return acctest.ConfigCompose(testAccMountTargetConfig_base(rName), ` resource "aws_efs_mount_target" "test" { file_system_id = aws_efs_file_system.test.id - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test[0].id } resource "aws_efs_mount_target" "test2" { file_system_id = aws_efs_file_system.test.id - subnet_id = aws_subnet.test2.id -} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = "tf-acc-efs-mount-target-test" - } -} - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "10.0.1.0/24" - - tags = { - Name = "tf-acc-efs-mount-target-test" - } -} - -resource "aws_subnet" "test2" { - vpc_id = aws_vpc.test.id - availability_zone = data.aws_availability_zones.available.names[1] - cidr_block = "10.0.2.0/24" - - tags = { - Name = "tf-acc-efs-mount-target-test2" - } + subnet_id = aws_subnet.test[1].id } -`, ct) +`) } func testAccMountTargetConfig_ipAddress(rName, ipAddress string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = %[1]q - } -} - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "10.0.0.0/24" - - tags = { - Name = %[1]q - } -} - -resource "aws_efs_file_system" "test" { - tags = { - Name = %[1]q - } -} - + return acctest.ConfigCompose(testAccMountTargetConfig_base(rName), fmt.Sprintf(` resource "aws_efs_mount_target" "test" { file_system_id = aws_efs_file_system.test.id - ip_address = %[2]q - subnet_id = aws_subnet.test.id + ip_address = %[1]q + subnet_id = aws_subnet.test[0].id } -`, rName, ipAddress) +`, ipAddress)) } func testAccMountTargetConfig_ipAddressNullIP(rName string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = %[1]q - } -} - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = "10.0.0.0/24" - - tags = { - Name = %[1]q - } -} - -resource "aws_efs_file_system" "test" { - tags = { - Name = %[1]q - } -} - + return acctest.ConfigCompose(testAccMountTargetConfig_base(rName), ` resource "aws_efs_mount_target" "test" { file_system_id = aws_efs_file_system.test.id ip_address = null - subnet_id = aws_subnet.test.id -} -`, rName) + subnet_id = aws_subnet.test[0].id } - -func testAccCheckVPNGatewayDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_vpn_gateway" { - continue - } - - // Try to find the resource - resp, err := conn.DescribeVpnGatewaysWithContext(ctx, &ec2.DescribeVpnGatewaysInput{ - VpnGatewayIds: []*string{aws.String(rs.Primary.ID)}, - }) - if err == nil { - var v *ec2.VpnGateway - for _, g := range resp.VpnGateways { - if *g.VpnGatewayId == rs.Primary.ID { - v = g - } - } - - if v == nil { - // wasn't found - return nil - } - - if *v.State != "deleted" { - return fmt.Errorf("Expected VPN Gateway to be in deleted state, but was not: %s", v) - } - return nil - } - - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "InvalidVpnGatewayID.NotFound" { - return err - } - } - - return nil - } +`) } diff --git a/website/docs/r/efs_mount_target.html.markdown b/website/docs/r/efs_mount_target.html.markdown index a049ba63a91..19fc8dfbae9 100644 --- a/website/docs/r/efs_mount_target.html.markdown +++ b/website/docs/r/efs_mount_target.html.markdown @@ -57,6 +57,13 @@ In addition to all arguments above, the following attributes are exported: * `availability_zone_id` - The unique and consistent identifier of the Availability Zone (AZ) that the mount target resides in. * `owner_id` - AWS account ID that owns the resource. +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +- `create` - (Default `30m`) +- `delete` - (Default `10m`) + ## Import The EFS mount targets can be imported using the `id`, e.g., From af9e4bb914726e4e4c6f20a7346b622fa002314d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:06:14 -0500 Subject: [PATCH 41/68] r/aws_elasticache_security_group: Use 'tfawserr.ErrCodeEquals'. --- .../service/elasticache/security_group.go | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/internal/service/elasticache/security_group.go b/internal/service/elasticache/security_group.go index e07865384d1..117dd69b89f 100644 --- a/internal/service/elasticache/security_group.go +++ b/internal/service/elasticache/security_group.go @@ -6,8 +6,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticache" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -102,22 +102,15 @@ func resourceSecurityGroupDelete(ctx context.Context, d *schema.ResourceData, me _, err := conn.DeleteCacheSecurityGroupWithContext(ctx, &elasticache.DeleteCacheSecurityGroupInput{ CacheSecurityGroupName: aws.String(d.Id()), }) + + if tfawserr.ErrCodeEquals(err, "InvalidCacheSecurityGroupState", "DependencyViolation") { + return resource.RetryableError(err) + } + if err != nil { - apierr, ok := err.(awserr.Error) - if !ok { - return resource.RetryableError(err) - } - log.Printf("[DEBUG] APIError.Code: %v", apierr.Code()) - switch apierr.Code() { - case "InvalidCacheSecurityGroupState": - return resource.RetryableError(err) - case "DependencyViolation": - // If it is a dependency violation, we want to retry - return resource.RetryableError(err) - default: - return resource.NonRetryableError(err) - } + return resource.RetryableError(err) } + return nil }) From dbf64f608514425234a22d3104226fc3baccfce0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:09:40 -0500 Subject: [PATCH 42/68] testAccCheckApplicationVersionDestroy: Use 'tfawserr.ErrCodeEquals'. --- .../service/elasticbeanstalk/application_version_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/service/elasticbeanstalk/application_version_test.go b/internal/service/elasticbeanstalk/application_version_test.go index eac66a21e8f..3f6f431689b 100644 --- a/internal/service/elasticbeanstalk/application_version_test.go +++ b/internal/service/elasticbeanstalk/application_version_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -131,11 +131,7 @@ func testAccCheckApplicationVersionDestroy(ctx context.Context) resource.TestChe return nil } - ec2err, ok := err.(awserr.Error) - if !ok { - return err - } - if ec2err.Code() != "InvalidParameterValue" { + if !tfawserr.ErrCodeEquals(err, "InvalidParameterValue") { return err } } From b9ae1daab4f73e8a17e93954be73146c23fcbcae Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:40:39 -0500 Subject: [PATCH 43/68] elasticsearch/testAccCheckELBDestroy: Remove. --- internal/service/elasticsearch/domain_test.go | 39 +------------------ 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/internal/service/elasticsearch/domain_test.go b/internal/service/elasticsearch/domain_test.go index 9a3bf866012..129a1eb33fd 100644 --- a/internal/service/elasticsearch/domain_test.go +++ b/internal/service/elasticsearch/domain_test.go @@ -8,10 +8,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" - "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -1297,7 +1295,7 @@ func TestAccElasticsearchDomain_tags(t *testing.T) { PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRole(t) }, ErrorCheck: acctest.ErrorCheck(t, elasticsearch.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckELBDestroy(ctx), + CheckDestroy: testAccCheckDomainDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccDomainConfig_tags1(rName, "key1", "value1"), @@ -3044,38 +3042,3 @@ func testAccPreCheckCognitoIdentityProvider(ctx context.Context, t *testing.T) { t.Fatalf("unexpected PreCheck error: %s", err) } } - -func testAccCheckELBDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_elb" { - continue - } - - describe, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(rs.Primary.ID)}, - }) - - if err == nil { - if len(describe.LoadBalancerDescriptions) != 0 && - *describe.LoadBalancerDescriptions[0].LoadBalancerName == rs.Primary.ID { - return fmt.Errorf("ELB still exists") - } - } - - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { - return err - } - - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } - } - - return nil - } -} From 37fa01265e83fafc704a28cb294bdb2ad3658577 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:44:32 -0500 Subject: [PATCH 44/68] r/aws_elastic_beanstalk_configuration_template: Add 'FindConfigurationSettingsByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccElasticBeanstalkConfigurationTemplate_' PKG=elasticbeanstalk ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elasticbeanstalk/... -v -count 1 -parallel 3 -run=TestAccElasticBeanstalkConfigurationTemplate_ -timeout 180m === RUN TestAccElasticBeanstalkConfigurationTemplate_basic === PAUSE TestAccElasticBeanstalkConfigurationTemplate_basic === RUN TestAccElasticBeanstalkConfigurationTemplate_disappears === PAUSE TestAccElasticBeanstalkConfigurationTemplate_disappears === RUN TestAccElasticBeanstalkConfigurationTemplate_vpc === PAUSE TestAccElasticBeanstalkConfigurationTemplate_vpc === RUN TestAccElasticBeanstalkConfigurationTemplate_settings === PAUSE TestAccElasticBeanstalkConfigurationTemplate_settings === CONT TestAccElasticBeanstalkConfigurationTemplate_basic === CONT TestAccElasticBeanstalkConfigurationTemplate_vpc === CONT TestAccElasticBeanstalkConfigurationTemplate_disappears --- PASS: TestAccElasticBeanstalkConfigurationTemplate_disappears (21.15s) === CONT TestAccElasticBeanstalkConfigurationTemplate_settings --- PASS: TestAccElasticBeanstalkConfigurationTemplate_basic (22.33s) --- PASS: TestAccElasticBeanstalkConfigurationTemplate_vpc (26.74s) --- PASS: TestAccElasticBeanstalkConfigurationTemplate_settings (20.37s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elasticbeanstalk 46.775s --- .../configuration_template.go | 76 +++++--- .../configuration_template_test.go | 175 ++++++++---------- 2 files changed, 130 insertions(+), 121 deletions(-) diff --git a/internal/service/elasticbeanstalk/configuration_template.go b/internal/service/elasticbeanstalk/configuration_template.go index ac49361221c..5480e93cfe6 100644 --- a/internal/service/elasticbeanstalk/configuration_template.go +++ b/internal/service/elasticbeanstalk/configuration_template.go @@ -3,15 +3,16 @@ package elasticbeanstalk import ( "context" "log" - "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceConfigurationTemplate() *schema.Resource { @@ -22,11 +23,6 @@ func ResourceConfigurationTemplate() *schema.Resource { DeleteWithoutTimeout: resourceConfigurationTemplateDelete, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "application": { Type: schema.TypeString, Required: true, @@ -41,6 +37,11 @@ func ResourceConfigurationTemplate() *schema.Resource { Optional: true, ForceNew: true, }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "setting": { Type: schema.TypeSet, Optional: true, @@ -99,33 +100,22 @@ func resourceConfigurationTemplateRead(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() - log.Printf("[DEBUG] Elastic Beanstalk configuration template read: %s", d.Get("name").(string)) + settings, err := FindConfigurationSettingsByTwoPartKey(ctx, conn, d.Get("application").(string), d.Id()) - resp, err := conn.DescribeConfigurationSettingsWithContext(ctx, &elasticbeanstalk.DescribeConfigurationSettingsInput{ - TemplateName: aws.String(d.Id()), - ApplicationName: aws.String(d.Get("application").(string)), - }) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Elastic Beanstalk Configuration Template (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } if err != nil { - if awsErr, ok := err.(awserr.Error); ok { - if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "No Configuration Template named") { - log.Printf("[WARN] No Configuration Template named (%s) found", d.Id()) - d.SetId("") - return diags - } else if awsErr.Code() == "InvalidParameterValue" && strings.Contains(awsErr.Message(), "No Platform named") { - log.Printf("[WARN] No Platform named (%s) found", d.Get("solution_stack_name").(string)) - d.SetId("") - return diags - } - } return sdkdiag.AppendErrorf(diags, "reading Elastic Beanstalk Configuration Template (%s): %s", d.Id(), err) } - if len(resp.ConfigurationSettings) != 1 { - return sdkdiag.AppendErrorf(diags, "reading application properties: found %d applications, expected 1", len(resp.ConfigurationSettings)) - } - - d.Set("description", resp.ConfigurationSettings[0].Description) + d.Set("application", settings.ApplicationName) + d.Set("description", settings.Description) + d.Set("name", settings.TemplateName) + d.Set("solution_stack_name", settings.SolutionStackName) return diags } @@ -243,6 +233,36 @@ func resourceConfigurationTemplateDelete(ctx context.Context, d *schema.Resource return diags } +func FindConfigurationSettingsByTwoPartKey(ctx context.Context, conn *elasticbeanstalk.ElasticBeanstalk, applicationName, templateName string) (*elasticbeanstalk.ConfigurationSettingsDescription, error) { + input := &elasticbeanstalk.DescribeConfigurationSettingsInput{ + ApplicationName: aws.String(applicationName), + TemplateName: aws.String(templateName), + } + + output, err := conn.DescribeConfigurationSettingsWithContext(ctx, input) + + if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "No Configuration Template named") || tfawserr.ErrMessageContains(err, "InvalidParameterValue", "No Application named") { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.ConfigurationSettings) == 0 || output.ConfigurationSettings[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.ConfigurationSettings); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.ConfigurationSettings[0], nil +} + func gatherOptionSettings(d *schema.ResourceData) []*elasticbeanstalk.ConfigurationOptionSetting { optionSettingsSet, ok := d.Get("setting").(*schema.Set) if !ok || optionSettingsSet == nil { diff --git a/internal/service/elasticbeanstalk/configuration_template_test.go b/internal/service/elasticbeanstalk/configuration_template_test.go index ff9e30142fd..b4e7c06256e 100644 --- a/internal/service/elasticbeanstalk/configuration_template_test.go +++ b/internal/service/elasticbeanstalk/configuration_template_test.go @@ -5,19 +5,21 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfelasticbeanstalk "github.com/hashicorp/terraform-provider-aws/internal/service/elasticbeanstalk" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_basic(t *testing.T) { +func TestAccElasticBeanstalkConfigurationTemplate_basic(t *testing.T) { ctx := acctest.Context(t) var config elasticbeanstalk.ConfigurationSettingsDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_configuration_template.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -26,18 +28,20 @@ func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_basic(t *testing.T) CheckDestroy: testAccCheckConfigurationTemplateDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccConfigurationTemplateConfig_basic(sdkacctest.RandString(5)), + Config: testAccConfigurationTemplateConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckConfigurationTemplateExists(ctx, "aws_elastic_beanstalk_configuration_template.tf_template", &config), + testAccCheckConfigurationTemplateExists(ctx, resourceName, &config), ), }, }, }) } -func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_vpc(t *testing.T) { +func TestAccElasticBeanstalkConfigurationTemplate_disappears(t *testing.T) { ctx := acctest.Context(t) var config elasticbeanstalk.ConfigurationSettingsDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_configuration_template.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -46,18 +50,22 @@ func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_vpc(t *testing.T) { CheckDestroy: testAccCheckConfigurationTemplateDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccConfigurationTemplateConfig_vpc(sdkacctest.RandString(5)), + Config: testAccConfigurationTemplateConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckConfigurationTemplateExists(ctx, "aws_elastic_beanstalk_configuration_template.tf_template", &config), + testAccCheckConfigurationTemplateExists(ctx, resourceName, &config), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelasticbeanstalk.ResourceConfigurationTemplate(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) } -func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_setting(t *testing.T) { +func TestAccElasticBeanstalkConfigurationTemplate_vpc(t *testing.T) { ctx := acctest.Context(t) var config elasticbeanstalk.ConfigurationSettingsDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_configuration_template.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -66,12 +74,33 @@ func TestAccElasticBeanstalkConfigurationTemplate_Beanstalk_setting(t *testing.T CheckDestroy: testAccCheckConfigurationTemplateDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccConfigurationTemplateConfig_setting(sdkacctest.RandString(5)), + Config: testAccConfigurationTemplateConfig_vpc(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckConfigurationTemplateExists(ctx, "aws_elastic_beanstalk_configuration_template.tf_template", &config), - resource.TestCheckResourceAttr( - "aws_elastic_beanstalk_configuration_template.tf_template", "setting.#", "1"), - resource.TestCheckTypeSetElemNestedAttrs("aws_elastic_beanstalk_configuration_template.tf_template", "setting.*", map[string]string{ + testAccCheckConfigurationTemplateExists(ctx, resourceName, &config), + ), + }, + }, + }) +} + +func TestAccElasticBeanstalkConfigurationTemplate_settings(t *testing.T) { + ctx := acctest.Context(t) + var config elasticbeanstalk.ConfigurationSettingsDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_configuration_template.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticbeanstalk.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckConfigurationTemplateDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccConfigurationTemplateConfig_setting(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckConfigurationTemplateExists(ctx, resourceName, &config), + resource.TestCheckResourceAttr(resourceName, "setting.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "setting.*", map[string]string{ "value": "m1.small", }), ), @@ -89,141 +118,101 @@ func testAccCheckConfigurationTemplateDestroy(ctx context.Context) resource.Test continue } - // Try to find the Configuration Template - opts := elasticbeanstalk.DescribeConfigurationSettingsInput{ - TemplateName: aws.String(rs.Primary.ID), - ApplicationName: aws.String(rs.Primary.Attributes["application"]), - } - resp, err := conn.DescribeConfigurationSettingsWithContext(ctx, &opts) - if err == nil { - if len(resp.ConfigurationSettings) > 0 { - return fmt.Errorf("Elastic Beanstalk Application still exists.") - } + _, err := tfelasticbeanstalk.FindConfigurationSettingsByTwoPartKey(ctx, conn, rs.Primary.Attributes["application"], rs.Primary.ID) - return nil + if tfresource.NotFound(err) { + continue } - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { + if err != nil { return err } - switch { - case ec2err.Code() == "InvalidBeanstalkConfigurationTemplateID.NotFound": - return nil - // This error can be returned when the beanstalk application no longer exists. - case ec2err.Code() == "InvalidParameterValue": - return nil - default: - return err - } + return fmt.Errorf("Elastic Beanstalk Configuration Template %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckConfigurationTemplateExists(ctx context.Context, n string, config *elasticbeanstalk.ConfigurationSettingsDescription) resource.TestCheckFunc { +func testAccCheckConfigurationTemplateExists(ctx context.Context, n string, v *elasticbeanstalk.ConfigurationSettingsDescription) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ElasticBeanstalkConn() rs, ok := s.RootModule().Resources[n] if !ok { return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("Elastic Beanstalk config ID is not set") + return fmt.Errorf("No Elastic Beanstalk Configuration Template ID is set") } - opts := elasticbeanstalk.DescribeConfigurationSettingsInput{ - TemplateName: aws.String(rs.Primary.ID), - ApplicationName: aws.String(rs.Primary.Attributes["application"]), - } - resp, err := conn.DescribeConfigurationSettingsWithContext(ctx, &opts) + conn := acctest.Provider.Meta().(*conns.AWSClient).ElasticBeanstalkConn() + + output, err := tfelasticbeanstalk.FindConfigurationSettingsByTwoPartKey(ctx, conn, rs.Primary.Attributes["application"], rs.Primary.ID) + if err != nil { return err } - if len(resp.ConfigurationSettings) == 0 { - return fmt.Errorf("Elastic Beanstalk Configurations not found.") - } - *config = *resp.ConfigurationSettings[0] + *v = *output return nil } } -func testAccConfigurationTemplateConfig_basic(r string) string { +func testAccConfigurationTemplateConfig_basic(rName string) string { return fmt.Sprintf(` -resource "aws_elastic_beanstalk_application" "tftest" { - name = "tf-test-%s" - description = "tf-test-desc-%s" +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + description = "testing" } -resource "aws_elastic_beanstalk_configuration_template" "tf_template" { - name = "tf-test-template-config" - application = aws_elastic_beanstalk_application.tftest.name +resource "aws_elastic_beanstalk_configuration_template" "test" { + name = %[1]q + application = aws_elastic_beanstalk_application.test.name solution_stack_name = "64bit Amazon Linux running Python" } -`, r, r) -} - -func testAccConfigurationTemplateConfig_vpc(name string) string { - return fmt.Sprintf(` -resource "aws_vpc" "tf_b_test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = "terraform-testacc-elastic-beanstalk-cfg-tpl-vpc" - } -} - -resource "aws_subnet" "main" { - vpc_id = aws_vpc.tf_b_test.id - cidr_block = "10.0.0.0/24" - - tags = { - Name = "tf-acc-elastic-beanstalk-cfg-tpl-vpc" - } +`, rName) } -resource "aws_elastic_beanstalk_application" "tftest" { - name = "tf-test-%s" - description = "tf-test-desc" +func testAccConfigurationTemplateConfig_vpc(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + description = "testing" } -resource "aws_elastic_beanstalk_configuration_template" "tf_template" { - name = "tf-test-%s" - application = aws_elastic_beanstalk_application.tftest.name +resource "aws_elastic_beanstalk_configuration_template" "test" { + name = %[1]q + application = aws_elastic_beanstalk_application.test.name solution_stack_name = "64bit Amazon Linux running Python" setting { namespace = "aws:ec2:vpc" name = "VPCId" - value = aws_vpc.tf_b_test.id + value = aws_vpc.test.id } setting { namespace = "aws:ec2:vpc" name = "Subnets" - value = aws_subnet.main.id + value = aws_subnet.test[0].id } } -`, name, name) +`, rName)) } -func testAccConfigurationTemplateConfig_setting(name string) string { +func testAccConfigurationTemplateConfig_setting(rName string) string { return fmt.Sprintf(` -resource "aws_elastic_beanstalk_application" "tftest" { - name = "tf-test-%s" - description = "tf-test-desc" +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + description = "testing" } -resource "aws_elastic_beanstalk_configuration_template" "tf_template" { - name = "tf-test-%s" - application = aws_elastic_beanstalk_application.tftest.name +resource "aws_elastic_beanstalk_configuration_template" "test" { + name = %[1]q + application = aws_elastic_beanstalk_application.test.name solution_stack_name = "64bit Amazon Linux running Python" @@ -233,5 +222,5 @@ resource "aws_elastic_beanstalk_configuration_template" "tf_template" { value = "m1.small" } } -`, name, name) +`, rName) } From 8b84c8d0a4e5ba265b831b95840a0e1014e05066 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:48:07 -0500 Subject: [PATCH 45/68] IoT: Use 'tfawserr.ErrCodeEquals'. --- internal/service/iot/certificate_test.go | 6 ++---- internal/service/iot/policy_test.go | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/service/iot/certificate_test.go b/internal/service/iot/certificate_test.go index c5d8d52691c..4ff0b1dca67 100644 --- a/internal/service/iot/certificate_test.go +++ b/internal/service/iot/certificate_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iot" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" @@ -108,10 +108,8 @@ func testAccCheckCertificateDestroy_basic(ctx context.Context) resource.TestChec } } - // Verify the error is what we want if err != nil { - iotErr, ok := err.(awserr.Error) - if !ok || iotErr.Code() != "ResourceNotFoundException" { + if !tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { return err } } diff --git a/internal/service/iot/policy_test.go b/internal/service/iot/policy_test.go index 28cbdb0a4ba..9cbfadb697a 100644 --- a/internal/service/iot/policy_test.go +++ b/internal/service/iot/policy_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iot" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -93,10 +93,8 @@ func testAccCheckPolicyDestroy_basic(ctx context.Context) resource.TestCheckFunc } } - // Verify the error is what we want if err != nil { - iotErr, ok := err.(awserr.Error) - if !ok || iotErr.Code() != "ResourceNotFoundException" { + if !tfawserr.ErrCodeEquals(err, iot.ErrCodeResourceNotFoundException) { return err } } From ea2cbc0e966c6dcabb8f6617b829e2acaf0c10f8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:49:29 -0500 Subject: [PATCH 46/68] opensearch/testAccCheckELBDestroy: Remove. --- internal/service/opensearch/domain_test.go | 39 +--------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/internal/service/opensearch/domain_test.go b/internal/service/opensearch/domain_test.go index a590d8a9687..de62b95cd2d 100644 --- a/internal/service/opensearch/domain_test.go +++ b/internal/service/opensearch/domain_test.go @@ -8,9 +8,7 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/cognitoidentityprovider" - "github.com/aws/aws-sdk-go/service/elb" "github.com/aws/aws-sdk-go/service/opensearchservice" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -1493,7 +1491,7 @@ func TestAccOpenSearchDomain_tags(t *testing.T) { PreCheck: func() { acctest.PreCheck(t); testAccPreCheckIAMServiceLinkedRole(t) }, ErrorCheck: acctest.ErrorCheck(t, opensearchservice.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckELBDestroy(ctx), + CheckDestroy: testAccCheckDomainDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccDomainConfig_tags1(rName, "key1", "value1"), @@ -2034,41 +2032,6 @@ func testAccPreCheckCognitoIdentityProvider(ctx context.Context, t *testing.T) { } } -func testAccCheckELBDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_elb" { - continue - } - - describe, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(rs.Primary.ID)}, - }) - - if err == nil { - if len(describe.LoadBalancerDescriptions) != 0 && - *describe.LoadBalancerDescriptions[0].LoadBalancerName == rs.Primary.ID { - return fmt.Errorf("ELB still exists") - } - } - - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { - return err - } - - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } - } - - return nil - } -} - func testAccDomainConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_opensearch_domain" "test" { From 166507f3d1cc14392677d67e35d891a52e12434a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:51:35 -0500 Subject: [PATCH 47/68] r/aws_redshift_security_group: Use 'tfawserr.ErrCodeEquals'. --- internal/service/redshift/security_group.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/service/redshift/security_group.go b/internal/service/redshift/security_group.go index 72a5b7fbb6f..b6d7b2d864d 100644 --- a/internal/service/redshift/security_group.go +++ b/internal/service/redshift/security_group.go @@ -8,8 +8,8 @@ import ( "regexp" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/redshift" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -170,11 +170,11 @@ func resourceSecurityGroupDelete(ctx context.Context, d *schema.ResourceData, me _, err := conn.DeleteClusterSecurityGroupWithContext(ctx, &opts) + if tfawserr.ErrCodeEquals(err, "InvalidRedshiftSecurityGroup.NotFound") { + return diags + } + if err != nil { - newerr, ok := err.(awserr.Error) - if ok && newerr.Code() == "InvalidRedshiftSecurityGroup.NotFound" { - return diags - } return sdkdiag.AppendErrorf(diags, "deleting Redshift Security Group (%s): %s", d.Id(), err) } From f3b34b44e3a5d60e46cd4b92a01d57e127f8fcc2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 17:53:37 -0500 Subject: [PATCH 48/68] r/aws_sagemaker_model: Use 'tfawserr.ErrCodeEquals'. --- internal/service/sagemaker/model_test.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/service/sagemaker/model_test.go b/internal/service/sagemaker/model_test.go index c0e8630c0ae..569cccc1ea2 100644 --- a/internal/service/sagemaker/model_test.go +++ b/internal/service/sagemaker/model_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/sagemaker" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" @@ -419,11 +419,7 @@ func testAccCheckModelDestroy(ctx context.Context) resource.TestCheckFunc { return nil } - sagemakerErr, ok := err.(awserr.Error) - if !ok { - return err - } - if sagemakerErr.Code() != "ResourceNotFound" { + if !tfawserr.ErrCodeEquals(err, sagemaker.ErrCodeResourceNotFound) { return err } } From b251a7d9f71a18ee6a5a0f1a0258d91b24ec98ab Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Sun, 5 Feb 2023 18:20:11 -0500 Subject: [PATCH 49/68] r/aws_servicecatalog_portfolio: Add 'FindPortfolioByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccServiceCatalogPortfolio_' PKG=servicecatalog ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/servicecatalog/... -v -count 1 -parallel 3 -run=TestAccServiceCatalogPortfolio_ -timeout 180m === RUN TestAccServiceCatalogPortfolio_basic === PAUSE TestAccServiceCatalogPortfolio_basic === RUN TestAccServiceCatalogPortfolio_disappears === PAUSE TestAccServiceCatalogPortfolio_disappears === RUN TestAccServiceCatalogPortfolio_tags === PAUSE TestAccServiceCatalogPortfolio_tags === CONT TestAccServiceCatalogPortfolio_basic === CONT TestAccServiceCatalogPortfolio_tags === CONT TestAccServiceCatalogPortfolio_disappears --- PASS: TestAccServiceCatalogPortfolio_disappears (14.34s) --- PASS: TestAccServiceCatalogPortfolio_basic (18.77s) --- PASS: TestAccServiceCatalogPortfolio_tags (41.22s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/servicecatalog 46.283s --- internal/service/servicecatalog/portfolio.go | 121 +++++++++++------- .../service/servicecatalog/portfolio_test.go | 60 ++++----- 2 files changed, 100 insertions(+), 81 deletions(-) diff --git a/internal/service/servicecatalog/portfolio.go b/internal/service/servicecatalog/portfolio.go index b95643d668e..3ed35f49c5d 100644 --- a/internal/service/servicecatalog/portfolio.go +++ b/internal/service/servicecatalog/portfolio.go @@ -6,8 +6,8 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/servicecatalog" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -45,17 +46,17 @@ func ResourcePortfolio() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "name": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringLenBetween(1, 100), - }, "description": { Type: schema.TypeString, Optional: true, Computed: true, ValidateFunc: validation.StringLenBetween(0, 2000), }, + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringLenBetween(1, 100), + }, "provider_name": { Type: schema.TypeString, Required: true, @@ -73,9 +74,11 @@ func resourcePortfolioCreate(ctx context.Context, d *schema.ResourceData, meta i conn := meta.(*conns.AWSClient).ServiceCatalogConn() defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - input := servicecatalog.CreatePortfolioInput{ + + name := d.Get("name").(string) + input := &servicecatalog.CreatePortfolioInput{ AcceptLanguage: aws.String(AcceptLanguageEnglish), - DisplayName: aws.String(d.Get("name").(string)), + DisplayName: aws.String(name), IdempotencyToken: aws.String(resource.UniqueId()), Tags: Tags(tags.IgnoreAWS()), } @@ -88,12 +91,13 @@ func resourcePortfolioCreate(ctx context.Context, d *schema.ResourceData, meta i input.ProviderName = aws.String(v.(string)) } - log.Printf("[DEBUG] Creating Service Catalog Portfolio: %#v", input) - resp, err := conn.CreatePortfolioWithContext(ctx, &input) + output, err := conn.CreatePortfolioWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Creating Service Catalog Portfolio failed: %s", err.Error()) + return sdkdiag.AppendErrorf(diags, "creating Service Catalog Portfolio (%s): %s", name, err) } - d.SetId(aws.StringValue(resp.PortfolioDetail.Id)) + + d.SetId(aws.StringValue(output.PortfolioDetail.Id)) return append(diags, resourcePortfolioRead(ctx, d, meta)...) } @@ -104,31 +108,26 @@ func resourcePortfolioRead(ctx context.Context, d *schema.ResourceData, meta int defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - input := servicecatalog.DescribePortfolioInput{ - AcceptLanguage: aws.String(AcceptLanguageEnglish), + output, err := FindPortfolioByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Service Catalog Portfolio (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - input.Id = aws.String(d.Id()) - log.Printf("[DEBUG] Reading Service Catalog Portfolio: %#v", input) - resp, err := conn.DescribePortfolioWithContext(ctx, &input) if err != nil { - if scErr, ok := err.(awserr.Error); ok && scErr.Code() == "ResourceNotFoundException" { - log.Printf("[WARN] Service Catalog Portfolio %q not found, removing from state", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "Reading ServiceCatalog Portfolio '%s' failed: %s", *input.Id, err.Error()) - } - portfolioDetail := resp.PortfolioDetail - if err := d.Set("created_time", portfolioDetail.CreatedTime.Format(time.RFC3339)); err != nil { - log.Printf("[DEBUG] Error setting created_time: %s", err) + return sdkdiag.AppendErrorf(diags, "reading Service Catalog Portfolio (%s): %s", d.Id(), err) } + + portfolioDetail := output.PortfolioDetail d.Set("arn", portfolioDetail.ARN) + d.Set("created_time", portfolioDetail.CreatedTime.Format(time.RFC3339)) d.Set("description", portfolioDetail.Description) d.Set("name", portfolioDetail.DisplayName) d.Set("provider_name", portfolioDetail.ProviderName) - tags := KeyValueTags(resp.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + tags := KeyValueTags(output.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { @@ -145,29 +144,26 @@ func resourcePortfolioRead(ctx context.Context, d *schema.ResourceData, meta int func resourcePortfolioUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ServiceCatalogConn() - input := servicecatalog.UpdatePortfolioInput{ + + input := &servicecatalog.UpdatePortfolioInput{ AcceptLanguage: aws.String(AcceptLanguageEnglish), Id: aws.String(d.Id()), } - if d.HasChange("name") { - v, _ := d.GetOk("name") - input.DisplayName = aws.String(v.(string)) - } - if d.HasChange("accept_language") { - v, _ := d.GetOk("accept_language") - input.AcceptLanguage = aws.String(v.(string)) + input.AcceptLanguage = aws.String(d.Get("accept_language").(string)) } if d.HasChange("description") { - v, _ := d.GetOk("description") - input.Description = aws.String(v.(string)) + input.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("name") { + input.DisplayName = aws.String(d.Get("name").(string)) } if d.HasChange("provider_name") { - v, _ := d.GetOk("provider_name") - input.ProviderName = aws.String(v.(string)) + input.ProviderName = aws.String(d.Get("provider_name").(string)) } if d.HasChange("tags_all") { @@ -177,24 +173,53 @@ func resourcePortfolioUpdate(ctx context.Context, d *schema.ResourceData, meta i input.RemoveTags = aws.StringSlice(tftags.New(o).IgnoreAWS().Keys()) } - log.Printf("[DEBUG] Update Service Catalog Portfolio: %#v", input) - _, err := conn.UpdatePortfolioWithContext(ctx, &input) + _, err := conn.UpdatePortfolioWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Updating Service Catalog Portfolio '%s' failed: %s", *input.Id, err.Error()) + return sdkdiag.AppendErrorf(diags, "updating Service Catalog Portfolio (%s): %s", d.Id(), err) } + return append(diags, resourcePortfolioRead(ctx, d, meta)...) } func resourcePortfolioDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ServiceCatalogConn() - input := servicecatalog.DeletePortfolioInput{} - input.Id = aws.String(d.Id()) - log.Printf("[DEBUG] Delete Service Catalog Portfolio: %#v", input) - _, err := conn.DeletePortfolioWithContext(ctx, &input) + log.Printf("[DEBUG] Deleting Service Catalog Portfolio: %s", d.Id()) + _, err := conn.DeletePortfolioWithContext(ctx, &servicecatalog.DeletePortfolioInput{ + Id: aws.String(d.Id()), + }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "Deleting Service Catalog Portfolio '%s' failed: %s", *input.Id, err.Error()) + return sdkdiag.AppendErrorf(diags, "deleting Service Catalog Portfolio (%s): %s", d.Id(), err) } + return diags } + +func FindPortfolioByID(ctx context.Context, conn *servicecatalog.ServiceCatalog, id string) (*servicecatalog.DescribePortfolioOutput, error) { + input := &servicecatalog.DescribePortfolioInput{ + AcceptLanguage: aws.String(AcceptLanguageEnglish), + Id: aws.String(id), + } + + output, err := conn.DescribePortfolioWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, servicecatalog.ErrCodeResourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output, nil +} diff --git a/internal/service/servicecatalog/portfolio_test.go b/internal/service/servicecatalog/portfolio_test.go index e46add375eb..3f6adec88b4 100644 --- a/internal/service/servicecatalog/portfolio_test.go +++ b/internal/service/servicecatalog/portfolio_test.go @@ -6,13 +6,14 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/servicecatalog" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfservicecatalog "github.com/hashicorp/terraform-provider-aws/internal/service/servicecatalog" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccServiceCatalogPortfolio_basic(t *testing.T) { @@ -30,7 +31,7 @@ func TestAccServiceCatalogPortfolio_basic(t *testing.T) { { Config: testAccPortfolioConfig_basic(name), Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolio(ctx, resourceName, &dpo), + testAccCheckPortfolioExists(ctx, resourceName, &dpo), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "catalog", regexp.MustCompile(`portfolio/.+`)), resource.TestCheckResourceAttrSet(resourceName, "created_time"), resource.TestCheckResourceAttr(resourceName, "name", name), @@ -63,8 +64,8 @@ func TestAccServiceCatalogPortfolio_disappears(t *testing.T) { { Config: testAccPortfolioConfig_basic(name), Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolio(ctx, resourceName, &dpo), - testAccCheckServiceCatlaogPortfolioDisappears(ctx, &dpo), + testAccCheckPortfolioExists(ctx, resourceName, &dpo), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfservicecatalog.ResourcePortfolio(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -87,7 +88,7 @@ func TestAccServiceCatalogPortfolio_tags(t *testing.T) { { Config: testAccPortfolioConfig_tags1(name, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolio(ctx, resourceName, &dpo), + testAccCheckPortfolioExists(ctx, resourceName, &dpo), resource.TestCheckResourceAttr(resourceName, "name", name), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), @@ -101,7 +102,7 @@ func TestAccServiceCatalogPortfolio_tags(t *testing.T) { { Config: testAccPortfolioConfig_tags2(name, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolio(ctx, resourceName, &dpo), + testAccCheckPortfolioExists(ctx, resourceName, &dpo), resource.TestCheckResourceAttr(resourceName, "name", name), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), @@ -111,7 +112,7 @@ func TestAccServiceCatalogPortfolio_tags(t *testing.T) { { Config: testAccPortfolioConfig_tags1(name, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckPortfolio(ctx, resourceName, &dpo), + testAccCheckPortfolioExists(ctx, resourceName, &dpo), resource.TestCheckResourceAttr(resourceName, "name", name), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), @@ -121,40 +122,28 @@ func TestAccServiceCatalogPortfolio_tags(t *testing.T) { }) } -func testAccCheckPortfolio(ctx context.Context, pr string, dpo *servicecatalog.DescribePortfolioOutput) resource.TestCheckFunc { +func testAccCheckPortfolioExists(ctx context.Context, n string, v *servicecatalog.DescribePortfolioOutput) resource.TestCheckFunc { return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceCatalogConn() - rs, ok := s.RootModule().Resources[pr] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", pr) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No Service Catalog Portfolio ID is set") } - input := servicecatalog.DescribePortfolioInput{} - input.Id = aws.String(rs.Primary.ID) + conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceCatalogConn() + + output, err := tfservicecatalog.FindPortfolioByID(ctx, conn, rs.Primary.ID) - resp, err := conn.DescribePortfolioWithContext(ctx, &input) if err != nil { return err } - *dpo = *resp - return nil - } -} - -func testAccCheckServiceCatlaogPortfolioDisappears(ctx context.Context, dpo *servicecatalog.DescribePortfolioOutput) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ServiceCatalogConn() - - input := servicecatalog.DeletePortfolioInput{} - input.Id = dpo.PortfolioDetail.Id + *v = *output - _, err := conn.DeletePortfolioWithContext(ctx, &input) - return err + return nil } } @@ -166,13 +155,18 @@ func testAccCheckServiceCatlaogPortfolioDestroy(ctx context.Context) resource.Te if rs.Type != "aws_servicecatalog_portfolio" { continue } - input := servicecatalog.DescribePortfolioInput{} - input.Id = aws.String(rs.Primary.ID) - _, err := conn.DescribePortfolioWithContext(ctx, &input) - if err == nil { - return fmt.Errorf("Portfolio still exists") + _, err := tfservicecatalog.FindPortfolioByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err } + + return fmt.Errorf("Service Catalog Portfolio %s still exists", rs.Primary.ID) } return nil From eeabf8f24d1d1040635b400d76102ec6da564783 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Feb 2023 08:27:30 -0500 Subject: [PATCH 50/68] Delete 'testAccCheckInstanceGroupDestroy'. --- internal/service/emr/instance_group_test.go | 50 ++++----------------- 1 file changed, 8 insertions(+), 42 deletions(-) diff --git a/internal/service/emr/instance_group_test.go b/internal/service/emr/instance_group_test.go index 68429ad2d2a..2442d48343c 100644 --- a/internal/service/emr/instance_group_test.go +++ b/internal/service/emr/instance_group_test.go @@ -3,11 +3,9 @@ package emr_test import ( "context" "fmt" - "log" "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/emr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -27,7 +25,7 @@ func TestAccEMRInstanceGroup_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -59,7 +57,7 @@ func TestAccEMRInstanceGroup_disappears(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -87,7 +85,7 @@ func TestAccEMRInstanceGroup_Disappears_emrCluster(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -112,7 +110,7 @@ func TestAccEMRInstanceGroup_bidPrice(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -165,7 +163,7 @@ func TestAccEMRInstanceGroup_sJSON(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_configurationsJSON(rName, "partitionName1"), @@ -209,7 +207,7 @@ func TestAccEMRInstanceGroup_autoScalingPolicy(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_autoScalingPolicy(rName, 1, 3), @@ -255,7 +253,7 @@ func TestAccEMRInstanceGroup_instanceCount(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_basic(rName), @@ -286,7 +284,7 @@ func TestAccEMRInstanceGroup_EBS_ebsOptimized(t *testing.T) { PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, emr.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckInstanceGroupDestroy(ctx), + CheckDestroy: acctest.CheckDestroyNoop, Steps: []resource.TestStep{ { Config: testAccInstanceGroupConfig_ebs(rName, true), @@ -315,38 +313,6 @@ func TestAccEMRInstanceGroup_EBS_ebsOptimized(t *testing.T) { }) } -func testAccCheckInstanceGroupDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).EMRConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_emr_cluster" { - continue - } - - params := &emr.DescribeClusterInput{ - ClusterId: aws.String(rs.Primary.ID), - } - - describe, err := conn.DescribeClusterWithContext(ctx, params) - - if err == nil { - if describe.Cluster != nil && - *describe.Cluster.Status.State == "WAITING" { - return fmt.Errorf("EMR Cluster still exists") - } - } - - if providerErr, ok := err.(awserr.Error); !ok { - log.Printf("[ERROR] %v", providerErr) - return err - } - } - - return nil - } -} - func testAccCheckInstanceGroupExists(ctx context.Context, name string, ig *emr.InstanceGroup) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] From debe1f64b0e3e8f4363dd20c91413eeba57ec672 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Feb 2023 09:30:42 -0500 Subject: [PATCH 51/68] r/aws_neptune_subnet_group: Add 'FindSubnetGroupByName'. Acceptance test output: % make testacc TESTARGS='-run=TestAccNeptuneSubnetGroup_' PKG=neptune ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/neptune/... -v -count 1 -parallel 3 -run=TestAccNeptuneSubnetGroup_ -timeout 180m === RUN TestAccNeptuneSubnetGroup_basic === PAUSE TestAccNeptuneSubnetGroup_basic === RUN TestAccNeptuneSubnetGroup_disappears === PAUSE TestAccNeptuneSubnetGroup_disappears === RUN TestAccNeptuneSubnetGroup_nameGenerated === PAUSE TestAccNeptuneSubnetGroup_nameGenerated === RUN TestAccNeptuneSubnetGroup_namePrefix === PAUSE TestAccNeptuneSubnetGroup_namePrefix === RUN TestAccNeptuneSubnetGroup_tags === PAUSE TestAccNeptuneSubnetGroup_tags === RUN TestAccNeptuneSubnetGroup_update === PAUSE TestAccNeptuneSubnetGroup_update === CONT TestAccNeptuneSubnetGroup_basic === CONT TestAccNeptuneSubnetGroup_namePrefix === CONT TestAccNeptuneSubnetGroup_update --- PASS: TestAccNeptuneSubnetGroup_namePrefix (26.21s) === CONT TestAccNeptuneSubnetGroup_nameGenerated --- PASS: TestAccNeptuneSubnetGroup_basic (26.61s) === CONT TestAccNeptuneSubnetGroup_tags --- PASS: TestAccNeptuneSubnetGroup_update (40.68s) === CONT TestAccNeptuneSubnetGroup_disappears --- PASS: TestAccNeptuneSubnetGroup_nameGenerated (26.71s) --- PASS: TestAccNeptuneSubnetGroup_disappears (21.79s) --- PASS: TestAccNeptuneSubnetGroup_tags (55.65s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/neptune 87.275s --- internal/service/neptune/subnet_group.go | 164 ++++----- internal/service/neptune/subnet_group_test.go | 339 ++++++++---------- 2 files changed, 229 insertions(+), 274 deletions(-) diff --git a/internal/service/neptune/subnet_group.go b/internal/service/neptune/subnet_group.go index 199db2682a0..26c35fd1619 100644 --- a/internal/service/neptune/subnet_group.go +++ b/internal/service/neptune/subnet_group.go @@ -11,8 +11,11 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -22,6 +25,7 @@ func ResourceSubnetGroup() *schema.Resource { ReadWithoutTimeout: resourceSubnetGroupRead, UpdateWithoutTimeout: resourceSubnetGroupUpdate, DeleteWithoutTimeout: resourceSubnetGroupDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -31,7 +35,11 @@ func ResourceSubnetGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, - + "description": { + Type: schema.TypeString, + Optional: true, + Default: "Managed by Terraform", + }, "name": { Type: schema.TypeString, Optional: true, @@ -48,21 +56,12 @@ func ResourceSubnetGroup() *schema.Resource { ConflictsWith: []string{"name"}, ValidateFunc: validSubnetGroupNamePrefix, }, - - "description": { - Type: schema.TypeString, - Optional: true, - Default: "Managed by Terraform", - }, - "subnet_ids": { Type: schema.TypeSet, Required: true, MinItems: 1, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, }, - "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -77,36 +76,22 @@ func resourceSubnetGroupCreate(ctx context.Context, d *schema.ResourceData, meta defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - subnetIdsSet := d.Get("subnet_ids").(*schema.Set) - subnetIds := make([]*string, subnetIdsSet.Len()) - for i, subnetId := range subnetIdsSet.List() { - subnetIds[i] = aws.String(subnetId.(string)) - } - - var groupName string - if v, ok := d.GetOk("name"); ok { - groupName = v.(string) - } else if v, ok := d.GetOk("name_prefix"); ok { - groupName = resource.PrefixedUniqueId(v.(string)) - } else { - groupName = resource.UniqueId() - } - - createOpts := neptune.CreateDBSubnetGroupInput{ - DBSubnetGroupName: aws.String(groupName), + name := create.Name(d.Get("name").(string), d.Get("name_prefix").(string)) + input := &neptune.CreateDBSubnetGroupInput{ + DBSubnetGroupName: aws.String(name), DBSubnetGroupDescription: aws.String(d.Get("description").(string)), - SubnetIds: subnetIds, + SubnetIds: flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set)), Tags: Tags(tags.IgnoreAWS()), } - log.Printf("[DEBUG] Create Neptune Subnet Group: %#v", createOpts) - _, err := conn.CreateDBSubnetGroupWithContext(ctx, &createOpts) + output, err := conn.CreateDBSubnetGroupWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating Neptune Subnet Group: %s", err) + return sdkdiag.AppendErrorf(diags, "creating Neptune Subnet Group (%s): %s", name, err) } - d.SetId(aws.StringValue(createOpts.DBSubnetGroupName)) - log.Printf("[INFO] Neptune Subnet Group ID: %s", d.Id()) + d.SetId(aws.StringValue(output.DBSubnetGroup.DBSubnetGroupName)) + return append(diags, resourceSubnetGroupRead(ctx, d, meta)...) } @@ -116,54 +101,33 @@ func resourceSubnetGroupRead(ctx context.Context, d *schema.ResourceData, meta i defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - describeOpts := neptune.DescribeDBSubnetGroupsInput{ - DBSubnetGroupName: aws.String(d.Id()), - } + subnetGroup, err := FindSubnetGroupByName(ctx, conn, d.Id()) - var subnetGroups []*neptune.DBSubnetGroup - if err := conn.DescribeDBSubnetGroupsPagesWithContext(ctx, &describeOpts, func(resp *neptune.DescribeDBSubnetGroupsOutput, lastPage bool) bool { - subnetGroups = append(subnetGroups, resp.DBSubnetGroups...) - return !lastPage - }); err != nil { - if tfawserr.ErrCodeEquals(err, neptune.ErrCodeDBSubnetGroupNotFoundFault) { - log.Printf("[WARN] Neptune Subnet Group (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading Neptune Subnet Group (%s): %s", d.Id(), err) - } - - if len(subnetGroups) == 0 { - log.Printf("[WARN] Unable to find Neptune Subnet Group: %#v, removing from state", subnetGroups) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Neptune Subnet Group (%s) not found, removing from state", d.Id()) d.SetId("") return diags } - subnetGroup := subnetGroups[0] - - if subnetGroup.DBSubnetGroupName == nil { - return sdkdiag.AppendErrorf(diags, "Unable to find Neptune Subnet Group: %#v", subnetGroups) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Neptune Subnet Group (%s): %s", d.Id(), err) } - d.Set("name", subnetGroup.DBSubnetGroupName) + arn := aws.StringValue(subnetGroup.DBSubnetGroupArn) + d.Set("arn", arn) d.Set("description", subnetGroup.DBSubnetGroupDescription) - - subnets := make([]string, 0, len(subnetGroup.Subnets)) - for _, s := range subnetGroup.Subnets { - subnets = append(subnets, aws.StringValue(s.SubnetIdentifier)) - } - if err := d.Set("subnet_ids", subnets); err != nil { - return sdkdiag.AppendErrorf(diags, "setting subnet_ids: %s", err) + d.Set("name", subnetGroup.DBSubnetGroupName) + d.Set("name_prefix", create.NamePrefixFromName(aws.StringValue(subnetGroup.DBSubnetGroupName))) + var subnetIDs []string + for _, v := range subnetGroup.Subnets { + subnetIDs = append(subnetIDs, aws.StringValue(v.SubnetIdentifier)) } + d.Set("subnet_ids", subnetIDs) - //Amazon Neptune shares the format of Amazon RDS ARNs. Neptune ARNs contain rds and not neptune. - //https://docs.aws.amazon.com/neptune/latest/userguide/tagging.ARN.html - d.Set("arn", subnetGroup.DBSubnetGroupArn) - - tags, err := ListTags(ctx, conn, d.Get("arn").(string)) + tags, err := ListTags(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "listing tags for Neptune Subnet Group (%s): %s", d.Get("arn").(string), err) + return sdkdiag.AppendErrorf(diags, "listing tags for Neptune Subnet Group (%s): %s", arn, err) } tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) @@ -183,23 +147,15 @@ func resourceSubnetGroupRead(ctx context.Context, d *schema.ResourceData, meta i func resourceSubnetGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).NeptuneConn() - if d.HasChanges("subnet_ids", "description") { - _, n := d.GetChange("subnet_ids") - if n == nil { - n = new(schema.Set) - } - ns := n.(*schema.Set) - - var sIds []*string - for _, s := range ns.List() { - sIds = append(sIds, aws.String(s.(string))) - } - _, err := conn.ModifyDBSubnetGroupWithContext(ctx, &neptune.ModifyDBSubnetGroupInput{ + if d.HasChanges("description", "subnet_ids") { + input := &neptune.ModifyDBSubnetGroupInput{ DBSubnetGroupName: aws.String(d.Id()), DBSubnetGroupDescription: aws.String(d.Get("description").(string)), - SubnetIds: sIds, - }) + SubnetIds: flex.ExpandStringSet(d.Get("subnet_ids").(*schema.Set)), + } + + _, err := conn.ModifyDBSubnetGroupWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating Neptune Subnet Group (%s): %s", d.Id(), err) @@ -221,18 +177,48 @@ func resourceSubnetGroupDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).NeptuneConn() - input := neptune.DeleteDBSubnetGroupInput{ + log.Printf("[DEBUG] Deleting Neptune Subnet Group: %s", d.Id()) + _, err := conn.DeleteDBSubnetGroupWithContext(ctx, &neptune.DeleteDBSubnetGroupInput{ DBSubnetGroupName: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, neptune.ErrCodeDBSubnetGroupNotFoundFault) { + return diags } - log.Printf("[DEBUG] Deleting Neptune Subnet Group: %s", d.Id()) - _, err := conn.DeleteDBSubnetGroupWithContext(ctx, &input) if err != nil { - if tfawserr.ErrCodeEquals(err, neptune.ErrCodeDBSubnetGroupNotFoundFault) { - return diags - } return sdkdiag.AppendErrorf(diags, "deleting Neptune Subnet Group (%s): %s", d.Id(), err) } return diags } + +func FindSubnetGroupByName(ctx context.Context, conn *neptune.Neptune, name string) (*neptune.DBSubnetGroup, error) { + input := &neptune.DescribeDBSubnetGroupsInput{ + DBSubnetGroupName: aws.String(name), + } + + output, err := conn.DescribeDBSubnetGroupsWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, neptune.ErrCodeDBSubnetGroupNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if output == nil || len(output.DBSubnetGroups) == 0 || output.DBSubnetGroups[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + dbSubnetGroup := output.DBSubnetGroups[0] + + // Eventual consistency check. + if aws.StringValue(dbSubnetGroup.DBSubnetGroupName) != name { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return dbSubnetGroup, nil +} diff --git a/internal/service/neptune/subnet_group_test.go b/internal/service/neptune/subnet_group_test.go index 509e4c96f02..c7243c9e526 100644 --- a/internal/service/neptune/subnet_group_test.go +++ b/internal/service/neptune/subnet_group_test.go @@ -6,21 +6,54 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/neptune" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfneptune "github.com/hashicorp/terraform-provider-aws/internal/service/neptune" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccNeptuneSubnetGroup_basic(t *testing.T) { ctx := acctest.Context(t) var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" - rName := fmt.Sprintf("tf-test-%d", sdkacctest.RandInt()) + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, neptune.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSubnetGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSubnetGroupConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("subgrp:%s$", rName))), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNeptuneSubnetGroup_disappears(t *testing.T) { + ctx := acctest.Context(t) + var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -31,15 +64,37 @@ func TestAccNeptuneSubnetGroup_basic(t *testing.T) { { Config: testAccSubnetGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckSubnetGroupExists(ctx, "aws_neptune_subnet_group.foo", &v), - resource.TestCheckResourceAttr( - "aws_neptune_subnet_group.foo", "name", rName), - resource.TestCheckResourceAttr( - "aws_neptune_subnet_group.foo", "description", "Managed by Terraform"), + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfneptune.ResourceSubnetGroup(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccNeptuneSubnetGroup_nameGenerated(t *testing.T) { + ctx := acctest.Context(t) + var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, neptune.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckSubnetGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccSubnetGroupConfig_nameGenerated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + acctest.CheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", resource.UniqueIdPrefix), ), }, { - ResourceName: "aws_neptune_subnet_group.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -50,6 +105,8 @@ func TestAccNeptuneSubnetGroup_basic(t *testing.T) { func TestAccNeptuneSubnetGroup_namePrefix(t *testing.T) { ctx := acctest.Context(t) var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -58,26 +115,27 @@ func TestAccNeptuneSubnetGroup_namePrefix(t *testing.T) { CheckDestroy: testAccCheckSubnetGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccSubnetGroupConfig_namePrefix(), + Config: testAccSubnetGroupConfig_namePrefix(rName, "tf-acc-test-prefix-"), Check: resource.ComposeTestCheckFunc( - testAccCheckSubnetGroupExists(ctx, "aws_neptune_subnet_group.test", &v), - resource.TestMatchResourceAttr( - "aws_neptune_subnet_group.test", "name", regexp.MustCompile("^tf_test-")), + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + acctest.CheckResourceAttrNameFromPrefix(resourceName, "name", "tf-acc-test-prefix-"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "tf-acc-test-prefix-"), ), }, { - ResourceName: "aws_neptune_subnet_group.test", - ImportState: true, - ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"name_prefix"}, + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) } -func TestAccNeptuneSubnetGroup_generatedName(t *testing.T) { +func TestAccNeptuneSubnetGroup_tags(t *testing.T) { ctx := acctest.Context(t) var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -86,25 +144,45 @@ func TestAccNeptuneSubnetGroup_generatedName(t *testing.T) { CheckDestroy: testAccCheckSubnetGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccSubnetGroupConfig_generatedName(), + Config: testAccSubnetGroupConfig_tags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckSubnetGroupExists(ctx, "aws_neptune_subnet_group.test", &v), + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { - ResourceName: "aws_neptune_subnet_group.test", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, + { + Config: testAccSubnetGroupConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccSubnetGroupConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, }, }) } -func TestAccNeptuneSubnetGroup_updateDescription(t *testing.T) { +func TestAccNeptuneSubnetGroup_update(t *testing.T) { ctx := acctest.Context(t) var v neptune.DBSubnetGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_neptune_subnet_group.test" - rName := fmt.Sprintf("tf-test-%d", sdkacctest.RandInt()) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, neptune.EndpointsID), @@ -114,25 +192,19 @@ func TestAccNeptuneSubnetGroup_updateDescription(t *testing.T) { { Config: testAccSubnetGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckSubnetGroupExists(ctx, "aws_neptune_subnet_group.foo", &v), - resource.TestCheckResourceAttr( - "aws_neptune_subnet_group.foo", "description", "Managed by Terraform"), + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "2"), ), }, - { - Config: testAccSubnetGroupConfig_updatedDescription(rName), + Config: testAccSubnetGroupConfig_update(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckSubnetGroupExists(ctx, "aws_neptune_subnet_group.foo", &v), - resource.TestCheckResourceAttr( - "aws_neptune_subnet_group.foo", "description", "foo description updated"), + testAccCheckSubnetGroupExists(ctx, resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "description", "test description updated"), + resource.TestCheckResourceAttr(resourceName, "subnet_ids.#", "3"), ), }, - { - ResourceName: "aws_neptune_subnet_group.foo", - ImportState: true, - ImportStateVerify: true, - }, }, }) } @@ -146,24 +218,17 @@ func testAccCheckSubnetGroupDestroy(ctx context.Context) resource.TestCheckFunc continue } - // Try to find the resource - resp, err := conn.DescribeDBSubnetGroupsWithContext(ctx, &neptune.DescribeDBSubnetGroupsInput{DBSubnetGroupName: aws.String(rs.Primary.ID)}) - if err == nil { - if len(resp.DBSubnetGroups) > 0 { - return fmt.Errorf("still exist.") - } + _, err := tfneptune.FindSubnetGroupByName(ctx, conn, rs.Primary.ID) - return nil + if tfresource.NotFound(err) { + continue } - // Verify the error is what we want - neptuneerr, ok := err.(awserr.Error) - if !ok { - return err - } - if neptuneerr.Code() != "DBSubnetGroupNotFoundFault" { + if err != nil { return err } + + return fmt.Errorf("Neptune Subnet Group %s still exists", rs.Primary.ID) } return nil @@ -178,176 +243,80 @@ func testAccCheckSubnetGroupExists(ctx context.Context, n string, v *neptune.DBS } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No Neptune Subnet Group ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).NeptuneConn() - resp, err := conn.DescribeDBSubnetGroupsWithContext(ctx, &neptune.DescribeDBSubnetGroupsInput{DBSubnetGroupName: aws.String(rs.Primary.ID)}) + + output, err := tfneptune.FindSubnetGroupByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } - if len(resp.DBSubnetGroups) == 0 { - return fmt.Errorf("DbSubnetGroup not found") - } - *v = *resp.DBSubnetGroups[0] + *v = *output return nil } } func testAccSubnetGroupConfig_basic(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = "terraform-testacc-neptune-subnet-group" - } -} - -resource "aws_subnet" "foo" { - cidr_block = "10.1.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - vpc_id = aws_vpc.foo.id - - tags = { - Name = "tf-acc-neptune-subnet-group-1" - } -} - -resource "aws_subnet" "bar" { - cidr_block = "10.1.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] - vpc_id = aws_vpc.foo.id - - tags = { - Name = "tf-acc-neptune-subnet-group-2" - } -} - -resource "aws_neptune_subnet_group" "foo" { - name = "%s" - subnet_ids = [aws_subnet.foo.id, aws_subnet.bar.id] - - tags = { - Name = "tf-neptunesubnet-group-test" - } + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_neptune_subnet_group" "test" { + name = %[1]q + subnet_ids = aws_subnet.test[*].id } `, rName)) } -func testAccSubnetGroupConfig_updatedDescription(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = "terraform-testacc-neptune-subnet-group-updated-description" - } -} - -resource "aws_subnet" "foo" { - cidr_block = "10.1.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - vpc_id = aws_vpc.foo.id - - tags = { - Name = "tf-acc-neptune-subnet-group-1" - } -} - -resource "aws_subnet" "bar" { - cidr_block = "10.1.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] - vpc_id = aws_vpc.foo.id - - tags = { - Name = "tf-acc-neptune-subnet-group-2" - } -} - -resource "aws_neptune_subnet_group" "foo" { - name = "%s" - description = "foo description updated" - subnet_ids = [aws_subnet.foo.id, aws_subnet.bar.id] - - tags = { - Name = "tf-neptunesubnet-group-test" - } -} -`, rName)) +func testAccSubnetGroupConfig_nameGenerated(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), ` +resource "aws_neptune_subnet_group" "test" { + subnet_ids = aws_subnet.test[*].id +}`) } -func testAccSubnetGroupConfig_namePrefix() string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), ` -resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = "terraform-testacc-neptune-subnet-group-name-prefix" - } +func testAccSubnetGroupConfig_namePrefix(rName, prefix string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_neptune_subnet_group" "test" { + name_prefix = %[1]q + subnet_ids = aws_subnet.test[*].id +}`, prefix)) } -resource "aws_subnet" "a" { - vpc_id = aws_vpc.test.id - cidr_block = "10.1.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] +func testAccSubnetGroupConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` +resource "aws_neptune_subnet_group" "test" { + name = %[1]q + subnet_ids = aws_subnet.test[*].id tags = { - Name = "tf-acc-neptune-subnet-group-name-prefix-a" + %[2]q = %[3]q } } - -resource "aws_subnet" "b" { - vpc_id = aws_vpc.test.id - cidr_block = "10.1.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] - - tags = { - Name = "tf-acc-neptune-subnet-group-name-prefix-b" - } +`, rName, tagKey1, tagValue1)) } +func testAccSubnetGroupConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 2), fmt.Sprintf(` resource "aws_neptune_subnet_group" "test" { - name_prefix = "tf_test-" - subnet_ids = [aws_subnet.a.id, aws_subnet.b.id] -} -`) -} - -func testAccSubnetGroupConfig_generatedName() string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), ` -resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" + name = %[1]q + subnet_ids = aws_subnet.test[*].id tags = { - Name = "terraform-testacc-neptune-subnet-group-generated-name" + %[2]q = %[3]q + %[4]q = %[5]q } } - -resource "aws_subnet" "a" { - vpc_id = aws_vpc.test.id - cidr_block = "10.1.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = "tf-acc-neptune-subnet-group-generated-name-a" - } -} - -resource "aws_subnet" "b" { - vpc_id = aws_vpc.test.id - cidr_block = "10.1.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] - - tags = { - Name = "tf-acc-neptune-subnet-group-generated-name-a" - } +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } +func testAccSubnetGroupConfig_update(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(` resource "aws_neptune_subnet_group" "test" { - subnet_ids = [aws_subnet.a.id, aws_subnet.b.id] + name = %[1]q + subnet_ids = aws_subnet.test[*].id + description = "test description updated" } -`) +`, rName)) } From e9ed5533042adadece2d02a8c433c988f8414546 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Feb 2023 13:46:28 -0500 Subject: [PATCH 52/68] r/aws_db_parameter_group: Add 'FindDBParameterGroupByName'. Acceptance test output: % make testacc TESTARGS='-run=TestAccRDSParameterGroup_' PKG=rds ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/rds/... -v -count 1 -parallel 3 -run=TestAccRDSParameterGroup_ -timeout 180m === RUN TestAccRDSParameterGroup_basic === PAUSE TestAccRDSParameterGroup_basic === RUN TestAccRDSParameterGroup_caseWithMixedParameters === PAUSE TestAccRDSParameterGroup_caseWithMixedParameters === RUN TestAccRDSParameterGroup_limit === PAUSE TestAccRDSParameterGroup_limit === RUN TestAccRDSParameterGroup_disappears === PAUSE TestAccRDSParameterGroup_disappears === RUN TestAccRDSParameterGroup_namePrefix === PAUSE TestAccRDSParameterGroup_namePrefix === RUN TestAccRDSParameterGroup_generatedName === PAUSE TestAccRDSParameterGroup_generatedName === RUN TestAccRDSParameterGroup_withApplyMethod === PAUSE TestAccRDSParameterGroup_withApplyMethod === RUN TestAccRDSParameterGroup_only === PAUSE TestAccRDSParameterGroup_only === RUN TestAccRDSParameterGroup_matchDefault === PAUSE TestAccRDSParameterGroup_matchDefault === RUN TestAccRDSParameterGroup_updateParameters === PAUSE TestAccRDSParameterGroup_updateParameters === RUN TestAccRDSParameterGroup_caseParameters === PAUSE TestAccRDSParameterGroup_caseParameters === CONT TestAccRDSParameterGroup_basic === CONT TestAccRDSParameterGroup_caseParameters === CONT TestAccRDSParameterGroup_updateParameters --- PASS: TestAccRDSParameterGroup_caseParameters (41.86s) === CONT TestAccRDSParameterGroup_matchDefault --- PASS: TestAccRDSParameterGroup_updateParameters (44.39s) === CONT TestAccRDSParameterGroup_only --- PASS: TestAccRDSParameterGroup_basic (64.17s) === CONT TestAccRDSParameterGroup_withApplyMethod --- PASS: TestAccRDSParameterGroup_matchDefault (24.87s) === CONT TestAccRDSParameterGroup_generatedName --- PASS: TestAccRDSParameterGroup_only (23.71s) === CONT TestAccRDSParameterGroup_namePrefix --- PASS: TestAccRDSParameterGroup_generatedName (16.49s) === CONT TestAccRDSParameterGroup_disappears --- PASS: TestAccRDSParameterGroup_namePrefix (16.01s) === CONT TestAccRDSParameterGroup_limit --- PASS: TestAccRDSParameterGroup_withApplyMethod (19.97s) === CONT TestAccRDSParameterGroup_caseWithMixedParameters --- PASS: TestAccRDSParameterGroup_disappears (15.45s) --- PASS: TestAccRDSParameterGroup_caseWithMixedParameters (41.93s) --- PASS: TestAccRDSParameterGroup_limit (46.02s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/rds 143.596s --- internal/service/neptune/subnet_group.go | 4 + .../service/rds/cluster_parameter_group.go | 4 + internal/service/rds/parameter_group.go | 176 ++++++++++-------- internal/service/rds/parameter_group_test.go | 141 ++++++-------- 4 files changed, 158 insertions(+), 167 deletions(-) diff --git a/internal/service/neptune/subnet_group.go b/internal/service/neptune/subnet_group.go index 26c35fd1619..ee1e1b39ff0 100644 --- a/internal/service/neptune/subnet_group.go +++ b/internal/service/neptune/subnet_group.go @@ -207,6 +207,10 @@ func FindSubnetGroupByName(ctx context.Context, conn *neptune.Neptune, name stri } } + if err != nil { + return nil, err + } + if output == nil || len(output.DBSubnetGroups) == 0 || output.DBSubnetGroups[0] == nil { return nil, tfresource.NewEmptyResultError(input) } diff --git a/internal/service/rds/cluster_parameter_group.go b/internal/service/rds/cluster_parameter_group.go index e97cbbcf2c9..0b8826353fa 100644 --- a/internal/service/rds/cluster_parameter_group.go +++ b/internal/service/rds/cluster_parameter_group.go @@ -354,6 +354,10 @@ func FindDBClusterParameterGroupByName(ctx context.Context, conn *rds.RDS, name } } + if err != nil { + return nil, err + } + if output == nil || len(output.DBClusterParameterGroups) == 0 || output.DBClusterParameterGroups[0] == nil { return nil, tfresource.NewEmptyResultError(input) } diff --git a/internal/service/rds/parameter_group.go b/internal/service/rds/parameter_group.go index c98bf35bb7f..a4073d92b6d 100644 --- a/internal/service/rds/parameter_group.go +++ b/internal/service/rds/parameter_group.go @@ -31,6 +31,7 @@ func ResourceParameterGroup() *schema.Resource { ReadWithoutTimeout: resourceParameterGroupRead, UpdateWithoutTimeout: resourceParameterGroupUpdate, DeleteWithoutTimeout: resourceParameterGroupDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -40,6 +41,17 @@ func ResourceParameterGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "Managed by Terraform", + }, + "family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "name": { Type: schema.TypeString, Optional: true, @@ -56,17 +68,6 @@ func ResourceParameterGroup() *schema.Resource { ConflictsWith: []string{"name"}, ValidateFunc: validParamGroupNamePrefix, }, - "family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Default: "Managed by Terraform", - }, "parameter": { Type: schema.TypeSet, Optional: true, @@ -89,7 +90,6 @@ func ResourceParameterGroup() *schema.Resource { }, Set: resourceParameterHash, }, - "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, @@ -104,32 +104,24 @@ func resourceParameterGroupCreate(ctx context.Context, d *schema.ResourceData, m defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) - var groupName string - if v, ok := d.GetOk("name"); ok { - groupName = v.(string) - } else if v, ok := d.GetOk("name_prefix"); ok { - groupName = resource.PrefixedUniqueId(v.(string)) - } else { - groupName = resource.UniqueId() - } - d.Set("name", groupName) - - createOpts := rds.CreateDBParameterGroupInput{ - DBParameterGroupName: aws.String(groupName), + name := create.Name(d.Get("name").(string), d.Get("name_prefix").(string)) + input := &rds.CreateDBParameterGroupInput{ DBParameterGroupFamily: aws.String(d.Get("family").(string)), + DBParameterGroupName: aws.String(name), Description: aws.String(d.Get("description").(string)), Tags: Tags(tags.IgnoreAWS()), } - log.Printf("[DEBUG] Create DB Parameter Group: %#v", createOpts) - resp, err := conn.CreateDBParameterGroupWithContext(ctx, &createOpts) + output, err := conn.CreateDBParameterGroupWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating DB Parameter Group: %s", err) + return sdkdiag.AppendErrorf(diags, "creatingDB Parameter Group (%s): %s", name, err) } - d.SetId(aws.StringValue(resp.DBParameterGroup.DBParameterGroupName)) - d.Set("arn", resp.DBParameterGroup.DBParameterGroupArn) - log.Printf("[INFO] DB Parameter Group ID: %s", d.Id()) + d.SetId(aws.StringValue(output.DBParameterGroup.DBParameterGroupName)) + + // Set for update + d.Set("arn", output.DBParameterGroup.DBParameterGroupArn) return append(diags, resourceParameterGroupUpdate(ctx, d, meta)...) } @@ -140,33 +132,29 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - describeOpts := rds.DescribeDBParameterGroupsInput{ - DBParameterGroupName: aws.String(d.Id()), + dbParameterGroup, err := FindDBParameterGroupByName(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] RDS DB Parameter Group (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - describeResp, err := conn.DescribeDBParameterGroupsWithContext(ctx, &describeOpts) if err != nil { - if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { - log.Printf("[WARN] DB Parameter Group (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s): %s", d.Id(), err) } - if len(describeResp.DBParameterGroups) != 1 || - aws.StringValue(describeResp.DBParameterGroups[0].DBParameterGroupName) != d.Id() { - return sdkdiag.AppendErrorf(diags, "Unable to find Parameter Group: %#v", describeResp.DBParameterGroups) - } - - d.Set("name", describeResp.DBParameterGroups[0].DBParameterGroupName) - d.Set("family", describeResp.DBParameterGroups[0].DBParameterGroupFamily) - d.Set("description", describeResp.DBParameterGroups[0].Description) + arn := aws.StringValue(dbParameterGroup.DBParameterGroupArn) + d.Set("arn", arn) + d.Set("description", dbParameterGroup.Description) + d.Set("family", dbParameterGroup.DBParameterGroupFamily) + d.Set("name", dbParameterGroup.DBParameterGroupName) - configParams := d.Get("parameter").(*schema.Set) - describeParametersOpts := rds.DescribeDBParametersInput{ + input := &rds.DescribeDBParametersInput{ DBParameterGroupName: aws.String(d.Id()), } + + configParams := d.Get("parameter").(*schema.Set) if configParams.Len() < 1 { // if we don't have any params in the ResourceData already, two possibilities // first, we don't have a config available to us. Second, we do, but it has @@ -177,17 +165,17 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met // an empty list anyways, so we just make some unnecessary requests. But in // the more common case (I assume) of an import, this will make fewer requests // and "do the right thing". - describeParametersOpts.Source = aws.String("user") + input.Source = aws.String("user") } var parameters []*rds.Parameter - err = conn.DescribeDBParametersPagesWithContext(ctx, &describeParametersOpts, - func(describeParametersResp *rds.DescribeDBParametersOutput, lastPage bool) bool { - parameters = append(parameters, describeParametersResp.Parameters...) - return !lastPage - }) + err = conn.DescribeDBParametersPagesWithContext(ctx, input, func(page *rds.DescribeDBParametersOutput, lastPage bool) bool { + parameters = append(parameters, page.Parameters...) + return !lastPage + }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading RDS DB Parameter Group (%s) parameters: %s", d.Id(), err) } var userParams []*rds.Parameter @@ -230,18 +218,14 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met } } - err = d.Set("parameter", flattenParameters(userParams)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "setting 'parameter' in state: %s", err) + if err := d.Set("parameter", flattenParameters(userParams)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting parameter: %s", err) } - arn := aws.StringValue(describeResp.DBParameterGroups[0].DBParameterGroupArn) - d.Set("arn", arn) - - tags, err := ListTags(ctx, conn, d.Get("arn").(string)) + tags, err := ListTags(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "listing tags for RDS DB Parameter Group (%s): %s", d.Get("arn").(string), err) + return sdkdiag.AppendErrorf(diags, "listing tags for RDS DB Parameter Group (%s): %s", arn, err) } tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) @@ -258,9 +242,10 @@ func resourceParameterGroupRead(ctx context.Context, d *schema.ResourceData, met return diags } -const maxParamModifyChunk = 20 - func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + const ( + maxParamModifyChunk = 20 + ) var diags diag.Diagnostics conn := meta.(*conns.AWSClient).RDSConn() @@ -287,15 +272,15 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m var paramsToModify []*rds.Parameter paramsToModify, parameters = ResourceParameterModifyChunk(parameters, maxParamModifyChunk) - modifyOpts := rds.ModifyDBParameterGroupInput{ - DBParameterGroupName: aws.String(d.Get("name").(string)), + input := &rds.ModifyDBParameterGroupInput{ + DBParameterGroupName: aws.String(d.Id()), Parameters: paramsToModify, } - log.Printf("[DEBUG] Modify DB Parameter Group: %s", modifyOpts) - _, err := conn.ModifyDBParameterGroupWithContext(ctx, &modifyOpts) + _, err := conn.ModifyDBParameterGroupWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "modifying DB Parameter Group: %s", err) + return sdkdiag.AppendErrorf(diags, "modifying DB Parameter Group (%s): %s", d.Id(), err) } } } @@ -328,17 +313,16 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m paramsToReset, resetParameters = resetParameters[:maxParamModifyChunk], resetParameters[maxParamModifyChunk:] } - parameterGroupName := d.Get("name").(string) - resetOpts := rds.ResetDBParameterGroupInput{ - DBParameterGroupName: aws.String(parameterGroupName), + input := &rds.ResetDBParameterGroupInput{ + DBParameterGroupName: aws.String(d.Id()), Parameters: paramsToReset, ResetAllParameters: aws.Bool(false), } - log.Printf("[DEBUG] Reset DB Parameter Group: %s", resetOpts) - _, err := conn.ResetDBParameterGroupWithContext(ctx, &resetOpts) + _, err := conn.ResetDBParameterGroupWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "resetting DB Parameter Group: %s", err) + return sdkdiag.AppendErrorf(diags, "resetting DB Parameter Group (%s): %s", d.Id(), err) } } } @@ -357,13 +341,13 @@ func resourceParameterGroupUpdate(ctx context.Context, d *schema.ResourceData, m func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) (diags diag.Diagnostics) { conn := meta.(*conns.AWSClient).RDSClient() - deleteOpts := rds_sdkv2.DeleteDBParameterGroupInput{ + input := &rds_sdkv2.DeleteDBParameterGroupInput{ DBParameterGroupName: aws.String(d.Id()), } log.Printf("[DEBUG] Deleting RDS DB Parameter Group: %s", d.Id()) err := resource.RetryContext(ctx, 3*time.Minute, func() *resource.RetryError { - _, err := conn.DeleteDBParameterGroup(ctx, &deleteOpts) + _, err := conn.DeleteDBParameterGroup(ctx, input) if errs.IsA[*types.DBParameterGroupNotFoundFault](err) { return nil } else if errs.IsA[*types.InvalidDBParameterGroupStateFault](err) { @@ -375,7 +359,7 @@ func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, m return nil }) if tfresource.TimedOut(err) { - _, err = conn.DeleteDBParameterGroup(ctx, &deleteOpts) + _, err = conn.DeleteDBParameterGroup(ctx, input) } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting RDS DB Parameter Group (%s): %s", d.Id(), err) @@ -383,6 +367,40 @@ func resourceParameterGroupDelete(ctx context.Context, d *schema.ResourceData, m return nil } +func FindDBParameterGroupByName(ctx context.Context, conn *rds.RDS, name string) (*rds.DBParameterGroup, error) { + input := &rds.DescribeDBParameterGroupsInput{ + DBParameterGroupName: aws.String(name), + } + + output, err := conn.DescribeDBParameterGroupsWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, rds.ErrCodeDBParameterGroupNotFoundFault) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.DBParameterGroups) == 0 || output.DBParameterGroups[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + dbParameterGroup := output.DBParameterGroups[0] + + // Eventual consistency check. + if aws.StringValue(dbParameterGroup.DBParameterGroupName) != name { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return dbParameterGroup, nil +} + func resourceParameterHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) diff --git a/internal/service/rds/parameter_group_test.go b/internal/service/rds/parameter_group_test.go index acb3b9707d2..16ad1c28476 100644 --- a/internal/service/rds/parameter_group_test.go +++ b/internal/service/rds/parameter_group_test.go @@ -8,7 +8,6 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/rds" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -16,13 +15,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfrds "github.com/hashicorp/terraform-provider-aws/internal/service/rds" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccRDSParameterGroup_basic(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -31,11 +31,11 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(groupName), + Config: testAccParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "character_set_results", @@ -49,7 +49,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { "name": "character_set_client", "value": "utf8", }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", groupName))), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", rName))), ), }, { @@ -58,11 +58,11 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccParameterGroupConfig_addParameters(groupName), + Config: testAccParameterGroupConfig_addParameters(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "collation_connection", @@ -84,14 +84,14 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { "name": "character_set_client", "value": "utf8", }), - acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", groupName))), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "rds", regexp.MustCompile(fmt.Sprintf("pg:%s$", rName))), ), }, { - Config: testAccParameterGroupConfig_basic(groupName), + Config: testAccParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), + testAccCheckParameterGroupAttributes(&v, rName), testAccCheckParameterNotUserDefined(ctx, resourceName, "collation_connection"), testAccCheckParameterNotUserDefined(ctx, resourceName, "collation_server"), resource.TestCheckResourceAttr(resourceName, "parameter.#", "3"), @@ -115,7 +115,7 @@ func TestAccRDSParameterGroup_basic(t *testing.T) { func TestAccRDSParameterGroup_caseWithMixedParameters(t *testing.T) { ctx := acctest.Context(t) - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -124,7 +124,7 @@ func TestAccRDSParameterGroup_caseWithMixedParameters(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_caseWithMixedParameters(groupName), + Config: testAccParameterGroupConfig_caseWithMixedParameters(rName), Check: resource.ComposeTestCheckFunc(), }, }, @@ -135,7 +135,7 @@ func TestAccRDSParameterGroup_limit(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -144,11 +144,11 @@ func TestAccRDSParameterGroup_limit(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_exceedDefaultLimit(groupName), + Config: testAccParameterGroupConfig_exceedDefaultLimit(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckResourceAttr(resourceName, "description", "RDS default parameter group: Exceed default AWS parameter group limit of twenty"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -323,11 +323,11 @@ func TestAccRDSParameterGroup_limit(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccParameterGroupConfig_updateExceedDefaultLimit(groupName), + Config: testAccParameterGroupConfig_updateExceedDefaultLimit(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckResourceAttr(resourceName, "description", "Updated RDS default parameter group: Exceed default AWS parameter group limit of twenty"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -504,7 +504,7 @@ func TestAccRDSParameterGroup_disappears(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -513,10 +513,10 @@ func TestAccRDSParameterGroup_disappears(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_basic(groupName), + Config: testAccParameterGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParamaterGroupDisappears(ctx, &v), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfrds.ResourceParameterGroup(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -569,7 +569,7 @@ func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -578,11 +578,11 @@ func TestAccRDSParameterGroup_withApplyMethod(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_applyMethod(groupName), + Config: testAccParameterGroupConfig_applyMethod(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ @@ -610,7 +610,7 @@ func TestAccRDSParameterGroup_only(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -619,11 +619,11 @@ func TestAccRDSParameterGroup_only(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_only(groupName), + Config: testAccParameterGroupConfig_only(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), ), }, @@ -640,7 +640,7 @@ func TestAccRDSParameterGroup_matchDefault(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -649,10 +649,10 @@ func TestAccRDSParameterGroup_matchDefault(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_includeDefault(groupName), + Config: testAccParameterGroupConfig_includeDefault(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "postgres9.4"), ), }, @@ -670,7 +670,7 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) { ctx := acctest.Context(t) var v rds.DBParameterGroup resourceName := "aws_db_parameter_group.test" - groupName := fmt.Sprintf("parameter-group-test-terraform-%d", sdkacctest.RandInt()) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -679,11 +679,11 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) { CheckDestroy: testAccCheckParameterGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccParameterGroupConfig_updateParametersInitial(groupName), + Config: testAccParameterGroupConfig_updateParametersInitial(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), - resource.TestCheckResourceAttr(resourceName, "name", groupName), + testAccCheckParameterGroupAttributes(&v, rName), + resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "family", "mysql5.6"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "character_set_results", @@ -705,10 +705,10 @@ func TestAccRDSParameterGroup_updateParameters(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccParameterGroupConfig_updateParametersUpdated(groupName), + Config: testAccParameterGroupConfig_updateParametersUpdated(rName), Check: resource.ComposeTestCheckFunc( testAccCheckParameterGroupExists(ctx, resourceName, &v), - testAccCheckParameterGroupAttributes(&v, groupName), + testAccCheckParameterGroupAttributes(&v, rName), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "parameter.*", map[string]string{ "name": "character_set_results", "value": "ascii", @@ -993,23 +993,6 @@ func TestDBParameterModifyChunk(t *testing.T) { } } -func testAccCheckParamaterGroupDisappears(ctx context.Context, v *rds.DBParameterGroup) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_db_parameter_group" { - continue - } - _, err := conn.DeleteDBParameterGroupWithContext(ctx, &rds.DeleteDBParameterGroupInput{ - DBParameterGroupName: v.DBParameterGroupName, - }) - return err - } - return nil - } -} - func testAccCheckParameterGroupDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() @@ -1019,26 +1002,17 @@ func testAccCheckParameterGroupDestroy(ctx context.Context) resource.TestCheckFu continue } - // Try to find the Group - resp, err := conn.DescribeDBParameterGroupsWithContext(ctx, &rds.DescribeDBParameterGroupsInput{ - DBParameterGroupName: aws.String(rs.Primary.ID), - }) + _, err := tfrds.FindDBParameterGroupByName(ctx, conn, rs.Primary.ID) - if err == nil { - if len(resp.DBParameterGroups) != 0 && - *resp.DBParameterGroups[0].DBParameterGroupName == rs.Primary.ID { - return fmt.Errorf("DB Parameter Group still exists") - } + if tfresource.NotFound(err) { + continue } - // Verify the error - newerr, ok := err.(awserr.Error) - if !ok { - return err - } - if newerr.Code() != "DBParameterGroupNotFound" { + if err != nil { return err } + + return fmt.Errorf("RDS DB Parameter Group %s still exists", rs.Primary.ID) } return nil @@ -1059,35 +1033,26 @@ func testAccCheckParameterGroupAttributes(v *rds.DBParameterGroup, name string) } } -func testAccCheckParameterGroupExists(ctx context.Context, rName string, v *rds.DBParameterGroup) resource.TestCheckFunc { +func testAccCheckParameterGroupExists(ctx context.Context, n string, v *rds.DBParameterGroup) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[rName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", rName) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No DB Parameter Group ID is set") + return fmt.Errorf("No RDS DB Parameter Group ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).RDSConn() - opts := rds.DescribeDBParameterGroupsInput{ - DBParameterGroupName: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeDBParameterGroupsWithContext(ctx, &opts) + output, err := tfrds.FindDBParameterGroupByName(ctx, conn, rs.Primary.ID) if err != nil { return err } - if len(resp.DBParameterGroups) != 1 || - *resp.DBParameterGroups[0].DBParameterGroupName != rs.Primary.ID { - return fmt.Errorf("DB Parameter Group not found") - } - - *v = *resp.DBParameterGroups[0] + *v = *output return nil } From b3b10c9855f9901f4a6c6b88330d01f69522e242 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Feb 2023 17:06:12 -0500 Subject: [PATCH 53/68] r/aws_waf_size_constraint_set: Add 'FindSizeConstraintSetByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFSizeConstraintSet_' PKG=waf ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/waf/... -v -count 1 -parallel 3 -run=TestAccWAFSizeConstraintSet_ -timeout 180m === RUN TestAccWAFSizeConstraintSet_basic === PAUSE TestAccWAFSizeConstraintSet_basic === RUN TestAccWAFSizeConstraintSet_changeNameForceNew === PAUSE TestAccWAFSizeConstraintSet_changeNameForceNew === RUN TestAccWAFSizeConstraintSet_disappears === PAUSE TestAccWAFSizeConstraintSet_disappears === RUN TestAccWAFSizeConstraintSet_changeConstraints === PAUSE TestAccWAFSizeConstraintSet_changeConstraints === RUN TestAccWAFSizeConstraintSet_noConstraints === PAUSE TestAccWAFSizeConstraintSet_noConstraints === CONT TestAccWAFSizeConstraintSet_basic === CONT TestAccWAFSizeConstraintSet_changeConstraints === CONT TestAccWAFSizeConstraintSet_noConstraints --- PASS: TestAccWAFSizeConstraintSet_noConstraints (21.01s) === CONT TestAccWAFSizeConstraintSet_disappears --- PASS: TestAccWAFSizeConstraintSet_basic (25.99s) === CONT TestAccWAFSizeConstraintSet_changeNameForceNew --- PASS: TestAccWAFSizeConstraintSet_changeConstraints (39.53s) --- PASS: TestAccWAFSizeConstraintSet_disappears (21.21s) --- PASS: TestAccWAFSizeConstraintSet_changeNameForceNew (44.18s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/waf 75.370s --- internal/service/waf/helpers.go | 16 +-- internal/service/waf/size_constraint_set.go | 112 +++++++++++------- .../service/waf/size_constraint_set_test.go | 87 +++----------- 3 files changed, 95 insertions(+), 120 deletions(-) diff --git a/internal/service/waf/helpers.go b/internal/service/waf/helpers.go index e8edc2fb136..daf54d562e0 100644 --- a/internal/service/waf/helpers.go +++ b/internal/service/waf/helpers.go @@ -14,20 +14,24 @@ import ( func SizeConstraintSetSchema() map[string]*schema.Schema { return map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, "name": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "arn": { - Type: schema.TypeString, - Computed: true, - }, "size_constraints": { Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "comparison_operator": { + Type: schema.TypeString, + Required: true, + }, "field_to_match": { Type: schema.TypeList, Required: true, @@ -45,10 +49,6 @@ func SizeConstraintSetSchema() map[string]*schema.Schema { }, }, }, - "comparison_operator": { - Type: schema.TypeString, - Required: true, - }, "size": { Type: schema.TypeInt, Required: true, diff --git a/internal/service/waf/size_constraint_set.go b/internal/service/waf/size_constraint_set.go index 1ac23e3da52..53b3f516bd5 100644 --- a/internal/service/waf/size_constraint_set.go +++ b/internal/service/waf/size_constraint_set.go @@ -7,12 +7,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/waf" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceSizeConstraintSet() *schema.Resource { @@ -21,6 +23,7 @@ func ResourceSizeConstraintSet() *schema.Resource { ReadWithoutTimeout: resourceSizeConstraintSetRead, UpdateWithoutTimeout: resourceSizeConstraintSetUpdate, DeleteWithoutTimeout: resourceSizeConstraintSetDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -33,23 +36,23 @@ func resourceSizeConstraintSetCreate(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics conn := meta.(*conns.AWSClient).WAFConn() - log.Printf("[INFO] Creating SizeConstraintSet: %s", d.Get("name").(string)) + name := d.Get("name").(string) + input := &waf.CreateSizeConstraintSetInput{ + Name: aws.String(name), + } wr := NewRetryer(conn) - out, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - params := &waf.CreateSizeConstraintSetInput{ - ChangeToken: token, - Name: aws.String(d.Get("name").(string)), - } + outputRaw, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { + input.ChangeToken = token - return conn.CreateSizeConstraintSetWithContext(ctx, params) + return conn.CreateSizeConstraintSetWithContext(ctx, input) }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating SizeConstraintSet: %s", err) + return sdkdiag.AppendErrorf(diags, "creating WAF Size Constraint Set (%s): %s", name, err) } - resp := out.(*waf.CreateSizeConstraintSetOutput) - d.SetId(aws.StringValue(resp.SizeConstraintSet.SizeConstraintSetId)) + d.SetId(aws.StringValue(outputRaw.(*waf.CreateSizeConstraintSetOutput).SizeConstraintSet.SizeConstraintSetId)) return append(diags, resourceSizeConstraintSetUpdate(ctx, d, meta)...) } @@ -57,24 +60,18 @@ func resourceSizeConstraintSetCreate(ctx context.Context, d *schema.ResourceData func resourceSizeConstraintSetRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).WAFConn() - log.Printf("[INFO] Reading SizeConstraintSet: %s", d.Get("name").(string)) - params := &waf.GetSizeConstraintSetInput{ - SizeConstraintSetId: aws.String(d.Id()), - } - resp, err := conn.GetSizeConstraintSetWithContext(ctx, params) - if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == waf.ErrCodeNonexistentItemException { - log.Printf("[WARN] WAF SizeConstraintSet (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } + sizeConstraintSet, err := FindSizeConstraintSetByID(ctx, conn, d.Id()) - return sdkdiag.AppendErrorf(diags, "reading WAF Size Constraint Set (%s): %s", d.Get("name").(string), err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAF Size Constraint Set (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - d.Set("name", resp.SizeConstraintSet.Name) - d.Set("size_constraints", FlattenSizeConstraints(resp.SizeConstraintSet.SizeConstraints)) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading WAF Size Constraint Set (%s): %s", d.Id(), err) + } arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, @@ -83,6 +80,10 @@ func resourceSizeConstraintSetRead(ctx context.Context, d *schema.ResourceData, Resource: fmt.Sprintf("sizeconstraintset/%s", d.Id()), } d.Set("arn", arn.String()) + d.Set("name", sizeConstraintSet.Name) + if err := d.Set("size_constraints", FlattenSizeConstraints(sizeConstraintSet.SizeConstraints)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting size_constraints: %s", err) + } return diags } @@ -95,9 +96,10 @@ func resourceSizeConstraintSetUpdate(ctx context.Context, d *schema.ResourceData o, n := d.GetChange("size_constraints") oldConstraints, newConstraints := o.(*schema.Set).List(), n.(*schema.Set).List() - err := updateSizeConstraintSetResource(ctx, d.Id(), oldConstraints, newConstraints, conn) + err := updateSizeConstraintSetResource(ctx, conn, d.Id(), oldConstraints, newConstraints) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating SizeConstraintSet: %s", err) + return sdkdiag.AppendErrorf(diags, "updating WAF Size Constraint Set (%s): %s", d.Id(), err) } } @@ -111,43 +113,65 @@ func resourceSizeConstraintSetDelete(ctx context.Context, d *schema.ResourceData oldConstraints := d.Get("size_constraints").(*schema.Set).List() if len(oldConstraints) > 0 { - noConstraints := []interface{}{} - err := updateSizeConstraintSetResource(ctx, d.Id(), oldConstraints, noConstraints, conn) + err := updateSizeConstraintSetResource(ctx, conn, d.Id(), oldConstraints, []interface{}{}) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting SizeConstraintSet: %s", err) + return sdkdiag.AppendErrorf(diags, "updating WAF Size Constraint Set (%s): %s", d.Id(), err) } } wr := NewRetryer(conn) _, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - req := &waf.DeleteSizeConstraintSetInput{ + return conn.DeleteSizeConstraintSetWithContext(ctx, &waf.DeleteSizeConstraintSetInput{ ChangeToken: token, SizeConstraintSetId: aws.String(d.Id()), - } - return conn.DeleteSizeConstraintSetWithContext(ctx, req) + }) }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting SizeConstraintSet: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting WAF Size Constraint Set (%s): %s", d.Id(), err) } return diags } -func updateSizeConstraintSetResource(ctx context.Context, id string, oldS, newS []interface{}, conn *waf.WAF) error { +func updateSizeConstraintSetResource(ctx context.Context, conn *waf.WAF, id string, oldS, newS []interface{}) error { + input := &waf.UpdateSizeConstraintSetInput{ + SizeConstraintSetId: aws.String(id), + Updates: DiffSizeConstraints(oldS, newS), + } + wr := NewRetryer(conn) _, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - req := &waf.UpdateSizeConstraintSetInput{ - ChangeToken: token, - SizeConstraintSetId: aws.String(id), - Updates: DiffSizeConstraints(oldS, newS), - } + input.ChangeToken = token - log.Printf("[INFO] Updating WAF Size Constraint constraints: %s", req) - return conn.UpdateSizeConstraintSetWithContext(ctx, req) + return conn.UpdateSizeConstraintSetWithContext(ctx, input) }) + + return err +} + +func FindSizeConstraintSetByID(ctx context.Context, conn *waf.WAF, id string) (*waf.SizeConstraintSet, error) { + input := &waf.GetSizeConstraintSetInput{ + SizeConstraintSetId: aws.String(id), + } + + output, err := conn.GetSizeConstraintSetWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + if err != nil { - return fmt.Errorf("Error updating SizeConstraintSet: %s", err) + return nil, err + } + + if output == nil || output.SizeConstraintSet == nil { + return nil, tfresource.NewEmptyResultError(input) } - return nil + return output.SizeConstraintSet, nil } diff --git a/internal/service/waf/size_constraint_set_test.go b/internal/service/waf/size_constraint_set_test.go index 30b7e9325d4..12bce3c3d5a 100644 --- a/internal/service/waf/size_constraint_set_test.go +++ b/internal/service/waf/size_constraint_set_test.go @@ -6,15 +6,14 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/waf" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwaf "github.com/hashicorp/terraform-provider-aws/internal/service/waf" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFSizeConstraintSet_basic(t *testing.T) { @@ -111,7 +110,7 @@ func TestAccWAFSizeConstraintSet_disappears(t *testing.T) { Config: testAccSizeConstraintSetConfig_basic(sizeConstraintSet), Check: resource.ComposeTestCheckFunc( testAccCheckSizeConstraintSetExists(ctx, resourceName, &v), - testAccCheckSizeConstraintSetDisappears(ctx, &v), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfwaf.ResourceSizeConstraintSet(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -205,47 +204,6 @@ func TestAccWAFSizeConstraintSet_noConstraints(t *testing.T) { }) } -func testAccCheckSizeConstraintSetDisappears(ctx context.Context, v *waf.SizeConstraintSet) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).WAFConn() - - wr := tfwaf.NewRetryer(conn) - _, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - req := &waf.UpdateSizeConstraintSetInput{ - ChangeToken: token, - SizeConstraintSetId: v.SizeConstraintSetId, - } - - for _, sizeConstraint := range v.SizeConstraints { - sizeConstraintUpdate := &waf.SizeConstraintSetUpdate{ - Action: aws.String("DELETE"), - SizeConstraint: &waf.SizeConstraint{ - FieldToMatch: sizeConstraint.FieldToMatch, - ComparisonOperator: sizeConstraint.ComparisonOperator, - Size: sizeConstraint.Size, - TextTransformation: sizeConstraint.TextTransformation, - }, - } - req.Updates = append(req.Updates, sizeConstraintUpdate) - } - return conn.UpdateSizeConstraintSetWithContext(ctx, req) - }) - if err != nil { - return fmt.Errorf("Error updating SizeConstraintSet: %s", err) - } - - _, err = wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - opts := &waf.DeleteSizeConstraintSetInput{ - ChangeToken: token, - SizeConstraintSetId: v.SizeConstraintSetId, - } - return conn.DeleteSizeConstraintSetWithContext(ctx, opts) - }) - - return err - } -} - func testAccCheckSizeConstraintSetExists(ctx context.Context, n string, v *waf.SizeConstraintSet) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -254,24 +212,20 @@ func testAccCheckSizeConstraintSetExists(ctx context.Context, n string, v *waf.S } if rs.Primary.ID == "" { - return fmt.Errorf("No WAF SizeConstraintSet ID is set") + return fmt.Errorf("No WAF Size Constraint Set ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFConn() - resp, err := conn.GetSizeConstraintSetWithContext(ctx, &waf.GetSizeConstraintSetInput{ - SizeConstraintSetId: aws.String(rs.Primary.ID), - }) + + output, err := tfwaf.FindSizeConstraintSetByID(ctx, conn, rs.Primary.ID) if err != nil { return err } - if *resp.SizeConstraintSet.SizeConstraintSetId == rs.Primary.ID { - *v = *resp.SizeConstraintSet - return nil - } + *v = *output - return fmt.Errorf("WAF SizeConstraintSet (%s) not found", rs.Primary.ID) + return nil } } @@ -283,21 +237,18 @@ func testAccCheckSizeConstraintSetDestroy(ctx context.Context) resource.TestChec } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFConn() - resp, err := conn.GetSizeConstraintSetWithContext(ctx, &waf.GetSizeConstraintSetInput{ - SizeConstraintSetId: aws.String(rs.Primary.ID), - }) - - if err == nil { - if *resp.SizeConstraintSet.SizeConstraintSetId == rs.Primary.ID { - return fmt.Errorf("WAF SizeConstraintSet %s still exists", rs.Primary.ID) - } - } - if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { + _, err := tfwaf.FindSizeConstraintSetByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { continue } - return err + if err != nil { + return err + } + + return fmt.Errorf("WAF Size Constraint Set %s still exists", rs.Primary.ID) } return nil @@ -307,7 +258,7 @@ func testAccCheckSizeConstraintSetDestroy(ctx context.Context) resource.TestChec func testAccSizeConstraintSetConfig_basic(name string) string { return fmt.Sprintf(` resource "aws_waf_size_constraint_set" "size_constraint_set" { - name = "%s" + name = %[1]q size_constraints { text_transformation = "NONE" @@ -325,7 +276,7 @@ resource "aws_waf_size_constraint_set" "size_constraint_set" { func testAccSizeConstraintSetConfig_changeName(name string) string { return fmt.Sprintf(` resource "aws_waf_size_constraint_set" "size_constraint_set" { - name = "%s" + name = %[1]q size_constraints { text_transformation = "NONE" @@ -343,7 +294,7 @@ resource "aws_waf_size_constraint_set" "size_constraint_set" { func testAccSizeConstraintSetConfig_changes(name string) string { return fmt.Sprintf(` resource "aws_waf_size_constraint_set" "size_constraint_set" { - name = "%s" + name = %[1]q size_constraints { text_transformation = "NONE" @@ -361,7 +312,7 @@ resource "aws_waf_size_constraint_set" "size_constraint_set" { func testAccSizeConstraintSetConfig_nos(name string) string { return fmt.Sprintf(` resource "aws_waf_size_constraint_set" "size_constraint_set" { - name = "%s" + name = %[1]q } `, name) } From fc0de5ecd1556b4f1058975291a9d8430bc5804a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 6 Feb 2023 17:06:41 -0500 Subject: [PATCH 54/68] r/aws_waf_rate_based_rule: Add 'FindRateBasedRuleByID'. Acceptance test output: % make testacc TESTARGS='-run=TestAccWAFRateBasedRule_' PKG=waf ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/waf/... -v -count 1 -parallel 3 -run=TestAccWAFRateBasedRule_ -timeout 180m === RUN TestAccWAFRateBasedRule_basic === PAUSE TestAccWAFRateBasedRule_basic === RUN TestAccWAFRateBasedRule_changeNameForceNew === PAUSE TestAccWAFRateBasedRule_changeNameForceNew === RUN TestAccWAFRateBasedRule_disappears === PAUSE TestAccWAFRateBasedRule_disappears === RUN TestAccWAFRateBasedRule_changePredicates === PAUSE TestAccWAFRateBasedRule_changePredicates === RUN TestAccWAFRateBasedRule_changeRateLimit === PAUSE TestAccWAFRateBasedRule_changeRateLimit === RUN TestAccWAFRateBasedRule_noPredicates === PAUSE TestAccWAFRateBasedRule_noPredicates === RUN TestAccWAFRateBasedRule_tags === PAUSE TestAccWAFRateBasedRule_tags === CONT TestAccWAFRateBasedRule_basic === CONT TestAccWAFRateBasedRule_changeRateLimit === CONT TestAccWAFRateBasedRule_tags --- PASS: TestAccWAFRateBasedRule_basic (31.49s) === CONT TestAccWAFRateBasedRule_noPredicates --- PASS: TestAccWAFRateBasedRule_changeRateLimit (42.03s) === CONT TestAccWAFRateBasedRule_disappears --- PASS: TestAccWAFRateBasedRule_tags (43.01s) === CONT TestAccWAFRateBasedRule_changePredicates --- PASS: TestAccWAFRateBasedRule_noPredicates (20.52s) === CONT TestAccWAFRateBasedRule_changeNameForceNew --- PASS: TestAccWAFRateBasedRule_disappears (23.44s) --- PASS: TestAccWAFRateBasedRule_changePredicates (55.51s) --- PASS: TestAccWAFRateBasedRule_changeNameForceNew (56.26s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/waf 113.631s --- internal/service/waf/rate_based_rule.go | 181 +++++++++++-------- internal/service/waf/rate_based_rule_test.go | 36 ++-- 2 files changed, 121 insertions(+), 96 deletions(-) diff --git a/internal/service/waf/rate_based_rule.go b/internal/service/waf/rate_based_rule.go index d53f2938d3a..5469ba69a95 100644 --- a/internal/service/waf/rate_based_rule.go +++ b/internal/service/waf/rate_based_rule.go @@ -7,14 +7,16 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/waf" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -24,15 +26,15 @@ func ResourceRateBasedRule() *schema.Resource { ReadWithoutTimeout: resourceRateBasedRuleRead, UpdateWithoutTimeout: resourceRateBasedRuleUpdate, DeleteWithoutTimeout: resourceRateBasedRuleDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ - "name": { + "arn": { Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, }, "metric_name": { Type: schema.TypeString, @@ -40,20 +42,25 @@ func ResourceRateBasedRule() *schema.Resource { ForceNew: true, ValidateFunc: validMetricName, }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "predicates": { Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "negated": { - Type: schema.TypeBool, - Required: true, - }, "data_id": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringLenBetween(0, 128), }, + "negated": { + Type: schema.TypeBool, + Required: true, + }, "type": { Type: schema.TypeString, Required: true, @@ -73,10 +80,6 @@ func ResourceRateBasedRule() *schema.Resource { }, "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), - "arn": { - Type: schema.TypeString, - Computed: true, - }, }, CustomizeDiff: verify.SetTagsDiff, @@ -89,34 +92,39 @@ func resourceRateBasedRuleCreate(ctx context.Context, d *schema.ResourceData, me defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + name := d.Get("name").(string) + input := &waf.CreateRateBasedRuleInput{ + MetricName: aws.String(d.Get("metric_name").(string)), + Name: aws.String(name), + RateKey: aws.String(d.Get("rate_key").(string)), + RateLimit: aws.Int64(int64(d.Get("rate_limit").(int))), + } + wr := NewRetryer(conn) - out, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - params := &waf.CreateRateBasedRuleInput{ - ChangeToken: token, - MetricName: aws.String(d.Get("metric_name").(string)), - Name: aws.String(d.Get("name").(string)), - RateKey: aws.String(d.Get("rate_key").(string)), - RateLimit: aws.Int64(int64(d.Get("rate_limit").(int))), - } + outputRaw, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { + input.ChangeToken = token if len(tags) > 0 { - params.Tags = Tags(tags.IgnoreAWS()) + input.Tags = Tags(tags.IgnoreAWS()) } - return conn.CreateRateBasedRuleWithContext(ctx, params) + return conn.CreateRateBasedRuleWithContext(ctx, input) }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating WAF Rate Based Rule (%s): %s", d.Get("name").(string), err) + return sdkdiag.AppendErrorf(diags, "creating WAF Rate Based Rule (%s): %s", name, err) } - resp := out.(*waf.CreateRateBasedRuleOutput) - d.SetId(aws.StringValue(resp.Rule.RuleId)) + + output := outputRaw.(*waf.CreateRateBasedRuleOutput) + + d.SetId(aws.StringValue(output.Rule.RuleId)) newPredicates := d.Get("predicates").(*schema.Set).List() if len(newPredicates) > 0 { - noPredicates := []interface{}{} - err := updateRateBasedRuleResource(ctx, *resp.Rule.RuleId, noPredicates, newPredicates, d.Get("rate_limit"), conn) + err := updateRateBasedRuleResource(ctx, conn, aws.StringValue(output.Rule.RuleId), []interface{}{}, newPredicates, d.Get("rate_limit")) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating WAF Rate Based Rule: %s", err) + return sdkdiag.AppendErrorf(diags, "updating WAF Rate Based Rule (%s): %s", d.Id(), err) } } @@ -129,24 +137,33 @@ func resourceRateBasedRuleRead(ctx context.Context, d *schema.ResourceData, meta defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - params := &waf.GetRateBasedRuleInput{ - RuleId: aws.String(d.Id()), + rule, err := FindRateBasedRuleByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] WAF Rate Based Rule (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - resp, err := conn.GetRateBasedRuleWithContext(ctx, params) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == waf.ErrCodeNonexistentItemException { - log.Printf("[WARN] WAF Rate Based Rule (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - - return sdkdiag.AppendErrorf(diags, "reading WAF Rate Based Rule (%s): %s", d.Get("name").(string), err) + return sdkdiag.AppendErrorf(diags, "reading WAF Rate Based Rule (%s): %s", d.Id(), err) } + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: "waf", + AccountID: meta.(*conns.AWSClient).AccountID, + Resource: fmt.Sprintf("ratebasedrule/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("name", rule.Name) + d.Set("metric_name", rule.MetricName) + d.Set("rate_key", rule.RateKey) + d.Set("rate_limit", rule.RateLimit) + var predicates []map[string]interface{} - for _, predicateSet := range resp.Rule.MatchPredicates { + for _, predicateSet := range rule.MatchPredicates { predicate := map[string]interface{}{ "negated": *predicateSet.Negated, "type": *predicateSet.Type, @@ -155,20 +172,17 @@ func resourceRateBasedRuleRead(ctx context.Context, d *schema.ResourceData, meta predicates = append(predicates, predicate) } - arn := arn.ARN{ - Partition: meta.(*conns.AWSClient).Partition, - Service: "waf", - AccountID: meta.(*conns.AWSClient).AccountID, - Resource: fmt.Sprintf("ratebasedrule/%s", d.Id()), - }.String() - d.Set("arn", arn) + if err := d.Set("predicates", predicates); err != nil { + return sdkdiag.AppendErrorf(diags, "setting predicates: %s", err) + } + + tags, err := ListTags(ctx, conn, arn) - tagList, err := ListTags(ctx, conn, arn) if err != nil { - return sdkdiag.AppendErrorf(diags, "to get WAF Rated Based Rule parameter tags for %s: %s", d.Get("name"), err) + return sdkdiag.AppendErrorf(diags, "listing tags for WAF Rate Based Rule (%s): %s", arn, err) } - tags := tagList.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { @@ -179,12 +193,6 @@ func resourceRateBasedRuleRead(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "setting tags_all: %s", err) } - d.Set("predicates", predicates) - d.Set("name", resp.Rule.Name) - d.Set("metric_name", resp.Rule.MetricName) - d.Set("rate_key", resp.Rule.RateKey) - d.Set("rate_limit", resp.Rule.RateLimit) - return diags } @@ -197,9 +205,10 @@ func resourceRateBasedRuleUpdate(ctx context.Context, d *schema.ResourceData, me oldP, newP := o.(*schema.Set).List(), n.(*schema.Set).List() rateLimit := d.Get("rate_limit") - err := updateRateBasedRuleResource(ctx, d.Id(), oldP, newP, rateLimit, conn) + err := updateRateBasedRuleResource(ctx, conn, d.Id(), oldP, newP, rateLimit) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating WAF Rule: %s", err) + return sdkdiag.AppendErrorf(diags, "updating WAF Rate Based Rule (%s): %s", d.Id(), err) } } @@ -223,43 +232,67 @@ func resourceRateBasedRuleDelete(ctx context.Context, d *schema.ResourceData, me noPredicates := []interface{}{} rateLimit := d.Get("rate_limit") - err := updateRateBasedRuleResource(ctx, d.Id(), oldPredicates, noPredicates, rateLimit, conn) + err := updateRateBasedRuleResource(ctx, conn, d.Id(), oldPredicates, noPredicates, rateLimit) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating WAF Rate Based Rule Predicates: %s", err) + return sdkdiag.AppendErrorf(diags, "updating WAF Rate Based Rule (%s): %s", d.Id(), err) } } + log.Printf("[INFO] Deleting WAF Rate Based Rule: %s", d.Id()) wr := NewRetryer(conn) _, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - req := &waf.DeleteRateBasedRuleInput{ + return conn.DeleteRateBasedRuleWithContext(ctx, &waf.DeleteRateBasedRuleInput{ ChangeToken: token, RuleId: aws.String(d.Id()), - } - log.Printf("[INFO] Deleting WAF Rate Based Rule") - return conn.DeleteRateBasedRuleWithContext(ctx, req) + }) }) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting WAF Rate Based Rule: %s", err) + return sdkdiag.AppendErrorf(diags, "deleting WAF Rate Based Rule (%s): %s", d.Id(), err) } return diags } -func updateRateBasedRuleResource(ctx context.Context, id string, oldP, newP []interface{}, rateLimit interface{}, conn *waf.WAF) error { +func updateRateBasedRuleResource(ctx context.Context, conn *waf.WAF, id string, oldP, newP []interface{}, rateLimit interface{}) error { + input := &waf.UpdateRateBasedRuleInput{ + RateLimit: aws.Int64(int64(rateLimit.(int))), + RuleId: aws.String(id), + Updates: DiffRulePredicates(oldP, newP), + } + wr := NewRetryer(conn) _, err := wr.RetryWithToken(ctx, func(token *string) (interface{}, error) { - req := &waf.UpdateRateBasedRuleInput{ - ChangeToken: token, - RuleId: aws.String(id), - Updates: DiffRulePredicates(oldP, newP), - RateLimit: aws.Int64(int64(rateLimit.(int))), - } + input.ChangeToken = token - return conn.UpdateRateBasedRuleWithContext(ctx, req) + return conn.UpdateRateBasedRuleWithContext(ctx, input) }) + + return err +} + +func FindRateBasedRuleByID(ctx context.Context, conn *waf.WAF, id string) (*waf.RateBasedRule, error) { + input := &waf.GetRateBasedRuleInput{ + RuleId: aws.String(id), + } + + output, err := conn.GetRateBasedRuleWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + if err != nil { - return fmt.Errorf("updating WAF Rate Based Rule: %s", err) + return nil, err + } + + if output == nil || output.Rule == nil { + return nil, tfresource.NewEmptyResultError(input) } - return nil + return output.Rule, nil } diff --git a/internal/service/waf/rate_based_rule_test.go b/internal/service/waf/rate_based_rule_test.go index d3ae6eee180..31b33033f87 100644 --- a/internal/service/waf/rate_based_rule_test.go +++ b/internal/service/waf/rate_based_rule_test.go @@ -6,15 +6,14 @@ import ( "regexp" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/waf" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfwaf "github.com/hashicorp/terraform-provider-aws/internal/service/waf" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccWAFRateBasedRule_basic(t *testing.T) { @@ -300,21 +299,18 @@ func testAccCheckRateBasedRuleDestroy(ctx context.Context) resource.TestCheckFun } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFConn() - resp, err := conn.GetRateBasedRuleWithContext(ctx, &waf.GetRateBasedRuleInput{ - RuleId: aws.String(rs.Primary.ID), - }) - - if err == nil { - if *resp.Rule.RuleId == rs.Primary.ID { - return fmt.Errorf("WAF Rule %s still exists", rs.Primary.ID) - } - } - if tfawserr.ErrCodeEquals(err, waf.ErrCodeNonexistentItemException) { + _, err := tfwaf.FindRateBasedRuleByID(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { continue } - return err + if err != nil { + return err + } + + return fmt.Errorf("WAF Rate Based Rule %s still exists", rs.Primary.ID) } return nil @@ -329,24 +325,20 @@ func testAccCheckRateBasedRuleExists(ctx context.Context, n string, v *waf.RateB } if rs.Primary.ID == "" { - return fmt.Errorf("No WAF Rule ID is set") + return fmt.Errorf("No WAF Rate Based Rule ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).WAFConn() - resp, err := conn.GetRateBasedRuleWithContext(ctx, &waf.GetRateBasedRuleInput{ - RuleId: aws.String(rs.Primary.ID), - }) + + output, err := tfwaf.FindRateBasedRuleByID(ctx, conn, rs.Primary.ID) if err != nil { return err } - if *resp.Rule.RuleId == rs.Primary.ID { - *v = *resp.Rule - return nil - } + *v = *output - return fmt.Errorf("WAF Rule (%s) not found", rs.Primary.ID) + return nil } } From f1c97dd732fa29e079766009dceadbefe3cb51ed Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 7 Feb 2023 18:00:59 -0500 Subject: [PATCH 55/68] r/aws_iam_account_password_policy: Add 'FindAccountPasswordPolicy'. Acceptance test output: % make testacc TESTARGS='-run=TestAccIAMAccountPasswordPolicy_serial' PKG=iam ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iam/... -v -count 1 -parallel 3 -run=TestAccIAMAccountPasswordPolicy_serial -timeout 180m === RUN TestAccIAMAccountPasswordPolicy_serial === PAUSE TestAccIAMAccountPasswordPolicy_serial === CONT TestAccIAMAccountPasswordPolicy_serial === RUN TestAccIAMAccountPasswordPolicy_serial/basic === RUN TestAccIAMAccountPasswordPolicy_serial/disappears --- PASS: TestAccIAMAccountPasswordPolicy_serial (38.69s) --- PASS: TestAccIAMAccountPasswordPolicy_serial/basic (28.25s) --- PASS: TestAccIAMAccountPasswordPolicy_serial/disappears (10.44s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 44.594s --- .../service/iam/account_password_policy.go | 63 ++++++++++++----- .../iam/account_password_policy_test.go | 69 ++++++++++++++----- 2 files changed, 96 insertions(+), 36 deletions(-) diff --git a/internal/service/iam/account_password_policy.go b/internal/service/iam/account_password_policy.go index e43bf34f3bf..3ea5a460e2a 100644 --- a/internal/service/iam/account_password_policy.go +++ b/internal/service/iam/account_password_policy.go @@ -8,9 +8,11 @@ import ( "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceAccountPasswordPolicy() *schema.Resource { @@ -19,6 +21,7 @@ func ResourceAccountPasswordPolicy() *schema.Resource { ReadWithoutTimeout: resourceAccountPasswordPolicyRead, UpdateWithoutTimeout: resourceAccountPasswordPolicyUpdate, DeleteWithoutTimeout: resourceAccountPasswordPolicyDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -112,12 +115,14 @@ func resourceAccountPasswordPolicyUpdate(ctx context.Context, d *schema.Resource } _, err := conn.UpdateAccountPasswordPolicyWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Password Policy: %s", err) + return sdkdiag.AppendErrorf(diags, "updating IAM Account Password Policy: %s", err) } - log.Println("[DEBUG] IAM account password policy updated") - d.SetId("iam-account-password-policy") + if d.IsNewResource() { + d.SetId("iam-account-password-policy") + } return append(diags, resourceAccountPasswordPolicyRead(ctx, d, meta)...) } @@ -126,18 +131,17 @@ func resourceAccountPasswordPolicyRead(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - input := &iam.GetAccountPasswordPolicyInput{} - resp, err := conn.GetAccountPasswordPolicyWithContext(ctx, input) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - log.Printf("[WARN] IAM Account Password Policy not found, removing from state") - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading IAM account password policy: %s", err) + policy, err := FindAccountPasswordPolicy(ctx, conn) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + log.Printf("[WARN] IAM Account Password Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - policy := resp.PasswordPolicy + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading IAM Account Password Policy (%s): %s", d.Id(), err) + } d.Set("allow_users_to_change_password", policy.AllowUsersToChangePassword) d.Set("expire_passwords", policy.ExpirePasswords) @@ -157,12 +161,35 @@ func resourceAccountPasswordPolicyDelete(ctx context.Context, d *schema.Resource var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - log.Println("[DEBUG] Deleting IAM account password policy") - input := &iam.DeleteAccountPasswordPolicyInput{} - if _, err := conn.DeleteAccountPasswordPolicyWithContext(ctx, input); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IAM Password Policy: %s", err) + log.Printf("[DEBUG] Deleting IAM Account Password Policy: %s", d.Id()) + _, err := conn.DeleteAccountPasswordPolicyWithContext(ctx, &iam.DeleteAccountPasswordPolicyInput{}) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting IAM Account Password Policy (%s): %s", d.Id(), err) } - log.Println("[DEBUG] Deleted IAM account password policy") return diags } + +func FindAccountPasswordPolicy(ctx context.Context, conn *iam.IAM) (*iam.PasswordPolicy, error) { + input := &iam.GetAccountPasswordPolicyInput{} + + output, err := conn.GetAccountPasswordPolicyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.PasswordPolicy == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.PasswordPolicy, nil +} diff --git a/internal/service/iam/account_password_policy_test.go b/internal/service/iam/account_password_policy_test.go index b3d994f49b3..9647095220a 100644 --- a/internal/service/iam/account_password_policy_test.go +++ b/internal/service/iam/account_password_policy_test.go @@ -5,20 +5,32 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func TestAccIAMAccountPasswordPolicy_basic(t *testing.T) { +func TestAccIAMAccountPasswordPolicy_serial(t *testing.T) { + t.Parallel() + + testCases := map[string]func(t *testing.T){ + "basic": testAccAccountPasswordPolicy_basic, + "disappears": testAccAccountPasswordPolicy_disappears, + } + + acctest.RunSerialTests1Level(t, testCases, 0) +} + +func testAccAccountPasswordPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - var policy iam.GetAccountPasswordPolicyOutput + var policy iam.PasswordPolicy resourceName := "aws_iam_account_password_policy.test" - resource.ParallelTest(t, resource.TestCase{ + resource.Test(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, @@ -47,6 +59,29 @@ func TestAccIAMAccountPasswordPolicy_basic(t *testing.T) { }) } +func testAccAccountPasswordPolicy_disappears(t *testing.T) { + ctx := acctest.Context(t) + var policy iam.PasswordPolicy + resourceName := "aws_iam_account_password_policy.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccountPasswordPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccountPasswordPolicyConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckAccountPasswordPolicyExists(ctx, resourceName, &policy), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceAccountPasswordPolicy(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckAccountPasswordPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn() @@ -56,27 +91,24 @@ func testAccCheckAccountPasswordPolicyDestroy(ctx context.Context) resource.Test continue } - // Try to get policy - _, err := conn.GetAccountPasswordPolicyWithContext(ctx, &iam.GetAccountPasswordPolicyInput{}) - if err == nil { - return fmt.Errorf("still exist.") - } + _, err := tfiam.FindAccountPasswordPolicy(ctx, conn) - // Verify the error is what we want - awsErr, ok := err.(awserr.Error) - if !ok { - return err + if tfresource.NotFound(err) { + continue } - if awsErr.Code() != "NoSuchEntity" { + + if err != nil { return err } + + return fmt.Errorf("IAM Account Password Policy %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckAccountPasswordPolicyExists(ctx context.Context, n string, res *iam.GetAccountPasswordPolicyOutput) resource.TestCheckFunc { +func testAccCheckAccountPasswordPolicyExists(ctx context.Context, n string, v *iam.PasswordPolicy) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -84,17 +116,18 @@ func testAccCheckAccountPasswordPolicyExists(ctx context.Context, n string, res } if rs.Primary.ID == "" { - return fmt.Errorf("No policy ID is set") + return fmt.Errorf("No IAM Account Password Policy ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn() - resp, err := conn.GetAccountPasswordPolicyWithContext(ctx, &iam.GetAccountPasswordPolicyInput{}) + output, err := tfiam.FindAccountPasswordPolicy(ctx, conn) + if err != nil { return err } - *res = *resp + *v = *output return nil } From c60bd3ac626df00626b9a56731e1ea75161713e1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 08:21:56 -0500 Subject: [PATCH 56/68] r/aws_iam_group: Add 'FindGroupByName'. Acceptance test output: % make testacc TESTARGS='-run=TestAccIAMGroup_' PKG=iam ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iam/... -v -count 1 -parallel 3 -run=TestAccIAMGroup_ -timeout 180m === RUN TestAccIAMGroup_basic === PAUSE TestAccIAMGroup_basic === RUN TestAccIAMGroup_disappears === PAUSE TestAccIAMGroup_disappears === RUN TestAccIAMGroup_nameChange === PAUSE TestAccIAMGroup_nameChange === RUN TestAccIAMGroup_path === PAUSE TestAccIAMGroup_path === CONT TestAccIAMGroup_basic === CONT TestAccIAMGroup_nameChange === CONT TestAccIAMGroup_path --- PASS: TestAccIAMGroup_basic (18.88s) === CONT TestAccIAMGroup_disappears --- PASS: TestAccIAMGroup_nameChange (26.71s) --- PASS: TestAccIAMGroup_path (28.87s) --- PASS: TestAccIAMGroup_disappears (11.27s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 35.526s --- internal/service/iam/group.go | 146 ++++++++++++++------------ internal/service/iam/group_test.go | 162 ++++++++++++++++++----------- 2 files changed, 180 insertions(+), 128 deletions(-) diff --git a/internal/service/iam/group.go b/internal/service/iam/group.go index 04cfb4fb566..9286d68c8ff 100644 --- a/internal/service/iam/group.go +++ b/internal/service/iam/group.go @@ -24,6 +24,7 @@ func ResourceGroup() *schema.Resource { ReadWithoutTimeout: resourceGroupRead, UpdateWithoutTimeout: resourceGroupUpdate, DeleteWithoutTimeout: resourceGroupDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -33,10 +34,6 @@ func ResourceGroup() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "unique_id": { - Type: schema.TypeString, - Computed: true, - }, "name": { Type: schema.TypeString, Required: true, @@ -50,6 +47,18 @@ func ResourceGroup() *schema.Resource { Optional: true, Default: "/", }, + "unique_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + + CustomizeDiff: func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + if d.HasChanges("name", "path") { + return d.SetNewComputed("arn") + } + + return nil }, } } @@ -57,19 +66,28 @@ func ResourceGroup() *schema.Resource { func resourceGroupCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - name := d.Get("name").(string) - path := d.Get("path").(string) - request := &iam.CreateGroupInput{ - Path: aws.String(path), + name := d.Get("name").(string) + input := &iam.CreateGroupInput{ GroupName: aws.String(name), + Path: aws.String(d.Get("path").(string)), } - createResp, err := conn.CreateGroupWithContext(ctx, request) + output, err := conn.CreateGroupWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating IAM Group %s: %s", name, err) + return sdkdiag.AppendErrorf(diags, "creating IAM Group (%s): %s", name, err) + } + + d.SetId(aws.StringValue(output.Group.GroupName)) + + _, err = tfresource.RetryWhenNotFound(ctx, propagationTimeout, func() (interface{}, error) { + return FindGroupByName(ctx, conn, d.Id()) + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for IAM Group (%s) create: %s", d.Id(), err) } - d.SetId(aws.StringValue(createResp.Group.GroupName)) return append(diags, resourceGroupRead(ctx, d, meta)...) } @@ -78,33 +96,9 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - request := &iam.GetGroupInput{ - GroupName: aws.String(d.Id()), - } - - var getResp *iam.GetGroupOutput - - err := resource.RetryContext(ctx, propagationTimeout, func() *resource.RetryError { - var err error - - getResp, err = conn.GetGroupWithContext(ctx, request) - - if d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return resource.RetryableError(err) - } - - if err != nil { - return resource.NonRetryableError(err) - } + group, err := FindGroupByName(ctx, conn, d.Id()) - return nil - }) - - if tfresource.TimedOut(err) { - getResp, err = conn.GetGroupWithContext(ctx, request) - } - - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] IAM Group (%s) not found, removing from state", d.Id()) d.SetId("") return diags @@ -114,55 +108,77 @@ func resourceGroupRead(ctx context.Context, d *schema.ResourceData, meta interfa return sdkdiag.AppendErrorf(diags, "reading IAM Group (%s): %s", d.Id(), err) } - if getResp == nil || getResp.Group == nil { - return sdkdiag.AppendErrorf(diags, "reading IAM Group (%s): empty response", d.Id()) - } - - group := getResp.Group - - d.Set("name", group.GroupName) d.Set("arn", group.Arn) + d.Set("name", group.GroupName) d.Set("path", group.Path) d.Set("unique_id", group.GroupId) + return diags } func resourceGroupUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - if d.HasChanges("name", "path") { - conn := meta.(*conns.AWSClient).IAMConn() - on, nn := d.GetChange("name") - _, np := d.GetChange("path") - - request := &iam.UpdateGroupInput{ - GroupName: aws.String(on.(string)), - NewGroupName: aws.String(nn.(string)), - NewPath: aws.String(np.(string)), - } - _, err := conn.UpdateGroupWithContext(ctx, request) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM Group %s: %s", d.Id(), err) - } - d.SetId(nn.(string)) - return append(diags, resourceGroupRead(ctx, d, meta)...) + conn := meta.(*conns.AWSClient).IAMConn() + + o, n := d.GetChange("name") + input := &iam.UpdateGroupInput{ + GroupName: aws.String(o.(string)), + NewGroupName: aws.String(n.(string)), + NewPath: aws.String(d.Get("path").(string)), } - return diags + + _, err := conn.UpdateGroupWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating IAM Group (%s): %s", d.Id(), err) + } + + d.SetId(n.(string)) + + return append(diags, resourceGroupRead(ctx, d, meta)...) } func resourceGroupDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - request := &iam.DeleteGroupInput{ + log.Printf("[DEBUG] Deleting IAM Group: %s", d.Id()) + _, err := conn.DeleteGroupWithContext(ctx, &iam.DeleteGroupInput{ GroupName: aws.String(d.Id()), - } + }) - if _, err := conn.DeleteGroupWithContext(ctx, request); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting IAM Group %s: %s", d.Id(), err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting IAM Group (%s): %s", d.Id(), err) } + return diags } +func FindGroupByName(ctx context.Context, conn *iam.IAM, name string) (*iam.Group, error) { + input := &iam.GetGroupInput{ + GroupName: aws.String(name), + } + + output, err := conn.GetGroupWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Group == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.Group, nil +} + func DeleteGroupPolicyAttachments(ctx context.Context, conn *iam.IAM, groupName string) error { var attachedPolicies []*iam.AttachedPolicy input := &iam.ListAttachedGroupPoliciesInput{ diff --git a/internal/service/iam/group_test.go b/internal/service/iam/group_test.go index e8935bffa11..d764803b2cc 100644 --- a/internal/service/iam/group_test.go +++ b/internal/service/iam/group_test.go @@ -2,28 +2,24 @@ package iam_test import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIAMGroup_basic(t *testing.T) { ctx := acctest.Context(t) - var conf iam.GetGroupOutput + var conf iam.Group + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_group.test" - resourceName2 := "aws_iam_group.test2" - rString := sdkacctest.RandString(8) - groupName := fmt.Sprintf("tf-acc-group-basic-%s", rString) - groupName2 := fmt.Sprintf("tf-acc-group-basic-2-%s", rString) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -32,10 +28,14 @@ func TestAccIAMGroup_basic(t *testing.T) { CheckDestroy: testAccCheckGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGroupConfig_basic(groupName), - Check: resource.ComposeTestCheckFunc( + Config: testAccGroupConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckGroupExists(ctx, resourceName, &conf), - testAccCheckGroupAttributes(&conf, groupName, "/"), + // arn:${Partition}:iam::${Account}:group/${GroupNameWithPath} + acctest.CheckResourceAttrGlobalARN(resourceName, "arn", "iam", fmt.Sprintf("group/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "path", "/"), + resource.TestCheckResourceAttrSet(resourceName, "unique_id"), ), }, { @@ -43,12 +43,29 @@ func TestAccIAMGroup_basic(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + }, + }) +} + +func TestAccIAMGroup_disappears(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Group + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGroupDestroy(ctx), + Steps: []resource.TestStep{ { - Config: testAccGroupConfig_2(groupName2), + Config: testAccGroupConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckGroupExists(ctx, resourceName2, &conf), - testAccCheckGroupAttributes(&conf, groupName2, "/funnypath/"), + testAccCheckGroupExists(ctx, resourceName, &conf), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceGroup(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) @@ -56,11 +73,10 @@ func TestAccIAMGroup_basic(t *testing.T) { func TestAccIAMGroup_nameChange(t *testing.T) { ctx := acctest.Context(t) - var conf iam.GetGroupOutput + var conf iam.Group + rName1 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + rName2 := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_group.test" - rString := sdkacctest.RandString(8) - groupName := fmt.Sprintf("tf-acc-group-basic-%s", rString) - groupName2 := fmt.Sprintf("tf-acc-group-basic-2-%s", rString) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -69,17 +85,58 @@ func TestAccIAMGroup_nameChange(t *testing.T) { CheckDestroy: testAccCheckGroupDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccGroupConfig_basic(groupName), + Config: testAccGroupConfig_basic(rName1), Check: resource.ComposeTestCheckFunc( testAccCheckGroupExists(ctx, resourceName, &conf), - testAccCheckGroupAttributes(&conf, groupName, "/"), + acctest.CheckResourceAttrGlobalARN(resourceName, "arn", "iam", fmt.Sprintf("group/%s", rName1)), + resource.TestCheckResourceAttr(resourceName, "name", rName1), ), }, { - Config: testAccGroupConfig_basic(groupName2), + Config: testAccGroupConfig_basic(rName2), Check: resource.ComposeTestCheckFunc( testAccCheckGroupExists(ctx, resourceName, &conf), - testAccCheckGroupAttributes(&conf, groupName2, "/"), + acctest.CheckResourceAttrGlobalARN(resourceName, "arn", "iam", fmt.Sprintf("group/%s", rName2)), + resource.TestCheckResourceAttr(resourceName, "name", rName2), + ), + }, + }, + }) +} + +func TestAccIAMGroup_path(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.Group + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_iam_group.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckGroupDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccGroupConfig_path(rName, "/path1/"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrGlobalARN(resourceName, "arn", "iam", fmt.Sprintf("group/path1/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "path", "/path1/"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccGroupConfig_path(rName, "/path2/"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckGroupExists(ctx, resourceName, &conf), + acctest.CheckResourceAttrGlobalARN(resourceName, "arn", "iam", fmt.Sprintf("group/path2/%s", rName)), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "path", "/path2/"), ), }, }, @@ -95,29 +152,24 @@ func testAccCheckGroupDestroy(ctx context.Context) resource.TestCheckFunc { continue } - // Try to get group - _, err := conn.GetGroupWithContext(ctx, &iam.GetGroupInput{ - GroupName: aws.String(rs.Primary.ID), - }) - if err == nil { - return errors.New("still exist.") - } + _, err := tfiam.FindGroupByName(ctx, conn, rs.Primary.ID) - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err + if tfresource.NotFound(err) { + continue } - if ec2err.Code() != "NoSuchEntity" { + + if err != nil { return err } + + return fmt.Errorf("IAM Group %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckGroupExists(ctx context.Context, n string, res *iam.GetGroupOutput) resource.TestCheckFunc { +func testAccCheckGroupExists(ctx context.Context, n string, v *iam.Group) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -125,52 +177,36 @@ func testAccCheckGroupExists(ctx context.Context, n string, res *iam.GetGroupOut } if rs.Primary.ID == "" { - return errors.New("No Group name is set") + return fmt.Errorf("No IAM Group ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn() - resp, err := conn.GetGroupWithContext(ctx, &iam.GetGroupInput{ - GroupName: aws.String(rs.Primary.ID), - }) + output, err := tfiam.FindGroupByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } - *res = *resp - - return nil - } -} - -func testAccCheckGroupAttributes(group *iam.GetGroupOutput, name string, path string) resource.TestCheckFunc { - return func(s *terraform.State) error { - if *group.Group.GroupName != name { - return fmt.Errorf("Bad name: %s when %s was expected", *group.Group.GroupName, name) - } - - if *group.Group.Path != path { - return fmt.Errorf("Bad path: %s when %s was expected", *group.Group.Path, path) - } + *v = *output return nil } } -func testAccGroupConfig_basic(groupName string) string { +func testAccGroupConfig_basic(rName string) string { return fmt.Sprintf(` resource "aws_iam_group" "test" { - name = "%s" - path = "/" + name = %[1]q } -`, groupName) +`, rName) } -func testAccGroupConfig_2(groupName string) string { +func testAccGroupConfig_path(rName, path string) string { return fmt.Sprintf(` -resource "aws_iam_group" "test2" { - name = "%s" - path = "/funnypath/" +resource "aws_iam_group" "test" { + name = %[1]q + path = %[2]q } -`, groupName) +`, rName, path) } From 488e7a3af66cf9569fb5c36fb36d5bd53630eec3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 09:01:31 -0500 Subject: [PATCH 57/68] r/aws_iam_user_ssh_key: Add 'FindSSHPublicKeyByThreePartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccIAMUserSSHKey_' PKG=iam ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/iam/... -v -count 1 -parallel 3 -run=TestAccIAMUserSSHKey_ -timeout 180m === RUN TestAccIAMUserSSHKey_basic === PAUSE TestAccIAMUserSSHKey_basic === RUN TestAccIAMUserSSHKey_disappears === PAUSE TestAccIAMUserSSHKey_disappears === RUN TestAccIAMUserSSHKey_pemEncoding === PAUSE TestAccIAMUserSSHKey_pemEncoding === CONT TestAccIAMUserSSHKey_basic === CONT TestAccIAMUserSSHKey_pemEncoding === CONT TestAccIAMUserSSHKey_disappears --- PASS: TestAccIAMUserSSHKey_disappears (14.63s) --- PASS: TestAccIAMUserSSHKey_basic (16.94s) --- PASS: TestAccIAMUserSSHKey_pemEncoding (16.86s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/iam 21.735s --- internal/service/iam/user_ssh_key.go | 169 ++++++++++++---------- internal/service/iam/user_ssh_key_test.go | 86 ++++++----- 2 files changed, 141 insertions(+), 114 deletions(-) diff --git a/internal/service/iam/user_ssh_key.go b/internal/service/iam/user_ssh_key.go index 7dd2be5bcea..6ef3af07822 100644 --- a/internal/service/iam/user_ssh_key.go +++ b/internal/service/iam/user_ssh_key.go @@ -24,24 +24,22 @@ func ResourceUserSSHKey() *schema.Resource { ReadWithoutTimeout: resourceUserSSHKeyRead, UpdateWithoutTimeout: resourceUserSSHKeyUpdate, DeleteWithoutTimeout: resourceUserSSHKeyDelete, + Importer: &schema.ResourceImporter{ StateContext: resourceUserSSHKeyImport, }, Schema: map[string]*schema.Schema{ - "ssh_public_key_id": { - Type: schema.TypeString, - Computed: true, + "encoding": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(iam.EncodingType_Values(), false), }, "fingerprint": { Type: schema.TypeString, Computed: true, }, - "username": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "public_key": { Type: schema.TypeString, Required: true, @@ -54,22 +52,20 @@ func ResourceUserSSHKey() *schema.Resource { return strings.Trim(old, "\n") == strings.Trim(new, "\n") }, }, - - "encoding": { + "ssh_public_key_id": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - iam.EncodingTypeSsh, - iam.EncodingTypePem, - }, false), + Computed: true, }, - "status": { Type: schema.TypeString, Optional: true, Computed: true, }, + "username": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, }, } } @@ -77,59 +73,54 @@ func ResourceUserSSHKey() *schema.Resource { func resourceUserSSHKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - username := d.Get("username").(string) - publicKey := d.Get("public_key").(string) - request := &iam.UploadSSHPublicKeyInput{ + username := d.Get("username").(string) + input := &iam.UploadSSHPublicKeyInput{ + SSHPublicKeyBody: aws.String(d.Get("public_key").(string)), UserName: aws.String(username), - SSHPublicKeyBody: aws.String(publicKey), } - log.Println("[DEBUG] Create IAM User SSH Key Request:", request) - createResp, err := conn.UploadSSHPublicKeyWithContext(ctx, request) + output, err := conn.UploadSSHPublicKeyWithContext(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating IAM User SSH Key %s: %s", username, err) + return sdkdiag.AppendErrorf(diags, "uploading IAM User SSH Key (%s): %s", username, err) } - d.SetId(aws.StringValue(createResp.SSHPublicKey.SSHPublicKeyId)) + d.SetId(aws.StringValue(output.SSHPublicKey.SSHPublicKeyId)) - return append(diags, resourceUserSSHKeyUpdate(ctx, d, meta)...) -} + _, err = tfresource.RetryWhenNotFound(ctx, propagationTimeout, func() (interface{}, error) { + return FindSSHPublicKeyByThreePartKey(ctx, conn, d.Id(), d.Get("encoding").(string), username) + }) -func resourceUserSSHKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).IAMConn() - username := d.Get("username").(string) - encoding := d.Get("encoding").(string) - request := &iam.GetSSHPublicKeyInput{ - UserName: aws.String(username), - SSHPublicKeyId: aws.String(d.Id()), - Encoding: aws.String(encoding), + if err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for IAM User SSH Key (%s) create: %s", d.Id(), err) } - var getResp *iam.GetSSHPublicKeyOutput - - err := resource.RetryContext(ctx, propagationTimeout, func() *resource.RetryError { - var err error - - getResp, err = conn.GetSSHPublicKeyWithContext(ctx, request) - - if d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { - return resource.RetryableError(err) + if v, ok := d.GetOk("status"); ok { + input := &iam.UpdateSSHPublicKeyInput{ + SSHPublicKeyId: aws.String(d.Id()), + Status: aws.String(v.(string)), + UserName: aws.String(username), } + _, err := conn.UpdateSSHPublicKeyWithContext(ctx, input) + if err != nil { - return resource.NonRetryableError(err) + return sdkdiag.AppendErrorf(diags, "updating IAM User SSH Key (%s): %s", d.Id(), err) } + } - return nil - }) + return append(diags, resourceUserSSHKeyRead(ctx, d, meta)...) +} - if tfresource.TimedOut(err) { - getResp, err = conn.GetSSHPublicKeyWithContext(ctx, request) - } +func resourceUserSSHKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).IAMConn() - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + encoding := d.Get("encoding").(string) + key, err := FindSSHPublicKeyByThreePartKey(ctx, conn, d.Id(), encoding, d.Get("username").(string)) + + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] IAM User SSH Key (%s) not found, removing from state", d.Id()) d.SetId("") return diags @@ -139,38 +130,34 @@ func resourceUserSSHKeyRead(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "reading IAM User SSH Key (%s): %s", d.Id(), err) } - if getResp == nil || getResp.SSHPublicKey == nil { - return sdkdiag.AppendErrorf(diags, "reading IAM User SSH Key (%s): empty response", d.Id()) - } - - publicKey := aws.StringValue(getResp.SSHPublicKey.SSHPublicKeyBody) + d.Set("fingerprint", key.Fingerprint) + publicKey := aws.StringValue(key.SSHPublicKeyBody) if encoding == iam.EncodingTypeSsh { publicKey = cleanSSHKey(publicKey) } - - d.Set("fingerprint", getResp.SSHPublicKey.Fingerprint) - d.Set("status", getResp.SSHPublicKey.Status) - d.Set("ssh_public_key_id", getResp.SSHPublicKey.SSHPublicKeyId) d.Set("public_key", publicKey) + d.Set("ssh_public_key_id", key.SSHPublicKeyId) + d.Set("status", key.Status) + return diags } func resourceUserSSHKeyUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - if d.HasChange("status") { - conn := meta.(*conns.AWSClient).IAMConn() + conn := meta.(*conns.AWSClient).IAMConn() - request := &iam.UpdateSSHPublicKeyInput{ - UserName: aws.String(d.Get("username").(string)), - SSHPublicKeyId: aws.String(d.Id()), - Status: aws.String(d.Get("status").(string)), - } + input := &iam.UpdateSSHPublicKeyInput{ + SSHPublicKeyId: aws.String(d.Id()), + Status: aws.String(d.Get("status").(string)), + UserName: aws.String(d.Get("username").(string)), + } - _, err := conn.UpdateSSHPublicKeyWithContext(ctx, request) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating IAM User SSH Key (%s): %s", d.Id(), err) - } + _, err := conn.UpdateSSHPublicKeyWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating IAM User SSH Key (%s): %s", d.Id(), err) } + return append(diags, resourceUserSSHKeyRead(ctx, d, meta)...) } @@ -178,15 +165,16 @@ func resourceUserSSHKeyDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).IAMConn() - request := &iam.DeleteSSHPublicKeyInput{ - UserName: aws.String(d.Get("username").(string)), + log.Printf("[DEBUG] Deleting IAM User SSH Key: %s", d.Id()) + _, err := conn.DeleteSSHPublicKeyWithContext(ctx, &iam.DeleteSSHPublicKeyInput{ SSHPublicKeyId: aws.String(d.Id()), - } + UserName: aws.String(d.Get("username").(string)), + }) - log.Println("[DEBUG] Delete IAM User SSH Key request:", request) - if _, err := conn.DeleteSSHPublicKeyWithContext(ctx, request); err != nil { + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting IAM User SSH Key (%s): %s", d.Id(), err) } + return diags } @@ -209,6 +197,33 @@ func resourceUserSSHKeyImport(ctx context.Context, d *schema.ResourceData, meta return []*schema.ResourceData{d}, nil } +func FindSSHPublicKeyByThreePartKey(ctx context.Context, conn *iam.IAM, id, encoding, username string) (*iam.SSHPublicKey, error) { + input := &iam.GetSSHPublicKeyInput{ + Encoding: aws.String(encoding), + SSHPublicKeyId: aws.String(id), + UserName: aws.String(username), + } + + output, err := conn.GetSSHPublicKeyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, iam.ErrCodeNoSuchEntityException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.SSHPublicKey == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.SSHPublicKey, nil +} + func cleanSSHKey(key string) string { // Remove comments from SSH Keys // Comments are anything after "ssh-rsa XXXX" where XXXX is the key. diff --git a/internal/service/iam/user_ssh_key_test.go b/internal/service/iam/user_ssh_key_test.go index 96efe1e53bf..76310c02df7 100644 --- a/internal/service/iam/user_ssh_key_test.go +++ b/internal/service/iam/user_ssh_key_test.go @@ -5,19 +5,19 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/iam" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfiam "github.com/hashicorp/terraform-provider-aws/internal/service/iam" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccIAMUserSSHKey_basic(t *testing.T) { ctx := acctest.Context(t) - var conf iam.GetSSHPublicKeyOutput + var conf iam.SSHPublicKey resourceName := "aws_iam_user_ssh_key.user" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -35,7 +35,8 @@ func TestAccIAMUserSSHKey_basic(t *testing.T) { { Config: testAccUserSSHKeyConfig_encoding(rName, publicKey), Check: resource.ComposeTestCheckFunc( - testAccCheckUserSSHKeyExists(ctx, resourceName, "Inactive", &conf), + testAccCheckUserSSHKeyExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "status", "Inactive"), ), }, { @@ -48,9 +49,38 @@ func TestAccIAMUserSSHKey_basic(t *testing.T) { }) } +func TestAccIAMUserSSHKey_disappears(t *testing.T) { + ctx := acctest.Context(t) + var conf iam.SSHPublicKey + resourceName := "aws_iam_user_ssh_key.user" + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + publicKey, _, err := RandSSHKeyPairSize(2048, acctest.DefaultEmailAddress) + if err != nil { + t.Fatalf("error generating random SSH key: %s", err) + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, iam.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckUserSSHKeyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccUserSSHKeyConfig_encoding(rName, publicKey), + Check: resource.ComposeTestCheckFunc( + testAccCheckUserSSHKeyExists(ctx, resourceName, &conf), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfiam.ResourceUserSSHKey(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccIAMUserSSHKey_pemEncoding(t *testing.T) { ctx := acctest.Context(t) - var conf iam.GetSSHPublicKeyOutput + var conf iam.SSHPublicKey rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_iam_user_ssh_key.user" @@ -63,7 +93,8 @@ func TestAccIAMUserSSHKey_pemEncoding(t *testing.T) { { Config: testAccSSHKeyConfig_pemEncoding(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckUserSSHKeyExists(ctx, resourceName, "Active", &conf), + testAccCheckUserSSHKeyExists(ctx, resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "status", "Active"), ), }, { @@ -85,32 +116,24 @@ func testAccCheckUserSSHKeyDestroy(ctx context.Context) resource.TestCheckFunc { continue } - username := rs.Primary.Attributes["username"] - encoding := rs.Primary.Attributes["encoding"] - _, err := conn.GetSSHPublicKeyWithContext(ctx, &iam.GetSSHPublicKeyInput{ - SSHPublicKeyId: aws.String(rs.Primary.ID), - UserName: aws.String(username), - Encoding: aws.String(encoding), - }) - if err == nil { - return fmt.Errorf("still exist.") - } + _, err := tfiam.FindSSHPublicKeyByThreePartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["encoding"], rs.Primary.Attributes["username"]) - // Verify the error is what we want - ec2err, ok := err.(awserr.Error) - if !ok { - return err + if tfresource.NotFound(err) { + continue } - if ec2err.Code() != "NoSuchEntity" { + + if err != nil { return err } + + return fmt.Errorf("IAM User SSH Key %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckUserSSHKeyExists(ctx context.Context, n, status string, res *iam.GetSSHPublicKeyOutput) resource.TestCheckFunc { +func testAccCheckUserSSHKeyExists(ctx context.Context, n string, v *iam.SSHPublicKey) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -118,29 +141,18 @@ func testAccCheckUserSSHKeyExists(ctx context.Context, n, status string, res *ia } if rs.Primary.ID == "" { - return fmt.Errorf("No SSHPublicKeyID is set") + return fmt.Errorf("No IAM User SSH Key ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).IAMConn() - username := rs.Primary.Attributes["username"] - encoding := rs.Primary.Attributes["encoding"] - resp, err := conn.GetSSHPublicKeyWithContext(ctx, &iam.GetSSHPublicKeyInput{ - SSHPublicKeyId: aws.String(rs.Primary.ID), - UserName: aws.String(username), - Encoding: aws.String(encoding), - }) + output, err := tfiam.FindSSHPublicKeyByThreePartKey(ctx, conn, rs.Primary.ID, rs.Primary.Attributes["encoding"], rs.Primary.Attributes["username"]) + if err != nil { return err } - *res = *resp - - keyStruct := resp.SSHPublicKey - - if *keyStruct.Status != status { - return fmt.Errorf("Key status has wrong status should be %s is %s", status, *keyStruct.Status) - } + *v = *output return nil } From f14dab2c2c8eb04195e3886f4b53303502c1d423 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 12:18:35 -0500 Subject: [PATCH 58/68] r/aws_app_cookie_stickiness_policy: Add 'FindLoadBalancerPolicyByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBAppCookieStickinessPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBAppCookieStickinessPolicy_ -timeout 180m === RUN TestAccELBAppCookieStickinessPolicy_basic === PAUSE TestAccELBAppCookieStickinessPolicy_basic === RUN TestAccELBAppCookieStickinessPolicy_disappears === PAUSE TestAccELBAppCookieStickinessPolicy_disappears === RUN TestAccELBAppCookieStickinessPolicy_Disappears_elb === PAUSE TestAccELBAppCookieStickinessPolicy_Disappears_elb === CONT TestAccELBAppCookieStickinessPolicy_basic === CONT TestAccELBAppCookieStickinessPolicy_Disappears_elb === CONT TestAccELBAppCookieStickinessPolicy_disappears --- PASS: TestAccELBAppCookieStickinessPolicy_Disappears_elb (21.13s) --- PASS: TestAccELBAppCookieStickinessPolicy_disappears (23.03s) --- PASS: TestAccELBAppCookieStickinessPolicy_basic (40.13s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 45.553s --- .../elb/app_cookie_stickiness_policy.go | 272 ++++++++++-------- .../elb/app_cookie_stickiness_policy_test.go | 167 +++++------ .../elb/lb_cookie_stickiness_policy.go | 38 +++ internal/service/elb/load_balancer.go | 36 +++ 4 files changed, 294 insertions(+), 219 deletions(-) diff --git a/internal/service/elb/app_cookie_stickiness_policy.go b/internal/service/elb/app_cookie_stickiness_policy.go index 05c9d836d7c..6fa79409b98 100644 --- a/internal/service/elb/app_cookie_stickiness_policy.go +++ b/internal/service/elb/app_cookie_stickiness_policy.go @@ -2,7 +2,6 @@ package elb import ( "context" - "errors" "fmt" "log" "regexp" @@ -13,53 +12,51 @@ import ( "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceAppCookieStickinessPolicy() *schema.Resource { return &schema.Resource{ - // There is no concept of "updating" an App Stickiness policy in - // the AWS API. CreateWithoutTimeout: resourceAppCookieStickinessPolicyCreate, ReadWithoutTimeout: resourceAppCookieStickinessPolicyRead, DeleteWithoutTimeout: resourceAppCookieStickinessPolicyDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ - "name": { + "cookie_name": { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { - value := v.(string) - if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { - es = append(es, fmt.Errorf( - "only alphanumeric characters and hyphens allowed in %q", k)) - } - return - }, }, - - "load_balancer": { - Type: schema.TypeString, + "lb_port": { + Type: schema.TypeInt, Required: true, ForceNew: true, }, - - "lb_port": { - Type: schema.TypeInt, + "load_balancer": { + Type: schema.TypeString, Required: true, ForceNew: true, }, - - "cookie_name": { + "name": { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { + value := v.(string) + if !regexp.MustCompile(`^[0-9A-Za-z-]+$`).MatchString(value) { + es = append(es, fmt.Errorf( + "only alphanumeric characters and hyphens allowed in %q", k)) + } + return + }, }, }, } @@ -69,31 +66,36 @@ func resourceAppCookieStickinessPolicyCreate(ctx context.Context, d *schema.Reso var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - // Provision the AppStickinessPolicy - acspOpts := &elb.CreateAppCookieStickinessPolicyInput{ - CookieName: aws.String(d.Get("cookie_name").(string)), - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - PolicyName: aws.String(d.Get("name").(string)), - } + lbName := d.Get("load_balancer").(string) + lbPort := d.Get("lb_port").(int) + policyName := d.Get("name").(string) + id := AppCookieStickinessPolicyCreateResourceID(lbName, lbPort, policyName) + { + input := &elb.CreateAppCookieStickinessPolicyInput{ + CookieName: aws.String(d.Get("cookie_name").(string)), + LoadBalancerName: aws.String(lbName), + PolicyName: aws.String(policyName), + } - if _, err := conn.CreateAppCookieStickinessPolicyWithContext(ctx, acspOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "creating AppCookieStickinessPolicy: %s", err) + if _, err := conn.CreateAppCookieStickinessPolicyWithContext(ctx, input); err != nil { + return sdkdiag.AppendErrorf(diags, "creating ELB Classic App Cookie Stickiness Policy (%s): %s", id, err) + } } - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{aws.String(d.Get("name").(string))}, - } + { + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{policyName}), + } - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting AppCookieStickinessPolicy: %s", err) + if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input); err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic App Cookie Stickiness Policy (%s): %s", id, err) + } } - d.SetId(fmt.Sprintf("%s:%d:%s", - *acspOpts.LoadBalancerName, - *setLoadBalancerOpts.LoadBalancerPort, - *acspOpts.PolicyName)) + d.SetId(id) + return diags } @@ -101,133 +103,157 @@ func resourceAppCookieStickinessPolicyRead(ctx context.Context, d *schema.Resour var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbName, lbPort, policyName := AppCookieStickinessPolicyParseID(d.Id()) + lbName, lbPort, policyName, err := AppCookieStickinessPolicyParseResourceID(d.Id()) - request := &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(lbName), - PolicyNames: []*string{aws.String(policyName)}, + if err != nil { + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - getResp, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, request) + policy, err := FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic App Cookie Stickiness Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) App Cookie Policy (%s) not found, removing from state", lbName, policyName) - d.SetId("") - return diags - } - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) not found, removing App Cookie Policy (%s) from state", lbName, policyName) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "retrieving ELB Classic (%s) App Cookie Policy (%s): %s", lbName, policyName, err) + return sdkdiag.AppendErrorf(diags, "reading ELB Classic App Cookie Stickiness Policy (%s): %s", d.Id(), err) } - if len(getResp.PolicyDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find policy %#v", getResp.PolicyDescriptions) + if len(policy.PolicyAttributeDescriptions) != 1 || aws.StringValue(policy.PolicyAttributeDescriptions[0].AttributeName) != "CookieName" { + return sdkdiag.AppendErrorf(diags, "cookie not found") } + cookieAttr := policy.PolicyAttributeDescriptions[0] + d.Set("cookie_name", cookieAttr.AttributeValue) + d.Set("lb_port", lbPort) + d.Set("load_balancer", lbName) + d.Set("name", policyName) + + return diags +} + +func resourceAppCookieStickinessPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).ELBConn() + + lbName, lbPort, policyName, err := AppCookieStickinessPolicyParseResourceID(d.Id()) - // we know the policy exists now, but we have to check if it's assigned to a listener - assigned, err := resourceSticknessPolicyAssigned(ctx, conn, policyName, lbName, lbPort) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ELB Classic App Cookie Stickiness Policy (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - if !d.IsNewResource() && !assigned { - log.Printf("[WARN] ELB Classic LB (%s) App Cookie Policy (%s) exists, but isn't assigned to a listener", lbName, policyName) - d.SetId("") - return diags + + // Perversely, if we Set an empty list of PolicyNames, we detach the + // policies attached to a listener, which is required to delete the + // policy itself. + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{}), } - // We can get away with this because there's only one attribute, the - // cookie expiration, in these descriptions. - policyDesc := getResp.PolicyDescriptions[0] - cookieAttr := policyDesc.PolicyAttributeDescriptions[0] - if aws.StringValue(cookieAttr.AttributeName) != "CookieName" { - return sdkdiag.AppendErrorf(diags, "Unable to find cookie Name.") + _, err = conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic App Cookie Stickiness Policy (%s): %s", d.Id(), err) } - d.Set("cookie_name", cookieAttr.AttributeValue) - d.Set("name", policyName) - d.Set("load_balancer", lbName) + log.Printf("[DEBUG] Deleting ELB Classic App Cookie Stickiness Policy: %s", d.Id()) + _, err = conn.DeleteLoadBalancerPolicyWithContext(ctx, &elb.DeleteLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(lbName), + PolicyName: aws.String(policyName), + }) - lbPortInt, _ := strconv.Atoi(lbPort) - d.Set("lb_port", lbPortInt) + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting ELB Classic App Cookie Stickiness Policy (%s): %s", d.Id(), err) + } return diags } -// Determine if a particular policy is assigned to an ELB listener -func resourceSticknessPolicyAssigned(ctx context.Context, conn *elb.ELB, policyName, lbName, lbPort string) (bool, error) { - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(lbName)}, +func FindLoadBalancerPolicyByTwoPartKey(ctx context.Context, conn *elb.ELB, lbName, policyName string) (*elb.PolicyDescription, error) { + input := &elb.DescribeLoadBalancerPoliciesInput{ + LoadBalancerName: aws.String(lbName), + PolicyNames: aws.StringSlice([]string{policyName}), } - describeResp, err := conn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) - if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - return false, nil + output, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException, elb.ErrCodeAccessPointNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } } if err != nil { - return false, fmt.Errorf("retrieving LB: %s", err) + return nil, err } - if len(describeResp.LoadBalancerDescriptions) != 1 { - return false, errors.New("retrieving LB: empty response") + if output == nil || len(output.PolicyDescriptions) == 0 || output.PolicyDescriptions[0] == nil { + return nil, tfresource.NewEmptyResultError(input) } - lb := describeResp.LoadBalancerDescriptions[0] - assigned := false - for _, listener := range lb.ListenerDescriptions { - if listener == nil || listener.Listener == nil || lbPort != strconv.Itoa(int(aws.Int64Value(listener.Listener.LoadBalancerPort))) { + if count := len(output.PolicyDescriptions); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.PolicyDescriptions[0], nil +} + +func FindLoadBalancerListenerPolicyByThreePartKey(ctx context.Context, conn *elb.ELB, lbName string, lbPort int, policyName string) (*elb.PolicyDescription, error) { + policy, err := FindLoadBalancerPolicyByTwoPartKey(ctx, conn, lbName, policyName) + + if err != nil { + return nil, err + } + + lb, err := FindLoadBalancerByName(ctx, conn, lbName) + + if err != nil { + return nil, err + } + + for _, v := range lb.ListenerDescriptions { + if v == nil || v.Listener == nil { + continue + } + + if aws.Int64Value(v.Listener.LoadBalancerPort) != int64(lbPort) { continue } - for _, name := range listener.PolicyNames { - if policyName == aws.StringValue(name) { - assigned = true - break + for _, v := range v.PolicyNames { + if aws.StringValue(v) == policyName { + return policy, nil } } } - return assigned, nil + return nil, &resource.NotFoundError{} } -func resourceAppCookieStickinessPolicyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ELBConn() +const appCookieStickinessPolicyResourceIDSeparator = ":" - lbName, _, policyName := AppCookieStickinessPolicyParseID(d.Id()) +func AppCookieStickinessPolicyCreateResourceID(lbName string, lbPort int, policyName string) string { + parts := []string{lbName, strconv.Itoa(lbPort), policyName} + id := strings.Join(parts, appCookieStickinessPolicyResourceIDSeparator) - // Perversely, if we Set an empty list of PolicyNames, we detach the - // policies attached to a listener, which is required to delete the - // policy itself. - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{}, - } + return id +} - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "removing AppCookieStickinessPolicy: %s", err) - } +func AppCookieStickinessPolicyParseResourceID(id string) (string, int, string, error) { + parts := strings.Split(id, appCookieStickinessPolicyResourceIDSeparator) - request := &elb.DeleteLoadBalancerPolicyInput{ - LoadBalancerName: aws.String(lbName), - PolicyName: aws.String(policyName), - } + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + v, err := strconv.Atoi(parts[1]) + + if err != nil { + return "", 0, "", err + } - if _, err := conn.DeleteLoadBalancerPolicyWithContext(ctx, request); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting App stickiness policy %s: %s", d.Id(), err) + return parts[0], v, parts[2], nil } - return diags -} -// AppCookieStickinessPolicyParseID takes an ID and parses it into -// it's constituent parts. You need three axes (LB name, policy name, and LB -// port) to create or identify a stickiness policy in AWS's API. -func AppCookieStickinessPolicyParseID(id string) (string, string, string) { - parts := strings.SplitN(id, ":", 3) - return parts[0], parts[1], parts[2] + return "", 0, "", fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sLBPORT%[2]sPOLICYNAME", id, appCookieStickinessPolicyResourceIDSeparator) } diff --git a/internal/service/elb/app_cookie_stickiness_policy_test.go b/internal/service/elb/app_cookie_stickiness_policy_test.go index bb0cee90a7d..e59e488b873 100644 --- a/internal/service/elb/app_cookie_stickiness_policy_test.go +++ b/internal/service/elb/app_cookie_stickiness_policy_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,11 +12,13 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBAppCookieStickinessPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_app_cookie_stickiness_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -27,35 +27,58 @@ func TestAccELBAppCookieStickinessPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckAppCookieStickinessPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAppCookieStickinessPolicyConfig_basic(lbName), + Config: testAccAppCookieStickinessPolicyConfig_basic(rName, "bourbon"), Check: resource.ComposeTestCheckFunc( - testAccCheckAppCookieStickinessPolicy(ctx, "aws_elb.lb", - "aws_app_cookie_stickiness_policy.foo", - ), + testAccCheckAppCookieStickinessPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "cookie_name", "bourbon"), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, { - ResourceName: "aws_app_cookie_stickiness_policy.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, { - Config: testAccAppCookieStickinessPolicyConfig_update(lbName), + Config: testAccAppCookieStickinessPolicyConfig_basic(rName, "custard-cream"), Check: resource.ComposeTestCheckFunc( - testAccCheckAppCookieStickinessPolicy(ctx, "aws_elb.lb", - "aws_app_cookie_stickiness_policy.foo", - ), + testAccCheckAppCookieStickinessPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "cookie_name", "custard-cream"), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, }, }) } +func TestAccELBAppCookieStickinessPolicy_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_app_cookie_stickiness_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAppCookieStickinessPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAppCookieStickinessPolicyConfig_basic(rName, "bourbon"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppCookieStickinessPolicyExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceAppCookieStickinessPolicy(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccELBAppCookieStickinessPolicy_Disappears_elb(t *testing.T) { ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) - elbResourceName := "aws_elb.lb" - resourceName := "aws_app_cookie_stickiness_policy.foo" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_app_cookie_stickiness_policy.test" + elbResourceName := "aws_elb.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -64,9 +87,9 @@ func TestAccELBAppCookieStickinessPolicy_Disappears_elb(t *testing.T) { CheckDestroy: testAccCheckAppCookieStickinessPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccAppCookieStickinessPolicyConfig_basic(lbName), + Config: testAccAppCookieStickinessPolicyConfig_basic(rName, "bourbon"), Check: resource.ComposeTestCheckFunc( - testAccCheckAppCookieStickinessPolicy(ctx, elbResourceName, resourceName), + testAccCheckAppCookieStickinessPolicyExists(ctx, resourceName), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceLoadBalancer(), elbResourceName), ), ExpectNonEmptyPlan: true, @@ -84,106 +107,58 @@ func testAccCheckAppCookieStickinessPolicyDestroy(ctx context.Context) resource. continue } - lbName, _, policyName := tfelb.AppCookieStickinessPolicyParseID( - rs.Primary.ID) - out, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(lbName), - PolicyNames: []*string{aws.String(policyName)}, - }) + lbName, lbPort, policyName, err := tfelb.AppCookieStickinessPolicyParseResourceID(rs.Primary.ID) + if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { - continue - } return err } - if len(out.PolicyDescriptions) > 0 { - return fmt.Errorf("Policy still exists") + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err } + + return fmt.Errorf("ELB Classic App Cookie Stickiness Policy %s still exists", rs.Primary.ID) } + return nil } } -func testAccCheckAppCookieStickinessPolicy(ctx context.Context, elbResource string, policyResource string) resource.TestCheckFunc { +func testAccCheckAppCookieStickinessPolicyExists(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[elbResource] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", elbResource) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No ELB Classic App Cookie Stickiness Policy ID is set") } - policy, ok := s.RootModule().Resources[policyResource] - if !ok { - return fmt.Errorf("Not found: %s", policyResource) + lbName, lbPort, policyName, err := tfelb.AppCookieStickinessPolicyParseResourceID(rs.Primary.ID) + + if err != nil { + return err } conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - elbName, _, policyName := tfelb.AppCookieStickinessPolicyParseID(policy.Primary.ID) - _, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(elbName), - PolicyNames: []*string{aws.String(policyName)}, - }) + + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) return err } } -func TestAccELBAppCookieStickinessPolicy_disappears(t *testing.T) { - ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) - elbResourceName := "aws_elb.lb" - resourceName := "aws_app_cookie_stickiness_policy.foo" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), - ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckAppCookieStickinessPolicyDestroy(ctx), - Steps: []resource.TestStep{ - { - Config: testAccAppCookieStickinessPolicyConfig_basic(lbName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAppCookieStickinessPolicy(ctx, elbResourceName, resourceName), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceAppCookieStickinessPolicy(), resourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - -func testAccAppCookieStickinessPolicyConfig_basic(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "lb" { - name = "%s" - availability_zones = [data.aws_availability_zones.available.names[0]] - - listener { - instance_port = 8000 - instance_protocol = "http" - lb_port = 80 - lb_protocol = "http" - } -} - -resource "aws_app_cookie_stickiness_policy" "foo" { - name = "foo-policy" - load_balancer = aws_elb.lb.id - lb_port = 80 - cookie_name = "MyAppCookie" -} -`, rName)) -} - -// Change the cookie_name to "MyOtherAppCookie". -func testAccAppCookieStickinessPolicyConfig_update(rName string) string { +func testAccAppCookieStickinessPolicyConfig_basic(rName, cookieName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "lb" { - name = "%s" +resource "aws_elb" "test" { + name = %[1]q availability_zones = [data.aws_availability_zones.available.names[0]] listener { @@ -194,11 +169,11 @@ resource "aws_elb" "lb" { } } -resource "aws_app_cookie_stickiness_policy" "foo" { - name = "foo-policy" - load_balancer = aws_elb.lb.id +resource "aws_app_cookie_stickiness_policy" "test" { + name = %[1]q + load_balancer = aws_elb.test.id lb_port = 80 - cookie_name = "MyOtherAppCookie" + cookie_name = %[2]q } -`, rName)) +`, rName, cookieName)) } diff --git a/internal/service/elb/lb_cookie_stickiness_policy.go b/internal/service/elb/lb_cookie_stickiness_policy.go index bb5205a746c..4b3106f39c2 100644 --- a/internal/service/elb/lb_cookie_stickiness_policy.go +++ b/internal/service/elb/lb_cookie_stickiness_policy.go @@ -2,6 +2,7 @@ package elb import ( "context" + "errors" "fmt" "log" "strconv" @@ -187,6 +188,43 @@ func resourceCookieStickinessPolicyDelete(ctx context.Context, d *schema.Resourc return diags } +// Determine if a particular policy is assigned to an ELB listener +func resourceSticknessPolicyAssigned(ctx context.Context, conn *elb.ELB, policyName, lbName, lbPort string) (bool, error) { + describeElbOpts := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: []*string{aws.String(lbName)}, + } + describeResp, err := conn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) + + if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { + return false, nil + } + + if err != nil { + return false, fmt.Errorf("retrieving LB: %s", err) + } + + if len(describeResp.LoadBalancerDescriptions) != 1 { + return false, errors.New("retrieving LB: empty response") + } + + lb := describeResp.LoadBalancerDescriptions[0] + assigned := false + for _, listener := range lb.ListenerDescriptions { + if listener == nil || listener.Listener == nil || lbPort != strconv.Itoa(int(aws.Int64Value(listener.Listener.LoadBalancerPort))) { + continue + } + + for _, v := range listener.PolicyNames { + if policyName == aws.StringValue(v) { + assigned = true + break + } + } + } + + return assigned, nil +} + // CookieStickinessPolicyParseID takes an ID and parses it into // it's constituent parts. You need three axes (LB name, policy name, and LB // port) to create or identify a stickiness policy in AWS's API. diff --git a/internal/service/elb/load_balancer.go b/internal/service/elb/load_balancer.go index 141f774ecd7..614bcde9a77 100644 --- a/internal/service/elb/load_balancer.go +++ b/internal/service/elb/load_balancer.go @@ -839,6 +839,42 @@ func resourceLoadBalancerDelete(ctx context.Context, d *schema.ResourceData, met return diags } +func FindLoadBalancerByName(ctx context.Context, conn *elb.ELB, name string) (*elb.LoadBalancerDescription, error) { + input := &elb.DescribeLoadBalancersInput{ + LoadBalancerNames: aws.StringSlice([]string{name}), + } + + output, err := conn.DescribeLoadBalancersWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || len(output.LoadBalancerDescriptions) == 0 || output.LoadBalancerDescriptions[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.LoadBalancerDescriptions); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + // Eventual consistency check. + if aws.StringValue(output.LoadBalancerDescriptions[0].LoadBalancerName) != name { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return output.LoadBalancerDescriptions[0], nil +} + func ListenerHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) From 73ca3c0652ac467692a5ac8dba7e10116daf638e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 14:34:29 -0500 Subject: [PATCH 59/68] Add 'acctest.TLSPEMRemovePublicKeyEncapsulationBoundaries'. --- internal/acctest/crypto.go | 23 ++++++++++++++++++++++- internal/acctest/crypto_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/internal/acctest/crypto.go b/internal/acctest/crypto.go index 7419b75cd1d..a20b21cc09d 100644 --- a/internal/acctest/crypto.go +++ b/internal/acctest/crypto.go @@ -25,7 +25,28 @@ const ( PEMBlockTypePublicKey = `PUBLIC KEY` ) -var tlsX509CertificateSerialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) //nolint:gomnd +var ( + tlsX509CertificateSerialNumberLimit = new(big.Int).Lsh(big.NewInt(1), 128) //nolint:gomnd +) + +// TLSPEMRemovePublicKeyEncapsulationBoundaries removes public key +// pre and post encapsulation boundaries from a PEM string. +func TLSPEMRemovePublicKeyEncapsulationBoundaries(pem string) string { + return removePEMEncapsulationBoundaries(pem, PEMBlockTypePublicKey) +} + +func removePEMEncapsulationBoundaries(pem, label string) string { + return strings.ReplaceAll(strings.ReplaceAll(pem, pemPreEncapsulationBoundary(label), ""), pemPostEncapsulationBoundary(label), "") +} + +// See https://www.rfc-editor.org/rfc/rfc7468#section-2. +func pemPreEncapsulationBoundary(label string) string { + return `-----BEGIN ` + label + `-----` +} + +func pemPostEncapsulationBoundary(label string) string { + return `-----END ` + label + `-----` +} // TLSECDSAPublicKeyPEM generates an ECDSA private key PEM string using the specified elliptic curve. // Wrap with TLSPEMEscapeNewlines() to allow simple fmt.Sprintf() diff --git a/internal/acctest/crypto_test.go b/internal/acctest/crypto_test.go index 73f66cf36bf..4060623cd76 100644 --- a/internal/acctest/crypto_test.go +++ b/internal/acctest/crypto_test.go @@ -87,3 +87,32 @@ func TestTLSECDSAPublicKeyPEM(t *testing.T) { t.Errorf("key does not contain PUBLIC KEY: %s", publicKey) } } + +func TestTLSPEMEscapeNewlines(t *testing.T) { + t.Parallel() + + input := ` +ABCD +12345 +` + want := "\\nABCD\\n12345\\n" + + if got := acctest.TLSPEMEscapeNewlines(input); got != want { + t.Errorf("got: %s\nwant: %s", got, want) + } +} + +func TestTLSPEMRemovePublicKeyEncapsulationBoundaries(t *testing.T) { + t.Parallel() + + input := `-----BEGIN PUBLIC KEY----- +ABCD +12345 +-----END PUBLIC KEY----- +` + want := "\nABCD\n12345\n\n" + + if got := acctest.TLSPEMRemovePublicKeyEncapsulationBoundaries(input); got != want { + t.Errorf("got: %s\nwant: %s", got, want) + } +} From 43d66ae0ad9f8ec5ed0f3d361959026761fd4931 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 14:37:16 -0500 Subject: [PATCH 60/68] Add 'acctest.TLSPEMRemoveNewlines'. --- internal/acctest/crypto.go | 4 ++++ internal/acctest/crypto_test.go | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/internal/acctest/crypto.go b/internal/acctest/crypto.go index a20b21cc09d..00c4d8f6d91 100644 --- a/internal/acctest/crypto.go +++ b/internal/acctest/crypto.go @@ -413,6 +413,10 @@ func TLSPEMEscapeNewlines(pem string) string { return strings.ReplaceAll(pem, "\n", "\\n") } +func TLSPEMRemoveNewlines(pem string) string { + return strings.ReplaceAll(pem, "\n", "") +} + func ellipticCurveForName(name string) elliptic.Curve { switch name { case "P-224": diff --git a/internal/acctest/crypto_test.go b/internal/acctest/crypto_test.go index 4060623cd76..7e78f56d9ea 100644 --- a/internal/acctest/crypto_test.go +++ b/internal/acctest/crypto_test.go @@ -116,3 +116,18 @@ ABCD t.Errorf("got: %s\nwant: %s", got, want) } } + +func TestTLSPEMRemoveNewlines(t *testing.T) { + t.Parallel() + + input := ` +ABCD +12345 + +` + want := "ABCD12345" + + if got := acctest.TLSPEMRemoveNewlines(input); got != want { + t.Errorf("got: %s\nwant: %s", got, want) + } +} From 7c0bcf0b79107bc9e5a58743c6c303af3cad2eca Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 14:58:42 -0500 Subject: [PATCH 61/68] r/aws_load_balancer_backend_server_policy: Add 'FindLoadBalancerBackendServerPolicyByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBBackendServerPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBBackendServerPolicy_ -timeout 180m === RUN TestAccELBBackendServerPolicy_basic === PAUSE TestAccELBBackendServerPolicy_basic === RUN TestAccELBBackendServerPolicy_disappears === PAUSE TestAccELBBackendServerPolicy_disappears === RUN TestAccELBBackendServerPolicy_update === PAUSE TestAccELBBackendServerPolicy_update === CONT TestAccELBBackendServerPolicy_basic === CONT TestAccELBBackendServerPolicy_update === CONT TestAccELBBackendServerPolicy_disappears --- PASS: TestAccELBBackendServerPolicy_basic (44.52s) --- PASS: TestAccELBBackendServerPolicy_disappears (52.17s) --- PASS: TestAccELBBackendServerPolicy_update (55.09s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 60.505s --- internal/service/elb/backend_server_policy.go | 159 ++++---- .../service/elb/backend_server_policy_test.go | 367 +++++++----------- 2 files changed, 244 insertions(+), 282 deletions(-) diff --git a/internal/service/elb/backend_server_policy.go b/internal/service/elb/backend_server_policy.go index b904604da21..b58e5302889 100644 --- a/internal/service/elb/backend_server_policy.go +++ b/internal/service/elb/backend_server_policy.go @@ -3,69 +3,69 @@ package elb import ( "context" "fmt" + "log" "strconv" "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceBackendServerPolicy() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceBackendServerPolicyCreate, + CreateWithoutTimeout: resourceBackendServerPolicySet, ReadWithoutTimeout: resourceBackendServerPolicyRead, - UpdateWithoutTimeout: resourceBackendServerPolicyCreate, + UpdateWithoutTimeout: resourceBackendServerPolicySet, DeleteWithoutTimeout: resourceBackendServerPolicyDelete, Schema: map[string]*schema.Schema{ + "instance_port": { + Type: schema.TypeInt, + Required: true, + }, "load_balancer_name": { Type: schema.TypeString, Required: true, }, - "policy_names": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, - Set: schema.HashString, - }, - - "instance_port": { - Type: schema.TypeInt, - Required: true, }, }, } } -func resourceBackendServerPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceBackendServerPolicySet(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName := d.Get("load_balancer_name") - - policyNames := []*string{} - if v, ok := d.GetOk("policy_names"); ok { - policyNames = flex.ExpandStringSet(v.(*schema.Set)) + instancePort := d.Get("instance_port").(int) + lbName := d.Get("load_balancer_name").(string) + id := BackendServerPolicyCreateResourceID(lbName, instancePort) + input := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + InstancePort: aws.Int64(int64(instancePort)), + LoadBalancerName: aws.String(lbName), } - setOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ - LoadBalancerName: aws.String(loadBalancerName.(string)), - InstancePort: aws.Int64(int64(d.Get("instance_port").(int))), - PolicyNames: policyNames, + if v, ok := d.GetOk("policy_names"); ok && v.(*schema.Set).Len() > 0 { + input.PolicyNames = flex.ExpandStringSet(v.(*schema.Set)) } - if _, err := conn.SetLoadBalancerPoliciesForBackendServerWithContext(ctx, setOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting LoadBalancerPoliciesForBackendServer: %s", err) + _, err := conn.SetLoadBalancerPoliciesForBackendServerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic Backend Server Policy (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s:%s", *setOpts.LoadBalancerName, strconv.FormatInt(*setOpts.InstancePort, 10))) + d.SetId(id) + return append(diags, resourceBackendServerPolicyRead(ctx, d, meta)...) } @@ -73,46 +73,27 @@ func resourceBackendServerPolicyRead(ctx context.Context, d *schema.ResourceData var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, instancePort := BackendServerPoliciesParseID(d.Id()) - - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - } - - describeResp, err := conn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) + lbName, instancePort, err := BackendServerPolicyParseResourceID(d.Id()) if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "LoadBalancerNotFound" { - d.SetId("") - return sdkdiag.AppendErrorf(diags, "LoadBalancerNotFound: %s", err) - } - } - return sdkdiag.AppendErrorf(diags, "retrieving ELB description: %s", err) - } - - if len(describeResp.LoadBalancerDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - lb := describeResp.LoadBalancerDescriptions[0] + policyNames, err := FindLoadBalancerBackendServerPolicyByTwoPartKey(ctx, conn, lbName, instancePort) - policyNames := []*string{} - for _, backendServer := range lb.BackendServerDescriptions { - if instancePort != strconv.Itoa(int(aws.Int64Value(backendServer.InstancePort))) { - continue - } - - policyNames = append(policyNames, backendServer.PolicyNames...) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic Backend Server Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - d.Set("load_balancer_name", loadBalancerName) - instancePortVal, err := strconv.ParseInt(instancePort, 10, 64) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing instance port: %s", err) + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Backend Server Policy (%s): %s", d.Id(), err) } - d.Set("instance_port", instancePortVal) - d.Set("policy_names", flex.FlattenStringList(policyNames)) + + d.Set("instance_port", instancePort) + d.Set("load_balancer_name", lbName) + d.Set("policy_names", policyNames) return diags } @@ -121,27 +102,73 @@ func resourceBackendServerPolicyDelete(ctx context.Context, d *schema.ResourceDa var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, instancePort := BackendServerPoliciesParseID(d.Id()) + lbName, instancePort, err := BackendServerPolicyParseResourceID(d.Id()) - instancePortInt, err := strconv.ParseInt(instancePort, 10, 64) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing instancePort as integer: %s", err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - setOpts := &elb.SetLoadBalancerPoliciesForBackendServerInput{ - LoadBalancerName: aws.String(loadBalancerName), - InstancePort: aws.Int64(instancePortInt), - PolicyNames: []*string{}, + input := &elb.SetLoadBalancerPoliciesForBackendServerInput{ + InstancePort: aws.Int64(int64(instancePort)), + LoadBalancerName: aws.String(lbName), + PolicyNames: aws.StringSlice([]string{}), } - if _, err := conn.SetLoadBalancerPoliciesForBackendServerWithContext(ctx, setOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting LoadBalancerPoliciesForBackendServer: %s", err) + log.Printf("[DEBUG] Deleting ELB Classic Backend Server Policy: %s", d.Id()) + _, err = conn.SetLoadBalancerPoliciesForBackendServerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic Backend Server Policy (%s): %s", d.Id(), err) } return diags } -func BackendServerPoliciesParseID(id string) (string, string) { - parts := strings.SplitN(id, ":", 2) - return parts[0], parts[1] +func FindLoadBalancerBackendServerPolicyByTwoPartKey(ctx context.Context, conn *elb.ELB, lbName string, instancePort int) ([]string, error) { + lb, err := FindLoadBalancerByName(ctx, conn, lbName) + + if err != nil { + return nil, err + } + + var policyNames []string + + for _, v := range lb.BackendServerDescriptions { + if v == nil { + continue + } + + if aws.Int64Value(v.InstancePort) != int64(instancePort) { + continue + } + + policyNames = append(policyNames, aws.StringValueSlice(v.PolicyNames)...) + } + + return policyNames, nil +} + +const backendServerPolicyResourceIDSeparator = ":" + +func BackendServerPolicyCreateResourceID(lbName string, instancePort int) string { + parts := []string{lbName, strconv.Itoa(instancePort)} + id := strings.Join(parts, backendServerPolicyResourceIDSeparator) + + return id +} + +func BackendServerPolicyParseResourceID(id string) (string, int, error) { + parts := strings.Split(id, backendServerPolicyResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + v, err := strconv.Atoi(parts[1]) + + if err != nil { + return "", 0, err + } + + return parts[0], v, nil + } + + return "", 0, fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sINSTANCEPORT", id, backendServerPolicyResourceIDSeparator) } diff --git a/internal/service/elb/backend_server_policy_test.go b/internal/service/elb/backend_server_policy_test.go index 395da930b79..5e7c5af6a43 100644 --- a/internal/service/elb/backend_server_policy_test.go +++ b/internal/service/elb/backend_server_policy_test.go @@ -5,27 +5,54 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBBackendServerPolicy_basic(t *testing.T) { ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) privateKey1 := acctest.TLSRSAPrivateKeyPEM(t, 2048) + publicKey1 := acctest.TLSRSAPublicKeyPEM(t, privateKey1) privateKey2 := acctest.TLSRSAPrivateKeyPEM(t, 2048) + publicKey2 := acctest.TLSRSAPublicKeyPEM(t, privateKey2) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, privateKey1, "example.com") + resourceName := "aws_load_balancer_backend_server_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckBackendServerPolicyDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccBackendServerPolicyConfig_basic(rName, privateKey1, certificate, publicKey1, publicKey2), + Check: resource.ComposeTestCheckFunc( + testAccCheckBackendServerPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "instance_port", "443"), + resource.TestCheckResourceAttr(resourceName, "policy_names.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "policy_names.*", "aws_load_balancer_policy.test1", "policy_name"), + ), + }, + }, + }) +} + +func TestAccELBBackendServerPolicy_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + privateKey1 := acctest.TLSRSAPrivateKeyPEM(t, 2048) publicKey1 := acctest.TLSRSAPublicKeyPEM(t, privateKey1) + privateKey2 := acctest.TLSRSAPrivateKeyPEM(t, 2048) publicKey2 := acctest.TLSRSAPublicKeyPEM(t, privateKey2) - certificate1 := acctest.TLSRSAX509SelfSignedCertificatePEM(t, privateKey1, "example.com") - rString := sdkacctest.RandString(8) - lbName := fmt.Sprintf("tf-acc-lb-bsp-basic-%s", rString) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, privateKey1, "example.com") + resourceName := "aws_load_balancer_backend_server_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -34,141 +61,116 @@ func TestAccELBBackendServerPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckBackendServerPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccBackendServerPolicyConfig_basic0(lbName, privateKey1, publicKey1, certificate1), + Config: testAccBackendServerPolicyConfig_basic(rName, privateKey1, certificate, publicKey1, publicKey2), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy0"), - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.test-backend-auth-policy0"), - testAccCheckBackendServerPolicyState(ctx, lbName, "test-backend-auth-policy0", true), + testAccCheckBackendServerPolicyExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceBackendServerPolicy(), resourceName), ), + ExpectNonEmptyPlan: true, }, + }, + }) +} + +func TestAccELBBackendServerPolicy_update(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + privateKey1 := acctest.TLSRSAPrivateKeyPEM(t, 2048) + publicKey1 := acctest.TLSRSAPublicKeyPEM(t, privateKey1) + privateKey2 := acctest.TLSRSAPrivateKeyPEM(t, 2048) + publicKey2 := acctest.TLSRSAPublicKeyPEM(t, privateKey2) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, privateKey1, "example.com") + resourceName := "aws_load_balancer_backend_server_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckBackendServerPolicyDestroy(ctx), + Steps: []resource.TestStep{ { - Config: testAccBackendServerPolicyConfig_basic1(lbName, privateKey1, publicKey1, certificate1, publicKey2), + Config: testAccBackendServerPolicyConfig_basic(rName, privateKey1, certificate, publicKey1, publicKey2), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy0"), - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.test-pubkey-policy1"), - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.test-backend-auth-policy0"), - testAccCheckBackendServerPolicyState(ctx, lbName, "test-backend-auth-policy0", true), + testAccCheckBackendServerPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "instance_port", "443"), + resource.TestCheckResourceAttr(resourceName, "policy_names.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "policy_names.*", "aws_load_balancer_policy.test1", "policy_name"), ), }, { - Config: testAccBackendServerPolicyConfig_basic2(lbName, privateKey1, certificate1), + Config: testAccBackendServerPolicyConfig_update(rName, privateKey1, certificate, publicKey1, publicKey2), Check: resource.ComposeTestCheckFunc( - testAccCheckBackendServerPolicyState(ctx, lbName, "test-backend-auth-policy0", false), + testAccCheckBackendServerPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "instance_port", "443"), + resource.TestCheckResourceAttr(resourceName, "policy_names.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "policy_names.*", "aws_load_balancer_policy.test3", "policy_name"), ), }, }, }) } -func policyInBackendServerPolicies(str string, list []string) bool { - for _, v := range list { - if v == str { - return true - } - } - return false -} - func testAccCheckBackendServerPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() for _, rs := range s.RootModule().Resources { - switch { - case rs.Type == "aws_load_balancer_policy": - loadBalancerName, policyName := tfelb.BackendServerPoliciesParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, - }) - if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { - continue - } - return err - } - if len(out.PolicyDescriptions) > 0 { - return fmt.Errorf("Policy still exists") - } - case rs.Type == "aws_load_balancer_backend_policy": - loadBalancerName, policyName := tfelb.BackendServerPoliciesParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - }) - - if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - continue - } - - if err != nil { - return err - } - - for _, backendServer := range out.LoadBalancerDescriptions[0].BackendServerDescriptions { - policyStrings := []string{} - for _, pol := range backendServer.PolicyNames { - policyStrings = append(policyStrings, *pol) - } - if policyInBackendServerPolicies(policyName, policyStrings) { - return fmt.Errorf("Policy still exists and is assigned") - } - } - default: + if rs.Type != "aws_load_balancer_backend_policy" { continue } - } - return nil - } -} -func testAccCheckBackendServerPolicyState(ctx context.Context, loadBalancerName string, loadBalancerBackendAuthPolicyName string, assigned bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + lbName, instancePort, err := tfelb.BackendServerPolicyParseResourceID(rs.Primary.ID) - loadBalancerDescription, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - }) - if err != nil { - return err - } + if err != nil { + return err + } + + _, err = tfelb.FindLoadBalancerBackendServerPolicyByTwoPartKey(ctx, conn, lbName, instancePort) - for _, backendServer := range loadBalancerDescription.LoadBalancerDescriptions[0].BackendServerDescriptions { - policyStrings := []string{} - for _, pol := range backendServer.PolicyNames { - policyStrings = append(policyStrings, *pol) + if tfresource.NotFound(err) { + continue } - if policyInBackendServerPolicies(loadBalancerBackendAuthPolicyName, policyStrings) != assigned { - if assigned { - return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerBackendAuthPolicyName, policyStrings) - } else { - return fmt.Errorf("Policy exists and is assigned") - } + + if err != nil { + return err } + + return fmt.Errorf("ELB Classic Backend Server Policy %s still exists", rs.Primary.ID) } return nil } } -func testAccBackendServerPolicyConfig_basic0(rName, privateKey, publicKey, certificate string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" +func testAccCheckBackendServerPolicyExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} + if rs.Primary.ID == "" { + return fmt.Errorf("No ELB Classic Backend Server Policy ID is set") + } -resource "aws_iam_server_certificate" "test-iam-cert0" { - name_prefix = "test_cert_" - certificate_body = "%[2]s" - private_key = "%[3]s" + lbName, instancePort, err := tfelb.BackendServerPolicyParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + + _, err = tfelb.FindLoadBalancerBackendServerPolicyByTwoPartKey(ctx, conn, lbName, instancePort) + + return err + } } -resource "aws_elb" "test-lb" { - name = "%[1]s" +func testAccBackendServerPolicyConfig_base(rName, privateKey, certificate, publicKey1, publicKey2 string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +resource "aws_elb" "test" { + name = %[1]q availability_zones = [data.aws_availability_zones.available.names[0]] listener { @@ -176,157 +178,90 @@ resource "aws_elb" "test-lb" { instance_protocol = "https" lb_port = 443 lb_protocol = "https" - ssl_certificate_id = aws_iam_server_certificate.test-iam-cert0.arn + ssl_certificate_id = aws_iam_server_certificate.test.arn } +} - tags = { - Name = "tf-acc-test" - } +resource "aws_iam_server_certificate" "test" { + name = %[1]q + certificate_body = "%[2]s" + private_key = "%[3]s" } -resource "aws_load_balancer_policy" "test-pubkey-policy0" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "test-pubkey-policy0" +resource "aws_load_balancer_policy" "test0" { + load_balancer_name = aws_elb.test.name + policy_name = "%[1]s-0" policy_type_name = "PublicKeyPolicyType" policy_attribute { name = "PublicKey" - value = replace(replace(replace("%[4]s", "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "") + value = "%[4]s" } } -resource "aws_load_balancer_policy" "test-backend-auth-policy0" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "test-backend-auth-policy0" +resource "aws_load_balancer_policy" "test1" { + load_balancer_name = aws_elb.test.name + policy_name = "%[1]s-1" policy_type_name = "BackendServerAuthenticationPolicyType" policy_attribute { name = "PublicKeyPolicyName" - value = aws_load_balancer_policy.test-pubkey-policy0.policy_name - } -} - -resource "aws_load_balancer_backend_server_policy" "test-backend-auth-policies-443" { - load_balancer_name = aws_elb.test-lb.name - instance_port = 443 - - policy_names = [ - aws_load_balancer_policy.test-backend-auth-policy0.policy_name, - ] -} -`, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(privateKey), acctest.TLSPEMEscapeNewlines(publicKey)) -} - -func testAccBackendServerPolicyConfig_basic1(rName, privateKey1, publicKey1, certificate1, publicKey2 string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_iam_server_certificate" "test-iam-cert0" { - name_prefix = "test_cert_" - certificate_body = "%[2]s" - private_key = "%[3]s" -} - -resource "aws_elb" "test-lb" { - name = "%[1]s" - availability_zones = [data.aws_availability_zones.available.names[0]] - - listener { - instance_port = 443 - instance_protocol = "https" - lb_port = 443 - lb_protocol = "https" - ssl_certificate_id = aws_iam_server_certificate.test-iam-cert0.arn - } - - tags = { - Name = "tf-acc-test" - } -} - -resource "aws_load_balancer_policy" "test-pubkey-policy0" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "test-pubkey-policy0" - policy_type_name = "PublicKeyPolicyType" - - policy_attribute { - name = "PublicKey" - value = replace(replace(replace("%[4]s", "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "") + value = aws_load_balancer_policy.test0.policy_name } } -resource "aws_load_balancer_policy" "test-pubkey-policy1" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "test-pubkey-policy1" +resource "aws_load_balancer_policy" "test2" { + load_balancer_name = aws_elb.test.name + policy_name = "%[1]s-2" policy_type_name = "PublicKeyPolicyType" policy_attribute { name = "PublicKey" - value = replace(replace(replace("%[5]s", "\n", ""), "-----BEGIN PUBLIC KEY-----", ""), "-----END PUBLIC KEY-----", "") + value = "%[5]s" } } -resource "aws_load_balancer_policy" "test-backend-auth-policy0" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "test-backend-auth-policy0" +resource "aws_load_balancer_policy" "test3" { + load_balancer_name = aws_elb.test.name + policy_name = "%[1]s-3" policy_type_name = "BackendServerAuthenticationPolicyType" policy_attribute { name = "PublicKeyPolicyName" - value = aws_load_balancer_policy.test-pubkey-policy1.policy_name + value = aws_load_balancer_policy.test2.policy_name } } +`, + rName, + acctest.TLSPEMEscapeNewlines(certificate), + acctest.TLSPEMEscapeNewlines(privateKey), + acctest.TLSPEMRemovePublicKeyEncapsulationBoundaries(acctest.TLSPEMRemoveNewlines(publicKey1)), + acctest.TLSPEMRemovePublicKeyEncapsulationBoundaries(acctest.TLSPEMRemoveNewlines(publicKey2)), + )) +} -resource "aws_load_balancer_backend_server_policy" "test-backend-auth-policies-443" { - load_balancer_name = aws_elb.test-lb.name +func testAccBackendServerPolicyConfig_basic(rName, privateKey, certificate, publicKey1, publicKey2 string) string { + return acctest.ConfigCompose(testAccBackendServerPolicyConfig_base(rName, privateKey, certificate, publicKey1, publicKey2), ` +resource "aws_load_balancer_backend_server_policy" "test" { + load_balancer_name = aws_elb.test.name instance_port = 443 policy_names = [ - aws_load_balancer_policy.test-backend-auth-policy0.policy_name, + aws_load_balancer_policy.test1.policy_name, ] } -`, rName, acctest.TLSPEMEscapeNewlines(certificate1), acctest.TLSPEMEscapeNewlines(privateKey1), acctest.TLSPEMEscapeNewlines(publicKey1), acctest.TLSPEMEscapeNewlines(publicKey2)) -} - -func testAccBackendServerPolicyConfig_basic2(rName, privateKey, certificate string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_iam_server_certificate" "test-iam-cert0" { - name_prefix = "test_cert_" - certificate_body = "%[2]s" - private_key = "%[3]s" +`) } -resource "aws_elb" "test-lb" { - name = "%[1]s" - availability_zones = [data.aws_availability_zones.available.names[0]] - - listener { - instance_port = 443 - instance_protocol = "https" - lb_port = 443 - lb_protocol = "https" - ssl_certificate_id = aws_iam_server_certificate.test-iam-cert0.arn - } +func testAccBackendServerPolicyConfig_update(rName, privateKey, certificate, publicKey1, publicKey2 string) string { + return acctest.ConfigCompose(testAccBackendServerPolicyConfig_base(rName, privateKey, certificate, publicKey1, publicKey2), ` +resource "aws_load_balancer_backend_server_policy" "test" { + load_balancer_name = aws_elb.test.name + instance_port = 443 - tags = { - Name = "tf-acc-test" - } + policy_names = [ + aws_load_balancer_policy.test3.policy_name, + ] } -`, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(privateKey)) +`) } From 3bbd12cb9b7c411c561a5863a8c8513e10e48b3a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 16:50:19 -0500 Subject: [PATCH 62/68] r/aws_lb_cookie_stickiness_policy: Use 'FindLoadBalancerListenerPolicyByThreePartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBCookieStickinessPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBCookieStickinessPolicy_ -timeout 180m === RUN TestAccELBCookieStickinessPolicy_basic === PAUSE TestAccELBCookieStickinessPolicy_basic === RUN TestAccELBCookieStickinessPolicy_disappears === PAUSE TestAccELBCookieStickinessPolicy_disappears === RUN TestAccELBCookieStickinessPolicy_Disappears_elb === PAUSE TestAccELBCookieStickinessPolicy_Disappears_elb === CONT TestAccELBCookieStickinessPolicy_basic === CONT TestAccELBCookieStickinessPolicy_disappears === CONT TestAccELBCookieStickinessPolicy_Disappears_elb --- PASS: TestAccELBCookieStickinessPolicy_Disappears_elb (20.21s) --- PASS: TestAccELBCookieStickinessPolicy_disappears (22.27s) --- PASS: TestAccELBCookieStickinessPolicy_basic (35.50s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 41.217s --- .../elb/app_cookie_stickiness_policy.go | 2 +- .../elb/lb_cookie_stickiness_policy.go | 222 +++++++----------- .../elb/lb_cookie_stickiness_policy_test.go | 185 +++++++-------- 3 files changed, 171 insertions(+), 238 deletions(-) diff --git a/internal/service/elb/app_cookie_stickiness_policy.go b/internal/service/elb/app_cookie_stickiness_policy.go index 6fa79409b98..883df48b405 100644 --- a/internal/service/elb/app_cookie_stickiness_policy.go +++ b/internal/service/elb/app_cookie_stickiness_policy.go @@ -96,7 +96,7 @@ func resourceAppCookieStickinessPolicyCreate(ctx context.Context, d *schema.Reso d.SetId(id) - return diags + return append(diags, resourceAppCookieStickinessPolicyRead(ctx, d, meta)...) } func resourceAppCookieStickinessPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { diff --git a/internal/service/elb/lb_cookie_stickiness_policy.go b/internal/service/elb/lb_cookie_stickiness_policy.go index 4b3106f39c2..56aae03c67d 100644 --- a/internal/service/elb/lb_cookie_stickiness_policy.go +++ b/internal/service/elb/lb_cookie_stickiness_policy.go @@ -2,7 +2,6 @@ package elb import ( "context" - "errors" "fmt" "log" "strconv" @@ -10,47 +9,42 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elb" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceCookieStickinessPolicy() *schema.Resource { return &schema.Resource{ - // There is no concept of "updating" an LB Stickiness policy in - // the AWS API. CreateWithoutTimeout: resourceCookieStickinessPolicyCreate, ReadWithoutTimeout: resourceCookieStickinessPolicyRead, DeleteWithoutTimeout: resourceCookieStickinessPolicyDelete, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, + "cookie_expiration_period": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "lb_port": { + Type: schema.TypeInt, Required: true, ForceNew: true, }, - "load_balancer": { Type: schema.TypeString, Required: true, ForceNew: true, }, - - "lb_port": { - Type: schema.TypeInt, + "name": { + Type: schema.TypeString, Required: true, ForceNew: true, }, - - "cookie_expiration_period": { - Type: schema.TypeInt, - Optional: true, - ForceNew: true, - ValidateFunc: validation.IntAtLeast(0), - }, }, } } @@ -59,101 +53,79 @@ func resourceCookieStickinessPolicyCreate(ctx context.Context, d *schema.Resourc var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - // Provision the LBStickinessPolicy - lbspOpts := &elb.CreateLBCookieStickinessPolicyInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - PolicyName: aws.String(d.Get("name").(string)), - } + lbName := d.Get("load_balancer").(string) + lbPort := d.Get("lb_port").(int) + policyName := d.Get("name").(string) + id := LBCookieStickinessPolicyCreateResourceID(lbName, lbPort, policyName) + { + input := &elb.CreateLBCookieStickinessPolicyInput{ + LoadBalancerName: aws.String(lbName), + PolicyName: aws.String(policyName), + } - if v := d.Get("cookie_expiration_period").(int); v > 0 { - lbspOpts.CookieExpirationPeriod = aws.Int64(int64(v)) - } + if v, ok := d.GetOk("cookie_expiration_period"); ok { + input.CookieExpirationPeriod = aws.Int64(int64(v.(int))) + } - log.Printf("[DEBUG] LB Cookie Stickiness Policy opts: %#v", lbspOpts) - if _, err := conn.CreateLBCookieStickinessPolicyWithContext(ctx, lbspOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "creating LBCookieStickinessPolicy: %s", err) - } + _, err := conn.CreateLBCookieStickinessPolicyWithContext(ctx, input) - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{aws.String(d.Get("name").(string))}, + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating ELB Classic LB Cookie Stickiness Policy (%s): %s", id, err) + } } - log.Printf("[DEBUG] LB Cookie Stickiness create configuration: %#v", setLoadBalancerOpts) - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting LBCookieStickinessPolicy: %s", err) + { + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{policyName}), + } + + _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic LB Cookie Stickiness Policy (%s): %s", id, err) + } } - d.SetId(fmt.Sprintf("%s:%d:%s", - *lbspOpts.LoadBalancerName, - *setLoadBalancerOpts.LoadBalancerPort, - *lbspOpts.PolicyName)) - return diags + d.SetId(id) + + return append(diags, resourceCookieStickinessPolicyRead(ctx, d, meta)...) } func resourceCookieStickinessPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbName, lbPort, policyName := CookieStickinessPolicyParseID(d.Id()) + lbName, lbPort, policyName, err := LBCookieStickinessPolicyParseResourceID(d.Id()) - request := &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(lbName), - PolicyNames: []*string{aws.String(policyName)}, - } - - getResp, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, request) if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) LB Cookie Policy (%s) not found, removing from state", lbName, policyName) - d.SetId("") - return diags - } - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) not found, removing from state", lbName) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "retrieving ELB Classic (%s) LB Cookie Policy (%s): %s", lbName, policyName, err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - if len(getResp.PolicyDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find policy %#v", getResp.PolicyDescriptions) + policy, err := FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic LB Cookie Stickiness Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - // we know the policy exists now, but we have to check if it's assigned to a listener - assigned, err := resourceSticknessPolicyAssigned(ctx, conn, policyName, lbName, lbPort) if err != nil { return sdkdiag.AppendErrorf(diags, "reading ELB Classic LB Cookie Stickiness Policy (%s): %s", d.Id(), err) } - if !d.IsNewResource() && !assigned { - // policy exists, but isn't assigned to a listener - log.Printf("[WARN] ELB Classic LB (%s) LB Cookie Policy (%s) exists, but isn't assigned to a listener", lbName, policyName) - d.SetId("") - return diags - } - // We can get away with this because there's only one attribute, the - // cookie expiration, in these descriptions. - policyDesc := getResp.PolicyDescriptions[0] - cookieAttr := policyDesc.PolicyAttributeDescriptions[0] - if aws.StringValue(cookieAttr.AttributeName) != "CookieExpirationPeriod" { - return sdkdiag.AppendErrorf(diags, "Unable to find cookie expiration period.") + if len(policy.PolicyAttributeDescriptions) != 1 || aws.StringValue(policy.PolicyAttributeDescriptions[0].AttributeName) != "CookieExpirationPeriod" { + return sdkdiag.AppendErrorf(diags, "cookie expiration period not found") } - cookieVal, err := strconv.Atoi(aws.StringValue(cookieAttr.AttributeValue)) - if err != nil { + if v, err := strconv.Atoi(aws.StringValue(policy.PolicyAttributeDescriptions[0].AttributeValue)); err != nil { return sdkdiag.AppendErrorf(diags, "parsing cookie expiration period: %s", err) + } else { + d.Set("cookie_expiration_period", v) } - d.Set("cookie_expiration_period", cookieVal) - - d.Set("name", policyName) + d.Set("lb_port", lbPort) d.Set("load_balancer", lbName) - lbPortInt, err := strconv.Atoi(lbPort) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ELB Classic LB Cookie Stickiness Policy (%s): parsing port number: %s", d.Id(), err) - } - d.Set("lb_port", lbPortInt) + d.Set("name", policyName) return diags } @@ -162,73 +134,61 @@ func resourceCookieStickinessPolicyDelete(ctx context.Context, d *schema.Resourc var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbName, _, policyName := CookieStickinessPolicyParseID(d.Id()) + lbName, lbPort, policyName, err := LBCookieStickinessPolicyParseResourceID(d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) + } // Perversely, if we Set an empty list of PolicyNames, we detach the // policies attached to a listener, which is required to delete the // policy itself. - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{}, + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{}), } - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "removing LBCookieStickinessPolicy: %s", err) + _, err = conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic LB Cookie Stickiness Policy (%s): %s", d.Id(), err) } - request := &elb.DeleteLoadBalancerPolicyInput{ + log.Printf("[DEBUG] Deleting ELB Classic LB Cookie Stickiness Policy: %s", d.Id()) + _, err = conn.DeleteLoadBalancerPolicyWithContext(ctx, &elb.DeleteLoadBalancerPolicyInput{ LoadBalancerName: aws.String(lbName), PolicyName: aws.String(policyName), - } + }) - if _, err := conn.DeleteLoadBalancerPolicyWithContext(ctx, request); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting LB stickiness policy %s: %s", d.Id(), err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting ELB Classic LB Cookie Stickiness Policy (%s): %s", d.Id(), err) } + return diags } -// Determine if a particular policy is assigned to an ELB listener -func resourceSticknessPolicyAssigned(ctx context.Context, conn *elb.ELB, policyName, lbName, lbPort string) (bool, error) { - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(lbName)}, - } - describeResp, err := conn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) +const lbCookieStickinessPolicyResourceIDSeparator = ":" - if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - return false, nil - } +func LBCookieStickinessPolicyCreateResourceID(lbName string, lbPort int, policyName string) string { + parts := []string{lbName, strconv.Itoa(lbPort), policyName} + id := strings.Join(parts, lbCookieStickinessPolicyResourceIDSeparator) - if err != nil { - return false, fmt.Errorf("retrieving LB: %s", err) - } + return id +} - if len(describeResp.LoadBalancerDescriptions) != 1 { - return false, errors.New("retrieving LB: empty response") - } +func LBCookieStickinessPolicyParseResourceID(id string) (string, int, string, error) { + parts := strings.Split(id, lbCookieStickinessPolicyResourceIDSeparator) - lb := describeResp.LoadBalancerDescriptions[0] - assigned := false - for _, listener := range lb.ListenerDescriptions { - if listener == nil || listener.Listener == nil || lbPort != strconv.Itoa(int(aws.Int64Value(listener.Listener.LoadBalancerPort))) { - continue - } + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + v, err := strconv.Atoi(parts[1]) - for _, v := range listener.PolicyNames { - if policyName == aws.StringValue(v) { - assigned = true - break - } + if err != nil { + return "", 0, "", err } - } - return assigned, nil -} + return parts[0], v, parts[2], nil + } -// CookieStickinessPolicyParseID takes an ID and parses it into -// it's constituent parts. You need three axes (LB name, policy name, and LB -// port) to create or identify a stickiness policy in AWS's API. -func CookieStickinessPolicyParseID(id string) (string, string, string) { - parts := strings.SplitN(id, ":", 3) - return parts[0], parts[1], parts[2] + return "", 0, "", fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sLBPORT%[2]sPOLICYNAME", id, lbCookieStickinessPolicyResourceIDSeparator) } diff --git a/internal/service/elb/lb_cookie_stickiness_policy_test.go b/internal/service/elb/lb_cookie_stickiness_policy_test.go index 8001e835763..8b578e10e7b 100644 --- a/internal/service/elb/lb_cookie_stickiness_policy_test.go +++ b/internal/service/elb/lb_cookie_stickiness_policy_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -14,12 +12,14 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBCookieStickinessPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) - resourceName := "aws_lb_cookie_stickiness_policy.foo" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb_cookie_stickiness_policy.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), @@ -27,89 +27,29 @@ func TestAccELBCookieStickinessPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckLBCookieStickinessPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLBCookieStickinessPolicyConfig_basic(lbName), + Config: testAccLBCookieStickinessPolicyConfig_basic(rName, 300), Check: resource.ComposeTestCheckFunc( + testAccCheckLBCookieStickinessPolicyExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "cookie_expiration_period", "300"), - testAccCheckLBCookieStickinessPolicy(ctx, "aws_elb.lb", - "aws_lb_cookie_stickiness_policy.foo", - ), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, { - Config: testAccLBCookieStickinessPolicyConfig_update(lbName), + Config: testAccLBCookieStickinessPolicyConfig_basic(rName, 0), Check: resource.ComposeTestCheckFunc( + testAccCheckLBCookieStickinessPolicyExists(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "cookie_expiration_period", "0"), - testAccCheckLBCookieStickinessPolicy(ctx, "aws_elb.lb", - "aws_lb_cookie_stickiness_policy.foo", - ), + resource.TestCheckResourceAttr(resourceName, "name", rName), ), }, }, }) } -func testAccCheckLBCookieStickinessPolicyDestroy(ctx context.Context) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lb_cookie_stickiness_policy" { - continue - } - - lbName, _, policyName := tfelb.CookieStickinessPolicyParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(lbName), - PolicyNames: []*string{aws.String(policyName)}, - }) - if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { - continue - } - return err - } - - if len(out.PolicyDescriptions) > 0 { - return fmt.Errorf("Policy still exists") - } - } - - return nil - } -} - -func testAccCheckLBCookieStickinessPolicy(ctx context.Context, elbResource string, policyResource string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[elbResource] - if !ok { - return fmt.Errorf("Not found: %s", elbResource) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - policy, ok := s.RootModule().Resources[policyResource] - if !ok { - return fmt.Errorf("Not found: %s", policyResource) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - elbName, _, policyName := tfelb.CookieStickinessPolicyParseID(policy.Primary.ID) - _, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(elbName), - PolicyNames: []*string{aws.String(policyName)}, - }) - - return err - } -} - func TestAccELBCookieStickinessPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) - elbResourceName := "aws_elb.lb" - resourceName := "aws_lb_cookie_stickiness_policy.foo" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb_cookie_stickiness_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -118,9 +58,9 @@ func TestAccELBCookieStickinessPolicy_disappears(t *testing.T) { CheckDestroy: testAccCheckLBCookieStickinessPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLBCookieStickinessPolicyConfig_basic(lbName), + Config: testAccLBCookieStickinessPolicyConfig_basic(rName, 300), Check: resource.ComposeTestCheckFunc( - testAccCheckLBCookieStickinessPolicy(ctx, elbResourceName, resourceName), + testAccCheckLBCookieStickinessPolicyExists(ctx, resourceName), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceCookieStickinessPolicy(), resourceName), ), ExpectNonEmptyPlan: true, @@ -131,9 +71,9 @@ func TestAccELBCookieStickinessPolicy_disappears(t *testing.T) { func TestAccELBCookieStickinessPolicy_Disappears_elb(t *testing.T) { ctx := acctest.Context(t) - lbName := fmt.Sprintf("tf-test-lb-%s", sdkacctest.RandString(5)) - elbResourceName := "aws_elb.lb" - resourceName := "aws_lb_cookie_stickiness_policy.foo" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lb_cookie_stickiness_policy.test" + elbResourceName := "aws_elb.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -142,9 +82,9 @@ func TestAccELBCookieStickinessPolicy_Disappears_elb(t *testing.T) { CheckDestroy: testAccCheckLBCookieStickinessPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccLBCookieStickinessPolicyConfig_basic(lbName), + Config: testAccLBCookieStickinessPolicyConfig_basic(rName, 300), Check: resource.ComposeTestCheckFunc( - testAccCheckLBCookieStickinessPolicy(ctx, elbResourceName, resourceName), + testAccCheckLBCookieStickinessPolicyExists(ctx, resourceName), acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceLoadBalancer(), elbResourceName), ), ExpectNonEmptyPlan: true, @@ -153,34 +93,67 @@ func TestAccELBCookieStickinessPolicy_Disappears_elb(t *testing.T) { }) } -func testAccLBCookieStickinessPolicyConfig_basic(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "lb" { - name = "%s" - availability_zones = [data.aws_availability_zones.available.names[0]] +func testAccCheckLBCookieStickinessPolicyDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - listener { - instance_port = 8000 - instance_protocol = "http" - lb_port = 80 - lb_protocol = "http" - } -} + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lb_cookie_stickiness_policy" { + continue + } -resource "aws_lb_cookie_stickiness_policy" "foo" { - name = "foo-policy" - load_balancer = aws_elb.lb.id - lb_port = 80 - cookie_expiration_period = 300 + lbName, lbPort, policyName, err := tfelb.LBCookieStickinessPolicyParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("ELB Classic LB Cookie Stickiness Policy %s still exists", rs.Primary.ID) + } + + return nil + } } -`, rName)) + +func testAccCheckLBCookieStickinessPolicyExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ELB Classic LB Cookie Stickiness Policy ID is set") + } + + lbName, lbPort, policyName, err := tfelb.LBCookieStickinessPolicyParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + return err + } } -// Sets the cookie_expiration_period to 0s. -func testAccLBCookieStickinessPolicyConfig_update(rName string) string { +func testAccLBCookieStickinessPolicyConfig_basic(rName string, expirationPeriod int) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "lb" { - name = "%s" +resource "aws_elb" "test" { + name = %[1]q availability_zones = [data.aws_availability_zones.available.names[0]] listener { @@ -191,11 +164,11 @@ resource "aws_elb" "lb" { } } -resource "aws_lb_cookie_stickiness_policy" "foo" { - name = "foo-policy" - load_balancer = aws_elb.lb.id +resource "aws_lb_cookie_stickiness_policy" "test" { + name = %[1]q + load_balancer = aws_elb.test.id lb_port = 80 - cookie_expiration_period = 0 + cookie_expiration_period = %[2]d } -`, rName)) +`, rName, expirationPeriod)) } From a901ed03c164d9172b8622f83a2d2fae3a2d99d5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 17:14:54 -0500 Subject: [PATCH 63/68] r/aws_lb_ssl_negotiation_policy: Use 'FindLoadBalancerListenerPolicyByThreePartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBSSLNegotiationPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBSSLNegotiationPolicy_ -timeout 180m === RUN TestAccELBSSLNegotiationPolicy_basic === PAUSE TestAccELBSSLNegotiationPolicy_basic === RUN TestAccELBSSLNegotiationPolicy_disappears === PAUSE TestAccELBSSLNegotiationPolicy_disappears === CONT TestAccELBSSLNegotiationPolicy_basic === CONT TestAccELBSSLNegotiationPolicy_disappears --- PASS: TestAccELBSSLNegotiationPolicy_disappears (30.06s) --- PASS: TestAccELBSSLNegotiationPolicy_basic (34.55s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 39.662s --- .../service/elb/lb_ssl_negotiation_policy.go | 199 +++++++++--------- .../elb/lb_ssl_negotiation_policy_test.go | 164 ++++----------- 2 files changed, 134 insertions(+), 229 deletions(-) diff --git a/internal/service/elb/lb_ssl_negotiation_policy.go b/internal/service/elb/lb_ssl_negotiation_policy.go index 1a482bdd4f7..98605224409 100644 --- a/internal/service/elb/lb_ssl_negotiation_policy.go +++ b/internal/service/elb/lb_ssl_negotiation_policy.go @@ -1,7 +1,6 @@ package elb import ( - "bytes" "context" "fmt" "log" @@ -10,41 +9,20 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elb" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceSSLNegotiationPolicy() *schema.Resource { return &schema.Resource{ - // There is no concept of "updating" an LB policy in - // the AWS API. CreateWithoutTimeout: resourceSSLNegotiationPolicyCreate, ReadWithoutTimeout: resourceSSLNegotiationPolicyRead, DeleteWithoutTimeout: resourceSSLNegotiationPolicyDelete, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "load_balancer": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "lb_port": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - }, - "attribute": { Type: schema.TypeSet, Optional: true, @@ -55,19 +33,27 @@ func ResourceSSLNegotiationPolicy() *schema.Resource { Type: schema.TypeString, Required: true, }, - "value": { Type: schema.TypeString, Required: true, }, }, }, - Set: func(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["name"].(string))) - return create.StringHashcode(buf.String()) - }, + }, + "lb_port": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "load_balancer": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, }, } @@ -77,77 +63,73 @@ func resourceSSLNegotiationPolicyCreate(ctx context.Context, d *schema.ResourceD var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - // Provision the SSLNegotiationPolicy - lbspOpts := &elb.CreateLoadBalancerPolicyInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - PolicyName: aws.String(d.Get("name").(string)), - PolicyTypeName: aws.String("SSLNegotiationPolicyType"), - } + lbName := d.Get("load_balancer").(string) + lbPort := d.Get("lb_port").(int) + policyName := d.Get("name").(string) + id := SSLNegotiationPolicyCreateResourceID(lbName, lbPort, policyName) - // Check for Policy Attributes - if v, ok := d.GetOk("attribute"); ok { - // Expand the "attribute" set to aws-sdk-go compat []*elb.PolicyAttribute - lbspOpts.PolicyAttributes = ExpandPolicyAttributes(v.(*schema.Set).List()) - } + { + input := &elb.CreateLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(lbName), + PolicyName: aws.String(policyName), + PolicyTypeName: aws.String("SSLNegotiationPolicyType"), + } - log.Printf("[DEBUG] Load Balancer Policy opts: %#v", lbspOpts) - if _, err := conn.CreateLoadBalancerPolicyWithContext(ctx, lbspOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "creating Load Balancer Policy: %s", err) - } + if v, ok := d.GetOk("attribute"); ok && v.(*schema.Set).Len() > 0 { + input.PolicyAttributes = ExpandPolicyAttributes(v.(*schema.Set).List()) + } + + _, err := conn.CreateLoadBalancerPolicyWithContext(ctx, input) - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{aws.String(d.Get("name").(string))}, + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating ELB Classic SSL Negotiation Policy (%s): %s", id, err) + } } - log.Printf("[DEBUG] SSL Negotiation create configuration: %#v", setLoadBalancerOpts) - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting SSLNegotiationPolicy: %s", err) + { + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{policyName}), + } + + _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic SSL Negotiation Policy (%s): %s", id, err) + } } - d.SetId(fmt.Sprintf("%s:%d:%s", - *lbspOpts.LoadBalancerName, - *setLoadBalancerOpts.LoadBalancerPort, - *lbspOpts.PolicyName)) - return diags + d.SetId(id) + + return append(diags, resourceSSLNegotiationPolicyRead(ctx, d, meta)...) } func resourceSSLNegotiationPolicyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbName, lbPort, policyName, err := SSLNegotiationPolicyParseID(d.Id()) + lbName, lbPort, policyName, err := SSLNegotiationPolicyParseResourceID(d.Id()) + if err != nil { - return sdkdiag.AppendErrorf(diags, "reading ELB Classic (%s) SSL Negotiation Policy: %s", lbName, err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - request := &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(lbName), - PolicyNames: []*string{aws.String(policyName)}, - } + _, err = FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) - getResp, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, request) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) policy (%s) not found, removing from state", lbName, policyName) - d.SetId("") - return diags - } else if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) not found, removing from state", lbName) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading ELB Classic (%s) SSL Negotiation Policy: %s", lbName, err) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic SSL Negotiation Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - if len(getResp.PolicyDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find policy %#v", getResp.PolicyDescriptions) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ELB Classic SSL Negotiation Policy (%s): %s", d.Id(), err) } - d.Set("name", policyName) - d.Set("load_balancer", lbName) d.Set("lb_port", lbPort) + d.Set("load_balancer", lbName) + d.Set("name", policyName) // TODO: fix attribute // This was previously erroneously setting "attributes", however this cannot @@ -171,49 +153,60 @@ func resourceSSLNegotiationPolicyDelete(ctx context.Context, d *schema.ResourceD var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbName, _, policyName, err := SSLNegotiationPolicyParseID(d.Id()) + lbName, lbPort, policyName, err := SSLNegotiationPolicyParseResourceID(d.Id()) + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting ELB Classic SSL Negotiation Policy (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } // Perversely, if we Set an empty list of PolicyNames, we detach the // policies attached to a listener, which is required to delete the // policy itself. - setLoadBalancerOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(d.Get("load_balancer").(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("lb_port").(int))), - PolicyNames: []*string{}, + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{}), } - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setLoadBalancerOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "removing SSLNegotiationPolicy: %s", err) + _, err = conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic SSL Negotiation Policy (%s): %s", d.Id(), err) } - request := &elb.DeleteLoadBalancerPolicyInput{ + _, err = conn.DeleteLoadBalancerPolicyWithContext(ctx, &elb.DeleteLoadBalancerPolicyInput{ LoadBalancerName: aws.String(lbName), PolicyName: aws.String(policyName), - } + }) - if _, err := conn.DeleteLoadBalancerPolicyWithContext(ctx, request); err != nil { + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting ELB Classic SSL Negotiation Policy (%s): %s", d.Id(), err) } + return diags } -// SSLNegotiationPolicyParseID takes an ID and parses it into -// it's constituent parts. You need three axes (LB name, policy name, and LB -// port) to create or identify an SSL negotiation policy in AWS's API. -func SSLNegotiationPolicyParseID(id string) (string, int, string, error) { - const partCount = 3 - parts := strings.SplitN(id, ":", partCount) - if n := len(parts); n != partCount { - return "", 0, "", fmt.Errorf("incorrect format of SSL negotiation policy resource ID. Expected %d parts, got %d", partCount, n) - } +const sslNegotiationPolicyResourceIDSeparator = ":" - port, err := strconv.Atoi(parts[1]) - if err != nil { - return "", 0, "", fmt.Errorf("parsing SSL negotiation policy resource ID port: %w", err) +func SSLNegotiationPolicyCreateResourceID(lbName string, lbPort int, policyName string) string { + parts := []string{lbName, strconv.Itoa(lbPort), policyName} + id := strings.Join(parts, sslNegotiationPolicyResourceIDSeparator) + + return id +} + +func SSLNegotiationPolicyParseResourceID(id string) (string, int, string, error) { + parts := strings.Split(id, sslNegotiationPolicyResourceIDSeparator) + + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + v, err := strconv.Atoi(parts[1]) + + if err != nil { + return "", 0, "", err + } + + return parts[0], v, parts[2], nil } - return parts[0], port, parts[2], nil + return "", 0, "", fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sLBPORT%[2]sPOLICYNAME", id, sslNegotiationPolicyResourceIDSeparator) } diff --git a/internal/service/elb/lb_ssl_negotiation_policy_test.go b/internal/service/elb/lb_ssl_negotiation_policy_test.go index abd402abacb..258db074346 100644 --- a/internal/service/elb/lb_ssl_negotiation_policy_test.go +++ b/internal/service/elb/lb_ssl_negotiation_policy_test.go @@ -3,11 +3,8 @@ package elb_test import ( "context" "fmt" - "log" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -15,16 +12,15 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBSSLNegotiationPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(8)) // ELB name cannot be longer than 32 characters - elbResourceName := "aws_elb.test" - resourceName := "aws_lb_ssl_negotiation_policy.test" - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") + resourceName := "aws_lb_ssl_negotiation_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -35,7 +31,7 @@ func TestAccELBSSLNegotiationPolicy_basic(t *testing.T) { { Config: testAccLBSSLNegotiationPolicyConfig_basic(rName, key, certificate), Check: resource.ComposeTestCheckFunc( - testAccCheckLBSSLNegotiationPolicy(ctx, elbResourceName, resourceName), + testAccCheckLBSSLNegotiationPolicy(ctx, resourceName), resource.TestCheckResourceAttr(resourceName, "attribute.#", "7"), ), }, @@ -45,13 +41,10 @@ func TestAccELBSSLNegotiationPolicy_basic(t *testing.T) { func TestAccELBSSLNegotiationPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - var loadBalancer elb.LoadBalancerDescription - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(8)) // ELB name cannot be longer than 32 characters - elbResourceName := "aws_elb.test" - resourceName := "aws_lb_ssl_negotiation_policy.test" - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) key := acctest.TLSRSAPrivateKeyPEM(t, 2048) certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, "example.com") + resourceName := "aws_lb_ssl_negotiation_policy.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, @@ -62,9 +55,8 @@ func TestAccELBSSLNegotiationPolicy_disappears(t *testing.T) { { Config: testAccLBSSLNegotiationPolicyConfig_basic(rName, key, certificate), Check: resource.ComposeTestCheckFunc( - testAccCheckLBSSLNegotiationPolicy(ctx, elbResourceName, resourceName), - testAccCheckLoadBalancerExists(ctx, elbResourceName, &loadBalancer), - testAccCheckLoadBalancerDisappears(ctx, &loadBalancer), + testAccCheckLBSSLNegotiationPolicy(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceSSLNegotiationPolicy(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -77,148 +69,68 @@ func testAccCheckLBSSLNegotiationPolicyDestroy(ctx context.Context) resource.Tes conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_elb" && rs.Type != "aws_lb_ssl_negotiation_policy" { + if rs.Type != "aws_lb_ssl_negotiation_policy" { continue } - // Check that the ELB is destroyed - if rs.Type == "aws_elb" { - describe, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(rs.Primary.ID)}, - }) - - if err == nil { - if len(describe.LoadBalancerDescriptions) != 0 && - *describe.LoadBalancerDescriptions[0].LoadBalancerName == rs.Primary.ID { - return fmt.Errorf("ELB still exists") - } - } - - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { - return err - } - - if providerErr.Code() != "LoadBalancerNotFound" { - return fmt.Errorf("Unexpected error: %s", err) - } - } else { - // Check that the SSL Negotiation Policy is destroyed - elbName, _, policyName, err := tfelb.SSLNegotiationPolicyParseID(rs.Primary.ID) - if err != nil { - return err - } - - _, err = conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(elbName), - PolicyNames: []*string{aws.String(policyName)}, - }) - - if err == nil { - return fmt.Errorf("ELB SSL Negotiation Policy still exists") - } + lbName, lbPort, policyName, err := tfelb.SSLNegotiationPolicyParseResourceID(rs.Primary.ID) + + if err != nil { + return err + } + + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) + + if tfresource.NotFound(err) { + continue } + + if err != nil { + return err + } + + return fmt.Errorf("ELB Classic SSL Negotiation Policy %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckLBSSLNegotiationPolicy(ctx context.Context, elbResource string, policyResource string) resource.TestCheckFunc { +func testAccCheckLBSSLNegotiationPolicy(ctx context.Context, n string) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[elbResource] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", elbResource) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - policy, ok := s.RootModule().Resources[policyResource] - if !ok { - return fmt.Errorf("Not found: %s", policyResource) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - elbName, _, policyName, err := tfelb.SSLNegotiationPolicyParseID(policy.Primary.ID) - if err != nil { - return err + return fmt.Errorf("No ELB Classic SSL Negotiation Policy ID is set") } - resp, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(elbName), - PolicyNames: []*string{aws.String(policyName)}, - }) + lbName, lbPort, policyName, err := tfelb.SSLNegotiationPolicyParseResourceID(rs.Primary.ID) if err != nil { - log.Printf("[ERROR] Problem describing load balancer policy '%s': %s", policyName, err) return err } - if len(resp.PolicyDescriptions) != 1 { - return fmt.Errorf("Unable to find policy %#v", resp.PolicyDescriptions) - } - - attrmap := policyAttributesToMap(&resp.PolicyDescriptions[0].PolicyAttributeDescriptions) - if attrmap["Protocol-TLSv1"] != "false" { - return fmt.Errorf("Policy attribute 'Protocol-TLSv1' was of value %s instead of false!", attrmap["Protocol-TLSv1"]) - } - if attrmap["Protocol-TLSv1.1"] != "false" { - return fmt.Errorf("Policy attribute 'Protocol-TLSv1.1' was of value %s instead of false!", attrmap["Protocol-TLSv1.1"]) - } - if attrmap["Protocol-TLSv1.2"] != "true" { - return fmt.Errorf("Policy attribute 'Protocol-TLSv1.2' was of value %s instead of true!", attrmap["Protocol-TLSv1.2"]) - } - if attrmap["Server-Defined-Cipher-Order"] != "true" { - return fmt.Errorf("Policy attribute 'Server-Defined-Cipher-Order' was of value %s instead of true!", attrmap["Server-Defined-Cipher-Order"]) - } - if attrmap["ECDHE-RSA-AES128-GCM-SHA256"] != "true" { - return fmt.Errorf("Policy attribute 'ECDHE-RSA-AES128-GCM-SHA256' was of value %s instead of true!", attrmap["ECDHE-RSA-AES128-GCM-SHA256"]) - } - if attrmap["AES128-GCM-SHA256"] != "true" { - return fmt.Errorf("Policy attribute 'AES128-GCM-SHA256' was of value %s instead of true!", attrmap["AES128-GCM-SHA256"]) - } - if attrmap["EDH-RSA-DES-CBC3-SHA"] != "false" { - return fmt.Errorf("Policy attribute 'EDH-RSA-DES-CBC3-SHA' was of value %s instead of false!", attrmap["EDH-RSA-DES-CBC3-SHA"]) - } - - return nil - } -} + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() -func policyAttributesToMap(attributes *[]*elb.PolicyAttributeDescription) map[string]string { - attrmap := make(map[string]string) + _, err = tfelb.FindLoadBalancerListenerPolicyByThreePartKey(ctx, conn, lbName, lbPort, policyName) - for _, attrdef := range *attributes { - attrmap[*attrdef.AttributeName] = *attrdef.AttributeValue + return err } - - return attrmap } -// Sets the SSL Negotiation policy with attributes. func testAccLBSSLNegotiationPolicyConfig_basic(rName, key, certificate string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_iam_server_certificate" "test" { - name = "%[1]s" + name = %[1]q certificate_body = "%[2]s" private_key = "%[3]s" } resource "aws_elb" "test" { - name = "%[1]s" + name = %[1]q availability_zones = [data.aws_availability_zones.available.names[0]] listener { @@ -231,7 +143,7 @@ resource "aws_elb" "test" { } resource "aws_lb_ssl_negotiation_policy" "test" { - name = "foo-policy" + name = %[1]q load_balancer = aws_elb.test.id lb_port = 443 @@ -270,5 +182,5 @@ resource "aws_lb_ssl_negotiation_policy" "test" { value = "false" } } -`, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) +`, rName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key))) } From 4716b4e9f1fd6be8c295f4841aa1af47db12b273 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 8 Feb 2023 18:05:14 -0500 Subject: [PATCH 64/68] r/aws_load_balancer_listener_policy: Add 'FindLoadBalancerListenerPolicyByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBListenerPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBListenerPolicy_ -timeout 180m === RUN TestAccELBListenerPolicy_basic === PAUSE TestAccELBListenerPolicy_basic === RUN TestAccELBListenerPolicy_disappears === PAUSE TestAccELBListenerPolicy_disappears === CONT TestAccELBListenerPolicy_basic === CONT TestAccELBListenerPolicy_disappears --- PASS: TestAccELBListenerPolicy_basic (20.56s) --- PASS: TestAccELBListenerPolicy_disappears (22.49s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 27.701s --- internal/service/elb/listener_policy.go | 161 +++++++------ internal/service/elb/listener_policy_test.go | 238 ++++++------------- 2 files changed, 163 insertions(+), 236 deletions(-) diff --git a/internal/service/elb/listener_policy.go b/internal/service/elb/listener_policy.go index 6b1a7b15254..6a99e05e7d8 100644 --- a/internal/service/elb/listener_policy.go +++ b/internal/service/elb/listener_policy.go @@ -3,24 +3,25 @@ package elb import ( "context" "fmt" + "log" "strconv" "strings" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceListenerPolicy() *schema.Resource { return &schema.Resource{ - CreateWithoutTimeout: resourceListenerPolicyCreate, + CreateWithoutTimeout: resourceListenerPolicySet, ReadWithoutTimeout: resourceListenerPolicyRead, - UpdateWithoutTimeout: resourceListenerPolicyCreate, + UpdateWithoutTimeout: resourceListenerPolicySet, DeleteWithoutTimeout: resourceListenerPolicyDelete, Schema: map[string]*schema.Schema{ @@ -28,44 +29,43 @@ func ResourceListenerPolicy() *schema.Resource { Type: schema.TypeString, Required: true, }, - - "policy_names": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Set: schema.HashString, - }, - "load_balancer_port": { Type: schema.TypeInt, Required: true, }, + "policy_names": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, }, } } -func resourceListenerPolicyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { +func resourceListenerPolicySet(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName := d.Get("load_balancer_name") - - policyNames := []*string{} - if v, ok := d.GetOk("policy_names"); ok { - policyNames = flex.ExpandStringSet(v.(*schema.Set)) + lbName := d.Get("load_balancer_name").(string) + lbPort := d.Get("load_balancer_port").(int) + id := ListenerPolicyCreateResourceID(lbName, lbPort) + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), } - setOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(loadBalancerName.(string)), - LoadBalancerPort: aws.Int64(int64(d.Get("load_balancer_port").(int))), - PolicyNames: policyNames, + if v, ok := d.GetOk("policy_names"); ok && v.(*schema.Set).Len() > 0 { + input.PolicyNames = flex.ExpandStringSet(v.(*schema.Set)) } - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting LoadBalancerPoliciesOfListener: %s", err) + _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic Listener Policy (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s:%s", *setOpts.LoadBalancerName, strconv.FormatInt(*setOpts.LoadBalancerPort, 10))) + d.SetId(id) + return append(diags, resourceListenerPolicyRead(ctx, d, meta)...) } @@ -73,46 +73,27 @@ func resourceListenerPolicyRead(ctx context.Context, d *schema.ResourceData, met var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, loadBalancerPort := ListenerPoliciesParseID(d.Id()) - - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - } - - describeResp, err := conn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) + lbName, lbPort, err := ListenerPolicyParseResourceID(d.Id()) if err != nil { - if ec2err, ok := err.(awserr.Error); ok { - if ec2err.Code() == "LoadBalancerNotFound" { - d.SetId("") - return sdkdiag.AppendErrorf(diags, "LoadBalancerNotFound: %s", err) - } - } - return sdkdiag.AppendErrorf(diags, "retrieving ELB description: %s", err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - if len(describeResp.LoadBalancerDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) - } + policyNames, err := FindLoadBalancerListenerPolicyByTwoPartKey(ctx, conn, lbName, lbPort) - lb := describeResp.LoadBalancerDescriptions[0] - - policyNames := []*string{} - for _, listener := range lb.ListenerDescriptions { - if loadBalancerPort != strconv.Itoa(int(aws.Int64Value(listener.Listener.LoadBalancerPort))) { - continue - } - - policyNames = append(policyNames, listener.PolicyNames...) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic Listener Policy (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags } - d.Set("load_balancer_name", loadBalancerName) - loadBalancerPortVal, err := strconv.ParseInt(loadBalancerPort, 10, 64) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing load balancer port: %s", err) + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Listener Policy (%s): %s", d.Id(), err) } - d.Set("load_balancer_port", loadBalancerPortVal) - d.Set("policy_names", flex.FlattenStringList(policyNames)) + + d.Set("load_balancer_name", lbName) + d.Set("load_balancer_port", lbPort) + d.Set("policy_names", policyNames) return diags } @@ -121,27 +102,73 @@ func resourceListenerPolicyDelete(ctx context.Context, d *schema.ResourceData, m var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, loadBalancerPort := ListenerPoliciesParseID(d.Id()) + lbName, lbPort, err := ListenerPolicyParseResourceID(d.Id()) - loadBalancerPortInt, err := strconv.ParseInt(loadBalancerPort, 10, 64) if err != nil { - return sdkdiag.AppendErrorf(diags, "parsing loadBalancerPort as integer: %s", err) + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - setOpts := &elb.SetLoadBalancerPoliciesOfListenerInput{ - LoadBalancerName: aws.String(loadBalancerName), - LoadBalancerPort: aws.Int64(loadBalancerPortInt), - PolicyNames: []*string{}, + input := &elb.SetLoadBalancerPoliciesOfListenerInput{ + LoadBalancerName: aws.String(lbName), + LoadBalancerPort: aws.Int64(int64(lbPort)), + PolicyNames: aws.StringSlice([]string{}), } - if _, err := conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, setOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "setting LoadBalancerPoliciesOfListener: %s", err) + log.Printf("[DEBUG] Deleting ELB Classic Listener Policy: %s", d.Id()) + _, err = conn.SetLoadBalancerPoliciesOfListenerWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "setting ELB Classic Listener Policy (%s): %s", d.Id(), err) } return diags } -func ListenerPoliciesParseID(id string) (string, string) { - parts := strings.SplitN(id, ":", 2) - return parts[0], parts[1] +func FindLoadBalancerListenerPolicyByTwoPartKey(ctx context.Context, conn *elb.ELB, lbName string, lbPort int) ([]string, error) { + lb, err := FindLoadBalancerByName(ctx, conn, lbName) + + if err != nil { + return nil, err + } + + var policyNames []string + + for _, v := range lb.ListenerDescriptions { + if v == nil { + continue + } + + if aws.Int64Value(v.Listener.LoadBalancerPort) != int64(lbPort) { + continue + } + + policyNames = append(policyNames, aws.StringValueSlice(v.PolicyNames)...) + } + + return policyNames, nil +} + +const listenerPolicyResourceIDSeparator = ":" + +func ListenerPolicyCreateResourceID(lbName string, lbPort int) string { + parts := []string{lbName, strconv.Itoa(lbPort)} + id := strings.Join(parts, listenerPolicyResourceIDSeparator) + + return id +} + +func ListenerPolicyParseResourceID(id string) (string, int, error) { + parts := strings.Split(id, listenerPolicyResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + v, err := strconv.Atoi(parts[1]) + + if err != nil { + return "", 0, err + } + + return parts[0], v, nil + } + + return "", 0, fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sLBPORT", id, listenerPolicyResourceIDSeparator) } diff --git a/internal/service/elb/listener_policy_test.go b/internal/service/elb/listener_policy_test.go index 9e49e6dbd9f..ae6feeb55e5 100644 --- a/internal/service/elb/listener_policy_test.go +++ b/internal/service/elb/listener_policy_test.go @@ -3,26 +3,23 @@ package elb_test import ( "context" "fmt" - "strings" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBListenerPolicy_basic(t *testing.T) { ctx := acctest.Context(t) - rChar := sdkacctest.RandStringFromCharSet(6, sdkacctest.CharSetAlpha) - lbName := rChar - mcName := rChar + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_load_balancer_listener_policy.test" + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), @@ -30,175 +27,102 @@ func TestAccELBListenerPolicy_basic(t *testing.T) { CheckDestroy: testAccCheckListenerPolicyDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccListenerPolicyConfig_basic0(lbName, mcName), - Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"), - testAccCheckListenerPolicyState(ctx, lbName, int64(80), mcName, true), - ), - }, - { - Config: testAccListenerPolicyConfig_basic1(lbName, mcName), + Config: testAccListenerPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckPolicyState(ctx, "aws_elb.test-lb", "aws_load_balancer_policy.magic-cookie-sticky"), - testAccCheckListenerPolicyState(ctx, lbName, int64(80), mcName, true), + testAccCheckListenerPolicyExists(ctx, resourceName), + resource.TestCheckResourceAttr(resourceName, "load_balancer_port", "80"), + resource.TestCheckResourceAttr(resourceName, "policy_names.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "policy_names.*", "aws_load_balancer_policy.test", "policy_name"), ), }, + }, + }) +} + +func TestAccELBListenerPolicy_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_load_balancer_listener_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, elb.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckListenerPolicyDestroy(ctx), + Steps: []resource.TestStep{ { - Config: testAccListenerPolicyConfig_basic2(lbName), + Config: testAccListenerPolicyConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckListenerPolicyState(ctx, lbName, int64(80), mcName, false), + testAccCheckListenerPolicyExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceListenerPolicy(), resourceName), ), + ExpectNonEmptyPlan: true, }, }, }) } -func policyInListenerPolicies(str string, list []string) bool { - for _, v := range list { - if v == str { - return true - } - } - return false -} - func testAccCheckListenerPolicyDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() for _, rs := range s.RootModule().Resources { - switch { - case rs.Type == "aws_load_balancer_policy": - loadBalancerName, policyName := tfelb.ListenerPoliciesParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, - }) - if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { - continue - } - return err - } - if len(out.PolicyDescriptions) > 0 { - return fmt.Errorf("Policy still exists") - } - case rs.Type == "aws_load_listener_policy": - loadBalancerName, _ := tfelb.ListenerPoliciesParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - }) - - if tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - continue - } - - if err != nil { - return err - } - - policyNames := []string{} - for k := range rs.Primary.Attributes { - if strings.HasPrefix(k, "policy_names.") && strings.HasSuffix(k, ".name") { - value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name")) - policyNames = append(policyNames, rs.Primary.Attributes[value_key]) - } - } - for _, policyName := range policyNames { - for _, listener := range out.LoadBalancerDescriptions[0].ListenerDescriptions { - policyStrings := []string{} - for _, pol := range listener.PolicyNames { - policyStrings = append(policyStrings, *pol) - } - if policyInListenerPolicies(policyName, policyStrings) { - return fmt.Errorf("Policy still exists and is assigned") - } - } - } - default: + if rs.Type != "aws_load_balancer_listener_policy" { continue } - } - return nil - } -} -func testAccCheckListenerPolicyState(ctx context.Context, loadBalancerName string, loadBalancerListenerPort int64, loadBalancerListenerPolicyName string, assigned bool) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + lbName, lbPort, err := tfelb.ListenerPolicyParseResourceID(rs.Primary.ID) - loadBalancerDescription, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(loadBalancerName)}, - }) - if err != nil { - return err - } + if err != nil { + return err + } - for _, listener := range loadBalancerDescription.LoadBalancerDescriptions[0].ListenerDescriptions { - if *listener.Listener.LoadBalancerPort != loadBalancerListenerPort { + _, err = tfelb.FindLoadBalancerListenerPolicyByTwoPartKey(ctx, conn, lbName, lbPort) + + if tfresource.NotFound(err) { continue } - policyStrings := []string{} - for _, pol := range listener.PolicyNames { - policyStrings = append(policyStrings, *pol) - } - if policyInListenerPolicies(loadBalancerListenerPolicyName, policyStrings) != assigned { - if assigned { - return fmt.Errorf("Policy no longer assigned %s not in %+v", loadBalancerListenerPolicyName, policyStrings) - } else { - return fmt.Errorf("Policy exists and is assigned") - } + + if err != nil { + return err } + + return fmt.Errorf("ELB Classic Listener Policy %s still exists", rs.Primary.ID) } return nil } } -func testAccListenerPolicyConfig_basic0(lbName, mcName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "test-lb" { - name = "%s" - availability_zones = [data.aws_availability_zones.available.names[0]] +func testAccCheckListenerPolicyExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } - listener { - instance_port = 80 - instance_protocol = "http" - lb_port = 80 - lb_protocol = "http" - } + if rs.Primary.ID == "" { + return fmt.Errorf("No ELB Classic Listener Policy ID is set") + } - tags = { - Name = "tf-acc-test" - } -} + lbName, lbPort, err := tfelb.ListenerPolicyParseResourceID(rs.Primary.ID) -resource "aws_load_balancer_policy" "magic-cookie-sticky" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "%s" - policy_type_name = "AppCookieStickinessPolicyType" + if err != nil { + return err + } - policy_attribute { - name = "CookieName" - value = "magic_cookie" - } -} + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() -resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" { - load_balancer_name = aws_elb.test-lb.name - load_balancer_port = 80 + _, err = tfelb.FindLoadBalancerListenerPolicyByTwoPartKey(ctx, conn, lbName, lbPort) - policy_names = [ - aws_load_balancer_policy.magic-cookie-sticky.policy_name, - ] -} -`, lbName, mcName)) + return err + } } -func testAccListenerPolicyConfig_basic1(lbName, mcName string) string { +func testAccListenerPolicyConfig_basic(rName string) string { return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "test-lb" { - name = "%s" +resource "aws_elb" "test" { + name = %[1]q availability_zones = [data.aws_availability_zones.available.names[0]] listener { @@ -207,50 +131,26 @@ resource "aws_elb" "test-lb" { lb_port = 80 lb_protocol = "http" } - - tags = { - Name = "tf-acc-test" - } } -resource "aws_load_balancer_policy" "magic-cookie-sticky" { - load_balancer_name = aws_elb.test-lb.name - policy_name = "%s" +resource "aws_load_balancer_policy" "test" { + load_balancer_name = aws_elb.test.name + policy_name = %[1]q policy_type_name = "AppCookieStickinessPolicyType" policy_attribute { name = "CookieName" - value = "unicorn_cookie" + value = "wafer" } } -resource "aws_load_balancer_listener_policy" "test-lb-listener-policies-80" { - load_balancer_name = aws_elb.test-lb.name +resource "aws_load_balancer_listener_policy" "test" { + load_balancer_name = aws_elb.test.name load_balancer_port = 80 policy_names = [ - aws_load_balancer_policy.magic-cookie-sticky.policy_name, + aws_load_balancer_policy.test.policy_name, ] } -`, lbName, mcName)) -} - -func testAccListenerPolicyConfig_basic2(lbName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` -resource "aws_elb" "test-lb" { - name = "%s" - availability_zones = [data.aws_availability_zones.available.names[0]] - - listener { - instance_port = 80 - instance_protocol = "http" - lb_port = 80 - lb_protocol = "http" - } - - tags = { - Name = "tf-acc-test" - } -} -`, lbName)) +`, rName)) } From 04f28ab6230279dd63db754aed5964a36f631e8b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 9 Feb 2023 08:37:48 -0500 Subject: [PATCH 65/68] r/aws_load_balancer_policy: Use 'FindLoadBalancerPolicyByTwoPartKey'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBPolicy_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBPolicy_ -timeout 180m === RUN TestAccELBPolicy_basic === PAUSE TestAccELBPolicy_basic === RUN TestAccELBPolicy_disappears === PAUSE TestAccELBPolicy_disappears === RUN TestAccELBPolicy_LBCookieStickinessPolicyType_computedAttributesOnly === PAUSE TestAccELBPolicy_LBCookieStickinessPolicyType_computedAttributesOnly === RUN TestAccELBPolicy_SSLNegotiationPolicyType_computedAttributesOnly === PAUSE TestAccELBPolicy_SSLNegotiationPolicyType_computedAttributesOnly === RUN TestAccELBPolicy_SSLNegotiationPolicyType_customPolicy === PAUSE TestAccELBPolicy_SSLNegotiationPolicyType_customPolicy === RUN TestAccELBPolicy_SSLSecurityPolicy_predefined === PAUSE TestAccELBPolicy_SSLSecurityPolicy_predefined === RUN TestAccELBPolicy_updateWhileAssigned === PAUSE TestAccELBPolicy_updateWhileAssigned === CONT TestAccELBPolicy_basic === CONT TestAccELBPolicy_updateWhileAssigned === CONT TestAccELBPolicy_SSLNegotiationPolicyType_computedAttributesOnly --- PASS: TestAccELBPolicy_SSLNegotiationPolicyType_computedAttributesOnly (24.02s) === CONT TestAccELBPolicy_SSLSecurityPolicy_predefined --- PASS: TestAccELBPolicy_basic (24.13s) === CONT TestAccELBPolicy_SSLNegotiationPolicyType_customPolicy --- PASS: TestAccELBPolicy_updateWhileAssigned (39.01s) === CONT TestAccELBPolicy_LBCookieStickinessPolicyType_computedAttributesOnly --- PASS: TestAccELBPolicy_LBCookieStickinessPolicyType_computedAttributesOnly (21.37s) === CONT TestAccELBPolicy_disappears --- PASS: TestAccELBPolicy_SSLSecurityPolicy_predefined (39.42s) --- PASS: TestAccELBPolicy_SSLNegotiationPolicyType_customPolicy (40.22s) --- PASS: TestAccELBPolicy_disappears (19.35s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 84.892s --- internal/service/elb/policy.go | 133 ++++++++++++++------------ internal/service/elb/policy_test.go | 141 +++++----------------------- 2 files changed, 94 insertions(+), 180 deletions(-) diff --git a/internal/service/elb/policy.go b/internal/service/elb/policy.go index 2fb6b613aa8..15e654204f3 100644 --- a/internal/service/elb/policy.go +++ b/internal/service/elb/policy.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourcePolicy() *schema.Resource { @@ -28,19 +29,6 @@ func ResourcePolicy() *schema.Resource { Required: true, ForceNew: true, }, - - "policy_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "policy_type_name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "policy_attribute": { Type: schema.TypeSet, Optional: true, @@ -54,7 +42,6 @@ func ResourcePolicy() *schema.Resource { Type: schema.TypeString, Optional: true, }, - "value": { Type: schema.TypeString, Optional: true, @@ -66,6 +53,16 @@ func ResourcePolicy() *schema.Resource { // differences caused by additional attributes returned by the API are suppressed. DiffSuppressFunc: suppressPolicyAttributeDiffs, }, + "policy_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "policy_type_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, }, } } @@ -74,23 +71,27 @@ func resourcePolicyCreate(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - lbspOpts := &elb.CreateLoadBalancerPolicyInput{ - LoadBalancerName: aws.String(d.Get("load_balancer_name").(string)), - PolicyName: aws.String(d.Get("policy_name").(string)), + lbName := d.Get("load_balancer_name").(string) + policyName := d.Get("policy_name").(string) + id := PolicyCreateResourceID(lbName, policyName) + input := &elb.CreateLoadBalancerPolicyInput{ + LoadBalancerName: aws.String(lbName), + PolicyName: aws.String(policyName), PolicyTypeName: aws.String(d.Get("policy_type_name").(string)), } if v, ok := d.GetOk("policy_attribute"); ok && v.(*schema.Set).Len() > 0 { - lbspOpts.PolicyAttributes = ExpandPolicyAttributes(v.(*schema.Set).List()) + input.PolicyAttributes = ExpandPolicyAttributes(v.(*schema.Set).List()) } - if _, err := conn.CreateLoadBalancerPolicyWithContext(ctx, lbspOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "creating LoadBalancerPolicy: %s", err) + _, err := conn.CreateLoadBalancerPolicyWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating ELB Classic Load Balancer Policy (%s): %s", id, err) } - d.SetId(fmt.Sprintf("%s:%s", - *lbspOpts.LoadBalancerName, - *lbspOpts.PolicyName)) + d.SetId(id) + return append(diags, resourcePolicyRead(ctx, d, meta)...) } @@ -98,44 +99,30 @@ func resourcePolicyRead(ctx context.Context, d *schema.ResourceData, meta interf var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, policyName := PolicyParseID(d.Id()) + lbName, policyName, err := PolicyParseResourceID(d.Id()) - request := &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, + if err != nil { + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) } - getResp, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, request) + policy, err := FindLoadBalancerPolicyByTwoPartKey(ctx, conn, lbName, policyName) - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, "LoadBalancerNotFound") { - log.Printf("[WARN] Load Balancer (%s) not found, removing from state", loadBalancerName) - d.SetId("") - return diags - } - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodePolicyNotFoundException) { - log.Printf("[WARN] Load Balancer Policy (%s) not found, removing from state", d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic Load Balancer Policy (%s) not found, removing from state", d.Id()) d.SetId("") return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "retrieving policy: %s", err) + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Load Balancer Policy (%s): %s", d.Id(), err) } - if len(getResp.PolicyDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find policy %#v", getResp.PolicyDescriptions) - } - - policyDesc := getResp.PolicyDescriptions[0] - policyTypeName := policyDesc.PolicyTypeName - policyAttributes := policyDesc.PolicyAttributeDescriptions - - d.Set("policy_name", policyName) - d.Set("policy_type_name", policyTypeName) - d.Set("load_balancer_name", loadBalancerName) - if err := d.Set("policy_attribute", FlattenPolicyAttributes(policyAttributes)); err != nil { + d.Set("load_balancer_name", lbName) + if err := d.Set("policy_attribute", FlattenPolicyAttributes(policy.PolicyAttributeDescriptions)); err != nil { return sdkdiag.AppendErrorf(diags, "setting policy_attribute: %s", err) } + d.Set("policy_name", policyName) + d.Set("policy_type_name", policy.PolicyTypeName) return diags } @@ -145,22 +132,26 @@ func resourcePolicyUpdate(ctx context.Context, d *schema.ResourceData, meta inte conn := meta.(*conns.AWSClient).ELBConn() reassignments := Reassignment{} - loadBalancerName, policyName := PolicyParseID(d.Id()) + lbName, policyName, err := PolicyParseResourceID(d.Id()) - assigned, err := resourcePolicyAssigned(ctx, policyName, loadBalancerName, conn) + if err != nil { + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) + } + + assigned, err := resourcePolicyAssigned(ctx, policyName, lbName, conn) if err != nil { return sdkdiag.AppendErrorf(diags, "determining assignment status of Load Balancer Policy %s: %s", policyName, err) } if assigned { - reassignments, err = resourcePolicyUnassign(ctx, policyName, loadBalancerName, conn) + reassignments, err = resourcePolicyUnassign(ctx, policyName, lbName, conn) if err != nil { return sdkdiag.AppendErrorf(diags, "unassigning Load Balancer Policy %s: %s", policyName, err) } } request := &elb.DeleteLoadBalancerPolicyInput{ - LoadBalancerName: aws.String(loadBalancerName), + LoadBalancerName: aws.String(lbName), PolicyName: aws.String(policyName), } @@ -192,37 +183,36 @@ func resourcePolicyDelete(ctx context.Context, d *schema.ResourceData, meta inte var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() - loadBalancerName, policyName := PolicyParseID(d.Id()) + lbName, policyName, err := PolicyParseResourceID(d.Id()) - assigned, err := resourcePolicyAssigned(ctx, policyName, loadBalancerName, conn) + if err != nil { + return sdkdiag.AppendErrorf(diags, "parsing resource ID: %s", err) + } + + assigned, err := resourcePolicyAssigned(ctx, policyName, lbName, conn) if err != nil { return sdkdiag.AppendErrorf(diags, "determining assignment status of Load Balancer Policy %s: %s", policyName, err) } if assigned { - _, err := resourcePolicyUnassign(ctx, policyName, loadBalancerName, conn) + _, err := resourcePolicyUnassign(ctx, policyName, lbName, conn) if err != nil { return sdkdiag.AppendErrorf(diags, "unassigning Load Balancer Policy %s: %s", policyName, err) } } request := &elb.DeleteLoadBalancerPolicyInput{ - LoadBalancerName: aws.String(loadBalancerName), + LoadBalancerName: aws.String(lbName), PolicyName: aws.String(policyName), } if _, err := conn.DeleteLoadBalancerPolicyWithContext(ctx, request); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting Load Balancer Policy %s: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting ELB Classic Load Balancer Policy (%s): %s", d.Id(), err) } return diags } -func PolicyParseID(id string) (string, string) { - parts := strings.SplitN(id, ":", 2) - return parts[0], parts[1] -} - func resourcePolicyAssigned(ctx context.Context, policyName, loadBalancerName string, conn *elb.ELB) (bool, error) { describeElbOpts := &elb.DescribeLoadBalancersInput{ LoadBalancerNames: []*string{aws.String(loadBalancerName)}, @@ -376,3 +366,22 @@ func suppressPolicyAttributeDiffs(k, old, new string, d *schema.ResourceData) bo // Suppress differences if the attributes returned from the API contain those configured return oldAttributes.Intersection(newAttributes).Len() == newAttributes.Len() } + +const policyResourceIDSeparator = ":" + +func PolicyCreateResourceID(lbName, policyName string) string { + parts := []string{lbName, policyName} + id := strings.Join(parts, policyResourceIDSeparator) + + return id +} + +func PolicyParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, backendServerPolicyResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected LBNAME%[2]sPOLICYNAME", id, policyResourceIDSeparator) +} diff --git a/internal/service/elb/policy_test.go b/internal/service/elb/policy_test.go index 0a985d47db1..cb24f4b9b54 100644 --- a/internal/service/elb/policy_test.go +++ b/internal/service/elb/policy_test.go @@ -4,12 +4,8 @@ import ( "context" "fmt" "regexp" - "strconv" - "strings" "testing" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -17,12 +13,12 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBPolicy_basic(t *testing.T) { ctx := acctest.Context(t) var policy elb.PolicyDescription - loadBalancerResourceName := "aws_elb.test-lb" resourceName := "aws_load_balancer_policy.test-policy" rInt := sdkacctest.RandInt() @@ -36,7 +32,6 @@ func TestAccELBPolicy_basic(t *testing.T) { Config: testAccPolicyConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &policy), - testAccCheckPolicyState(ctx, loadBalancerResourceName, resourceName), ), }, }, @@ -45,9 +40,7 @@ func TestAccELBPolicy_basic(t *testing.T) { func TestAccELBPolicy_disappears(t *testing.T) { ctx := acctest.Context(t) - var loadBalancer elb.LoadBalancerDescription var policy elb.PolicyDescription - loadBalancerResourceName := "aws_elb.test-lb" resourceName := "aws_load_balancer_policy.test-policy" rInt := sdkacctest.RandInt() @@ -60,18 +53,8 @@ func TestAccELBPolicy_disappears(t *testing.T) { { Config: testAccPolicyConfig_basic(rInt), Check: resource.ComposeTestCheckFunc( - testAccCheckLoadBalancerExists(ctx, loadBalancerResourceName, &loadBalancer), testAccCheckPolicyExists(ctx, resourceName, &policy), - testAccCheckPolicyDisappears(ctx, &loadBalancer, &policy), - ), - ExpectNonEmptyPlan: true, - }, - { - Config: testAccPolicyConfig_basic(rInt), - Check: resource.ComposeTestCheckFunc( - testAccCheckLoadBalancerExists(ctx, loadBalancerResourceName, &loadBalancer), - testAccCheckPolicyExists(ctx, resourceName, &policy), - testAccCheckLoadBalancerDisappears(ctx, &loadBalancer), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourcePolicy(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -223,7 +206,6 @@ func TestAccELBPolicy_SSLSecurityPolicy_predefined(t *testing.T) { func TestAccELBPolicy_updateWhileAssigned(t *testing.T) { ctx := acctest.Context(t) var policy elb.PolicyDescription - loadBalancerResourceName := "aws_elb.test-lb" resourceName := "aws_load_balancer_policy.test-policy" rInt := sdkacctest.RandInt() @@ -237,51 +219,44 @@ func TestAccELBPolicy_updateWhileAssigned(t *testing.T) { Config: testAccPolicyConfig_updateWhileAssigned0(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &policy), - testAccCheckPolicyState(ctx, loadBalancerResourceName, resourceName), ), }, { Config: testAccPolicyConfig_updateWhileAssigned1(rInt), Check: resource.ComposeTestCheckFunc( testAccCheckPolicyExists(ctx, resourceName, &policy), - testAccCheckPolicyState(ctx, loadBalancerResourceName, resourceName), ), }, }, }) } -func testAccCheckPolicyExists(ctx context.Context, resourceName string, policyDescription *elb.PolicyDescription) resource.TestCheckFunc { +func testAccCheckPolicyExists(ctx context.Context, n string, v *elb.PolicyDescription) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceName] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", resourceName) + return fmt.Errorf("Not found: %s", n) } if rs.Primary.ID == "" { - return fmt.Errorf("No Load Balancer Policy ID is set for %s", resourceName) + return fmt.Errorf("No ELB Classic Load Balancer Policy is set") } - loadBalancerName, policyName := tfelb.PolicyParseID(rs.Primary.ID) + lbName, policyName, err := tfelb.PolicyParseResourceID(rs.Primary.ID) - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - input := &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, + if err != nil { + return err } - output, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, input) + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + + output, err := tfelb.FindLoadBalancerPolicyByTwoPartKey(ctx, conn, lbName, policyName) if err != nil { return err } - if output == nil || len(output.PolicyDescriptions) == 0 { - return fmt.Errorf("Load Balancer Policy (%s) not found", rs.Primary.ID) - } - - *policyDescription = *output.PolicyDescriptions[0] + *output = *v return nil } @@ -296,93 +271,23 @@ func testAccCheckPolicyDestroy(ctx context.Context) resource.TestCheckFunc { continue } - loadBalancerName, policyName := tfelb.PolicyParseID(rs.Primary.ID) - out, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, - }) + lbName, policyName, err := tfelb.PolicyParseResourceID(rs.Primary.ID) + if err != nil { - if ec2err, ok := err.(awserr.Error); ok && (ec2err.Code() == "PolicyNotFound" || ec2err.Code() == "LoadBalancerNotFound") { - continue - } return err } - if len(out.PolicyDescriptions) > 0 { - return fmt.Errorf("Policy still exists") - } - } - return nil - } -} - -func testAccCheckPolicyDisappears(ctx context.Context, loadBalancer *elb.LoadBalancerDescription, policy *elb.PolicyDescription) resource.TestCheckFunc { - return func(s *terraform.State) error { - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() + _, err = tfelb.FindLoadBalancerPolicyByTwoPartKey(ctx, conn, lbName, policyName) - input := elb.DeleteLoadBalancerPolicyInput{ - LoadBalancerName: loadBalancer.LoadBalancerName, - PolicyName: policy.PolicyName, - } - _, err := conn.DeleteLoadBalancerPolicyWithContext(ctx, &input) - - return err - } -} - -func testAccCheckPolicyState(ctx context.Context, elbResource string, policyResource string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[elbResource] - if !ok { - return fmt.Errorf("Not found: %s", elbResource) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - policy, ok := s.RootModule().Resources[policyResource] - if !ok { - return fmt.Errorf("Not found: %s", policyResource) - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - loadBalancerName, policyName := tfelb.PolicyParseID(policy.Primary.ID) - loadBalancerPolicies, err := conn.DescribeLoadBalancerPoliciesWithContext(ctx, &elb.DescribeLoadBalancerPoliciesInput{ - LoadBalancerName: aws.String(loadBalancerName), - PolicyNames: []*string{aws.String(policyName)}, - }) - - if err != nil { - return err - } + if tfresource.NotFound(err) { + continue + } - for _, loadBalancerPolicy := range loadBalancerPolicies.PolicyDescriptions { - if *loadBalancerPolicy.PolicyName == policyName { - if *loadBalancerPolicy.PolicyTypeName != policy.Primary.Attributes["policy_type_name"] { - return fmt.Errorf("PolicyTypeName does not match") - } - policyAttributeCount, err := strconv.Atoi(policy.Primary.Attributes["policy_attribute.#"]) - if err != nil { - return err - } - if len(loadBalancerPolicy.PolicyAttributeDescriptions) != policyAttributeCount { - return fmt.Errorf("PolicyAttributeDescriptions length mismatch") - } - policyAttributes := make(map[string]string) - for k, v := range policy.Primary.Attributes { - if strings.HasPrefix(k, "policy_attribute.") && strings.HasSuffix(k, ".name") { - key := v - value_key := fmt.Sprintf("%s.value", strings.TrimSuffix(k, ".name")) - policyAttributes[key] = policy.Primary.Attributes[value_key] - } - } - for _, policyAttribute := range loadBalancerPolicy.PolicyAttributeDescriptions { - if *policyAttribute.AttributeValue != policyAttributes[*policyAttribute.AttributeName] { - return fmt.Errorf("PollicyAttribute Value mismatch %s != %s: %s", *policyAttribute.AttributeValue, policyAttributes[*policyAttribute.AttributeName], policyAttributes) - } - } + if err != nil { + return err } + + return fmt.Errorf("ELB Classic Load Balancer Policy %s still exists", rs.Primary.ID) } return nil From ccd77c524f8ab873da75e511f01386459d08f55e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 9 Feb 2023 08:56:03 -0500 Subject: [PATCH 66/68] r/aws_elb: Use 'FindLoadBalancerByName'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBLoadBalancer_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBLoadBalancer_ -timeout 180m === RUN TestAccELBLoadBalancer_basic === PAUSE TestAccELBLoadBalancer_basic === RUN TestAccELBLoadBalancer_disappears === PAUSE TestAccELBLoadBalancer_disappears === RUN TestAccELBLoadBalancer_fullCharacterRange === PAUSE TestAccELBLoadBalancer_fullCharacterRange === RUN TestAccELBLoadBalancer_AccessLogs_enabled === PAUSE TestAccELBLoadBalancer_AccessLogs_enabled === RUN TestAccELBLoadBalancer_AccessLogs_disabled === PAUSE TestAccELBLoadBalancer_AccessLogs_disabled === RUN TestAccELBLoadBalancer_namePrefix === PAUSE TestAccELBLoadBalancer_namePrefix === RUN TestAccELBLoadBalancer_generatedName === PAUSE TestAccELBLoadBalancer_generatedName === RUN TestAccELBLoadBalancer_generatesNameForZeroValue === PAUSE TestAccELBLoadBalancer_generatesNameForZeroValue === RUN TestAccELBLoadBalancer_availabilityZones === PAUSE TestAccELBLoadBalancer_availabilityZones === RUN TestAccELBLoadBalancer_tags === PAUSE TestAccELBLoadBalancer_tags === RUN TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate === PAUSE TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate === RUN TestAccELBLoadBalancer_Swap_subnets === PAUSE TestAccELBLoadBalancer_Swap_subnets === RUN TestAccELBLoadBalancer_instanceAttaching === PAUSE TestAccELBLoadBalancer_instanceAttaching === RUN TestAccELBLoadBalancer_listener === PAUSE TestAccELBLoadBalancer_listener === RUN TestAccELBLoadBalancer_healthCheck === PAUSE TestAccELBLoadBalancer_healthCheck === RUN TestAccELBLoadBalancer_timeout === PAUSE TestAccELBLoadBalancer_timeout === RUN TestAccELBLoadBalancer_connectionDraining === PAUSE TestAccELBLoadBalancer_connectionDraining === RUN TestAccELBLoadBalancer_securityGroups === PAUSE TestAccELBLoadBalancer_securityGroups === RUN TestAccELBLoadBalancer_desyncMitigationMode === PAUSE TestAccELBLoadBalancer_desyncMitigationMode === RUN TestAccELBLoadBalancer_desyncMitigationMode_update === PAUSE TestAccELBLoadBalancer_desyncMitigationMode_update === CONT TestAccELBLoadBalancer_basic === CONT TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate === CONT TestAccELBLoadBalancer_timeout --- PASS: TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate (35.37s) === CONT TestAccELBLoadBalancer_desyncMitigationMode_update --- PASS: TestAccELBLoadBalancer_basic (24.59s) === CONT TestAccELBLoadBalancer_desyncMitigationMode --- PASS: TestAccELBLoadBalancer_timeout (34.63s) === CONT TestAccELBLoadBalancer_securityGroups --- PASS: TestAccELBLoadBalancer_desyncMitigationMode (22.26s) === CONT TestAccELBLoadBalancer_connectionDraining --- PASS: TestAccELBLoadBalancer_desyncMitigationMode_update (55.45s) === CONT TestAccELBLoadBalancer_namePrefix --- PASS: TestAccELBLoadBalancer_securityGroups (39.28s) === CONT TestAccELBLoadBalancer_tags --- PASS: TestAccELBLoadBalancer_namePrefix (19.52s) === CONT TestAccELBLoadBalancer_availabilityZones --- PASS: TestAccELBLoadBalancer_connectionDraining (49.97s) === CONT TestAccELBLoadBalancer_generatedName --- PASS: TestAccELBLoadBalancer_generatedName (19.55s) === CONT TestAccELBLoadBalancer_listener --- PASS: TestAccELBLoadBalancer_availabilityZones (34.47s) === CONT TestAccELBLoadBalancer_healthCheck --- PASS: TestAccELBLoadBalancer_tags (54.00s) === CONT TestAccELBLoadBalancer_AccessLogs_enabled --- PASS: TestAccELBLoadBalancer_healthCheck (34.51s) === CONT TestAccELBLoadBalancer_AccessLogs_disabled --- PASS: TestAccELBLoadBalancer_AccessLogs_enabled (57.24s) === CONT TestAccELBLoadBalancer_instanceAttaching --- PASS: TestAccELBLoadBalancer_AccessLogs_disabled (59.45s) === CONT TestAccELBLoadBalancer_fullCharacterRange --- PASS: TestAccELBLoadBalancer_listener (97.40s) === CONT TestAccELBLoadBalancer_disappears --- PASS: TestAccELBLoadBalancer_disappears (18.22s) === CONT TestAccELBLoadBalancer_Swap_subnets --- PASS: TestAccELBLoadBalancer_fullCharacterRange (20.83s) === CONT TestAccELBLoadBalancer_generatesNameForZeroValue --- PASS: TestAccELBLoadBalancer_generatesNameForZeroValue (20.43s) --- PASS: TestAccELBLoadBalancer_instanceAttaching (92.92s) --- PASS: TestAccELBLoadBalancer_Swap_subnets (54.62s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 291.820s --- internal/service/elb/load_balancer.go | 370 +++++++++------------ internal/service/elb/load_balancer_test.go | 90 ++--- 2 files changed, 184 insertions(+), 276 deletions(-) diff --git a/internal/service/elb/load_balancer.go b/internal/service/elb/load_balancer.go index 614bcde9a77..a4440c389c9 100644 --- a/internal/service/elb/load_balancer.go +++ b/internal/service/elb/load_balancer.go @@ -43,131 +43,126 @@ func ResourceLoadBalancer() *schema.Resource { CustomizeDiff: verify.SetTagsDiff, Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - ConflictsWith: []string{"name_prefix"}, - ValidateFunc: ValidName, - }, - "name_prefix": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"name"}, - ValidateFunc: validNamePrefix, + "access_logs": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket": { + Type: schema.TypeString, + Required: true, + }, + "bucket_prefix": { + Type: schema.TypeString, + Optional: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "interval": { + Type: schema.TypeInt, + Optional: true, + Default: 60, + ValidateFunc: ValidAccessLogsInterval, + }, + }, + }, }, - "arn": { Type: schema.TypeString, Computed: true, }, - - "internal": { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Computed: true, - }, - - "cross_zone_load_balancing": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - "availability_zones": { Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Computed: true, - Set: schema.HashString, - }, - - "instances": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Computed: true, - Set: schema.HashString, - }, - - "security_groups": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Computed: true, - Set: schema.HashString, - }, - - "source_security_group": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - - "source_security_group_id": { - Type: schema.TypeString, - Computed: true, - }, - - "subnets": { - Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Computed: true, - Set: schema.HashString, }, - - "idle_timeout": { - Type: schema.TypeInt, - Optional: true, - Default: 60, - ValidateFunc: validation.IntBetween(1, 4000), - }, - "connection_draining": { Type: schema.TypeBool, Optional: true, Default: false, }, - "connection_draining_timeout": { Type: schema.TypeInt, Optional: true, Default: 300, }, - - "access_logs": { + "cross_zone_load_balancing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "desync_mitigation_mode": { + Type: schema.TypeString, + Optional: true, + Default: "defensive", + ValidateFunc: validation.StringInSlice([]string{ + "monitor", + "defensive", + "strictest", + }, false), + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + "health_check": { Type: schema.TypeList, Optional: true, + Computed: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "healthy_threshold": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(2, 10), + }, "interval": { Type: schema.TypeInt, - Optional: true, - Default: 60, - ValidateFunc: ValidAccessLogsInterval, + Required: true, + ValidateFunc: validation.IntBetween(5, 300), }, - "bucket": { - Type: schema.TypeString, - Required: true, + "target": { + Type: schema.TypeString, + Required: true, + ValidateFunc: ValidHeathCheckTarget, }, - "bucket_prefix": { - Type: schema.TypeString, - Optional: true, + "timeout": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(2, 60), }, - "enabled": { - Type: schema.TypeBool, - Optional: true, - Default: true, + "unhealthy_threshold": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(2, 10), }, }, }, }, - + "idle_timeout": { + Type: schema.TypeInt, + Optional: true, + Default: 60, + ValidateFunc: validation.IntBetween(1, 4000), + }, + "instances": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "internal": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + Computed: true, + }, "listener": { Type: schema.TypeSet, Required: true, @@ -178,25 +173,21 @@ func ResourceLoadBalancer() *schema.Resource { Required: true, ValidateFunc: validation.IntBetween(1, 65535), }, - "instance_protocol": { Type: schema.TypeString, Required: true, ValidateFunc: validateListenerProtocol(), }, - "lb_port": { Type: schema.TypeInt, Required: true, ValidateFunc: validation.IntBetween(1, 65535), }, - "lb_protocol": { Type: schema.TypeString, Required: true, ValidateFunc: validateListenerProtocol(), }, - "ssl_certificate_id": { Type: schema.TypeString, Optional: true, @@ -206,77 +197,55 @@ func ResourceLoadBalancer() *schema.Resource { }, Set: ListenerHash, }, - - "health_check": { - Type: schema.TypeList, + "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ConflictsWith: []string{"name_prefix"}, + ValidateFunc: ValidName, + }, + "name_prefix": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"name"}, + ValidateFunc: validNamePrefix, + }, + "security_groups": { + Type: schema.TypeSet, Optional: true, Computed: true, - MaxItems: 1, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "healthy_threshold": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(2, 10), - }, - - "unhealthy_threshold": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(2, 10), - }, - - "target": { - Type: schema.TypeString, - Required: true, - ValidateFunc: ValidHeathCheckTarget, - }, - - "interval": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(5, 300), - }, - - "timeout": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(2, 60), - }, - }, - }, + Elem: &schema.Schema{Type: schema.TypeString}, }, - - "dns_name": { + "source_security_group": { Type: schema.TypeString, + Optional: true, Computed: true, }, - - "zone_id": { + "source_security_group_id": { Type: schema.TypeString, Computed: true, }, - - "desync_mitigation_mode": { - Type: schema.TypeString, + "subnets": { + Type: schema.TypeSet, Optional: true, - Default: "defensive", - ValidateFunc: validation.StringInSlice([]string{ - "monitor", - "defensive", - "strictest", - }, false), + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, }, - "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), + "zone_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - elbconn := meta.(*conns.AWSClient).ELBConn() + conn := meta.(*conns.AWSClient).ELBConn() defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) @@ -323,22 +292,10 @@ func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, met elbOpts.Subnets = flex.ExpandStringSet(v.(*schema.Set)) } - err = resource.RetryContext(ctx, 5*time.Minute, func() *resource.RetryError { - _, err := elbconn.CreateLoadBalancerWithContext(ctx, elbOpts) - - if tfawserr.ErrCodeEquals(err, elb.ErrCodeCertificateNotFoundException) { - return resource.RetryableError(fmt.Errorf("creating ELB Listener with SSL Cert, retrying: %w", err)) - } + _, err = tfresource.RetryWhenAWSErrCodeEquals(ctx, 5*time.Minute, func() (interface{}, error) { + return conn.CreateLoadBalancerWithContext(ctx, elbOpts) + }, elb.ErrCodeCertificateNotFoundException) - if err != nil { - return resource.NonRetryableError(err) - } - - return nil - }) - if tfresource.TimedOut(err) { - _, err = elbconn.CreateLoadBalancerWithContext(ctx, elbOpts) - } if err != nil { return sdkdiag.AppendErrorf(diags, "creating ELB Classic Load Balancer (%s): %s", elbName, err) } @@ -352,11 +309,21 @@ func resourceLoadBalancerCreate(ctx context.Context, d *schema.ResourceData, met func resourceLoadBalancerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - elbconn := meta.(*conns.AWSClient).ELBConn() + conn := meta.(*conns.AWSClient).ELBConn() defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - elbName := d.Id() + lb, err := FindLoadBalancerByName(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] ELB Classic Load Balancer (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Load Balancer (%s): %s", d.Id(), err) + } arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, @@ -367,26 +334,7 @@ func resourceLoadBalancerRead(ctx context.Context, d *schema.ResourceData, meta } d.Set("arn", arn.String()) - // Retrieve the ELB properties for updating the state - describeElbOpts := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(elbName)}, - } - - describeResp, err := elbconn.DescribeLoadBalancersWithContext(ctx, describeElbOpts) - if err != nil { - if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, elb.ErrCodeAccessPointNotFoundException) { - log.Printf("[WARN] ELB Classic LB (%s) not found, removing from state", elbName) - d.SetId("") - return diags - } - - return sdkdiag.AppendErrorf(diags, "retrieving ELB Classic LB (%s): %s", elbName, err) - } - if len(describeResp.LoadBalancerDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "Unable to find ELB: %#v", describeResp.LoadBalancerDescriptions) - } - - if err := flattenLoadBalancerResource(ctx, d, meta.(*conns.AWSClient).EC2Conn(), elbconn, describeResp.LoadBalancerDescriptions[0], ignoreTagsConfig, defaultTagsConfig); err != nil { + if err := flattenLoadBalancerResource(ctx, d, meta.(*conns.AWSClient).EC2Conn(), conn, lb, ignoreTagsConfig, defaultTagsConfig); err != nil { return sdkdiag.AppendFromErr(diags, err) } return diags @@ -502,7 +450,7 @@ func flattenLoadBalancerResource(ctx context.Context, d *schema.ResourceData, ec func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - elbconn := meta.(*conns.AWSClient).ELBConn() + conn := meta.(*conns.AWSClient).ELBConn() if d.HasChange("listener") { o, n := d.GetChange("listener") @@ -527,7 +475,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } log.Printf("[DEBUG] ELB Delete Listeners opts: %s", deleteListenersOpts) - _, err := elbconn.DeleteLoadBalancerListenersWithContext(ctx, deleteListenersOpts) + _, err := conn.DeleteLoadBalancerListenersWithContext(ctx, deleteListenersOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure removing outdated ELB listeners: %s", err) } @@ -542,7 +490,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met // Occasionally AWS will error with a 'duplicate listener', without any // other listeners on the ELB. Retry here to eliminate that. err := resource.RetryContext(ctx, 5*time.Minute, func() *resource.RetryError { - _, err := elbconn.CreateLoadBalancerListenersWithContext(ctx, input) + _, err := conn.CreateLoadBalancerListenersWithContext(ctx, input) if err != nil { if tfawserr.ErrCodeEquals(err, elb.ErrCodeDuplicateListenerException) { log.Printf("[DEBUG] Duplicate listener found for ELB (%s), retrying", d.Id()) @@ -560,7 +508,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met return nil }) if tfresource.TimedOut(err) { - _, err = elbconn.CreateLoadBalancerListenersWithContext(ctx, input) + _, err = conn.CreateLoadBalancerListenersWithContext(ctx, input) } if err != nil { return sdkdiag.AppendErrorf(diags, "Failure adding new or updated ELB listeners: %s", err) @@ -584,7 +532,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met Instances: add, } - _, err := elbconn.RegisterInstancesWithLoadBalancerWithContext(ctx, ®isterInstancesOpts) + _, err := conn.RegisterInstancesWithLoadBalancerWithContext(ctx, ®isterInstancesOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure registering instances with ELB: %s", err) } @@ -595,7 +543,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met Instances: remove, } - _, err := elbconn.DeregisterInstancesFromLoadBalancerWithContext(ctx, &deRegisterInstancesOpts) + _, err := conn.DeregisterInstancesFromLoadBalancerWithContext(ctx, &deRegisterInstancesOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure deregistering instances from ELB: %s", err) } @@ -638,7 +586,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } log.Printf("[DEBUG] ELB Modify Load Balancer Attributes Request: %#v", attrs) - _, err := elbconn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) + _, err := conn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure configuring ELB attributes: %s", err) } @@ -662,7 +610,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met }, } - _, err := elbconn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) + _, err := conn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure configuring ELB attributes: %s", err) } @@ -680,7 +628,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met }, } - _, err := elbconn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) + _, err := conn.ModifyLoadBalancerAttributesWithContext(ctx, &attrs) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure configuring ELB attributes: %s", err) } @@ -700,7 +648,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met Timeout: aws.Int64(int64(check["timeout"].(int))), }, } - _, err := elbconn.ConfigureHealthCheckWithContext(ctx, &configureHealthCheckOpts) + _, err := conn.ConfigureHealthCheckWithContext(ctx, &configureHealthCheckOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure configuring health check for ELB: %s", err) } @@ -713,7 +661,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met SecurityGroups: flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)), } - _, err := elbconn.ApplySecurityGroupsToLoadBalancerWithContext(ctx, &applySecurityGroupsOpts) + _, err := conn.ApplySecurityGroupsToLoadBalancerWithContext(ctx, &applySecurityGroupsOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure applying security groups to ELB: %s", err) } @@ -734,7 +682,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } log.Printf("[DEBUG] ELB enable availability zones opts: %s", enableOpts) - _, err := elbconn.EnableAvailabilityZonesForLoadBalancerWithContext(ctx, enableOpts) + _, err := conn.EnableAvailabilityZonesForLoadBalancerWithContext(ctx, enableOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure enabling ELB availability zones: %s", err) } @@ -747,7 +695,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } log.Printf("[DEBUG] ELB disable availability zones opts: %s", disableOpts) - _, err := elbconn.DisableAvailabilityZonesForLoadBalancerWithContext(ctx, disableOpts) + _, err := conn.DisableAvailabilityZonesForLoadBalancerWithContext(ctx, disableOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure disabling ELB availability zones: %s", err) } @@ -769,7 +717,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met } log.Printf("[DEBUG] ELB detach subnets opts: %s", detachOpts) - _, err := elbconn.DetachLoadBalancerFromSubnetsWithContext(ctx, detachOpts) + _, err := conn.DetachLoadBalancerFromSubnetsWithContext(ctx, detachOpts) if err != nil { return sdkdiag.AppendErrorf(diags, "Failure removing ELB subnets: %s", err) } @@ -783,7 +731,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met log.Printf("[DEBUG] ELB attach subnets opts: %s", attachOpts) err := resource.RetryContext(ctx, 5*time.Minute, func() *resource.RetryError { - _, err := elbconn.AttachLoadBalancerToSubnetsWithContext(ctx, attachOpts) + _, err := conn.AttachLoadBalancerToSubnetsWithContext(ctx, attachOpts) if err != nil { if tfawserr.ErrMessageContains(err, elb.ErrCodeInvalidConfigurationRequestException, "cannot be attached to multiple subnets in the same AZ") { // eventually consistent issue with removing a subnet in AZ1 and @@ -796,7 +744,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met return nil }) if tfresource.TimedOut(err) { - _, err = elbconn.AttachLoadBalancerToSubnetsWithContext(ctx, attachOpts) + _, err = conn.AttachLoadBalancerToSubnetsWithContext(ctx, attachOpts) } if err != nil { return sdkdiag.AppendErrorf(diags, "Failure adding ELB subnets: %s", err) @@ -807,7 +755,7 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") - if err := UpdateTags(ctx, elbconn, d.Id(), o, n); err != nil { + if err := UpdateTags(ctx, conn, d.Id(), o, n); err != nil { return sdkdiag.AppendErrorf(diags, "updating ELB(%s) tags: %s", d.Id(), err) } } @@ -817,23 +765,21 @@ func resourceLoadBalancerUpdate(ctx context.Context, d *schema.ResourceData, met func resourceLoadBalancerDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - elbconn := meta.(*conns.AWSClient).ELBConn() - - log.Printf("[INFO] Deleting ELB: %s", d.Id()) + conn := meta.(*conns.AWSClient).ELBConn() - // Destroy the load balancer - deleteElbOpts := elb.DeleteLoadBalancerInput{ + log.Printf("[INFO] Deleting ELB Classic Load Balancer: %s", d.Id()) + _, err := conn.DeleteLoadBalancerWithContext(ctx, &elb.DeleteLoadBalancerInput{ LoadBalancerName: aws.String(d.Id()), - } - if _, err := elbconn.DeleteLoadBalancerWithContext(ctx, &deleteElbOpts); err != nil { - return sdkdiag.AppendErrorf(diags, "deleting ELB: %s", err) + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting ELB Classic Load Balancer (%s): %s", d.Id(), err) } - name := d.Get("name").(string) + err = cleanupNetworkInterfaces(ctx, meta.(*conns.AWSClient).EC2Conn(), d.Id()) - err := CleanupNetworkInterfaces(ctx, meta.(*conns.AWSClient).EC2Conn(), name) if err != nil { - log.Printf("[WARN] Failed to cleanup ENIs for ELB %q: %#v", name, err) + diags = sdkdiag.AppendWarningf(diags, "cleaning up ELB Classic Load Balancer (%s) ENIs: %s", d.Id(), err) } return diags @@ -999,7 +945,7 @@ func validateListenerProtocol() schema.SchemaValidateFunc { // but the cleanup is asynchronous and may take time // which then blocks IGW, SG or VPC on deletion // So we make the cleanup "synchronous" here -func CleanupNetworkInterfaces(ctx context.Context, conn *ec2.EC2, name string) error { +func cleanupNetworkInterfaces(ctx context.Context, conn *ec2.EC2, name string) error { // https://aws.amazon.com/premiumsupport/knowledge-center/elb-find-load-balancer-IP/. networkInterfaces, err := tfec2.FindNetworkInterfacesByAttachmentInstanceOwnerIDAndDescription(ctx, conn, "amazon-elb", "ELB "+name) diff --git a/internal/service/elb/load_balancer_test.go b/internal/service/elb/load_balancer_test.go index 66f059d3a49..bdad5b03072 100644 --- a/internal/service/elb/load_balancer_test.go +++ b/internal/service/elb/load_balancer_test.go @@ -10,7 +10,6 @@ import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports "time" "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/elb" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -18,6 +17,7 @@ import ( // nosemgrep:ci.aws-sdk-go-multiple-service-imports "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfelb "github.com/hashicorp/terraform-provider-aws/internal/service/elb" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func TestAccELBLoadBalancer_basic(t *testing.T) { @@ -74,7 +74,7 @@ func TestAccELBLoadBalancer_disappears(t *testing.T) { Config: testAccLoadBalancerConfig_basic, Check: resource.ComposeTestCheckFunc( testAccCheckLoadBalancerExists(ctx, resourceName, &loadBalancer), - testAccCheckLoadBalancerDisappears(ctx, &loadBalancer), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelb.ResourceLoadBalancer(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -358,7 +358,7 @@ func TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate(t *tes Steps: []resource.TestStep{ { Config: testAccLoadBalancerConfig_listenerIAMServerCertificate(rName, certificate, key, "tcp"), - ExpectError: regexp.MustCompile(`ssl_certificate_id may be set only when protocol is 'https' or 'ssl'`), + ExpectError: regexp.MustCompile(`"ssl_certificate_id" may be set only when "protocol" is "https" or "ssl"`), }, { Config: testAccLoadBalancerConfig_listenerIAMServerCertificate(rName, certificate, key, "https"), @@ -369,7 +369,7 @@ func TestAccELBLoadBalancer_ListenerSSLCertificateID_iamServerCertificate(t *tes }, { Config: testAccLoadBalancerConfig_listenerIAMServerCertificateAddInvalidListener(rName, certificate, key), - ExpectError: regexp.MustCompile(`ssl_certificate_id may be set only when protocol is 'https' or 'ssl'`), + ExpectError: regexp.MustCompile(`"ssl_certificate_id" may be set only when "protocol" is "https" or "ssl"`), }, }, }) @@ -983,42 +983,45 @@ func testAccCheckLoadBalancerDestroy(ctx context.Context) resource.TestCheckFunc continue } - describe, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(rs.Primary.ID)}, - }) + _, err := tfelb.FindLoadBalancerByName(ctx, conn, rs.Primary.ID) - if err == nil { - if len(describe.LoadBalancerDescriptions) != 0 && - *describe.LoadBalancerDescriptions[0].LoadBalancerName == rs.Primary.ID { - return fmt.Errorf("ELB still exists") - } + if tfresource.NotFound(err) { + continue } - // Verify the error - providerErr, ok := err.(awserr.Error) - if !ok { + if err != nil { return err } - if providerErr.Code() != elb.ErrCodeAccessPointNotFoundException { - return fmt.Errorf("Unexpected error: %s", err) - } + return fmt.Errorf("ELB Classic Load Balancer %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckLoadBalancerDisappears(ctx context.Context, loadBalancer *elb.LoadBalancerDescription) resource.TestCheckFunc { +func testAccCheckLoadBalancerExists(ctx context.Context, n string, v *elb.LoadBalancerDescription) resource.TestCheckFunc { return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ELB Classic Load Balancer ID is set") + } + conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - input := elb.DeleteLoadBalancerInput{ - LoadBalancerName: loadBalancer.LoadBalancerName, + output, err := tfelb.FindLoadBalancerByName(ctx, conn, rs.Primary.ID) + + if err != nil { + return err } - _, err := conn.DeleteLoadBalancerWithContext(ctx, &input) - return err + *v = *output + + return nil } } @@ -1046,47 +1049,6 @@ func testAccCheckLoadBalancerAttributes(conf *elb.LoadBalancerDescription) resou } } -func testAccCheckLoadBalancerExists(ctx context.Context, n string, res *elb.LoadBalancerDescription) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[n] - if !ok { - return fmt.Errorf("Not found: %s", n) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ELB ID is set") - } - - conn := acctest.Provider.Meta().(*conns.AWSClient).ELBConn() - - describe, err := conn.DescribeLoadBalancersWithContext(ctx, &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: []*string{aws.String(rs.Primary.ID)}, - }) - - if err != nil { - return err - } - - if len(describe.LoadBalancerDescriptions) != 1 || - *describe.LoadBalancerDescriptions[0].LoadBalancerName != rs.Primary.ID { - return fmt.Errorf("ELB not found") - } - - *res = *describe.LoadBalancerDescriptions[0] - - // Confirm source_security_group_id for ELBs in a VPC - // See https://github.com/hashicorp/terraform/pull/3780 - if res.VPCId != nil { - sgid := rs.Primary.Attributes["source_security_group_id"] - if sgid == "" { - return fmt.Errorf("Expected to find source_security_group_id for ELB, but was empty") - } - } - - return nil - } -} - const testAccLoadBalancerConfig_basic = ` data "aws_availability_zones" "available" { state = "available" From 54a2bf7b50e6b6929b0c81fc529a8fb7517cd7c3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 9 Feb 2023 09:05:31 -0500 Subject: [PATCH 67/68] d/aws_elb: Use 'FindLoadBalancerByName'. Acceptance test output: % make testacc TESTARGS='-run=TestAccELBLoadBalancerDataSource_' PKG=elb ACCTEST_PARALLELISM=3 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/elb/... -v -count 1 -parallel 3 -run=TestAccELBLoadBalancerDataSource_ -timeout 180m === RUN TestAccELBLoadBalancerDataSource_basic === PAUSE TestAccELBLoadBalancerDataSource_basic === CONT TestAccELBLoadBalancerDataSource_basic --- PASS: TestAccELBLoadBalancerDataSource_basic (39.76s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/elb 45.324s --- .../service/elb/load_balancer_data_source.go | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/internal/service/elb/load_balancer_data_source.go b/internal/service/elb/load_balancer_data_source.go index 8aef38e9ca4..11e9c10297d 100644 --- a/internal/service/elb/load_balancer_data_source.go +++ b/internal/service/elb/load_balancer_data_source.go @@ -3,7 +3,6 @@ package elb import ( "context" "fmt" - "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/arn" @@ -60,7 +59,6 @@ func DataSourceLoadBalancer() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, "connection_draining": { @@ -125,7 +123,6 @@ func DataSourceLoadBalancer() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, "internal": { @@ -171,7 +168,6 @@ func DataSourceLoadBalancer() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, "source_security_group": { @@ -188,7 +184,6 @@ func DataSourceLoadBalancer() *schema.Resource { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Computed: true, - Set: schema.HashString, }, "desync_mitigation_mode": { @@ -209,46 +204,38 @@ func DataSourceLoadBalancer() *schema.Resource { func dataSourceLoadBalancerRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ELBConn() + ec2conn := meta.(*conns.AWSClient).EC2Conn() ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig lbName := d.Get("name").(string) + lb, err := FindLoadBalancerByName(ctx, conn, lbName) - input := &elb.DescribeLoadBalancersInput{ - LoadBalancerNames: aws.StringSlice([]string{lbName}), + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Load Balancer (%s): %s", lbName, err) } - log.Printf("[DEBUG] Reading ELB: %s", input) - resp, err := conn.DescribeLoadBalancersWithContext(ctx, input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "retrieving LB: %s", err) + d.SetId(aws.StringValue(lb.LoadBalancerName)) + + input := &elb.DescribeLoadBalancerAttributesInput{ + LoadBalancerName: aws.String(d.Id()), } - if len(resp.LoadBalancerDescriptions) != 1 { - return sdkdiag.AppendErrorf(diags, "search returned %d results, please revise so only one is returned", len(resp.LoadBalancerDescriptions)) + + output, err := conn.DescribeLoadBalancerAttributesWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading ELB Classic Load Balancer (%s) attributes: %s", d.Id(), err) } - d.SetId(aws.StringValue(resp.LoadBalancerDescriptions[0].LoadBalancerName)) + + lbAttrs := output.LoadBalancerAttributes arn := arn.ARN{ Partition: meta.(*conns.AWSClient).Partition, Region: meta.(*conns.AWSClient).Region, Service: "elasticloadbalancing", AccountID: meta.(*conns.AWSClient).AccountID, - Resource: fmt.Sprintf("loadbalancer/%s", aws.StringValue(resp.LoadBalancerDescriptions[0].LoadBalancerName)), + Resource: fmt.Sprintf("loadbalancer/%s", d.Id()), } d.Set("arn", arn.String()) - - lb := resp.LoadBalancerDescriptions[0] - ec2conn := meta.(*conns.AWSClient).EC2Conn() - - describeAttrsOpts := &elb.DescribeLoadBalancerAttributesInput{ - LoadBalancerName: aws.String(d.Id()), - } - describeAttrsResp, err := conn.DescribeLoadBalancerAttributesWithContext(ctx, describeAttrsOpts) - if err != nil { - return sdkdiag.AppendErrorf(diags, "retrieving ELB: %s", err) - } - - lbAttrs := describeAttrsResp.LoadBalancerAttributes - d.Set("name", lb.LoadBalancerName) d.Set("dns_name", lb.DNSName) d.Set("zone_id", lb.CanonicalHostedZoneNameID) From aa9c28a7b9cd36699e9f3d74e7cb01437437640e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 9 Feb 2023 11:01:22 -0500 Subject: [PATCH 68/68] r/aws_elb: Fix sweeper. --- internal/service/elb/sweep.go | 52 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/internal/service/elb/sweep.go b/internal/service/elb/sweep.go index 1997c0a98f9..137829cc761 100644 --- a/internal/service/elb/sweep.go +++ b/internal/service/elb/sweep.go @@ -7,6 +7,7 @@ import ( "fmt" "log" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elb" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -24,39 +25,42 @@ func sweepLoadBalancers(region string) error { ctx := sweep.Context(region) client, err := sweep.SharedRegionalSweepClient(region) if err != nil { - return fmt.Errorf("getting client: %s", err) + return fmt.Errorf("error getting client: %s", err) } conn := client.(*conns.AWSClient).ELBConn() + input := &elb.DescribeLoadBalancersInput{} + sweepResources := make([]sweep.Sweepable, 0) - err = conn.DescribeLoadBalancersPagesWithContext(ctx, &elb.DescribeLoadBalancersInput{}, func(out *elb.DescribeLoadBalancersOutput, lastPage bool) bool { - if len(out.LoadBalancerDescriptions) == 0 { - log.Println("[INFO] No ELBs found for sweeping") - return false + err = conn.DescribeLoadBalancersPagesWithContext(ctx, input, func(page *elb.DescribeLoadBalancersOutput, lastPage bool) bool { + if page == nil { + return !lastPage } - for _, lb := range out.LoadBalancerDescriptions { - log.Printf("[INFO] Deleting ELB: %s", *lb.LoadBalancerName) - - _, err := conn.DeleteLoadBalancerWithContext(ctx, &elb.DeleteLoadBalancerInput{ - LoadBalancerName: lb.LoadBalancerName, - }) - if err != nil { - log.Printf("[ERROR] Failed to delete ELB %s: %s", *lb.LoadBalancerName, err) - continue - } - err = CleanupNetworkInterfaces(ctx, client.(*conns.AWSClient).EC2Conn(), *lb.LoadBalancerName) - if err != nil { - log.Printf("[WARN] Failed to cleanup ENIs for ELB %q: %s", *lb.LoadBalancerName, err) - } + for _, v := range page.LoadBalancerDescriptions { + r := ResourceLoadBalancer() + d := r.Data(nil) + d.SetId(aws.StringValue(v.LoadBalancerName)) + + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } + return !lastPage }) + + if sweep.SkipSweepError(err) { + log.Printf("[WARN] Skipping ELB Classic Load Balancer sweep for %s: %s", region, err) + return nil + } + if err != nil { - if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping ELB sweep for %s: %s", region, err) - return nil - } - return fmt.Errorf("Error retrieving ELBs: %s", err) + return fmt.Errorf("error listing ELB Classic Load Balancers (%s): %w", region, err) } + + err = sweep.SweepOrchestratorWithContext(ctx, sweepResources) + + if err != nil { + return fmt.Errorf("error sweeping ELB Classic Load Balancers (%s): %w", region, err) + } + return nil }