From d6b4d8f1ce5594e65c185a416fbd594673b7e14d Mon Sep 17 00:00:00 2001 From: Austin Burdine Date: Thu, 29 Nov 2018 11:15:54 -0500 Subject: [PATCH] r/codedeploy_config: add support for lambda and traffic_routing fixes #5393, refs #4839 - adds traffic_routing_config sub-resource to codedeploy_deployment_config resource - add computed_platform optional element - make minimum_healthy_hosts optional as per aws api spec --- ...source_aws_codedeploy_deployment_config.go | 192 +++++++++++++++++- ...e_aws_codedeploy_deployment_config_test.go | 142 +++++++++++++ ...codedeploy_deployment_config.html.markdown | 57 +++++- 3 files changed, 382 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_codedeploy_deployment_config.go b/aws/resource_aws_codedeploy_deployment_config.go index e88bef02062..50aab97c0d8 100644 --- a/aws/resource_aws_codedeploy_deployment_config.go +++ b/aws/resource_aws_codedeploy_deployment_config.go @@ -27,16 +27,28 @@ func resourceAwsCodeDeployDeploymentConfig() *schema.Resource { ForceNew: true, }, + "compute_platform": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + codedeploy.ComputePlatformServer, + codedeploy.ComputePlatformLambda, + codedeploy.ComputePlatformEcs, + }, false), + Default: codedeploy.ComputePlatformServer, + }, + "minimum_healthy_hosts": { Type: schema.TypeList, - Required: true, + Optional: true, ForceNew: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{ codedeploy.MinimumHealthyHostsTypeHostCount, @@ -52,6 +64,72 @@ func resourceAwsCodeDeployDeploymentConfig() *schema.Resource { }, }, + "traffic_routing_config": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + codedeploy.TrafficRoutingTypeAllAtOnce, + codedeploy.TrafficRoutingTypeTimeBasedCanary, + codedeploy.TrafficRoutingTypeTimeBasedLinear, + }, false), + Default: codedeploy.TrafficRoutingTypeAllAtOnce, + }, + + "time_based_canary": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"traffic_routing_config.0.time_based_linear"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interval": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "percentage": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + + "time_based_linear": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"traffic_routing_config.0.time_based_canary"}, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "interval": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "percentage": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + }, + }, + }, + }, + }, + }, + "deployment_config_id": { Type: schema.TypeString, Computed: true, @@ -65,7 +143,9 @@ func resourceAwsCodeDeployDeploymentConfigCreate(d *schema.ResourceData, meta in input := &codedeploy.CreateDeploymentConfigInput{ DeploymentConfigName: aws.String(d.Get("deployment_config_name").(string)), + ComputePlatform: aws.String(d.Get("compute_platform").(string)), MinimumHealthyHosts: expandAwsCodeDeployConfigMinimumHealthHosts(d), + TrafficRoutingConfig: expandAwsCodeDeployTrafficRoutingConfig(d), } _, err := conn.CreateDeploymentConfig(input) @@ -104,8 +184,14 @@ func resourceAwsCodeDeployDeploymentConfigRead(d *schema.ResourceData, meta inte if err := d.Set("minimum_healthy_hosts", flattenAwsCodeDeployConfigMinimumHealthHosts(resp.DeploymentConfigInfo.MinimumHealthyHosts)); err != nil { return err } + + if err := d.Set("traffic_routing_config", flattenAwsCodeDeployTrafficRoutingConfig(resp.DeploymentConfigInfo.TrafficRoutingConfig)); err != nil { + return err + } + d.Set("deployment_config_id", resp.DeploymentConfigInfo.DeploymentConfigId) d.Set("deployment_config_name", resp.DeploymentConfigInfo.DeploymentConfigName) + d.Set("compute_platform", resp.DeploymentConfigInfo.ComputePlatform) return nil } @@ -126,8 +212,11 @@ func resourceAwsCodeDeployDeploymentConfigDelete(d *schema.ResourceData, meta in } func expandAwsCodeDeployConfigMinimumHealthHosts(d *schema.ResourceData) *codedeploy.MinimumHealthyHosts { - hosts := d.Get("minimum_healthy_hosts").([]interface{}) - host := hosts[0].(map[string]interface{}) + hosts, ok := d.GetOk("minimum_healthy_hosts") + if !ok { + return nil + } + host := hosts.([]interface{})[0].(map[string]interface{}) minimumHealthyHost := codedeploy.MinimumHealthyHosts{ Type: aws.String(host["type"].(string)), @@ -137,15 +226,102 @@ func expandAwsCodeDeployConfigMinimumHealthHosts(d *schema.ResourceData) *codede return &minimumHealthyHost } +func expandAwsCodeDeployTrafficRoutingConfig(d *schema.ResourceData) *codedeploy.TrafficRoutingConfig { + block, ok := d.GetOk("traffic_routing_config") + if !ok { + return nil + } + config := block.([]interface{})[0].(map[string]interface{}) + trafficRoutingConfig := codedeploy.TrafficRoutingConfig{} + + if trafficType, ok := config["type"]; ok { + trafficRoutingConfig.Type = aws.String(trafficType.(string)) + } + if canary, ok := config["time_based_canary"]; ok && len(canary.([]interface{})) > 0 { + canaryConfig := canary.([]interface{})[0].(map[string]interface{}) + trafficRoutingConfig.TimeBasedCanary = expandAwsCodeDeployTrafficTimeBasedCanaryConfig(canaryConfig) + } + if linear, ok := config["time_based_linear"]; ok && len(linear.([]interface{})) > 0 { + linearConfig := linear.([]interface{})[0].(map[string]interface{}) + trafficRoutingConfig.TimeBasedLinear = expandAwsCodeDeployTrafficTimeBasedLinearConfig(linearConfig) + } + + return &trafficRoutingConfig +} + +func expandAwsCodeDeployTrafficTimeBasedCanaryConfig(config map[string]interface{}) *codedeploy.TimeBasedCanary { + canary := codedeploy.TimeBasedCanary{} + if interval, ok := config["interval"]; ok { + canary.CanaryInterval = aws.Int64(int64(interval.(int))) + } + if percentage, ok := config["percentage"]; ok { + canary.CanaryPercentage = aws.Int64(int64(percentage.(int))) + } + return &canary +} + +func expandAwsCodeDeployTrafficTimeBasedLinearConfig(config map[string]interface{}) *codedeploy.TimeBasedLinear { + linear := codedeploy.TimeBasedLinear{} + if interval, ok := config["interval"]; ok { + linear.LinearInterval = aws.Int64(int64(interval.(int))) + } + if percentage, ok := config["percentage"]; ok { + linear.LinearPercentage = aws.Int64(int64(percentage.(int))) + } + return &linear +} + func flattenAwsCodeDeployConfigMinimumHealthHosts(hosts *codedeploy.MinimumHealthyHosts) []map[string]interface{} { result := make([]map[string]interface{}, 0) + if hosts == nil { + return result + } + + item := make(map[string]interface{}) + + item["type"] = aws.StringValue(hosts.Type) + item["value"] = aws.Int64Value(hosts.Value) + + return append(result, item) +} + +func flattenAwsCodeDeployTrafficRoutingConfig(config *codedeploy.TrafficRoutingConfig) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + if config == nil { + return result + } item := make(map[string]interface{}) - item["type"] = *hosts.Type - item["value"] = *hosts.Value + item["type"] = aws.StringValue(config.Type) + item["time_based_canary"] = flattenAwsCodeDeployTrafficRoutingCanaryConfig(config.TimeBasedCanary) + item["time_based_linear"] = flattenAwsCodeDeployTrafficRoutingLinearConfig(config.TimeBasedLinear) + + return append(result, item) +} + +func flattenAwsCodeDeployTrafficRoutingCanaryConfig(canary *codedeploy.TimeBasedCanary) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + if canary == nil { + return result + } - result = append(result, item) + item := make(map[string]interface{}) + item["interval"] = aws.Int64Value(canary.CanaryInterval) + item["percentage"] = aws.Int64Value(canary.CanaryPercentage) + + return append(result, item) +} + +func flattenAwsCodeDeployTrafficRoutingLinearConfig(linear *codedeploy.TimeBasedLinear) []map[string]interface{} { + result := make([]map[string]interface{}, 0) + if linear == nil { + return result + } + + item := make(map[string]interface{}) + item["interval"] = aws.Int64Value(linear.LinearInterval) + item["percentage"] = aws.Int64Value(linear.LinearPercentage) - return result + return append(result, item) } diff --git a/aws/resource_aws_codedeploy_deployment_config_test.go b/aws/resource_aws_codedeploy_deployment_config_test.go index 2132009d000..e5088e944c1 100644 --- a/aws/resource_aws_codedeploy_deployment_config_test.go +++ b/aws/resource_aws_codedeploy_deployment_config_test.go @@ -27,6 +27,8 @@ func TestAccAWSCodeDeployDeploymentConfig_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSCodeDeployDeploymentConfigExists(resourceName, &config1), resource.TestCheckResourceAttr(resourceName, "deployment_config_name", rName), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "0"), ), }, { @@ -55,6 +57,8 @@ func TestAccAWSCodeDeployDeploymentConfig_fleetPercent(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "1"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.type", "FLEET_PERCENT"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.value", "75"), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "0"), ), }, { @@ -65,6 +69,8 @@ func TestAccAWSCodeDeployDeploymentConfig_fleetPercent(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "1"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.type", "FLEET_PERCENT"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.value", "50"), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "0"), ), }, { @@ -93,6 +99,8 @@ func TestAccAWSCodeDeployDeploymentConfig_hostCount(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "1"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.type", "HOST_COUNT"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.value", "1"), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "0"), ), }, { @@ -103,6 +111,104 @@ func TestAccAWSCodeDeployDeploymentConfig_hostCount(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "1"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.type", "HOST_COUNT"), resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.0.value", "2"), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Server"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentConfig_trafficCanary(t *testing.T) { + var config1, config2 codedeploy.DeploymentConfigInfo + resourceName := "aws_codedeploy_deployment_config.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployDeploymentConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCodeDeployDeploymentConfigTrafficCanary(rName, 10, 50), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentConfigExists(resourceName, &config1), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Lambda"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.type", "TimeBasedCanary"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.0.interval", "10"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.0.percentage", "50"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.#", "0"), + resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "0"), + ), + }, + { + Config: testAccAWSCodeDeployDeploymentConfigTrafficCanary(rName, 3, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentConfigExists(resourceName, &config2), + testAccCheckAWSCodeDeployDeploymentConfigRecreated(&config1, &config2), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Lambda"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.type", "TimeBasedCanary"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.0.interval", "3"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.0.percentage", "10"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.#", "0"), + resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSCodeDeployDeploymentConfig_trafficLinear(t *testing.T) { + var config1, config2 codedeploy.DeploymentConfigInfo + resourceName := "aws_codedeploy_deployment_config.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployDeploymentConfigDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCodeDeployDeploymentConfigTrafficLinear(rName, 10, 50), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentConfigExists(resourceName, &config1), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Lambda"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.type", "TimeBasedLinear"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.0.interval", "10"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.0.percentage", "50"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "0"), + ), + }, + { + Config: testAccAWSCodeDeployDeploymentConfigTrafficLinear(rName, 3, 10), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentConfigExists(resourceName, &config2), + testAccCheckAWSCodeDeployDeploymentConfigRecreated(&config1, &config2), + resource.TestCheckResourceAttr(resourceName, "compute_platform", "Lambda"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.type", "TimeBasedLinear"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.#", "1"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.0.interval", "3"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_linear.0.percentage", "10"), + resource.TestCheckResourceAttr(resourceName, "traffic_routing_config.0.time_based_canary.#", "0"), + resource.TestCheckResourceAttr(resourceName, "minimum_healthy_hosts.#", "0"), ), }, { @@ -200,3 +306,39 @@ resource "aws_codedeploy_deployment_config" "test" { } `, rName, value) } + +func testAccAWSCodeDeployDeploymentConfigTrafficCanary(rName string, interval, percentage int) string { + return fmt.Sprintf(` +resource "aws_codedeploy_deployment_config" "test" { + deployment_config_name = %q + compute_platform = "Lambda" + + traffic_routing_config { + type = "TimeBasedCanary" + + time_based_canary { + interval = %d + percentage = %d + } + } +} +`, rName, interval, percentage) +} + +func testAccAWSCodeDeployDeploymentConfigTrafficLinear(rName string, interval, percentage int) string { + return fmt.Sprintf(` +resource "aws_codedeploy_deployment_config" "test" { + deployment_config_name = %q + compute_platform = "Lambda" + + traffic_routing_config { + type = "TimeBasedLinear" + + time_based_linear { + interval = %d + percentage = %d + } + } +} +`, rName, interval, percentage) +} diff --git a/website/docs/r/codedeploy_deployment_config.html.markdown b/website/docs/r/codedeploy_deployment_config.html.markdown index 0294e46b231..87c8bb6ec20 100644 --- a/website/docs/r/codedeploy_deployment_config.html.markdown +++ b/website/docs/r/codedeploy_deployment_config.html.markdown @@ -12,6 +12,8 @@ Provides a CodeDeploy deployment config for an application ## Example Usage +### Server Usage + ```hcl resource "aws_codedeploy_deployment_config" "foo" { deployment_config_name = "test-deployment-config" @@ -52,14 +54,51 @@ resource "aws_codedeploy_deployment_group" "foo" { } ``` +### Lambda Usage + +```hcl +resource "aws_codedeploy_deployment_config" "foo" { + deployment_config_name = "test-deployment-config" + compute_platform = "Lambda" + + traffic_routing_config { + type = "TimeBasedLinear" + + time_based_linear { + interval = 10 + percentage = 10 + } + } +} + +resource "aws_codedeploy_deployment_group" "foo" { + app_name = "${aws_codedeploy_app.foo_app.name}" + deployment_group_name = "bar" + service_role_arn = "${aws_iam_role.foo_role.arn}" + deployment_config_name = "${aws_codedeploy_deployment_config.foo.id}" + + auto_rollback_configuration { + enabled = true + events = ["DEPLOYMENT_STOP_ON_ALARM"] + } + + alarm_configuration { + alarms = ["my-alarm-name"] + enabled = true + } +} +``` + ## Argument Reference The following arguments are supported: * `deployment_config_name` - (Required) The name of the deployment config. +* `compute_platform` - (Optional) The compute platform can be `Server`, `Lambda`, or `ECS`. Default is `Server`. * `minimum_healthy_hosts` - (Optional) A minimum_healthy_hosts block. Minimum Healthy Hosts are documented below. +* `traffic_routing_config` - (Optional) A traffic_routing_config block. Traffic Routing Config is documented below. -A `minimum_healthy_hosts` block support the following: +The `minimum_healthy_hosts` block supports the following: * `type` - (Required) The type can either be `FLEET_PERCENT` or `HOST_COUNT`. * `value` - (Required) The value when the type is `FLEET_PERCENT` represents the minimum number of healthy instances as @@ -67,6 +106,22 @@ a percentage of the total number of instances in the deployment. If you specify deployment, AWS CodeDeploy converts the percentage to the equivalent number of instance and rounds up fractional instances. When the type is `HOST_COUNT`, the value represents the minimum number of healthy instances as an absolute value. +The `traffic_routing_config` block supports the following: + +* `type` - (Optional) Type of traffic routing config. One of `TimeBasedCanary`, `TimeBasedLinear`, `AllAtOnce`. +* `time_based_canary` - (Optional) The time based canary configuration information. If `type` is `TimeBasedLinear`, use `time_based_linear` instead. +* `time_based_linear` - (Optional) The time based linear configuration information. If `type` is `TimeBasedCanary`, use `time_based_canary` instead. + +The `time_based_canary` block supports the following: + +* `interval` - (Optional) The number of minutes between the first and second traffic shifts of a `TimeBasedCanary` deployment. +* `percentage` - (Optional) The percentage of traffic to shift in the first increment of a `TimeBasedCanary` deployment. + +The `time_based_linear` block supports the following: + +* `interval` - (Optional) The number of minutes between each incremental traffic shift of a `TimeBasedLinear` deployment. +* `percentage` - (Optional) The percentage of traffic that is shifted at the start of each increment of a `TimeBasedLinear` deployment. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: