From 41b398fcdf69da649522c3f92ca13840943c8eb9 Mon Sep 17 00:00:00 2001 From: Raymond Fallon Date: Thu, 1 Dec 2016 09:12:14 -0500 Subject: [PATCH] Automatic Rollback of CodeDeploy deployments and CloudWatch Alarms for a Deployment Group (#9039) * provider/aws: Add DeploymentRollback as a valid TriggerEvent type * provider/aws: Add auto_rollback_configuration to aws_codedeploy_deployment_group * provider/aws: Document auto_rollback_configuration - part of aws_codedeploy_deployment_group * provider/aws: Support removing and disabling auto_rollback_configuration - part of aws_codedeploy_deployment_group resource - when removing configuration, ensure events are removed - when disabling configuration, preserve events in case configuration is re-enabled * provider/aws: Add alarm_configuration to aws_codedeploy_deployment_group * provider/aws: Document alarm_configuration - part of aws_codedeploy_deployment_group * provider/aws: Support removing alarm_configuration - part of aws_codedeploy_deployment_group resource - disabling configuration doesn't appear to work... * provider/aws: Refactor auto_rollback_configuration tests - Add create test - SKIP failing test for now - Add tests for build & map functions * provider/aws: Refactor new aws_code_deploy_deployment_group tests - alarm_configuration and auto_rollback_configuration only - add assertions to deployment_group basic test - rename config funcs to be more easy to read - group public tests together * provider/aws: A max of 10 alarms can be added to a deployment group. - aws_code_deploy_deployment_group.alarm_configuration.alarms - verified this causes test failure with expected exception * provider/aws: Test disabling alarm_configuration and auto_rollback_configuration - the tests now pass after rebasing the latest master branch --- resource_aws_codedeploy_deployment_group.go | 177 ++++- ...ce_aws_codedeploy_deployment_group_test.go | 619 +++++++++++++++++- 2 files changed, 785 insertions(+), 11 deletions(-) diff --git a/resource_aws_codedeploy_deployment_group.go b/resource_aws_codedeploy_deployment_group.go index 0ff59564f060..930c6bde3617 100644 --- a/resource_aws_codedeploy_deployment_group.go +++ b/resource_aws_codedeploy_deployment_group.go @@ -57,6 +57,55 @@ func resourceAwsCodeDeployDeploymentGroup() *schema.Resource { Required: true, }, + "alarm_configuration": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "alarms": &schema.Schema{ + Type: schema.TypeSet, + MaxItems: 10, + Optional: true, + Set: schema.HashString, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + + "ignore_poll_alarm_failure": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + }, + }, + }, + + "auto_rollback_configuration": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + + "events": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Set: schema.HashString, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + "autoscaling_groups": &schema.Schema{ Type: schema.TypeSet, Optional: true, @@ -190,6 +239,14 @@ func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta int input.TriggerConfigurations = triggerConfigs } + if attr, ok := d.GetOk("auto_rollback_configuration"); ok { + input.AutoRollbackConfiguration = buildAutoRollbackConfig(attr.([]interface{})) + } + + if attr, ok := d.GetOk("alarm_configuration"); ok { + input.AlarmConfiguration = buildAlarmConfig(attr.([]interface{})) + } + // Retry to handle IAM role eventual consistency. var resp *codedeploy.CreateDeploymentGroupOutput var err error @@ -262,6 +319,14 @@ func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta inter return err } + if err := d.Set("auto_rollback_configuration", autoRollbackConfigToMap(resp.DeploymentGroupInfo.AutoRollbackConfiguration)); err != nil { + return err + } + + if err := d.Set("alarm_configuration", alarmConfigToMap(resp.DeploymentGroupInfo.AlarmConfiguration)); err != nil { + return err + } + return nil } @@ -303,6 +368,16 @@ func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta int input.TriggerConfigurations = triggerConfigs } + if d.HasChange("auto_rollback_configuration") { + _, n := d.GetChange("auto_rollback_configuration") + input.AutoRollbackConfiguration = buildAutoRollbackConfig(n.([]interface{})) + } + + if d.HasChange("alarm_configuration") { + _, n := d.GetChange("alarm_configuration") + input.AlarmConfiguration = buildAlarmConfig(n.([]interface{})) + } + 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 { @@ -416,6 +491,52 @@ func buildTriggerConfigs(configured []interface{}) []*codedeploy.TriggerConfig { return configs } +// buildAutoRollbackConfig converts a raw schema list containing a map[string]interface{} +// into a single codedeploy.AutoRollbackConfiguration +func buildAutoRollbackConfig(configured []interface{}) *codedeploy.AutoRollbackConfiguration { + result := &codedeploy.AutoRollbackConfiguration{} + + if len(configured) == 1 { + config := configured[0].(map[string]interface{}) + result.Enabled = aws.Bool(config["enabled"].(bool)) + result.Events = expandStringSet(config["events"].(*schema.Set)) + } else { // delete the configuration + result.Enabled = aws.Bool(false) + result.Events = make([]*string, 0) + } + + return result +} + +// buildAlarmConfig converts a raw schema list containing a map[string]interface{} +// into a single codedeploy.AlarmConfiguration +func buildAlarmConfig(configured []interface{}) *codedeploy.AlarmConfiguration { + result := &codedeploy.AlarmConfiguration{} + + if len(configured) == 1 { + config := configured[0].(map[string]interface{}) + names := expandStringSet(config["alarms"].(*schema.Set)) + alarms := make([]*codedeploy.Alarm, 0, len(names)) + + for _, name := range names { + alarm := &codedeploy.Alarm{ + Name: name, + } + alarms = append(alarms, alarm) + } + + result.Alarms = alarms + result.Enabled = aws.Bool(config["enabled"].(bool)) + result.IgnorePollAlarmFailure = aws.Bool(config["ignore_poll_alarm_failure"].(bool)) + } else { // delete the configuration + result.Alarms = make([]*codedeploy.Alarm, 0) + result.Enabled = aws.Bool(false) + result.IgnorePollAlarmFailure = aws.Bool(false) + } + + return result +} + // ec2TagFiltersToMap converts lists of tag filters into a []map[string]string. func ec2TagFiltersToMap(list []*codedeploy.EC2TagFilter) []map[string]string { result := make([]map[string]string, 0, len(list)) @@ -467,6 +588,47 @@ func triggerConfigsToMap(list []*codedeploy.TriggerConfig) []map[string]interfac return result } +// autoRollbackConfigToMap converts a codedeploy.AutoRollbackConfiguration +// into a []map[string]interface{} list containing a single item +func autoRollbackConfigToMap(config *codedeploy.AutoRollbackConfiguration) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + // only create configurations that are enabled or temporarily disabled (retaining events) + // otherwise empty configurations will be created + if config != nil && (*config.Enabled == true || len(config.Events) > 0) { + item := make(map[string]interface{}) + item["enabled"] = *config.Enabled + item["events"] = schema.NewSet(schema.HashString, flattenStringList(config.Events)) + result = append(result, item) + } + + return result +} + +// alarmConfigToMap converts a codedeploy.AlarmConfiguration +// into a []map[string]interface{} list containing a single item +func alarmConfigToMap(config *codedeploy.AlarmConfiguration) []map[string]interface{} { + result := make([]map[string]interface{}, 0, 1) + + // only create configurations that are enabled or temporarily disabled (retaining alarms) + // otherwise empty configurations will be created + if config != nil && (*config.Enabled == true || len(config.Alarms) > 0) { + names := make([]*string, 0, len(config.Alarms)) + for _, alarm := range config.Alarms { + names = append(names, alarm.Name) + } + + item := make(map[string]interface{}) + item["alarms"] = schema.NewSet(schema.HashString, flattenStringList(names)) + item["enabled"] = *config.Enabled + item["ignore_poll_alarm_failure"] = *config.IgnorePollAlarmFailure + + result = append(result, item) + } + + return result +} + func resourceAwsCodeDeployTagFilterHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) @@ -510,13 +672,14 @@ func resourceAwsCodeDeployTriggerConfigHash(v interface{}) int { func validateTriggerEvent(v interface{}, k string) (ws []string, errors []error) { value := v.(string) triggerEvents := map[string]bool{ - "DeploymentStart": true, - "DeploymentStop": true, - "DeploymentSuccess": true, - "DeploymentFailure": true, - "InstanceStart": true, - "InstanceSuccess": true, - "InstanceFailure": true, + "DeploymentStart": true, + "DeploymentStop": true, + "DeploymentSuccess": true, + "DeploymentFailure": true, + "DeploymentRollback": true, + "InstanceStart": true, + "InstanceSuccess": true, + "InstanceFailure": true, } if !triggerEvents[value] { diff --git a/resource_aws_codedeploy_deployment_group_test.go b/resource_aws_codedeploy_deployment_group_test.go index 4e8955b22d4e..90941fbbfdba 100644 --- a/resource_aws_codedeploy_deployment_group_test.go +++ b/resource_aws_codedeploy_deployment_group_test.go @@ -46,6 +46,13 @@ func TestAccAWSCodeDeployDeploymentGroup_basic(t *testing.T) { "aws_codedeploy_deployment_group.foo", "ec2_tag_filter.2916377465.type", "KEY_AND_VALUE"), resource.TestCheckResourceAttr( "aws_codedeploy_deployment_group.foo", "ec2_tag_filter.2916377465.value", "filtervalue"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "alarm_configuration.#", "0"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "auto_rollback_configuration.#", "0"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "trigger_configuration.#", "0"), ), }, resource.TestStep{ @@ -67,6 +74,13 @@ func TestAccAWSCodeDeployDeploymentGroup_basic(t *testing.T) { "aws_codedeploy_deployment_group.foo", "ec2_tag_filter.2369538975.type", "KEY_AND_VALUE"), resource.TestCheckResourceAttr( "aws_codedeploy_deployment_group.foo", "ec2_tag_filter.2369538975.value", "anotherfiltervalue"), + + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "alarm_configuration.#", "0"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "auto_rollback_configuration.#", "0"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo", "trigger_configuration.#", "0"), ), }, }, @@ -223,6 +237,334 @@ func TestAccAWSCodeDeployDeploymentGroup_triggerConfiguration_multiple(t *testin }) } +func TestAccAWSCodeDeployDeploymentGroup_autoRollbackConfiguration_create(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{ + resource.TestStep{ + Config: test_config_auto_rollback_configuration_delete(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "0"), + ), + }, + resource.TestStep{ + Config: test_config_auto_rollback_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_autoRollbackConfiguration_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{ + resource.TestStep{ + Config: test_config_auto_rollback_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + resource.TestStep{ + Config: test_config_auto_rollback_configuration_update(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "2"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.104943466", "DEPLOYMENT_STOP_ON_ALARM"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_autoRollbackConfiguration_delete(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{ + resource.TestStep{ + Config: test_config_auto_rollback_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + resource.TestStep{ + Config: test_config_auto_rollback_configuration_delete(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_autoRollbackConfiguration_disable(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{ + resource.TestStep{ + Config: test_config_auto_rollback_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + resource.TestStep{ + Config: test_config_auto_rollback_configuration_disable(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.enabled", "false"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "auto_rollback_configuration.0.events.135881253", "DEPLOYMENT_FAILURE"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_alarmConfiguration_create(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{ + resource.TestStep{ + Config: test_config_alarm_configuration_delete(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "0"), + ), + }, + resource.TestStep{ + Config: test_config_alarm_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "false"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_alarmConfiguration_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{ + resource.TestStep{ + Config: test_config_alarm_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "false"), + ), + }, + resource.TestStep{ + Config: test_config_alarm_configuration_update(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "2"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.1996459178", "bar"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "true"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_alarmConfiguration_delete(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{ + resource.TestStep{ + Config: test_config_alarm_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "false"), + ), + }, + resource.TestStep{ + Config: test_config_alarm_configuration_delete(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "0"), + ), + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentGroup_alarmConfiguration_disable(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{ + resource.TestStep{ + Config: test_config_alarm_configuration_create(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "true"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "false"), + ), + }, + resource.TestStep{ + Config: test_config_alarm_configuration_disable(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo_group", &group), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.enabled", "false"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.#", "1"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.alarms.2356372769", "foo"), + resource.TestCheckResourceAttr( + "aws_codedeploy_deployment_group.foo_group", "alarm_configuration.0.ignore_poll_alarm_failure", "false"), + ), + }, + }, + }) +} + func TestValidateAWSCodeDeployTriggerEvent(t *testing.T) { cases := []struct { Value string @@ -244,6 +586,10 @@ func TestValidateAWSCodeDeployTriggerEvent(t *testing.T) { Value: "DeploymentFailure", ErrCount: 0, }, + { + Value: "DeploymentRollback", + ErrCount: 0, + }, { Value: "InstanceStart", ErrCount: 0, @@ -356,6 +702,144 @@ func TestTriggerConfigsToMap(t *testing.T) { } } +func TestBuildAutoRollbackConfig(t *testing.T) { + input := []interface{}{ + map[string]interface{}{ + "events": schema.NewSet(schema.HashString, []interface{}{ + "DEPLOYMENT_FAILURE", + }), + "enabled": true, + }, + } + + expected := &codedeploy.AutoRollbackConfiguration{ + Events: []*string{ + aws.String("DEPLOYMENT_FAILURE"), + }, + Enabled: aws.Bool(true), + } + + actual := buildAutoRollbackConfig(input) + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("buildAutoRollbackConfig output is not correct.\nGot:\n%#v\nExpected:\n%#v\n", + actual, expected) + } +} + +func TestAutoRollbackConfigToMap(t *testing.T) { + input := &codedeploy.AutoRollbackConfiguration{ + Events: []*string{ + aws.String("DEPLOYMENT_FAILURE"), + aws.String("DEPLOYMENT_STOP_ON_ALARM"), + }, + Enabled: aws.Bool(false), + } + + expected := map[string]interface{}{ + "events": schema.NewSet(schema.HashString, []interface{}{ + "DEPLOYMENT_FAILURE", + "DEPLOYMENT_STOP_ON_ALARM", + }), + "enabled": false, + } + + actual := autoRollbackConfigToMap(input)[0] + + fatal := false + + if actual["enabled"] != expected["enabled"] { + fatal = true + } + + actualEvents := actual["events"].(*schema.Set) + expectedEvents := expected["events"].(*schema.Set) + if !actualEvents.Equal(expectedEvents) { + fatal = true + } + + if fatal { + t.Fatalf("autoRollbackConfigToMap output is not correct.\nGot:\n%#v\nExpected:\n%#v\n", + actual, expected) + } +} + +func TestBuildAlarmConfig(t *testing.T) { + input := []interface{}{ + map[string]interface{}{ + "alarms": schema.NewSet(schema.HashString, []interface{}{ + "foo-alarm", + }), + "enabled": true, + "ignore_poll_alarm_failure": false, + }, + } + + expected := &codedeploy.AlarmConfiguration{ + Alarms: []*codedeploy.Alarm{ + { + Name: aws.String("foo-alarm"), + }, + }, + Enabled: aws.Bool(true), + IgnorePollAlarmFailure: aws.Bool(false), + } + + actual := buildAlarmConfig(input) + + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("buildAlarmConfig output is not correct.\nGot:\n%#v\nExpected:\n%#v\n", + actual, expected) + } +} + +func TestAlarmConfigToMap(t *testing.T) { + input := &codedeploy.AlarmConfiguration{ + Alarms: []*codedeploy.Alarm{ + { + Name: aws.String("bar-alarm"), + }, + { + Name: aws.String("foo-alarm"), + }, + }, + Enabled: aws.Bool(false), + IgnorePollAlarmFailure: aws.Bool(true), + } + + expected := map[string]interface{}{ + "alarms": schema.NewSet(schema.HashString, []interface{}{ + "bar-alarm", + "foo-alarm", + }), + "enabled": false, + "ignore_poll_alarm_failure": true, + } + + actual := alarmConfigToMap(input)[0] + + fatal := false + + if actual["enabled"] != expected["enabled"] { + fatal = true + } + + if actual["ignore_poll_alarm_failure"] != expected["ignore_poll_alarm_failure"] { + fatal = true + } + + actualAlarms := actual["alarms"].(*schema.Set) + expectedAlarms := expected["alarms"].(*schema.Set) + if !actualAlarms.Equal(expectedAlarms) { + fatal = true + } + + if fatal { + t.Fatalf("alarmConfigToMap output is not correct.\nGot:\n%#v\nExpected:\n%#v\n", + actual, expected) + } +} + func testAccCheckTriggerEvents(group *codedeploy.DeploymentGroupInfo, triggerName string, expectedEvents []string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -748,7 +1232,7 @@ func testAccAWSCodeDeployDeploymentGroup_triggerConfiguration_create(rName strin 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" @@ -766,7 +1250,7 @@ func testAccAWSCodeDeployDeploymentGroup_triggerConfiguration_update(rName strin 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" @@ -784,7 +1268,7 @@ func testAccAWSCodeDeployDeploymentGroup_triggerConfiguration_createMultiple(rNa return fmt.Sprintf(` %s - + resource "aws_sns_topic" "bar_topic" { name = "bar-topic-%s" } @@ -812,7 +1296,7 @@ func testAccAWSCodeDeployDeploymentGroup_triggerConfiguration_updateMultiple(rNa return fmt.Sprintf(` %s - + resource "aws_sns_topic" "bar_topic" { name = "bar-topic-%s" } @@ -839,3 +1323,130 @@ resource "aws_codedeploy_deployment_group" "foo_group" { } }`, baseCodeDeployConfig(rName), rName, rName, rName) } + +func test_config_auto_rollback_configuration_create(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}" + + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE"] + } +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_auto_rollback_configuration_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}" + + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_FAILURE", "DEPLOYMENT_STOP_ON_ALARM"] + } +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_auto_rollback_configuration_delete(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}" +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_auto_rollback_configuration_disable(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}" + + auto_rollback_configuration { + enabled = false + events = ["DEPLOYMENT_FAILURE"] + } +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_alarm_configuration_create(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}" + + alarm_configuration { + alarms = ["foo"] + enabled = true + } +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_alarm_configuration_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}" + + alarm_configuration { + alarms = ["foo", "bar"] + enabled = true + ignore_poll_alarm_failure = true + } +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_alarm_configuration_delete(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}" +}`, baseCodeDeployConfig(rName), rName) +} + +func test_config_alarm_configuration_disable(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}" + + alarm_configuration { + alarms = ["foo"] + enabled = false + } +}`, baseCodeDeployConfig(rName), rName) +}