From e59fb4e6ca2e6c184acbdc3c7e14d07f0b2e0a83 Mon Sep 17 00:00:00 2001 From: Christopher Tiwald Date: Sun, 19 Jul 2015 00:09:00 -0400 Subject: [PATCH 1/7] aws: Add support for "aws_codedeploy_app" resources. --- builtin/providers/aws/config.go | 5 + builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_codedeploy_app.go | 127 ++++++++++++++++++ 3 files changed, 133 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_codedeploy_app.go diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index 8b9428fbc2..dfd8b1b2ec 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -14,6 +14,7 @@ import ( "github.com/aws/aws-sdk-go/service/autoscaling" "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/codedeploy" "github.com/aws/aws-sdk-go/service/directoryservice" "github.com/aws/aws-sdk-go/service/dynamodb" "github.com/aws/aws-sdk-go/service/ec2" @@ -70,6 +71,7 @@ type AWSClient struct { lambdaconn *lambda.Lambda opsworksconn *opsworks.OpsWorks glacierconn *glacier.Glacier + codedeployconn *codedeploy.CodeDeploy } // Client configures and returns a fully initialized AWSClient @@ -192,6 +194,9 @@ func (c *Config) Client() (interface{}, error) { log.Println("[INFO] Initializing Glacier connection") client.glacierconn = glacier.New(awsConfig) + + log.Println("[INFO] Initializing CodeDeploy Connection") + client.codedeployconn = codedeploy.New(awsConfig) } if len(errs) > 0 { diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index f73580d0f7..132fa4678a 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -166,6 +166,7 @@ func Provider() terraform.ResourceProvider { "aws_cloudwatch_log_group": resourceAwsCloudWatchLogGroup(), "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), + "aws_codedeploy_app": resourceAwsCodeDeployApp(), "aws_customer_gateway": resourceAwsCustomerGateway(), "aws_db_instance": resourceAwsDbInstance(), "aws_db_parameter_group": resourceAwsDbParameterGroup(), diff --git a/builtin/providers/aws/resource_aws_codedeploy_app.go b/builtin/providers/aws/resource_aws_codedeploy_app.go new file mode 100644 index 0000000000..ccf07a82d8 --- /dev/null +++ b/builtin/providers/aws/resource_aws_codedeploy_app.go @@ -0,0 +1,127 @@ +package aws + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/codedeploy" +) + +func resourceAwsCodeDeployApp() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCodeDeployAppCreate, + Read: resourceAwsCodeDeployAppRead, + Update: resourceAwsCodeDeployUpdate, + Delete: resourceAwsCodeDeployAppDelete, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + // The unique ID is set by AWS on create. + "unique_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + } +} + +func resourceAwsCodeDeployAppCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + application := d.Get("name").(string) + log.Printf("[DEBUG] Creating CodeDeploy application %s", application) + + resp, err := conn.CreateApplication(&codedeploy.CreateApplicationInput{ + ApplicationName: aws.String(application), + }) + if err != nil { + return err + } + log.Printf("[DEBUG] CodeDeploy application %s created", *resp.ApplicationId) + + // Despite giving the application a unique ID, AWS doesn't actually use + // it in API calls. Use it and the app name to identify the resource in + // the state file. This allows us to reliably detect both when the TF + // config file changes and when the user deletes the app without removing + // it first from the TF config. + d.SetId(fmt.Sprintf("%s:%s", *resp.ApplicationId, application)) + + return resourceAwsCodeDeployAppRead(d, meta) +} + +func resourceAwsCodeDeployAppRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + _, application := resourceAwsCodeDeployAppParseId(d.Id()) + log.Printf("[DEBUG] Reading CodeDeploy application %s", application) + resp, err := conn.GetApplication(&codedeploy.GetApplicationInput{ + ApplicationName: aws.String(application), + }) + if err != nil { + if codedeployerr, ok := err.(awserr.Error); ok && codedeployerr.Code() == "ApplicationDoesNotExistException" { + d.SetId("") + return nil + } else { + log.Printf("[ERROR] Error finding CodeDeploy application: %s", err) + return err + } + } + + d.Set("name", *resp.Application.ApplicationName) + + return nil +} + +func resourceAwsCodeDeployUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + o, n := d.GetChange("name") + + _, err := conn.UpdateApplication(&codedeploy.UpdateApplicationInput{ + ApplicationName: aws.String(o.(string)), + NewApplicationName: aws.String(n.(string)), + }) + if err != nil { + return err + } + log.Printf("[DEBUG] CodeDeploy application %s updated", n) + + d.Set("name", n) + + return nil +} + +func resourceAwsCodeDeployAppDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + _, err := conn.DeleteApplication(&codedeploy.DeleteApplicationInput{ + ApplicationName: aws.String(d.Get("name").(string)), + }) + if err != nil { + if cderr, ok := err.(awserr.Error); ok && cderr.Code() == "InvalidApplicationNameException" { + d.SetId("") + return nil + } else { + log.Printf("[ERROR] Error deleting CodeDeploy application: %s", err) + return err + } + } + + return nil +} + +func resourceAwsCodeDeployAppParseId(id string) (string, string) { + parts := strings.SplitN(id, ":", 2) + return parts[0], parts[1] +} From 42c077700a26b4cf65999db199030ac88bc68d14 Mon Sep 17 00:00:00 2001 From: Christopher Tiwald Date: Sun, 19 Jul 2015 00:09:25 -0400 Subject: [PATCH 2/7] aws: Add acceptance tests for "aws_codedeploy_app" resources. --- .../aws/resource_aws_codedeploy_app_test.go | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_codedeploy_app_test.go diff --git a/builtin/providers/aws/resource_aws_codedeploy_app_test.go b/builtin/providers/aws/resource_aws_codedeploy_app_test.go new file mode 100644 index 0000000000..9c016f1842 --- /dev/null +++ b/builtin/providers/aws/resource_aws_codedeploy_app_test.go @@ -0,0 +1,78 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codedeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSCodeDeployApp_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployAppDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSCodeDeployApp, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployAppExists("aws_codedeploy_app.foo"), + ), + }, + resource.TestStep{ + Config: testAccAWSCodeDeployAppModifier, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployAppExists("aws_codedeploy_app.foo"), + ), + }, + }, + }) +} + +func testAccCheckAWSCodeDeployAppDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).codedeployconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_codedeploy_app" { + continue + } + + resp, err := conn.GetApplication(&codedeploy.GetApplicationInput{ + ApplicationName: aws.String(rs.Primary.ID), + }) + + if err == nil { + if resp.Application != nil { + return fmt.Errorf("CodeDeploy app still exists:\n%#v", *resp.Application.ApplicationId) + } + } + + return err + } + + return nil +} + +func testAccCheckAWSCodeDeployAppExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +var testAccAWSCodeDeployApp = ` +resource "aws_codedeploy_app" "foo" { + name = "foo" +}` + +var testAccAWSCodeDeployAppModifier = ` +resource "aws_codedeploy_app" "foo" { + name = "bar" +}` From fa3dfd1420dea811e8b0992092a43afaee703e8e Mon Sep 17 00:00:00 2001 From: Christopher Tiwald Date: Sun, 19 Jul 2015 00:07:43 -0400 Subject: [PATCH 3/7] aws: Add documentation for "aws_codedeploy_app" resources. --- .../aws/r/codedeploy_app.html.markdown | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 website/source/docs/providers/aws/r/codedeploy_app.html.markdown diff --git a/website/source/docs/providers/aws/r/codedeploy_app.html.markdown b/website/source/docs/providers/aws/r/codedeploy_app.html.markdown new file mode 100644 index 0000000000..054fd1eda9 --- /dev/null +++ b/website/source/docs/providers/aws/r/codedeploy_app.html.markdown @@ -0,0 +1,32 @@ +--- +layout: "aws" +page_title: "AWS: aws_codedeploy_app" +sidebar_current: "docs-aws-resource-codedeploy-app" +description: |\ + Provides a CodeDeploy application. +--- + +# aws\_codedeploy\_app + +Provides a CodeDeploy application to be used as a basis for deployments + +## Example Usage + +``` +resource "aws_codedeploy_app" "foo" { + name = "foo" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the application. + +## Attribute Reference + +The following arguments are exported: + +* `id` - Amazon's assigned ID for the application. +* `name` - The application's name. From a546a12c2dbbeb6215c4c16ce5e6dbf3a00554dc Mon Sep 17 00:00:00 2001 From: Christopher Tiwald Date: Tue, 20 Oct 2015 18:03:57 -0400 Subject: [PATCH 4/7] aws: Add support for aws_codedeploy_deployment_group resources --- builtin/providers/aws/provider.go | 1 + ...esource_aws_codedeploy_deployment_group.go | 375 ++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_codedeploy_deployment_group.go diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 132fa4678a..fed004741d 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -167,6 +167,7 @@ func Provider() terraform.ResourceProvider { "aws_autoscaling_lifecycle_hook": resourceAwsAutoscalingLifecycleHook(), "aws_cloudwatch_metric_alarm": resourceAwsCloudWatchMetricAlarm(), "aws_codedeploy_app": resourceAwsCodeDeployApp(), + "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), "aws_customer_gateway": resourceAwsCustomerGateway(), "aws_db_instance": resourceAwsDbInstance(), "aws_db_parameter_group": resourceAwsDbParameterGroup(), diff --git a/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go b/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go new file mode 100644 index 0000000000..a9f3acb078 --- /dev/null +++ b/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go @@ -0,0 +1,375 @@ +package aws + +import ( + "bytes" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/codedeploy" +) + +func resourceAwsCodeDeployDeploymentGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCodeDeployDeploymentGroupCreate, + Read: resourceAwsCodeDeployDeploymentGroupRead, + Update: resourceAwsCodeDeployDeploymentGroupUpdate, + Delete: resourceAwsCodeDeployDeploymentGroupDelete, + + Schema: map[string]*schema.Schema{ + "application_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 100 { + errors = append(errors, fmt.Errorf( + "%q cannot exceed 100 characters", k)) + } + return + }, + }, + + "deployment_group_name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 100 { + errors = append(errors, fmt.Errorf( + "%q cannot exceed 100 characters", k)) + } + return + }, + }, + + "service_role_arn": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "autoscaling_groups": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + + "deployment_config_name": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "CodeDeployDefault.OneAtATime", + ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 100 { + errors = append(errors, fmt.Errorf( + "%q cannot exceed 100 characters", k)) + } + return + }, + }, + + "ec2_tag_filter": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTagFilters, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAwsCodeDeployTagFilterHash, + }, + + "on_premises_instance_tag_filter": &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + + "type": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTagFilters, + }, + + "value": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + }, + }, + Set: resourceAwsCodeDeployTagFilterHash, + }, + }, + } +} + +func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + application := d.Get("application_name").(string) + deploymentGroup := d.Get("deployment_group_name").(string) + + input := codedeploy.CreateDeploymentGroupInput{ + ApplicationName: aws.String(application), + DeploymentGroupName: aws.String(deploymentGroup), + ServiceRoleArn: aws.String(d.Get("service_role_arn").(string)), + } + if attr, ok := d.GetOk("deployment_config_name"); ok { + input.DeploymentConfigName = aws.String(attr.(string)) + } + if attr, ok := d.GetOk("autoscaling_groups"); ok { + input.AutoScalingGroups = expandStringList(attr.(*schema.Set).List()) + } + if attr, ok := d.GetOk("on_premises_instance_tag_filters"); ok { + onPremFilters := buildOnPremTagFilters(attr.(*schema.Set).List()) + input.OnPremisesInstanceTagFilters = onPremFilters + } + if attr, ok := d.GetOk("ec2_tag_filter"); ok { + ec2TagFilters := buildEC2TagFilters(attr.(*schema.Set).List()) + input.Ec2TagFilters = ec2TagFilters + } + + // Retry to handle IAM role eventual consistency. + var resp *codedeploy.CreateDeploymentGroupOutput + var err error + err = resource.Retry(2*time.Minute, func() error { + resp, err = conn.CreateDeploymentGroup(&input) + if err != nil { + codedeployErr, ok := err.(awserr.Error) + if !ok { + return &resource.RetryError{Err: err} + } + if codedeployErr.Code() == "InvalidRoleException" { + log.Printf("[DEBUG] Trying to create deployment group again: %q", + codedeployErr.Message()) + return err + } + + return &resource.RetryError{Err: err} + } + return nil + }) + if err != nil { + return err + } + + d.SetId(*resp.DeploymentGroupId) + + return resourceAwsCodeDeployDeploymentGroupRead(d, meta) +} + +func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id()) + resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{ + ApplicationName: aws.String(d.Get("application_name").(string)), + DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), + }) + if err != nil { + return err + } + + d.Set("application_name", *resp.DeploymentGroupInfo.ApplicationName) + d.Set("autoscaling_groups", resp.DeploymentGroupInfo.AutoScalingGroups) + d.Set("deployment_config_name", *resp.DeploymentGroupInfo.DeploymentConfigName) + d.Set("deployment_group_name", *resp.DeploymentGroupInfo.DeploymentGroupName) + d.Set("service_role_arn", *resp.DeploymentGroupInfo.ServiceRoleArn) + if err := d.Set("ec2_tag_filter", ec2TagFiltersToMap(resp.DeploymentGroupInfo.Ec2TagFilters)); err != nil { + return err + } + if err := d.Set("on_premises_instance_tag_filter", onPremisesTagFiltersToMap(resp.DeploymentGroupInfo.OnPremisesInstanceTagFilters)); err != nil { + return err + } + + return nil +} + +func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + input := codedeploy.UpdateDeploymentGroupInput{ + ApplicationName: aws.String(d.Get("application_name").(string)), + CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(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)) + } + if d.HasChange("deployment_group_name") { + _, n := d.GetChange("deployment_group_name") + input.NewDeploymentGroupName = aws.String(n.(string)) + } + + // 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") + onPremFilters := buildOnPremTagFilters(n.(*schema.Set).List()) + input.OnPremisesInstanceTagFilters = onPremFilters + } + if d.HasChange("ec2_tag_filter") { + _, n := d.GetChange("ec2_tag_filter") + ec2Filters := buildEC2TagFilters(n.(*schema.Set).List()) + input.Ec2TagFilters = ec2Filters + } + + log.Printf("[DEBUG] Updating CodeDeploy DeploymentGroup %s", d.Id()) + _, err := conn.UpdateDeploymentGroup(&input) + if err != nil { + return err + } + + return resourceAwsCodeDeployDeploymentGroupRead(d, meta) +} + +func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codedeployconn + + log.Printf("[DEBUG] Deleting CodeDeploy DeploymentGroup %s", d.Id()) + _, err := conn.DeleteDeploymentGroup(&codedeploy.DeleteDeploymentGroupInput{ + ApplicationName: aws.String(d.Get("application_name").(string)), + DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), + }) + if err != nil { + return err + } + + d.SetId("") + + return nil +} + +// buildOnPremTagFilters converts raw schema lists into a list of +// codedeploy.TagFilters. +func buildOnPremTagFilters(configured []interface{}) []*codedeploy.TagFilter { + filters := make([]*codedeploy.TagFilter, 0) + for _, raw := range configured { + var filter codedeploy.TagFilter + m := raw.(map[string]interface{}) + + filter.Key = aws.String(m["key"].(string)) + filter.Type = aws.String(m["type"].(string)) + filter.Value = aws.String(m["value"].(string)) + + filters = append(filters, &filter) + } + + return filters +} + +// buildEC2TagFilters converts raw schema lists into a list of +// codedeploy.EC2TagFilters. +func buildEC2TagFilters(configured []interface{}) []*codedeploy.EC2TagFilter { + filters := make([]*codedeploy.EC2TagFilter, 0) + for _, raw := range configured { + var filter codedeploy.EC2TagFilter + m := raw.(map[string]interface{}) + + filter.Key = aws.String(m["key"].(string)) + filter.Type = aws.String(m["type"].(string)) + filter.Value = aws.String(m["value"].(string)) + + filters = append(filters, &filter) + } + + return filters +} + +// 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)) + for _, tf := range list { + l := make(map[string]string) + if *tf.Key != "" { + l["key"] = *tf.Key + } + if *tf.Value != "" { + l["value"] = *tf.Value + } + if *tf.Type != "" { + l["type"] = *tf.Type + } + result = append(result, l) + } + return result +} + +// onPremisesTagFiltersToMap converts lists of on-prem tag filters into a []map[string]string. +func onPremisesTagFiltersToMap(list []*codedeploy.TagFilter) []map[string]string { + result := make([]map[string]string, 0, len(list)) + for _, tf := range list { + l := make(map[string]string) + if *tf.Key != "" { + l["key"] = *tf.Key + } + if *tf.Value != "" { + l["value"] = *tf.Value + } + if *tf.Type != "" { + l["type"] = *tf.Type + } + result = append(result, l) + } + return result +} + +// validateTagFilters confirms the "value" component of a tag filter is one of +// AWS's three allowed types. +func validateTagFilters(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if value != "KEY_ONLY" && value != "VALUE_ONLY" && value != "KEY_AND_VALUE" { + errors = append(errors, fmt.Errorf( + "%q must be one of \"KEY_ONLY\", \"VALUE_ONLY\", or \"KEY_AND_VALUE\"", k)) + } + return +} + +func resourceAwsCodeDeployTagFilterHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + // Nothing's actually required in tag filters, so we must check the + // presence of all values before attempting a hash. + if v, ok := m["key"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + if v, ok := m["type"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + if v, ok := m["value"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} From 390f226eb51d66cf9398a882d605658d9c683d44 Mon Sep 17 00:00:00 2001 From: Christopher Tiwald Date: Tue, 20 Oct 2015 18:04:15 -0400 Subject: [PATCH 5/7] aws: Add aws_codedeploy_deployment_group tests --- ...ce_aws_codedeploy_deployment_group_test.go | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go diff --git a/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go b/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go new file mode 100644 index 0000000000..d883b26b8d --- /dev/null +++ b/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go @@ -0,0 +1,199 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codedeploy" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSCodeDeployDeploymentGroup_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeDeployDeploymentGroupDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSCodeDeployDeploymentGroup, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo"), + ), + }, + resource.TestStep{ + Config: testAccAWSCodeDeployDeploymentGroupModifier, + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeDeployDeploymentGroupExists("aws_codedeploy_deployment_group.foo"), + ), + }, + }, + }) +} + +func testAccCheckAWSCodeDeployDeploymentGroupDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).codedeployconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_codedeploy_deployment_group" { + continue + } + + resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{ + ApplicationName: aws.String(rs.Primary.Attributes["application_name"]), + DeploymentGroupName: aws.String(rs.Primary.Attributes["deployment_group_name"]), + }) + + if err == nil { + if resp.DeploymentGroupInfo.DeploymentGroupName != nil { + return fmt.Errorf("CodeDeploy deployment group still exists:\n%#v", *resp.DeploymentGroupInfo.DeploymentGroupName) + } + } + + return err + } + + return nil +} + +func testAccCheckAWSCodeDeployDeploymentGroupExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +var testAccAWSCodeDeployDeploymentGroup = ` +resource "aws_codedeploy_app" "foo_app" { + name = "foo_app" +} + +resource "aws_iam_role_policy" "foo_policy" { + name = "foo_policy" + role = "${aws_iam_role.foo_role.id}" + policy = < Date: Tue, 20 Oct 2015 18:04:39 -0400 Subject: [PATCH 6/7] aws: Add docs for aws_codedeploy_deployment_group --- .../codedeploy_deployment_group.html.markdown | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown diff --git a/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown b/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown new file mode 100644 index 0000000000..cb2417fed6 --- /dev/null +++ b/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown @@ -0,0 +1,108 @@ +--- +layout: "aws" +page_title: "AWS: aws_codedeploy_deployment_group" +sidebar_current: "docs-aws-resource-codedeploy-deployment-group" +description: |\ + Provides a CodeDeploy deployment group. +--- + +# aws\_codedeploy\_deployment\_group + +Provides a CodeDeploy deployment group for an application + +## Example Usage + +``` +resource "aws_codedeploy_app" "foo_app" { + name = "foo_app" +} + +resource "aws_iam_role_policy" "foo_policy" { + name = "foo_policy" + role = "${aws_iam_role.foo_role.id}" + policy = < Date: Wed, 21 Oct 2015 08:34:46 -0700 Subject: [PATCH 7/7] Codeploy deployment group app_name instead of application_name. The corresponding resource is called aws_codeploy_app, so for consistency we'll name the attribute app_name instead of application_name. --- .../aws/resource_aws_codedeploy_deployment_group.go | 12 ++++++------ .../resource_aws_codedeploy_deployment_group_test.go | 6 +++--- .../aws/r/codedeploy_deployment_group.html.markdown | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go b/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go index a9f3acb078..ee81f1cf3c 100644 --- a/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go +++ b/builtin/providers/aws/resource_aws_codedeploy_deployment_group.go @@ -23,7 +23,7 @@ func resourceAwsCodeDeployDeploymentGroup() *schema.Resource { Delete: resourceAwsCodeDeployDeploymentGroupDelete, Schema: map[string]*schema.Schema{ - "application_name": &schema.Schema{ + "app_name": &schema.Schema{ Type: schema.TypeString, Required: true, ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { @@ -132,7 +132,7 @@ func resourceAwsCodeDeployDeploymentGroup() *schema.Resource { func resourceAwsCodeDeployDeploymentGroupCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).codedeployconn - application := d.Get("application_name").(string) + application := d.Get("app_name").(string) deploymentGroup := d.Get("deployment_group_name").(string) input := codedeploy.CreateDeploymentGroupInput{ @@ -189,14 +189,14 @@ func resourceAwsCodeDeployDeploymentGroupRead(d *schema.ResourceData, meta inter log.Printf("[DEBUG] Reading CodeDeploy DeploymentGroup %s", d.Id()) resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{ - ApplicationName: aws.String(d.Get("application_name").(string)), + ApplicationName: aws.String(d.Get("app_name").(string)), DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), }) if err != nil { return err } - d.Set("application_name", *resp.DeploymentGroupInfo.ApplicationName) + d.Set("app_name", *resp.DeploymentGroupInfo.ApplicationName) d.Set("autoscaling_groups", resp.DeploymentGroupInfo.AutoScalingGroups) d.Set("deployment_config_name", *resp.DeploymentGroupInfo.DeploymentConfigName) d.Set("deployment_group_name", *resp.DeploymentGroupInfo.DeploymentGroupName) @@ -215,7 +215,7 @@ func resourceAwsCodeDeployDeploymentGroupUpdate(d *schema.ResourceData, meta int conn := meta.(*AWSClient).codedeployconn input := codedeploy.UpdateDeploymentGroupInput{ - ApplicationName: aws.String(d.Get("application_name").(string)), + ApplicationName: aws.String(d.Get("app_name").(string)), CurrentDeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), } @@ -258,7 +258,7 @@ func resourceAwsCodeDeployDeploymentGroupDelete(d *schema.ResourceData, meta int log.Printf("[DEBUG] Deleting CodeDeploy DeploymentGroup %s", d.Id()) _, err := conn.DeleteDeploymentGroup(&codedeploy.DeleteDeploymentGroupInput{ - ApplicationName: aws.String(d.Get("application_name").(string)), + ApplicationName: aws.String(d.Get("app_name").(string)), DeploymentGroupName: aws.String(d.Get("deployment_group_name").(string)), }) if err != nil { diff --git a/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go b/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go index d883b26b8d..7608b1f585 100644 --- a/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go +++ b/builtin/providers/aws/resource_aws_codedeploy_deployment_group_test.go @@ -41,7 +41,7 @@ func testAccCheckAWSCodeDeployDeploymentGroupDestroy(s *terraform.State) error { } resp, err := conn.GetDeploymentGroup(&codedeploy.GetDeploymentGroupInput{ - ApplicationName: aws.String(rs.Primary.Attributes["application_name"]), + ApplicationName: aws.String(rs.Primary.Attributes["app_name"]), DeploymentGroupName: aws.String(rs.Primary.Attributes["deployment_group_name"]), }) @@ -123,7 +123,7 @@ EOF } resource "aws_codedeploy_deployment_group" "foo" { - application_name = "${aws_codedeploy_app.foo_app.name}" + app_name = "${aws_codedeploy_app.foo_app.name}" deployment_group_name = "foo" service_role_arn = "${aws_iam_role.foo_role.arn}" ec2_tag_filter { @@ -188,7 +188,7 @@ EOF } resource "aws_codedeploy_deployment_group" "foo" { - application_name = "${aws_codedeploy_app.foo_app.name}" + app_name = "${aws_codedeploy_app.foo_app.name}" deployment_group_name = "bar" service_role_arn = "${aws_iam_role.foo_role.arn}" ec2_tag_filter { diff --git a/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown b/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown index cb2417fed6..ae0c3b6455 100644 --- a/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown +++ b/website/source/docs/providers/aws/r/codedeploy_deployment_group.html.markdown @@ -67,7 +67,7 @@ EOF } resource "aws_codedeploy_deployment_group" "foo" { - application_name = "${aws_codedeploy_app.foo_app.name}" + app_name = "${aws_codedeploy_app.foo_app.name}" deployment_group_name = "bar" service_role_arn = "${aws_iam_role.foo_role.arn}" ec2_tag_filter { @@ -82,7 +82,7 @@ resource "aws_codedeploy_deployment_group" "foo" { The following arguments are supported: -* `application_name` - (Required) The name of the application. +* `app_name` - (Required) The name of the application. * `deployment_group_name` - (Required) The name of the deployment group. * `service_role_arn` - (Required) The service role ARN that allows deployments. * `autoscaling_groups` - (Optional) Autoscaling groups associated with the deployment group. @@ -101,7 +101,7 @@ Both ec2_tag_filter and on_premises_tag_filter blocks support the following: The following attributes are exported: * `id` - The deployment group's ID. -* `application_name` - The group's assigned application. +* `app_name` - The group's assigned application. * `deployment_group_name` - The group's name. * `service_role_arn` - The group's service role ARN. * `autoscaling_groups` - The autoscaling groups associated with the deployment group.