From 90659a307cb22dff0f52912c3c79979404e710cb Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Sun, 9 Sep 2018 16:41:01 -0400 Subject: [PATCH 1/6] codedeploy_deployment_group: extract retry error handling. --- ...esource_aws_codedeploy_deployment_group.go | 94 +++++++++---------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_group.go b/aws/resource_aws_codedeploy_deployment_group.go index cc28bd05293..37a5a5a5bf8 100644 --- a/aws/resource_aws_codedeploy_deployment_group.go +++ b/aws/resource_aws_codedeploy_deployment_group.go @@ -435,31 +435,9 @@ func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta int var err error err = resource.Retry(5*time.Minute, func() *resource.RetryError { resp, err = conn.CreateDeploymentGroup(&input) - if err != nil { - retry := false - codedeployErr, ok := err.(awserr.Error) - if !ok { - return resource.NonRetryableError(err) - } - if codedeployErr.Code() == "InvalidRoleException" { - retry = true - } - if codedeployErr.Code() == "InvalidTriggerConfigException" { - r := regexp.MustCompile("^Topic ARN .+ is not valid$") - if r.MatchString(codedeployErr.Message()) { - retry = true - } - } - if retry { - log.Printf("[DEBUG] Trying to create deployment group again: %q", - codedeployErr.Message()) - return resource.RetryableError(err) - } - - return resource.NonRetryableError(err) - } - return nil + return handleCreateError(err) }) + if err != nil { return err } @@ -607,33 +585,11 @@ func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta int } log.Printf("[DEBUG] Updating CodeDeploy DeploymentGroup %s", d.Id()) - // Retry to handle IAM role eventual consistency. - err := resource.Retry(5*time.Minute, func() *resource.RetryError { - _, err := conn.UpdateDeploymentGroup(&input) - if err != nil { - retry := false - codedeployErr, ok := err.(awserr.Error) - if !ok { - return resource.NonRetryableError(err) - } - if codedeployErr.Code() == "InvalidRoleException" { - retry = true - } - if codedeployErr.Code() == "InvalidTriggerConfigException" { - r := regexp.MustCompile("^Topic ARN .+ is not valid$") - if r.MatchString(codedeployErr.Message()) { - retry = true - } - } - if retry { - log.Printf("[DEBUG] Retrying Code Deployment Group Update: %q", - codedeployErr.Message()) - return resource.RetryableError(err) - } - return resource.NonRetryableError(err) - } - return nil + var err error + err = resource.Retry(5*time.Minute, func() *resource.RetryError { + _, err = conn.UpdateDeploymentGroup(&input) + return handleUpdateError(err) }) if err != nil { @@ -658,6 +614,44 @@ func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta int return nil } +func handleCreateError(err error) *resource.RetryError { + return handleCodeDeployApiError(err, "create") +} + +func handleUpdateError(err error) *resource.RetryError { + return handleCodeDeployApiError(err, "update") +} + +func handleCodeDeployApiError(err error, operation string) *resource.RetryError { + if err == nil { + return nil + } + + retry := false + codedeployErr, ok := err.(awserr.Error) + if !ok { + return resource.NonRetryableError(err) + } + + if codedeployErr.Code() == "InvalidRoleException" { + retry = true + } + + if codedeployErr.Code() == "InvalidTriggerConfigException" { + r := regexp.MustCompile("^Topic ARN .+ is not valid$") + if r.MatchString(codedeployErr.Message()) { + retry = true + } + } + + if retry { + log.Printf("[DEBUG] Trying to %s DeploymentGroup again: %q", operation, codedeployErr.Message()) + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) +} + // buildOnPremTagFilters converts raw schema lists into a list of // codedeploy.TagFilters. func buildOnPremTagFilters(configured []interface{}) []*codedeploy.TagFilter { From 2eb868939125cd466dfc8a20c5ebd54e4c3dcba4 Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Sun, 9 Sep 2018 16:48:13 -0400 Subject: [PATCH 2/6] codedeploy_deployment_group: make inputs for create and update the same. --- ...esource_aws_codedeploy_deployment_group.go | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_group.go b/aws/resource_aws_codedeploy_deployment_group.go index 37a5a5a5bf8..2a5274f457d 100644 --- a/aws/resource_aws_codedeploy_deployment_group.go +++ b/aws/resource_aws_codedeploy_deployment_group.go @@ -375,13 +375,15 @@ func resourceAwsCodeDeployDeploymentGroup() *schema.Resource { func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).codedeployconn - application := d.Get("app_name").(string) - deploymentGroup := d.Get("deployment_group_name").(string) + // required fields + applicationName := d.Get("app_name").(string) + deploymentGroupName := d.Get("deployment_group_name").(string) + serviceRoleArn := d.Get("service_role_arn").(string) input := codedeploy.CreateDeploymentGroupInput{ - ApplicationName: aws.String(application), - DeploymentGroupName: aws.String(deploymentGroup), - ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)), + ApplicationName: aws.String(applicationName), + DeploymentGroupName: aws.String(deploymentGroupName), + ServiceRoleArn: aws.String(serviceRoleArn), } if attr, ok := d.GetOk("deployment_style"); ok { @@ -430,7 +432,8 @@ func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta int input.BlueGreenDeploymentConfiguration = expandBlueGreenDeploymentConfig(attr.([]interface{})) } - // Retry to handle IAM role eventual consistency. + log.Printf("[DEBUG] Creating CodeDeploy DeploymentGroup %s", applicationName) + var resp *codedeploy.CreateDeploymentGroupOutput var err error err = resource.Retry(5*time.Minute, func() *resource.RetryError { @@ -451,10 +454,12 @@ func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta inter conn := meta.(*AWSClient).codedeployconn log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id()) + resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{ ApplicationName: aws.String(d.Get("app_name").(string)), DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), }) + if err != nil { if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "DeploymentGroupDoesNotExistException" { log.Printf("[INFO] CodeDeployment DeploymentGroup %s not found", d.Get("deployment_group_name").(string)) @@ -513,20 +518,15 @@ func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta inter func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).codedeployconn - input := codedeploy.UpdateDeploymentGroupInput{ - ApplicationName: aws.String(d.Get("app_name").(string)), - CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), - ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)), - } + // required fields + applicationName := d.Get("app_name").(string) + deploymentGroupName := d.Get("deployment_group_name").(string) + serviceRoleArn := d.Get("service_role_arn").(string) - if d.HasChange("autoscaling_groups") { - _, n := d.GetChange("autoscaling_groups") - input.AutoScalingGroups = expandStringList(n.(*schema.Set).List()) - } - - if d.HasChange("deployment_config_name") { - _, n := d.GetChange("deployment_config_name") - input.DeploymentConfigName = aws.String(n.(string)) + input := codedeploy.UpdateDeploymentGroupInput{ + ApplicationName: aws.String(applicationName), + CurrentDeploymentGroupName: aws.String(deploymentGroupName), + ServiceRoleArn: aws.String(serviceRoleArn), } if d.HasChange("deployment_group_name") { @@ -539,6 +539,16 @@ func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta int input.DeploymentStyle = expandDeploymentStyle(n.([]interface{})) } + if d.HasChange("deployment_config_name") { + _, n := d.GetChange("deployment_config_name") + input.DeploymentConfigName = aws.String(n.(string)) + } + + if d.HasChange("autoscaling_groups") { + _, n := d.GetChange("autoscaling_groups") + input.AutoScalingGroups = expandStringList(n.(*schema.Set).List()) + } + // TagFilters aren't like tags. They don't append. They simply replace. if d.HasChange("on_premises_instance_tag_filter") { _, n := d.GetChange("on_premises_instance_tag_filter") @@ -607,6 +617,7 @@ func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta int ApplicationName: aws.String(d.Get("app_name").(string)), DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), }) + if err != nil { return err } From acc89e0d6ab6b6268d65aec9871846979773d5cf Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Sun, 9 Sep 2018 17:02:44 -0400 Subject: [PATCH 3/6] codedeploy_deployment_group: some blue/green updates require autoscaling groups. - when using COPY_AUTO_SCALING_GROUP option. - includes test case for this scenario. --- ...esource_aws_codedeploy_deployment_group.go | 3 +- ...ce_aws_codedeploy_deployment_group_test.go | 124 +++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_group.go b/aws/resource_aws_codedeploy_deployment_group.go index 2a5274f457d..92b73b2d740 100644 --- a/aws/resource_aws_codedeploy_deployment_group.go +++ b/aws/resource_aws_codedeploy_deployment_group.go @@ -544,7 +544,8 @@ func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta int input.DeploymentConfigName = aws.String(n.(string)) } - if d.HasChange("autoscaling_groups") { + // include (original or new) autoscaling groups when blue_green_deployment_config changes + if d.HasChange("autoscaling_groups") || d.HasChange("blue_green_deployment_config") { _, n := d.GetChange("autoscaling_groups") input.AutoScalingGroups = expandStringList(n.(*schema.Set).List()) } diff --git a/aws/resource_aws_codedeploy_deployment_group_test.go b/aws/resource_aws_codedeploy_deployment_group_test.go index 9f107231c5e..44cf2aef920 100644 --- a/aws/resource_aws_codedeploy_deployment_group_test.go +++ b/aws/resource_aws_codedeploy_deployment_group_test.go @@ -1048,7 +1048,6 @@ func TestAccAWSCodeDeployDeploymentGroup_in_place_deployment_with_traffic_contro "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.#", "0"), ), }, - { Config: test_config_in_place_deployment_with_traffic_control_create(rName), Check: resource.ComposeTestCheckFunc( @@ -1121,6 +1120,74 @@ func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeploymentConfiguration_create }) } +func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeploymentConfiguration_update_with_asg(t *testing.T) { + var group codedeploy.DeploymentGroupInfo + + rName := acctest.RandString(5) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployDeploymentGroupDestroy, + Steps: []resource.TestStep{ + { + Config: test_config_blue_green_deployment_config_create_with_asg(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.#", "1"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.action_on_timeout", "STOP_DEPLOYMENT"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.wait_time_in_minutes", "60"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.0.action", "COPY_AUTO_SCALING_GROUP"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.action", "TERMINATE"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.termination_wait_time_in_minutes", "120"), + ), + }, + { + Config: test_config_blue_green_deployment_config_update_with_asg(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.#", "1"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.action_on_timeout", "STOP_DEPLOYMENT"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.wait_time_in_minutes", "60"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.0.action", "COPY_AUTO_SCALING_GROUP"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.action", "KEEP_ALIVE"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.termination_wait_time_in_minutes", "120"), + ), + }, + }, + }) +} + func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeploymentConfiguration_update(t *testing.T) { var group codedeploy.DeploymentGroupInfo @@ -2638,6 +2705,61 @@ resource "aws_codedeploy_deployment_group" "foo_group" { }`, baseCodeDeployConfig(rName), rName, rName) } +func test_config_blue_green_deployment_config_update_with_asg(rName string) string { + return fmt.Sprintf(` + + %s + +resource "aws_launch_configuration" "foo_lc" { + image_id = "ami-21f78e11" + instance_type = "t1.micro" + "name_prefix" = "foo-lc-" + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_autoscaling_group" "foo_asg" { + name = "foo-asg-%s" + max_size = 2 + min_size = 0 + desired_capacity = 1 + + availability_zones = ["us-west-2a"] + + launch_configuration = "${aws_launch_configuration.foo_lc.name}" + + lifecycle { + create_before_destroy = true + } +} + +resource "aws_codedeploy_deployment_group" "foo_group" { + app_name = "${aws_codedeploy_app.foo_app.name}" + deployment_group_name = "foo-group-%s" + service_role_arn = "${aws_iam_role.foo_role.arn}" + + autoscaling_groups = ["${aws_autoscaling_group.foo_asg.name}"] + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "STOP_DEPLOYMENT" + wait_time_in_minutes = 60 + } + + green_fleet_provisioning_option { + action = "COPY_AUTO_SCALING_GROUP" + } + + terminate_blue_instances_on_deployment_success { + action = "KEEP_ALIVE" + termination_wait_time_in_minutes = 120 + } + } +}`, baseCodeDeployConfig(rName), rName, rName) +} + func test_config_blue_green_deployment_config_create_no_asg(rName string) string { return fmt.Sprintf(` From 58419fd0f6379f8a96f160bc8ceb43770cfaeff3 Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Sun, 9 Sep 2018 18:31:43 -0400 Subject: [PATCH 4/6] codedeploy_deployment_group: more tests for updating groups. - to check for issues propagating load_balancer_info. --- ...ce_aws_codedeploy_deployment_group_test.go | 192 +++++++++++++++++- 1 file changed, 185 insertions(+), 7 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_group_test.go b/aws/resource_aws_codedeploy_deployment_group_test.go index 44cf2aef920..18510dcbd4c 100644 --- a/aws/resource_aws_codedeploy_deployment_group_test.go +++ b/aws/resource_aws_codedeploy_deployment_group_test.go @@ -1071,6 +1071,73 @@ func TestAccAWSCodeDeployDeploymentGroup_in_place_deployment_with_traffic_contro }) } +func TestAccAWSCodeDeployDeploymentGroup_in_place_deployment_with_traffic_control_update(t *testing.T) { + var group codedeploy.DeploymentGroupInfo + + rName := acctest.RandString(5) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployDeploymentGroupDestroy, + Steps: []resource.TestStep{ + { + Config: test_config_in_place_deployment_with_traffic_control_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_option", "WITH_TRAFFIC_CONTROL"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_type", "IN_PLACE"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.2441772102.name", "foo-elb"), + ), + }, + { + Config: test_config_in_place_deployment_with_traffic_control_update(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_option", "WITH_TRAFFIC_CONTROL"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_type", "BLUE_GREEN"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.2441772102.name", "foo-elb"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.action_on_timeout", "CONTINUE_DEPLOYMENT"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.0.action", "DISCOVER_EXISTING"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.action", "KEEP_ALIVE"), + ), + }, + }, + }) +} + func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeploymentConfiguration_create(t *testing.T) { var group codedeploy.DeploymentGroupInfo @@ -1180,8 +1247,6 @@ func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeploymentConfiguration_update "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.#", "1"), resource.TestCheckResourceAttr( "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.action", "KEEP_ALIVE"), - resource.TestCheckResourceAttr( - "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.termination_wait_time_in_minutes", "120"), ), }, }, @@ -1355,6 +1420,46 @@ func TestAccAWSCodeDeployDeploymentGroup_blueGreenDeployment_complete(t *testing "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.termination_wait_time_in_minutes", "0"), ), }, + { + Config: test_config_blue_green_deployment_complete_updated(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_option", "WITH_TRAFFIC_CONTROL"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "deployment_style.0.deployment_type", "BLUE_GREEN"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "load_balancer_info.0.elb_info.2441772102.name", "foo-elb"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.#", "1"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.deployment_ready_option.0.action_on_timeout", "CONTINUE_DEPLOYMENT"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.green_fleet_provisioning_option.0.action", "DISCOVER_EXISTING"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.action", "KEEP_ALIVE"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "blue_green_deployment_config.0.terminate_blue_instances_on_deployment_success.0.termination_wait_time_in_minutes", "0"), + ), + }, }, }) } @@ -1635,7 +1740,7 @@ func TestAWSCodeDeployDeploymentGroup_expandBlueGreenDeploymentConfig(t *testing "terminate_blue_instances_on_deployment_success": []interface{}{ map[string]interface{}{ - "action": "TERMINATE", + "action": "TERMINATE", "termination_wait_time_in_minutes": 90, }, }, @@ -1653,7 +1758,7 @@ func TestAWSCodeDeployDeploymentGroup_expandBlueGreenDeploymentConfig(t *testing }, TerminateBlueInstancesOnDeploymentSuccess: &codedeploy.BlueInstanceTerminationOption{ - Action: aws.String("TERMINATE"), + Action: aws.String("TERMINATE"), TerminationWaitTimeInMinutes: aws.Int64(90), }, } @@ -1678,7 +1783,7 @@ func TestAWSCodeDeployDeploymentGroup_flattenBlueGreenDeploymentConfig(t *testin }, TerminateBlueInstancesOnDeploymentSuccess: &codedeploy.BlueInstanceTerminationOption{ - Action: aws.String("KEEP_ALIVE"), + Action: aws.String("KEEP_ALIVE"), TerminationWaitTimeInMinutes: aws.Int64(90), }, } @@ -1699,7 +1804,7 @@ func TestAWSCodeDeployDeploymentGroup_flattenBlueGreenDeploymentConfig(t *testin "terminate_blue_instances_on_deployment_success": []map[string]interface{}{ { - "action": "KEEP_ALIVE", + "action": "KEEP_ALIVE", "termination_wait_time_in_minutes": 90, }, }, @@ -2638,6 +2743,43 @@ resource "aws_codedeploy_deployment_group" "foo_group" { }`, baseCodeDeployConfig(rName), rName) } +func test_config_in_place_deployment_with_traffic_control_update(rName string) string { + return fmt.Sprintf(` + + %s + +resource "aws_codedeploy_deployment_group" "foo_group" { + app_name = "${aws_codedeploy_app.foo_app.name}" + deployment_group_name = "foo-group-%s" + service_role_arn = "${aws_iam_role.foo_role.arn}" + + deployment_style { + deployment_option = "WITH_TRAFFIC_CONTROL" + deployment_type = "BLUE_GREEN" + } + + load_balancer_info { + elb_info { + name = "foo-elb" + } + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + + green_fleet_provisioning_option { + action = "DISCOVER_EXISTING" + } + + terminate_blue_instances_on_deployment_success { + action = "KEEP_ALIVE" + } + } +}`, baseCodeDeployConfig(rName), rName) +} + func test_config_blue_green_deployment_config_default(rName string) string { return fmt.Sprintf(` @@ -2754,7 +2896,6 @@ resource "aws_codedeploy_deployment_group" "foo_group" { terminate_blue_instances_on_deployment_success { action = "KEEP_ALIVE" - termination_wait_time_in_minutes = 120 } } }`, baseCodeDeployConfig(rName), rName, rName) @@ -2851,3 +2992,40 @@ resource "aws_codedeploy_deployment_group" "foo_group" { } }`, baseCodeDeployConfig(rName), rName) } + +func test_config_blue_green_deployment_complete_updated(rName string) string { + return fmt.Sprintf(` + + %s + +resource "aws_codedeploy_deployment_group" "foo_group" { + app_name = "${aws_codedeploy_app.foo_app.name}" + deployment_group_name = "foo-group-%s" + service_role_arn = "${aws_iam_role.foo_role.arn}" + + deployment_style { + deployment_option = "WITH_TRAFFIC_CONTROL" + deployment_type = "BLUE_GREEN" + } + + load_balancer_info { + elb_info { + name = "foo-elb" + } + } + + blue_green_deployment_config { + deployment_ready_option { + action_on_timeout = "CONTINUE_DEPLOYMENT" + } + + green_fleet_provisioning_option { + action = "DISCOVER_EXISTING" + } + + terminate_blue_instances_on_deployment_success { + action = "KEEP_ALIVE" + } + } +}`, baseCodeDeployConfig(rName), rName) +} From 0ddcaad9d236120313efd8c5424e41f1545c32ba Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Mon, 10 Sep 2018 12:33:15 -0400 Subject: [PATCH 5/6] codedeploy_deployment_group: tests for handleCodeDeployApiError. --- ...ce_aws_codedeploy_deployment_group_test.go | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/aws/resource_aws_codedeploy_deployment_group_test.go b/aws/resource_aws_codedeploy_deployment_group_test.go index 18510dcbd4c..9ae002b1022 100644 --- a/aws/resource_aws_codedeploy_deployment_group_test.go +++ b/aws/resource_aws_codedeploy_deployment_group_test.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "reflect" "regexp" @@ -2046,6 +2047,52 @@ func testAccCheckAWSCodeDeployDeploymentGroupExists(name string, group *codedepl } } +func TestAWSCodeDeployDeploymentGroup_handleCodeDeployApiError(t *testing.T) { + notAnAwsError := errors.New("Not an awserr") + invalidRoleException := awserr.New("InvalidRoleException", "Invalid role exception", nil) + invalidTriggerConfigExceptionNoMatch := awserr.New("InvalidTriggerConfigException", "Some other error message", nil) + invalidTriggerConfigExceptionMatch := awserr.New("InvalidTriggerConfigException", "Topic ARN foo is not valid", nil) + fakeAwsError := awserr.New("FakeAwsException", "Not a real AWS error", nil) + + testCases := []struct { + Input error + Expected *resource.RetryError + }{ + { + Input: nil, + Expected: nil, + }, + { + Input: notAnAwsError, + Expected: resource.NonRetryableError(notAnAwsError), + }, + { + Input: invalidRoleException, + Expected: resource.RetryableError(invalidRoleException), + }, + { + Input: invalidTriggerConfigExceptionNoMatch, + Expected: resource.NonRetryableError(invalidTriggerConfigExceptionNoMatch), + }, + { + Input: invalidTriggerConfigExceptionMatch, + Expected: resource.RetryableError(invalidTriggerConfigExceptionMatch), + }, + { + Input: fakeAwsError, + Expected: resource.NonRetryableError(fakeAwsError), + }, + } + + for _, tc := range testCases { + actual := handleCodeDeployApiError(tc.Input, "test") + if !reflect.DeepEqual(actual, tc.Expected) { + t.Fatalf(`handleCodeDeployApiError output is not correct. Expected: %v, Actual: %v.`, + tc.Expected, actual) + } + } +} + func testAccAWSCodeDeployDeploymentGroup(rName string, tagGroup bool) string { var tagGroupOrFilter string if tagGroup { From 434070dec6ca7cfd39344b36da7c8aeb3b250e96 Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Mon, 10 Sep 2018 13:23:03 -0400 Subject: [PATCH 6/6] codedeploy_deployment_group: fix gofmt error. - needed to upgrade to go version 1.11 --- aws/resource_aws_codedeploy_deployment_group_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_group_test.go b/aws/resource_aws_codedeploy_deployment_group_test.go index 9ae002b1022..c2bdd334b71 100644 --- a/aws/resource_aws_codedeploy_deployment_group_test.go +++ b/aws/resource_aws_codedeploy_deployment_group_test.go @@ -1741,7 +1741,7 @@ func TestAWSCodeDeployDeploymentGroup_expandBlueGreenDeploymentConfig(t *testing "terminate_blue_instances_on_deployment_success": []interface{}{ map[string]interface{}{ - "action": "TERMINATE", + "action": "TERMINATE", "termination_wait_time_in_minutes": 90, }, }, @@ -1759,7 +1759,7 @@ func TestAWSCodeDeployDeploymentGroup_expandBlueGreenDeploymentConfig(t *testing }, TerminateBlueInstancesOnDeploymentSuccess: &codedeploy.BlueInstanceTerminationOption{ - Action: aws.String("TERMINATE"), + Action: aws.String("TERMINATE"), TerminationWaitTimeInMinutes: aws.Int64(90), }, } @@ -1784,7 +1784,7 @@ func TestAWSCodeDeployDeploymentGroup_flattenBlueGreenDeploymentConfig(t *testin }, TerminateBlueInstancesOnDeploymentSuccess: &codedeploy.BlueInstanceTerminationOption{ - Action: aws.String("KEEP_ALIVE"), + Action: aws.String("KEEP_ALIVE"), TerminationWaitTimeInMinutes: aws.Int64(90), }, } @@ -1805,7 +1805,7 @@ func TestAWSCodeDeployDeploymentGroup_flattenBlueGreenDeploymentConfig(t *testin "terminate_blue_instances_on_deployment_success": []map[string]interface{}{ { - "action": "KEEP_ALIVE", + "action": "KEEP_ALIVE", "termination_wait_time_in_minutes": 90, }, },