From 2d223850a656582a197d5715c12f28c6dd5f6b2f Mon Sep 17 00:00:00 2001 From: nikhil Date: Sat, 20 Feb 2021 13:55:51 +0530 Subject: [PATCH 01/14] aws_elastic_beanstalk_application resource service_role should have ARN validation --- aws/resource_aws_elastic_beanstalk_application.go | 1 + 1 file changed, 1 insertion(+) diff --git a/aws/resource_aws_elastic_beanstalk_application.go b/aws/resource_aws_elastic_beanstalk_application.go index 4d4cffa79809..aba02a1a0ce1 100644 --- a/aws/resource_aws_elastic_beanstalk_application.go +++ b/aws/resource_aws_elastic_beanstalk_application.go @@ -46,6 +46,7 @@ func resourceAwsElasticBeanstalkApplication() *schema.Resource { "service_role": { Type: schema.TypeString, Required: true, + ValidateFunc: validateArn, }, "max_age_in_days": { Type: schema.TypeInt, From e360395e212d76c46f524e0ab982822cba423de4 Mon Sep 17 00:00:00 2001 From: nikhil Date: Sat, 20 Feb 2021 14:03:14 +0530 Subject: [PATCH 02/14] LogEncryptionKmsKeyId for EMR Clusters --- aws/resource_aws_elastic_beanstalk_application.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_elastic_beanstalk_application.go b/aws/resource_aws_elastic_beanstalk_application.go index aba02a1a0ce1..6bb8e28d9a0b 100644 --- a/aws/resource_aws_elastic_beanstalk_application.go +++ b/aws/resource_aws_elastic_beanstalk_application.go @@ -44,8 +44,8 @@ func resourceAwsElasticBeanstalkApplication() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "service_role": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, ValidateFunc: validateArn, }, "max_age_in_days": { From 752b5dd0ef4f165d262b222c0dd269456f6faa07 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 10:45:14 -0400 Subject: [PATCH 03/14] Revert "LogEncryptionKmsKeyId for EMR Clusters" This reverts commit e360395e212d76c46f524e0ab982822cba423de4. --- aws/resource_aws_elastic_beanstalk_application.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_elastic_beanstalk_application.go b/aws/resource_aws_elastic_beanstalk_application.go index 6bb8e28d9a0b..aba02a1a0ce1 100644 --- a/aws/resource_aws_elastic_beanstalk_application.go +++ b/aws/resource_aws_elastic_beanstalk_application.go @@ -44,8 +44,8 @@ func resourceAwsElasticBeanstalkApplication() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "service_role": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, ValidateFunc: validateArn, }, "max_age_in_days": { From 7e953082aa55dbb21dcd60c0d9fce107fc615b53 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 10:46:25 -0400 Subject: [PATCH 04/14] Revert "aws_elastic_beanstalk_application resource service_role should have ARN validation" This reverts commit 2d223850a656582a197d5715c12f28c6dd5f6b2f. --- aws/resource_aws_elastic_beanstalk_application.go | 1 - 1 file changed, 1 deletion(-) diff --git a/aws/resource_aws_elastic_beanstalk_application.go b/aws/resource_aws_elastic_beanstalk_application.go index aba02a1a0ce1..4d4cffa79809 100644 --- a/aws/resource_aws_elastic_beanstalk_application.go +++ b/aws/resource_aws_elastic_beanstalk_application.go @@ -46,7 +46,6 @@ func resourceAwsElasticBeanstalkApplication() *schema.Resource { "service_role": { Type: schema.TypeString, Required: true, - ValidateFunc: validateArn, }, "max_age_in_days": { Type: schema.TypeInt, From f7538f2c0f5a4fda40c279fec01a493bec672a36 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 10:53:08 -0400 Subject: [PATCH 05/14] r/aws_elastic_beanstalk_application: Validate that 'appversion_lifecycle.service_role' is an ARN. --- internal/service/elasticbeanstalk/application.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index 68dc1f50a2f8..ea96b6866340 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -55,8 +55,9 @@ func ResourceApplication() *schema.Resource { Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "service_role": { - Type: schema.TypeString, - Required: true, + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, }, "max_age_in_days": { Type: schema.TypeInt, From d0754fa301f44df2e3efc22195e18aa34c707896 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 10:55:08 -0400 Subject: [PATCH 06/14] Add CHANGELOG entry. --- .changelog/17727.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/17727.txt diff --git a/.changelog/17727.txt b/.changelog/17727.txt new file mode 100644 index 000000000000..6e482b2cfd6e --- /dev/null +++ b/.changelog/17727.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_elastic_beanstalk_application: Add plan time validation to `appversion_lifecycle.service_role` argument +``` \ No newline at end of file From 1ba3c611ed5756ee160ca51f7ca1959e38aa75e8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 10:59:21 -0400 Subject: [PATCH 07/14] r/aws_elastic_beanstalk_application: Alphabetize attributes. --- .../service/elasticbeanstalk/application.go | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index ea96b6866340..803c67d090b5 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -28,6 +28,7 @@ func ResourceApplication() *schema.Resource { ReadWithoutTimeout: resourceApplicationRead, UpdateWithoutTimeout: resourceApplicationUpdate, DeleteWithoutTimeout: resourceApplicationDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -35,29 +36,15 @@ func ResourceApplication() *schema.Resource { CustomizeDiff: verify.SetTagsDiff, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "description": { - Type: schema.TypeString, - Optional: true, - }, "appversion_lifecycle": { Type: schema.TypeList, Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "service_role": { - Type: schema.TypeString, - Required: true, - ValidateFunc: verify.ValidARN, + "delete_source_from_s3": { + Type: schema.TypeBool, + Optional: true, }, "max_age_in_days": { Type: schema.TypeInt, @@ -67,13 +54,27 @@ func ResourceApplication() *schema.Resource { Type: schema.TypeInt, Optional: true, }, - "delete_source_from_s3": { - Type: schema.TypeBool, - Optional: true, + "service_role": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, }, }, }, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), }, From 474c6db4af15910b1e92a06aac179eb5e4144499 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 11:10:22 -0400 Subject: [PATCH 08/14] r/aws_elastic_beanstalk_application: Tidy up resource Create and Delete. --- .../service/elasticbeanstalk/application.go | 61 +++++++++++-------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index 803c67d090b5..c2073919d877 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -83,24 +83,24 @@ func ResourceApplication() *schema.Resource { func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - beanstalkConn := meta.(*conns.AWSClient).ElasticBeanstalkConn() + conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() name := d.Get("name").(string) - description := d.Get("description").(string) - req := &elasticbeanstalk.CreateApplicationInput{ + input := &elasticbeanstalk.CreateApplicationInput{ ApplicationName: aws.String(name), - Description: aws.String(description), + Description: aws.String(d.Get("description").(string)), Tags: GetTagsIn(ctx), } - app, err := beanstalkConn.CreateApplicationWithContext(ctx, req) + output, err := conn.CreateApplicationWithContext(ctx, input) + if err != nil { return sdkdiag.AppendErrorf(diags, "creating Elastic Beanstalk Application (%s): %s", name, err) } d.SetId(name) - if err = resourceApplicationAppVersionLifecycleUpdate(ctx, beanstalkConn, d, app.Application); err != nil { + if err = resourceApplicationAppVersionLifecycleUpdate(ctx, conn, d, output.Application); err != nil { return sdkdiag.AppendErrorf(diags, "creating Elastic Beanstalk Application (%s): %s", name, err) } @@ -259,37 +259,50 @@ func resourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta i func resourceApplicationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics - beanstalkConn := meta.(*conns.AWSClient).ElasticBeanstalkConn() + conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() - _, err := beanstalkConn.DeleteApplicationWithContext(ctx, &elasticbeanstalk.DeleteApplicationInput{ + log.Printf("[DEBUG] Deleting Elastic Beanstalk Application: %s", d.Id()) + _, err := conn.DeleteApplicationWithContext(ctx, &elasticbeanstalk.DeleteApplicationInput{ ApplicationName: aws.String(d.Id()), }) + if err != nil { return sdkdiag.AppendErrorf(diags, "deleting Elastic Beanstalk Application (%s): %s", d.Id(), err) } - var app *elasticbeanstalk.ApplicationDescription - err = retry.RetryContext(ctx, 10*time.Second, func() *retry.RetryError { - app, err = getApplication(ctx, d.Id(), meta.(*conns.AWSClient).ElasticBeanstalkConn()) - if err != nil { - return retry.NonRetryableError(err) - } - - if app != nil { - return retry.RetryableError( - fmt.Errorf("Beanstalk Application (%s) still exists: %s", d.Id(), err)) - } - return nil + _, err = tfresource.RetryUntilNotFound(ctx, 10*time.Second, func() (interface{}, error) { + return FindApplicationByName(ctx, conn, d.Id()) }) - if tfresource.TimedOut(err) { - app, err = getApplication(ctx, d.Id(), meta.(*conns.AWSClient).ElasticBeanstalkConn()) - } + if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting Elastic Beanstalk Application (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "waiting for Elastic Beanstalk Application (%s) delete: %s", d.Id(), err) } + return diags } +func FindApplicationByName(ctx context.Context, conn *elasticbeanstalk.ElasticBeanstalk, name string) (*elasticbeanstalk.ApplicationDescription, error) { + input := &elasticbeanstalk.DescribeApplicationsInput{ + ApplicationNames: aws.StringSlice([]string{name}), + } + + output, err := conn.DescribeApplicationsWithContext(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.Applications) == 0 || output.Applications[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output.Applications); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output.Applications[0], nil +} + func getApplication(ctx context.Context, id string, conn *elasticbeanstalk.ElasticBeanstalk) (*elasticbeanstalk.ApplicationDescription, error) { resp, err := conn.DescribeApplicationsWithContext(ctx, &elasticbeanstalk.DescribeApplicationsInput{ ApplicationNames: []*string{aws.String(id)}, From a3dd7a9549ad5ec82bd1d8ee29462191742589d0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 11:36:30 -0400 Subject: [PATCH 09/14] d/aws_elastic_beanstalk_application: Alphabetize attributes. --- .../application_data_source.go | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/internal/service/elasticbeanstalk/application_data_source.go b/internal/service/elasticbeanstalk/application_data_source.go index 3abd5282ad50..3f54021ac1f1 100644 --- a/internal/service/elasticbeanstalk/application_data_source.go +++ b/internal/service/elasticbeanstalk/application_data_source.go @@ -17,25 +17,13 @@ func DataSourceApplication() *schema.Resource { ReadWithoutTimeout: dataSourceApplicationRead, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "name": { - Type: schema.TypeString, - Required: true, - }, - "description": { - Type: schema.TypeString, - Computed: true, - }, "appversion_lifecycle": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "service_role": { - Type: schema.TypeString, + "delete_source_from_s3": { + Type: schema.TypeBool, Computed: true, }, "max_age_in_days": { @@ -46,13 +34,25 @@ func DataSourceApplication() *schema.Resource { Type: schema.TypeInt, Computed: true, }, - "delete_source_from_s3": { - Type: schema.TypeBool, + "service_role": { + Type: schema.TypeString, Computed: true, }, }, }, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, }, } } @@ -83,7 +83,7 @@ func dataSourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta d.Set("description", app.Description) if app.ResourceLifecycleConfig != nil { - d.Set("appversion_lifecycle", flattenResourceLifecycleConfig(app.ResourceLifecycleConfig)) + d.Set("appversion_lifecycle", flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)) } return diags From 29ec2515000841f5a601165709ab1bc572693642 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 11:41:12 -0400 Subject: [PATCH 10/14] r/aws_elastic_beanstalk_application: Tidy up resource Read. --- .../service/elasticbeanstalk/application.go | 122 +++++++++++------- .../application_data_source.go | 26 ++-- .../application_data_source_test.go | 14 +- internal/service/elasticbeanstalk/flex.go | 31 ----- 4 files changed, 90 insertions(+), 103 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index c2073919d877..43ace20ac1eb 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -10,7 +10,6 @@ import ( "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/retry" "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" @@ -100,6 +99,14 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta d.SetId(name) + _, err = tfresource.RetryWhenNotFound(ctx, 30*time.Second, func() (interface{}, error) { + return FindApplicationByName(ctx, conn, d.Id()) + }) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for Elastic Beanstalk Application (%s) create: %s", d.Id(), err) + } + if err = resourceApplicationAppVersionLifecycleUpdate(ctx, conn, d, output.Application); err != nil { return sdkdiag.AppendErrorf(diags, "creating Elastic Beanstalk Application (%s): %s", name, err) } @@ -107,6 +114,34 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta return append(diags, resourceApplicationRead(ctx, d, meta)...) } +func resourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() + + app, err := FindApplicationByName(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] Elastic Beanstalk Application (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading Elastic Beanstalk Application (%s): %s", d.Id(), err) + } + + if app.ResourceLifecycleConfig != nil { + if err := d.Set("appversion_lifecycle", []interface{}{flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) + } + } + d.Set("arn", app.ApplicationArn) + d.Set("description", app.Description) + d.Set("name", app.ApplicationName) + + return diags +} + func resourceApplicationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() @@ -212,51 +247,6 @@ func resourceApplicationAppVersionLifecycleUpdate(ctx context.Context, beanstalk return nil } -func resourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() - - var app *elasticbeanstalk.ApplicationDescription - err := retry.RetryContext(ctx, 30*time.Second, func() *retry.RetryError { - var err error - app, err = getApplication(ctx, d.Id(), conn) - if err != nil { - return retry.NonRetryableError(err) - } - - if app == nil { - err = fmt.Errorf("Elastic Beanstalk Application %q not found", d.Id()) - if d.IsNewResource() { - return retry.RetryableError(err) - } - return retry.NonRetryableError(err) - } - return nil - }) - if tfresource.TimedOut(err) { - app, err = getApplication(ctx, d.Id(), conn) - } - if err != nil { - if app == nil { - log.Printf("[WARN] %s, removing from state", err) - d.SetId("") - return diags - } - return sdkdiag.AppendErrorf(diags, "reading Elastic Beanstalk Application (%s): %s", d.Id(), err) - } - - arn := aws.StringValue(app.ApplicationArn) - d.Set("arn", arn) - d.Set("name", app.ApplicationName) - d.Set("description", app.Description) - - if app.ResourceLifecycleConfig != nil { - d.Set("appversion_lifecycle", flattenResourceLifecycleConfig(app.ResourceLifecycleConfig)) - } - - return diags -} - func resourceApplicationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() @@ -324,3 +314,43 @@ func getApplication(ctx context.Context, id string, conn *elasticbeanstalk.Elast return resp.Applications[0], nil } + +func flattenApplicationResourceLifecycleConfig(apiObject *elasticbeanstalk.ApplicationResourceLifecycleConfig) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if apiObject := apiObject.VersionLifecycleConfig; apiObject != nil { + if apiObject := apiObject.MaxAgeRule; apiObject != nil && aws.BoolValue(apiObject.Enabled) { + if v := apiObject.DeleteSourceFromS3; v != nil { + tfMap["delete_source_from_s3"] = aws.BoolValue(v) + } + + if v := apiObject.MaxAgeInDays; v != nil { + tfMap["max_age_in_days"] = aws.Int64Value(v) + } + } + + if apiObject := apiObject.MaxCountRule; apiObject != nil && aws.BoolValue(apiObject.Enabled) { + if v := apiObject.DeleteSourceFromS3; v != nil { + tfMap["delete_source_from_s3"] = aws.BoolValue(v) + } + + if v := apiObject.MaxCount; v != nil { + tfMap["max_count"] = aws.Int64Value(v) + } + } + } + + if len(tfMap) == 0 { + return nil + } + + if v := apiObject.ServiceRole; v != nil { + tfMap["service_role"] = aws.StringValue(v) + } + + return tfMap +} diff --git a/internal/service/elasticbeanstalk/application_data_source.go b/internal/service/elasticbeanstalk/application_data_source.go index 3f54021ac1f1..e9add2ffe966 100644 --- a/internal/service/elasticbeanstalk/application_data_source.go +++ b/internal/service/elasticbeanstalk/application_data_source.go @@ -3,8 +3,6 @@ package elasticbeanstalk import ( "context" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/elasticbeanstalk" "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" @@ -61,30 +59,22 @@ func dataSourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() - // Get the name and description name := d.Get("name").(string) + app, err := FindApplicationByName(ctx, conn, name) - resp, err := conn.DescribeApplicationsWithContext(ctx, &elasticbeanstalk.DescribeApplicationsInput{ - ApplicationNames: []*string{aws.String(name)}, - }) if err != nil { - return sdkdiag.AppendErrorf(diags, "describing Applications (%s): %s", name, err) + return sdkdiag.AppendErrorf(diags, "reading Elastic Beanstalk Application (%s): %s", name, err) } - if len(resp.Applications) > 1 || len(resp.Applications) < 1 { - return sdkdiag.AppendErrorf(diags, "%d Applications matched, expected 1", len(resp.Applications)) - } - - app := resp.Applications[0] - d.SetId(name) - d.Set("arn", app.ApplicationArn) - d.Set("name", app.ApplicationName) - d.Set("description", app.Description) - if app.ResourceLifecycleConfig != nil { - d.Set("appversion_lifecycle", flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)) + if err := d.Set("appversion_lifecycle", []interface{}{flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)}); err != nil { + return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) + } } + d.Set("arn", app.ApplicationArn) + d.Set("description", app.Description) + d.Set("name", app.ApplicationName) return diags } diff --git a/internal/service/elasticbeanstalk/application_data_source_test.go b/internal/service/elasticbeanstalk/application_data_source_test.go index e00a1cdadfb6..c9a8c466a9f5 100644 --- a/internal/service/elasticbeanstalk/application_data_source_test.go +++ b/internal/service/elasticbeanstalk/application_data_source_test.go @@ -25,12 +25,12 @@ func TestAccElasticBeanstalkApplicationDataSource_basic(t *testing.T) { Config: testAccApplicationDataSourceConfig_basic(rName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttrSet(dataSourceResourceName, "arn"), - resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceResourceName, "name"), - resource.TestCheckResourceAttrPair(resourceName, "description", dataSourceResourceName, "description"), resource.TestCheckResourceAttr(dataSourceResourceName, "appversion_lifecycle.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.service_role", dataSourceResourceName, "appversion_lifecycle.0.service_role"), - resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.max_age_in_days", dataSourceResourceName, "appversion_lifecycle.0.max_age_in_days"), resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.delete_source_from_s3", dataSourceResourceName, "appversion_lifecycle.0.delete_source_from_s3"), + resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.max_age_in_days", dataSourceResourceName, "appversion_lifecycle.0.max_age_in_days"), + resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.service_role", dataSourceResourceName, "appversion_lifecycle.0.service_role"), + resource.TestCheckResourceAttrPair(resourceName, "description", dataSourceResourceName, "description"), + resource.TestCheckResourceAttrPair(resourceName, "name", dataSourceResourceName, "name"), ), }, }, @@ -38,11 +38,9 @@ func TestAccElasticBeanstalkApplicationDataSource_basic(t *testing.T) { } func testAccApplicationDataSourceConfig_basic(rName string) string { - return fmt.Sprintf(` -%s - + return acctest.ConfigCompose(testAccApplicationConfig_maxAge(rName), ` data "aws_elastic_beanstalk_application" "test" { name = aws_elastic_beanstalk_application.tftest.name } -`, testAccApplicationConfig_maxAge(rName)) +`) } diff --git a/internal/service/elasticbeanstalk/flex.go b/internal/service/elasticbeanstalk/flex.go index 754b66f0ae81..efb3c0bea279 100644 --- a/internal/service/elasticbeanstalk/flex.go +++ b/internal/service/elasticbeanstalk/flex.go @@ -1,7 +1,6 @@ package elasticbeanstalk import ( - "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" ) @@ -64,33 +63,3 @@ func flattenTriggers(list []*elasticbeanstalk.Trigger) []string { } return strs } - -func flattenResourceLifecycleConfig(rlc *elasticbeanstalk.ApplicationResourceLifecycleConfig) []map[string]interface{} { - result := make([]map[string]interface{}, 0, 1) - - anything_enabled := false - appversion_lifecycle := make(map[string]interface{}) - - if rlc.ServiceRole != nil { - appversion_lifecycle["service_role"] = aws.StringValue(rlc.ServiceRole) - } - - if vlc := rlc.VersionLifecycleConfig; vlc != nil { - if mar := vlc.MaxAgeRule; mar != nil && aws.BoolValue(mar.Enabled) { - anything_enabled = true - appversion_lifecycle["max_age_in_days"] = aws.Int64Value(mar.MaxAgeInDays) - appversion_lifecycle["delete_source_from_s3"] = aws.BoolValue(mar.DeleteSourceFromS3) - } - if mcr := vlc.MaxCountRule; mcr != nil && aws.BoolValue(mcr.Enabled) { - anything_enabled = true - appversion_lifecycle["max_count"] = aws.Int64Value(mcr.MaxCount) - appversion_lifecycle["delete_source_from_s3"] = aws.BoolValue(mcr.DeleteSourceFromS3) - } - } - - if anything_enabled { - result = append(result, appversion_lifecycle) - } - - return result -} From 2abe7436d2e013e751cc9f450a1dbc99c0274240 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 12:02:58 -0400 Subject: [PATCH 11/14] r/aws_elastic_beanstalk_application: Tidy up resource Update. --- .../service/elasticbeanstalk/application.go | 164 +++++++----------- 1 file changed, 62 insertions(+), 102 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index 43ace20ac1eb..3f99e9d275e4 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -2,13 +2,11 @@ package elasticbeanstalk import ( "context" - "fmt" "log" "time" "github.com/aws/aws-sdk-go/aws" "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/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -91,7 +89,7 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta Tags: GetTagsIn(ctx), } - output, err := conn.CreateApplicationWithContext(ctx, input) + _, err := conn.CreateApplicationWithContext(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "creating Elastic Beanstalk Application (%s): %s", name, err) @@ -107,8 +105,17 @@ func resourceApplicationCreate(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "waiting for Elastic Beanstalk Application (%s) create: %s", d.Id(), err) } - if err = resourceApplicationAppVersionLifecycleUpdate(ctx, conn, d, output.Application); err != nil { - return sdkdiag.AppendErrorf(diags, "creating Elastic Beanstalk Application (%s): %s", name, err) + if v, ok := d.GetOk("appversion_lifecycle"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input := &elasticbeanstalk.UpdateApplicationResourceLifecycleInput{ + ApplicationName: aws.String(d.Id()), + ResourceLifecycleConfig: expandApplicationResourceLifecycleConfig(v.([]interface{})[0].(map[string]interface{})), + } + + _, err := conn.UpdateApplicationResourceLifecycleWithContext(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating Elastic Beanstalk Application (%s) resource lifecycle: %s", d.Id(), err) + } } return append(diags, resourceApplicationRead(ctx, d, meta)...) @@ -147,104 +154,40 @@ func resourceApplicationUpdate(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).ElasticBeanstalkConn() if d.HasChange("description") { - if err := resourceApplicationDescriptionUpdate(ctx, conn, d); err != nil { - return sdkdiag.AppendErrorf(diags, "updating Elastic Beanstalk Application (%s): %s", d.Id(), err) + input := &elasticbeanstalk.UpdateApplicationInput{ + ApplicationName: aws.String(d.Id()), + Description: aws.String(d.Get("description").(string)), } - } - if d.HasChange("appversion_lifecycle") { - if err := resourceApplicationAppVersionLifecycleUpdate(ctx, conn, d, nil); err != nil { + _, err := conn.UpdateApplicationWithContext(ctx, input) + + if err != nil { return sdkdiag.AppendErrorf(diags, "updating Elastic Beanstalk Application (%s): %s", d.Id(), err) } } - return append(diags, resourceApplicationRead(ctx, d, meta)...) -} - -func resourceApplicationDescriptionUpdate(ctx context.Context, beanstalkConn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData) error { - name := d.Get("name").(string) - description := d.Get("description").(string) - - log.Printf("[DEBUG] Elastic Beanstalk application: %s, update description: %s", name, description) - - _, err := beanstalkConn.UpdateApplicationWithContext(ctx, &elasticbeanstalk.UpdateApplicationInput{ - ApplicationName: aws.String(name), - Description: aws.String(description), - }) - - return err -} - -func resourceApplicationAppVersionLifecycleUpdate(ctx context.Context, beanstalkConn *elasticbeanstalk.ElasticBeanstalk, d *schema.ResourceData, app *elasticbeanstalk.ApplicationDescription) error { - name := d.Get("name").(string) - appversion_lifecycles := d.Get("appversion_lifecycle").([]interface{}) - var appversion_lifecycle map[string]interface{} - if len(appversion_lifecycles) == 1 { - appversion_lifecycle = appversion_lifecycles[0].(map[string]interface{}) - } - - if appversion_lifecycle == nil && app != nil && app.ResourceLifecycleConfig.ServiceRole == nil { - // We want appversion lifecycle management to be disabled, and it currently is, and there's no way to reproduce - // this state in a UpdateApplicationResourceLifecycle service call (fails w/ ServiceRole is not a valid arn). So, - // in this special case we just do nothing. - log.Printf("[DEBUG] Elastic Beanstalk application: %s, update appversion_lifecycle is anticipated no-op", name) - return nil - } - - rlc := &elasticbeanstalk.ApplicationResourceLifecycleConfig{ - ServiceRole: nil, - VersionLifecycleConfig: &elasticbeanstalk.ApplicationVersionLifecycleConfig{ - MaxCountRule: &elasticbeanstalk.MaxCountRule{ - Enabled: aws.Bool(false), - }, - MaxAgeRule: &elasticbeanstalk.MaxAgeRule{ - Enabled: aws.Bool(false), - }, - }, - } + if d.HasChange("appversion_lifecycle") { + var resourceLifecycleConfig *elasticbeanstalk.ApplicationResourceLifecycleConfig - if appversion_lifecycle != nil { - service_role, ok := appversion_lifecycle["service_role"] - if ok { - rlc.ServiceRole = aws.String(service_role.(string)) + if v, ok := d.GetOk("appversion_lifecycle"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + resourceLifecycleConfig = expandApplicationResourceLifecycleConfig(v.([]interface{})[0].(map[string]interface{})) + } else { + resourceLifecycleConfig = expandApplicationResourceLifecycleConfig(map[string]interface{}{}) } - rlc.VersionLifecycleConfig = &elasticbeanstalk.ApplicationVersionLifecycleConfig{ - MaxCountRule: &elasticbeanstalk.MaxCountRule{ - Enabled: aws.Bool(false), - }, - MaxAgeRule: &elasticbeanstalk.MaxAgeRule{ - Enabled: aws.Bool(false), - }, + input := &elasticbeanstalk.UpdateApplicationResourceLifecycleInput{ + ApplicationName: aws.String(d.Id()), + ResourceLifecycleConfig: resourceLifecycleConfig, } - max_age_in_days, ok := appversion_lifecycle["max_age_in_days"] - if ok && max_age_in_days != 0 { - rlc.VersionLifecycleConfig.MaxAgeRule = &elasticbeanstalk.MaxAgeRule{ - Enabled: aws.Bool(true), - DeleteSourceFromS3: aws.Bool(appversion_lifecycle["delete_source_from_s3"].(bool)), - MaxAgeInDays: aws.Int64(int64(max_age_in_days.(int))), - } - } + _, err := conn.UpdateApplicationResourceLifecycleWithContext(ctx, input) - max_count, ok := appversion_lifecycle["max_count"] - if ok && max_count != 0 { - rlc.VersionLifecycleConfig.MaxCountRule = &elasticbeanstalk.MaxCountRule{ - Enabled: aws.Bool(true), - DeleteSourceFromS3: aws.Bool(appversion_lifecycle["delete_source_from_s3"].(bool)), - MaxCount: aws.Int64(int64(max_count.(int))), - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating Elastic Beanstalk Application (%s) resource lifecycle: %s", d.Id(), err) } } - _, err := beanstalkConn.UpdateApplicationResourceLifecycleWithContext(ctx, &elasticbeanstalk.UpdateApplicationResourceLifecycleInput{ - ApplicationName: aws.String(name), - ResourceLifecycleConfig: rlc, - }) - if err != nil { - return fmt.Errorf("updating application resource lifecycle: %w", err) - } - return nil + return append(diags, resourceApplicationRead(ctx, d, meta)...) } func resourceApplicationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -293,26 +236,43 @@ func FindApplicationByName(ctx context.Context, conn *elasticbeanstalk.ElasticBe return output.Applications[0], nil } -func getApplication(ctx context.Context, id string, conn *elasticbeanstalk.ElasticBeanstalk) (*elasticbeanstalk.ApplicationDescription, error) { - resp, err := conn.DescribeApplicationsWithContext(ctx, &elasticbeanstalk.DescribeApplicationsInput{ - ApplicationNames: []*string{aws.String(id)}, - }) - if err != nil { - if tfawserr.ErrCodeEquals(err, "InvalidBeanstalkAppID.NotFound") { - return nil, nil - } - return nil, err +func expandApplicationResourceLifecycleConfig(tfMap map[string]interface{}) *elasticbeanstalk.ApplicationResourceLifecycleConfig { + if tfMap == nil { + return nil } - if len(resp.Applications) > 1 { - return nil, fmt.Errorf("Error %d Applications matched, expected 1", len(resp.Applications)) + apiObject := &elasticbeanstalk.ApplicationResourceLifecycleConfig{ + VersionLifecycleConfig: &elasticbeanstalk.ApplicationVersionLifecycleConfig{ + MaxCountRule: &elasticbeanstalk.MaxCountRule{ + Enabled: aws.Bool(false), + }, + MaxAgeRule: &elasticbeanstalk.MaxAgeRule{ + Enabled: aws.Bool(false), + }, + }, + } + + if v, ok := tfMap["service_role"].(string); ok && v != "" { + apiObject.ServiceRole = aws.String(v) } - if len(resp.Applications) == 0 { - return nil, nil + if v, ok := tfMap["max_age_in_days"].(int); ok && v != 0 { + apiObject.VersionLifecycleConfig.MaxAgeRule = &elasticbeanstalk.MaxAgeRule{ + DeleteSourceFromS3: aws.Bool(tfMap["delete_source_from_s3"].(bool)), + Enabled: aws.Bool(true), + MaxAgeInDays: aws.Int64(int64(v)), + } + } + + if v, ok := tfMap["max_count"].(int); ok && v != 0 { + apiObject.VersionLifecycleConfig.MaxCountRule = &elasticbeanstalk.MaxCountRule{ + DeleteSourceFromS3: aws.Bool(tfMap["delete_source_from_s3"].(bool)), + Enabled: aws.Bool(true), + MaxCount: aws.Int64(int64(v)), + } } - return resp.Applications[0], nil + return apiObject } func flattenApplicationResourceLifecycleConfig(apiObject *elasticbeanstalk.ApplicationResourceLifecycleConfig) map[string]interface{} { From 40d64d7e47865438803078fef393af30efe63166 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 12:46:07 -0400 Subject: [PATCH 12/14] r/aws_elastic_beanstalk_application: Start to tidy up acceptance tests. --- .../service/elasticbeanstalk/application.go | 10 ++--- .../application_data_source.go | 6 +-- .../elasticbeanstalk/application_test.go | 44 ++++++++----------- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index 3f99e9d275e4..d9761cd74e21 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -137,10 +137,8 @@ func resourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta i return sdkdiag.AppendErrorf(diags, "reading Elastic Beanstalk Application (%s): %s", d.Id(), err) } - if app.ResourceLifecycleConfig != nil { - if err := d.Set("appversion_lifecycle", []interface{}{flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)}); err != nil { - return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) - } + if err := d.Set("appversion_lifecycle", flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) } d.Set("arn", app.ApplicationArn) d.Set("description", app.Description) @@ -275,7 +273,7 @@ func expandApplicationResourceLifecycleConfig(tfMap map[string]interface{}) *ela return apiObject } -func flattenApplicationResourceLifecycleConfig(apiObject *elasticbeanstalk.ApplicationResourceLifecycleConfig) map[string]interface{} { +func flattenApplicationResourceLifecycleConfig(apiObject *elasticbeanstalk.ApplicationResourceLifecycleConfig) []interface{} { if apiObject == nil { return nil } @@ -312,5 +310,5 @@ func flattenApplicationResourceLifecycleConfig(apiObject *elasticbeanstalk.Appli tfMap["service_role"] = aws.StringValue(v) } - return tfMap + return []interface{}{tfMap} } diff --git a/internal/service/elasticbeanstalk/application_data_source.go b/internal/service/elasticbeanstalk/application_data_source.go index e9add2ffe966..b8fca5d261ad 100644 --- a/internal/service/elasticbeanstalk/application_data_source.go +++ b/internal/service/elasticbeanstalk/application_data_source.go @@ -67,10 +67,8 @@ func dataSourceApplicationRead(ctx context.Context, d *schema.ResourceData, meta } d.SetId(name) - if app.ResourceLifecycleConfig != nil { - if err := d.Set("appversion_lifecycle", []interface{}{flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)}); err != nil { - return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) - } + if err := d.Set("appversion_lifecycle", flattenApplicationResourceLifecycleConfig(app.ResourceLifecycleConfig)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting appversion_lifecycle: %s", err) } d.Set("arn", app.ApplicationArn) d.Set("description", app.Description) diff --git a/internal/service/elasticbeanstalk/application_test.go b/internal/service/elasticbeanstalk/application_test.go index 87f37ee9086e..d5251ffdb9ae 100644 --- a/internal/service/elasticbeanstalk/application_test.go +++ b/internal/service/elasticbeanstalk/application_test.go @@ -5,17 +5,17 @@ import ( "fmt" "testing" - "github.com/aws/aws-sdk-go/aws" "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" "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 TestAccElasticBeanstalkApplication_BeanstalkApp_basic(t *testing.T) { +func TestAccElasticBeanstalkApplication_basic(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -42,7 +42,7 @@ func TestAccElasticBeanstalkApplication_BeanstalkApp_basic(t *testing.T) { }) } -func TestAccElasticBeanstalkApplication_BeanstalkApp_appVersionLifecycle(t *testing.T) { +func TestAccElasticBeanstalkApplication_appVersionLifecycle(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -103,7 +103,7 @@ func TestAccElasticBeanstalkApplication_BeanstalkApp_appVersionLifecycle(t *test }) } -func TestAccElasticBeanstalkApplication_BeanstalkApp_tags(t *testing.T) { +func TestAccElasticBeanstalkApplication_tags(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -170,28 +170,24 @@ func testAccCheckApplicationDestroy(ctx context.Context) resource.TestCheckFunc continue } - // Try to find the application - DescribeBeanstalkAppOpts := &elasticbeanstalk.DescribeApplicationsInput{ - ApplicationNames: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeApplicationsWithContext(ctx, DescribeBeanstalkAppOpts) - if err == nil { - if len(resp.Applications) > 0 { - return fmt.Errorf("Elastic Beanstalk Application still exists.") - } - return nil + _, err := tfelasticbeanstalk.FindApplicationByName(ctx, conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - if !tfawserr.ErrCodeEquals(err, "InvalidBeanstalkAppID.NotFound") { + if err != nil { return err } + + return fmt.Errorf("Elastic Beanstalk Application %s still exists", rs.Primary.ID) } return nil } } -func testAccCheckApplicationExists(ctx context.Context, n string, app *elasticbeanstalk.ApplicationDescription) resource.TestCheckFunc { +func testAccCheckApplicationExists(ctx context.Context, n string, v *elasticbeanstalk.ApplicationDescription) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -199,22 +195,18 @@ func testAccCheckApplicationExists(ctx context.Context, n string, app *elasticbe } if rs.Primary.ID == "" { - return fmt.Errorf("Elastic Beanstalk app ID is not set") + return fmt.Errorf("No Elastic Beanstalk Application ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).ElasticBeanstalkConn() - DescribeBeanstalkAppOpts := &elasticbeanstalk.DescribeApplicationsInput{ - ApplicationNames: []*string{aws.String(rs.Primary.ID)}, - } - resp, err := conn.DescribeApplicationsWithContext(ctx, DescribeBeanstalkAppOpts) + + output, err := tfelasticbeanstalk.FindApplicationByName(ctx, conn, rs.Primary.ID) + if err != nil { return err } - if len(resp.Applications) == 0 { - return fmt.Errorf("Elastic Beanstalk Application not found.") - } - *app = *resp.Applications[0] + *v = *output return nil } From bf31c13fd4fd0c4f0a07788b55e5400eed71f1f9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 19 Apr 2023 13:09:10 -0400 Subject: [PATCH 13/14] r/aws_elastic_beanstalk_application: Tidy up acceptance tests. --- .changelog/17727.txt | 2 +- .../service/elasticbeanstalk/application.go | 8 +- .../application_data_source_test.go | 7 +- .../elasticbeanstalk/application_test.go | 288 +++++++++++------- 4 files changed, 181 insertions(+), 124 deletions(-) diff --git a/.changelog/17727.txt b/.changelog/17727.txt index 6e482b2cfd6e..168f4bdba396 100644 --- a/.changelog/17727.txt +++ b/.changelog/17727.txt @@ -1,3 +1,3 @@ ```release-note:enhancement -resource/aws_elastic_beanstalk_application: Add plan time validation to `appversion_lifecycle.service_role` argument +resource/aws_elastic_beanstalk_application: Add plan time validation to `appversion_lifecycle.service_role` and `name` arguments ``` \ No newline at end of file diff --git a/internal/service/elasticbeanstalk/application.go b/internal/service/elasticbeanstalk/application.go index d9761cd74e21..4f53134b50ce 100644 --- a/internal/service/elasticbeanstalk/application.go +++ b/internal/service/elasticbeanstalk/application.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/elasticbeanstalk" "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" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" @@ -68,9 +69,10 @@ func ResourceApplication() *schema.Resource { Optional: true, }, "name": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringLenBetween(1, 100), }, names.AttrTags: tftags.TagsSchema(), names.AttrTagsAll: tftags.TagsSchemaComputed(), diff --git a/internal/service/elasticbeanstalk/application_data_source_test.go b/internal/service/elasticbeanstalk/application_data_source_test.go index c9a8c466a9f5..3b38c9b80182 100644 --- a/internal/service/elasticbeanstalk/application_data_source_test.go +++ b/internal/service/elasticbeanstalk/application_data_source_test.go @@ -1,7 +1,6 @@ package elasticbeanstalk_test import ( - "fmt" "testing" "github.com/aws/aws-sdk-go/service/elasticbeanstalk" @@ -12,9 +11,9 @@ import ( func TestAccElasticBeanstalkApplicationDataSource_basic(t *testing.T) { ctx := acctest.Context(t) - rName := fmt.Sprintf("tf-acc-test-%s", sdkacctest.RandString(5)) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) dataSourceResourceName := "data.aws_elastic_beanstalk_application.test" - resourceName := "aws_elastic_beanstalk_application.tftest" + resourceName := "aws_elastic_beanstalk_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -40,7 +39,7 @@ func TestAccElasticBeanstalkApplicationDataSource_basic(t *testing.T) { func testAccApplicationDataSourceConfig_basic(rName string) string { return acctest.ConfigCompose(testAccApplicationConfig_maxAge(rName), ` data "aws_elastic_beanstalk_application" "test" { - name = aws_elastic_beanstalk_application.tftest.name + name = aws_elastic_beanstalk_application.test.name } `) } diff --git a/internal/service/elasticbeanstalk/application_test.go b/internal/service/elasticbeanstalk/application_test.go index d5251ffdb9ae..9af13b027f5f 100644 --- a/internal/service/elasticbeanstalk/application_test.go +++ b/internal/service/elasticbeanstalk/application_test.go @@ -19,7 +19,7 @@ func TestAccElasticBeanstalkApplication_basic(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_elastic_beanstalk_application.tftest" + resourceName := "aws_elastic_beanstalk_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -31,6 +31,11 @@ func TestAccElasticBeanstalkApplication_basic(t *testing.T) { Config: testAccApplicationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -42,10 +47,11 @@ func TestAccElasticBeanstalkApplication_basic(t *testing.T) { }) } -func TestAccElasticBeanstalkApplication_appVersionLifecycle(t *testing.T) { +func TestAccElasticBeanstalkApplication_disappears(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -56,58 +62,66 @@ func TestAccElasticBeanstalkApplication_appVersionLifecycle(t *testing.T) { { Config: testAccApplicationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckApplicationExists(ctx, "aws_elastic_beanstalk_application.tftest", &app), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.service_role"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_age_in_days"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_count"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.delete_source_from_s3"), + testAccCheckApplicationExists(ctx, resourceName, &app), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfelasticbeanstalk.ResourceApplication(), resourceName), ), + ExpectNonEmptyPlan: true, }, + }, + }) +} + +func TestAccElasticBeanstalkApplication_tags(t *testing.T) { + ctx := acctest.Context(t) + var app elasticbeanstalk.ApplicationDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticbeanstalk.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckApplicationDestroy(ctx), + Steps: []resource.TestStep{ { - Config: testAccApplicationConfig_maxAge(rName), + Config: testAccApplicationConfig_tags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( - testAccCheckApplicationExists(ctx, "aws_elastic_beanstalk_application.tftest", &app), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.#", "1"), - resource.TestCheckResourceAttrPair( - "aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.service_role", - "aws_iam_role.beanstalk_service", "arn"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_age_in_days", "90"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_count", "0"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.delete_source_from_s3", "true"), + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { - Config: testAccApplicationConfig_maxCount(rName), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccApplicationConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckApplicationExists(ctx, "aws_elastic_beanstalk_application.tftest", &app), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.#", "1"), - resource.TestCheckResourceAttrPair( - "aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.service_role", - "aws_iam_role.beanstalk_service", "arn"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_age_in_days", "0"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_count", "10"), - resource.TestCheckResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.delete_source_from_s3", "false"), + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, { - Config: testAccApplicationConfig_basic(rName), + Config: testAccApplicationConfig_tags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( - testAccCheckApplicationExists(ctx, "aws_elastic_beanstalk_application.tftest", &app), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.service_role"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_age_in_days"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.max_count"), - resource.TestCheckNoResourceAttr("aws_elastic_beanstalk_application.tftest", "appversion_lifecycle.0.delete_source_from_s3"), + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, }, }) } -func TestAccElasticBeanstalkApplication_tags(t *testing.T) { +func TestAccElasticBeanstalkApplication_description(t *testing.T) { ctx := acctest.Context(t) var app elasticbeanstalk.ApplicationDescription rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceName := "aws_elastic_beanstalk_application.tftest" + resourceName := "aws_elastic_beanstalk_application.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(ctx, t) }, @@ -116,40 +130,57 @@ func TestAccElasticBeanstalkApplication_tags(t *testing.T) { CheckDestroy: testAccCheckApplicationDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccApplicationConfig_tags2(rName, "test1", "test2"), + Config: testAccApplicationConfig_description(rName, "description 1"), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationExists(ctx, resourceName, &app), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.firstTag", "test1"), - resource.TestCheckResourceAttr(resourceName, "tags.secondTag", "test2"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "description", "description 1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { - Config: testAccApplicationConfig_tags2(rName, "updateTest1", "updateTest2"), - Check: resource.ComposeTestCheckFunc( - testAccCheckApplicationExists(ctx, resourceName, &app), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.firstTag", "updateTest1"), - resource.TestCheckResourceAttr(resourceName, "tags.secondTag", "updateTest2"), - ), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, { - Config: testAccApplicationConfig_tags(rName, "updateTest1", "updateTest2", "addTest3"), + Config: testAccApplicationConfig_description(rName, "description 2"), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationExists(ctx, resourceName, &app), - resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), - resource.TestCheckResourceAttr(resourceName, "tags.firstTag", "updateTest1"), - resource.TestCheckResourceAttr(resourceName, "tags.secondTag", "updateTest2"), - resource.TestCheckResourceAttr(resourceName, "tags.thirdTag", "addTest3"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "arn"), + resource.TestCheckResourceAttr(resourceName, "description", "description 2"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, + }, + }) +} + +func TestAccElasticBeanstalkApplication_appVersionLifecycle(t *testing.T) { + ctx := acctest.Context(t) + var app elasticbeanstalk.ApplicationDescription + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_elastic_beanstalk_application.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, elasticbeanstalk.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckApplicationDestroy(ctx), + Steps: []resource.TestStep{ { - Config: testAccApplicationConfig_tags2(rName, "updateTest1", "updateTest2"), + Config: testAccApplicationConfig_maxAge(rName), Check: resource.ComposeTestCheckFunc( testAccCheckApplicationExists(ctx, resourceName, &app), - resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), - resource.TestCheckResourceAttr(resourceName, "tags.firstTag", "updateTest1"), - resource.TestCheckResourceAttr(resourceName, "tags.secondTag", "updateTest2"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.service_role", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_age_in_days", "90"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_count", "0"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.delete_source_from_s3", "true"), ), }, { @@ -157,6 +188,35 @@ func TestAccElasticBeanstalkApplication_tags(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccApplicationConfig_maxCount(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.service_role", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_age_in_days", "0"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_count", "10"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.delete_source_from_s3", "false"), + ), + }, + { + Config: testAccApplicationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "0"), + ), + }, + { + Config: testAccApplicationConfig_maxAge(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckApplicationExists(ctx, resourceName, &app), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "appversion_lifecycle.0.service_role", "aws_iam_role.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_age_in_days", "90"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.max_count", "0"), + resource.TestCheckResourceAttr(resourceName, "appversion_lifecycle.0.delete_source_from_s3", "true"), + ), + }, }, }) } @@ -214,17 +274,50 @@ func testAccCheckApplicationExists(ctx context.Context, n string, v *elasticbean func testAccApplicationConfig_basic(rName string) string { return fmt.Sprintf(` -resource "aws_elastic_beanstalk_application" "tftest" { - name = "%s" - description = "tf-test-desc" +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q } `, rName) } -func testAccApplicationConfig_serviceRole(rName string) string { +func testAccApplicationConfig_tags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccApplicationConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccApplicationConfig_description(rName, description string) string { + return fmt.Sprintf(` +resource "aws_elastic_beanstalk_application" "test" { + name = %[1]q + description = %[2]q +} +`, rName, description) +} + +func testAccApplicationConfig_baseServiceRole(rName string) string { return fmt.Sprintf(` -resource "aws_iam_role" "beanstalk_service" { - name = "%[1]s" +resource "aws_iam_role" "test" { + name = %[1]q assume_role_policy = < Date: Wed, 19 Apr 2023 14:12:33 -0400 Subject: [PATCH 14/14] Fix terrafmt error. --- internal/service/elasticbeanstalk/application_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/elasticbeanstalk/application_test.go b/internal/service/elasticbeanstalk/application_test.go index 9af13b027f5f..d4173232a478 100644 --- a/internal/service/elasticbeanstalk/application_test.go +++ b/internal/service/elasticbeanstalk/application_test.go @@ -377,7 +377,7 @@ resource "aws_elastic_beanstalk_application" "test" { func testAccApplicationConfig_maxCount(rName string) string { return acctest.ConfigCompose(testAccApplicationConfig_baseServiceRole(rName), fmt.Sprintf(` resource "aws_elastic_beanstalk_application" "test" { - name = %[1]q + name = %[1]q appversion_lifecycle { service_role = aws_iam_role.test.arn