From 4aa5117e82382fadc84a0f6682dd85a83da1b794 Mon Sep 17 00:00:00 2001 From: liamjbennett Date: Thu, 12 Jan 2017 16:01:15 +0000 Subject: [PATCH] New resource aws_codebuild_project --- CHANGELOG.md | 43 -- builtin/providers/aws/config.go | 3 + builtin/providers/aws/provider.go | 1 + .../aws/resource_aws_codebuild_project.go | 713 ++++++++++++++++++ .../resource_aws_codebuild_project_test.go | 367 +++++++++ builtin/providers/aws/tagsCodeBuild.go | 67 ++ builtin/providers/aws/tagsCodeBuild_test.go | 103 +++ .../aws/r/codebuild_project.html.markdown | 158 ---- 8 files changed, 1254 insertions(+), 201 deletions(-) create mode 100644 builtin/providers/aws/resource_aws_codebuild_project.go create mode 100644 builtin/providers/aws/resource_aws_codebuild_project_test.go create mode 100644 builtin/providers/aws/tagsCodeBuild.go create mode 100644 builtin/providers/aws/tagsCodeBuild_test.go delete mode 100644 website/source/docs/providers/aws/r/codebuild_project.html.markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index 973c389af..2f2263ac7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,49 +34,6 @@ BUG FIXES: ## 0.8.6 (unreleased) -FEATURES: - - * **New Data Source:** `aws_kms_secret` [GH-11460] - - -IMPROVEMENTS: - - * provider/aws: Add EBS Volume support for EMR Instance Groups [GH-11411] - * provider/aws: Add support for policy to AWS provider assume_role [GH-11501] - * provider/aws: Add support for more sns_topic_subscription parameters on import command [GH-10408] - * provider/aws: Add support for Sever Side Encryption with default S3 KMS key to `aws_s3_bucket_object` [GH-11261] - * provider/aws: Add support for Cross Region RDS Cluster Replica [GH-11428] - * provider/azurerm: Add support for scale sets overprovision [GH-11516] - * provider/fastly: Adds papertrail logging [GH-11491] - * provider/google: allow instance group managers in region other than project [GH-11294] - * provider/opsgenie: Descriptions for Teams [GH-11391] - * provider/rancher: rancher_registration_token add image parameter [GH-11551] - -BUG FIXES: - - * core: Remove missed subfields when parent list is removed [GH-11498] - * provider/aws: Fix issue with `path` not updated when modifying AWS API Gateway Resource [GH-11443] - * provider/aws: Fix AWS Lambda Qualifier Regexp for `aws_lambda_permission` [GH-11383] - * provider/aws: allow destroy of LB stickiness policy with missing LB [GH-11462] - * provider/aws: ECS Placement constraints fix [GH-11475] - * provider/aws: retry kms_key CreateKey if arn in policy not yet seen [GH-11509] - * provider/aws: Fix ALB Listener Rule Import [GH-1174] - * provider/azurerm: Scale Sets Load balancer pools should not be computed [GH-11516] - * provider/azurerm: Scale Sets ip configuration handling and update support for load balancer backend pools. [GH-11516] - * provider/azurerm: check if lb sub resources exist when reading [GH-11553] - * provider/ignition: Allow to add authorized keys without user creation [GH-11406] - * provider/ignition: mount and path are mutually exclusive [GH-11409] - * provider/ns1: Fix "use_client_subnet" in ns1_record [GH-11368] - * provider/openstack: Remove Default Security Group Rules on Create [GH-11466] - * provider/pagerduty: Allow timeouts to be disabled (pagerduty_service) [GH-11483] - * provider/rancher: Use environment specific client for accessing resources [GH-11503] - * provider/rancher: Refresh rancher stack from state on delete [GH-11539] - * provider/rancher: Refresh rancher token and registry from state on not found [GH-11543] - * provider/rancher: return error when Rancher template not found [GH-11544] - * provider/rancher: rancher_stack set docker_compose and rancher_compose [GH-11550] - * provider/statuscake: Remove computed from statuscake_test timeout parameter [GH-11541] - * provisioner/chef: Attributes JSON coming from computed source validates [GH-11502] - ## 0.8.5 (26 January 2017) BACKWARDS INCOMPATIBILITIES / NOTES: diff --git a/builtin/providers/aws/config.go b/builtin/providers/aws/config.go index f3c721c97..d15b0f4bc 100644 --- a/builtin/providers/aws/config.go +++ b/builtin/providers/aws/config.go @@ -24,6 +24,7 @@ import ( "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/aws/aws-sdk-go/service/cloudwatchevents" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/codebuild" "github.com/aws/aws-sdk-go/service/codecommit" "github.com/aws/aws-sdk-go/service/codedeploy" "github.com/aws/aws-sdk-go/service/databasemigrationservice" @@ -146,6 +147,7 @@ type AWSClient struct { lightsailconn *lightsail.Lightsail opsworksconn *opsworks.OpsWorks glacierconn *glacier.Glacier + codebuildconn *codebuild.CodeBuild codedeployconn *codedeploy.CodeDeploy codecommitconn *codecommit.CodeCommit sfnconn *sfn.SFN @@ -277,6 +279,7 @@ func (c *Config) Client() (interface{}, error) { client.cloudwatcheventsconn = cloudwatchevents.New(sess) client.cloudwatchlogsconn = cloudwatchlogs.New(sess) client.codecommitconn = codecommit.New(sess) + client.codebuildconn = codebuild.New(sess) client.codedeployconn = codedeploy.New(sess) client.dmsconn = databasemigrationservice.New(sess) client.dsconn = directoryservice.New(sess) diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 429fc3a90..fcdcf5ca5 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -238,6 +238,7 @@ func Provider() terraform.ResourceProvider { "aws_codedeploy_deployment_group": resourceAwsCodeDeployDeploymentGroup(), "aws_codecommit_repository": resourceAwsCodeCommitRepository(), "aws_codecommit_trigger": resourceAwsCodeCommitTrigger(), + "aws_codebuild_project": resourceAwsCodeBuildProject(), "aws_customer_gateway": resourceAwsCustomerGateway(), "aws_db_event_subscription": resourceAwsDbEventSubscription(), "aws_db_instance": resourceAwsDbInstance(), diff --git a/builtin/providers/aws/resource_aws_codebuild_project.go b/builtin/providers/aws/resource_aws_codebuild_project.go new file mode 100644 index 000000000..379002664 --- /dev/null +++ b/builtin/providers/aws/resource_aws_codebuild_project.go @@ -0,0 +1,713 @@ +package aws + +import ( + "bytes" + "fmt" + "log" + "regexp" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codebuild" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsCodeBuildProject() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsCodeBuildProjectCreate, + Read: resourceAwsCodeBuildProjectRead, + Update: resourceAwsCodeBuildProjectUpdate, + Delete: resourceAwsCodeBuildProjectDelete, + + Schema: map[string]*schema.Schema{ + "artifacts": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + "location": { + Type: schema.TypeString, + Optional: true, + }, + "namespace_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateAwsCodeBuildArifactsNamespaceType, + }, + "packaging": { + Type: schema.TypeString, + Optional: true, + }, + "path": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsCodeBuildArifactsType, + }, + }, + }, + Set: resourceAwsCodeBuildProjectArtifactsHash, + }, + "description": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsCodeBuildProjectDescription, + }, + "encryption_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "environment": &schema.Schema{ + Type: schema.TypeSet, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "compute_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsCodeBuildEnvironmentComputeType, + }, + "environment_variable": &schema.Schema{ + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "image": { + Type: schema.TypeString, + Required: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsCodeBuildEnvironmentType, + }, + }, + }, + Set: resourceAwsCodeBuildProjectEnvironmentHash, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAwsCodeBuildProjectName, + }, + "service_role": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "source": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "auth": &schema.Schema{ + Type: schema.TypeSet, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "resource": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsCodeBuildSourceAuthType, + }, + }, + }, + Optional: true, + }, + "buildspec": { + Type: schema.TypeString, + Optional: true, + }, + "location": { + Type: schema.TypeString, + Optional: true, + }, + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAwsCodeBuildSourceType, + }, + }, + }, + Required: true, + MaxItems: 1, + }, + "timeout": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateAwsCodeBuildTimeout, + }, + "tags": tagsSchema(), + }, + } +} + +func resourceAwsCodeBuildProjectCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codebuildconn + + projectEnv := expandProjectEnvironment(d) + projectSource := expandProjectSource(d) + projectArtifacts := expandProjectArtifacts(d) + + params := &codebuild.CreateProjectInput{ + Environment: projectEnv, + Name: aws.String(d.Get("name").(string)), + Source: &projectSource, + Artifacts: &projectArtifacts, + } + + if v, ok := d.GetOk("description"); ok { + params.Description = aws.String(v.(string)) + } + + if v, ok := d.GetOk("encryption_key"); ok { + params.EncryptionKey = aws.String(v.(string)) + } + + if v, ok := d.GetOk("service_role"); ok { + params.ServiceRole = aws.String(v.(string)) + } + + if v, ok := d.GetOk("timeout"); ok { + params.TimeoutInMinutes = aws.Int64(int64(v.(int))) + } + + var resp *codebuild.CreateProjectOutput + err := resource.Retry(30*time.Second, func() *resource.RetryError { + var err error + + resp, err = conn.CreateProject(params) + + if err != nil { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + }) + + if err != nil { + return fmt.Errorf("[ERROR] Error creating CodeBuild project: %s", err) + } + + d.SetId(*resp.Project.Arn) + + return resourceAwsCodeBuildProjectUpdate(d, meta) +} + +func expandProjectArtifacts(d *schema.ResourceData) codebuild.ProjectArtifacts { + configs := d.Get("artifacts").(*schema.Set).List() + data := configs[0].(map[string]interface{}) + + projectArtifacts := codebuild.ProjectArtifacts{ + Type: aws.String(data["type"].(string)), + } + + if data["location"].(string) != "" { + projectArtifacts.Location = aws.String(data["location"].(string)) + } + + if data["name"].(string) != "" { + projectArtifacts.Name = aws.String(data["name"].(string)) + } + + if data["namespace_type"].(string) != "" { + projectArtifacts.NamespaceType = aws.String(data["namespace_type"].(string)) + } + + if data["packaging"].(string) != "" { + projectArtifacts.Packaging = aws.String(data["packaging"].(string)) + } + + if data["path"].(string) != "" { + projectArtifacts.Path = aws.String(data["path"].(string)) + } + + return projectArtifacts +} + +func expandProjectEnvironment(d *schema.ResourceData) *codebuild.ProjectEnvironment { + configs := d.Get("environment").(*schema.Set).List() + projectEnv := &codebuild.ProjectEnvironment{} + + envConfig := configs[0].(map[string]interface{}) + + if v := envConfig["compute_type"]; v != nil { + projectEnv.ComputeType = aws.String(v.(string)) + } + + if v := envConfig["image"]; v != nil { + projectEnv.Image = aws.String(v.(string)) + } + + if v := envConfig["type"]; v != nil { + projectEnv.Type = aws.String(v.(string)) + } + + if v := envConfig["environment_variable"]; v != nil { + envVariables := v.([]interface{}) + if len(envVariables) > 0 { + projectEnvironmentVariables := make([]*codebuild.EnvironmentVariable, 0, len(envVariables)) + + for _, envVariablesConfig := range envVariables { + config := envVariablesConfig.(map[string]interface{}) + + projectEnvironmentVar := &codebuild.EnvironmentVariable{} + + if v := config["name"].(string); v != "" { + projectEnvironmentVar.Name = &v + } + + if v := config["value"].(string); v != "" { + projectEnvironmentVar.Value = &v + } + + projectEnvironmentVariables = append(projectEnvironmentVariables, projectEnvironmentVar) + } + + projectEnv.EnvironmentVariables = projectEnvironmentVariables + } + } + + return projectEnv +} + +func expandProjectSource(d *schema.ResourceData) codebuild.ProjectSource { + configs := d.Get("source").(*schema.Set).List() + projectSource := codebuild.ProjectSource{} + + for _, configRaw := range configs { + data := configRaw.(map[string]interface{}) + + sourceType := data["type"].(string) + location := data["location"].(string) + buildspec := data["buildspec"].(string) + + projectSource = codebuild.ProjectSource{ + Type: &sourceType, + Location: &location, + Buildspec: &buildspec, + } + + if v, ok := data["auth"]; ok { + if len(v.(*schema.Set).List()) > 0 { + auth := v.(*schema.Set).List()[0].(map[string]interface{}) + + projectSource.Auth = &codebuild.SourceAuth{ + Type: aws.String(auth["type"].(string)), + Resource: aws.String(auth["resource"].(string)), + } + } + } + } + + return projectSource +} + +func resourceAwsCodeBuildProjectRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codebuildconn + + resp, err := conn.BatchGetProjects(&codebuild.BatchGetProjectsInput{ + Names: []*string{ + aws.String(d.Id()), + }, + }) + + if err != nil { + return fmt.Errorf("[ERROR] Error retreiving Projects: %q", err) + } + + // if nothing was found, then return no state + if len(resp.Projects) == 0 { + log.Printf("[INFO]: No projects were found, removing from state") + d.SetId("") + return nil + } + + project := resp.Projects[0] + + if err := d.Set("artifacts", flattenAwsCodebuildProjectArtifacts(project.Artifacts)); err != nil { + return err + } + + if err := d.Set("environment", schema.NewSet(resourceAwsCodeBuildProjectEnvironmentHash, flattenAwsCodebuildProjectEnvironment(project.Environment))); err != nil { + return err + } + + if err := d.Set("source", flattenAwsCodebuildProjectSource(project.Source)); err != nil { + return err + } + + d.Set("description", project.Description) + d.Set("encryption_key", project.EncryptionKey) + d.Set("name", project.Name) + d.Set("service_role", project.ServiceRole) + d.Set("timeout", project.TimeoutInMinutes) + + if err := d.Set("tags", tagsToMapCodeBuild(project.Tags)); err != nil { + return err + } + + return nil +} + +func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codebuildconn + + params := &codebuild.UpdateProjectInput{ + Name: aws.String(d.Get("name").(string)), + } + + if d.HasChange("environment") { + projectEnv := expandProjectEnvironment(d) + params.Environment = projectEnv + } + + if d.HasChange("source") { + projectSource := expandProjectSource(d) + params.Source = &projectSource + } + + if d.HasChange("artifacts") { + projectArtifacts := expandProjectArtifacts(d) + params.Artifacts = &projectArtifacts + } + + if d.HasChange("description") { + params.Description = aws.String(d.Get("description").(string)) + } + + if d.HasChange("encryption_key") { + params.EncryptionKey = aws.String(d.Get("encryption_key").(string)) + } + + if d.HasChange("service_role") { + params.ServiceRole = aws.String(d.Get("service_role").(string)) + } + + if d.HasChange("timeout") { + params.TimeoutInMinutes = aws.Int64(int64(d.Get("timeout").(int))) + } + + if d.HasChange("tags") { + params.Tags = tagsFromMapCodeBuild(d.Get("tags").(map[string]interface{})) + } + + _, err := conn.UpdateProject(params) + + if err != nil { + return fmt.Errorf( + "[ERROR] Error updating CodeBuild project (%s): %s", + d.Id(), err) + } + + return resourceAwsCodeBuildProjectRead(d, meta) +} + +func resourceAwsCodeBuildProjectDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).codebuildconn + + _, err := conn.DeleteProject(&codebuild.DeleteProjectInput{ + Name: aws.String(d.Id()), + }) + + if err != nil { + return err + } + + d.SetId("") + + return nil +} + +func flattenAwsCodebuildProjectArtifacts(artifacts *codebuild.ProjectArtifacts) *schema.Set { + + artifactSet := schema.Set{ + F: resourceAwsCodeBuildProjectArtifactsHash, + } + + values := map[string]interface{}{} + + values["type"] = *artifacts.Type + + if artifacts.Location != nil { + values["location"] = *artifacts.Location + } + + if artifacts.Name != nil { + values["name"] = *artifacts.Name + } + + if artifacts.NamespaceType != nil { + values["namespace_type"] = *artifacts.NamespaceType + } + + if artifacts.Packaging != nil { + values["packaging"] = *artifacts.Packaging + } + + if artifacts.Path != nil { + values["path"] = *artifacts.Path + } + + artifactSet.Add(values) + + return &artifactSet +} + +func flattenAwsCodebuildProjectEnvironment(environment *codebuild.ProjectEnvironment) []interface{} { + envConfig := map[string]interface{}{} + + envConfig["type"] = *environment.Type + envConfig["compute_type"] = *environment.ComputeType + envConfig["image"] = *environment.Image + + if environment.EnvironmentVariables != nil { + envConfig["environment_variable"] = environmentVariablesToMap(environment.EnvironmentVariables) + } + + return []interface{}{envConfig} + +} + +func flattenAwsCodebuildProjectSource(source *codebuild.ProjectSource) *schema.Set { + + sourceSet := schema.Set{ + F: resourceAwsCodeBuildProjectSourceHash, + } + + sourceConfig := map[string]interface{}{} + + sourceConfig["type"] = *source.Type + + if source.Auth != nil { + sourceConfig["auth"] = sourceAuthToMap(source.Auth) + } + + if source.Buildspec != nil { + sourceConfig["buildspec"] = *source.Buildspec + } + + if source.Location != nil { + sourceConfig["location"] = *source.Location + } + + sourceSet.Add(sourceConfig) + + return &sourceSet + +} + +func resourceAwsCodeBuildProjectArtifactsHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + artifactType := m["type"].(string) + + buf.WriteString(fmt.Sprintf("%s-", artifactType)) + + return hashcode.String(buf.String()) +} + +func resourceAwsCodeBuildProjectEnvironmentHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + environmentType := m["type"].(string) + computeType := m["compute_type"].(string) + image := m["image"].(string) + + buf.WriteString(fmt.Sprintf("%s-", environmentType)) + buf.WriteString(fmt.Sprintf("%s-", computeType)) + buf.WriteString(fmt.Sprintf("%s-", image)) + + return hashcode.String(buf.String()) +} + +func resourceAwsCodeBuildProjectSourceHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + sourceType := m["type"].(string) + buildspec := m["buildspec"].(string) + location := m["location"].(string) + + buf.WriteString(fmt.Sprintf("%s-", sourceType)) + buf.WriteString(fmt.Sprintf("%s-", buildspec)) + buf.WriteString(fmt.Sprintf("%s-", location)) + + return hashcode.String(buf.String()) +} + +func environmentVariablesToMap(environmentVariables []*codebuild.EnvironmentVariable) []map[string]interface{} { + + envVariables := make([]map[string]interface{}, len(environmentVariables)) + + if len(environmentVariables) > 0 { + for i := 0; i < len(environmentVariables); i++ { + env := environmentVariables[i] + item := make(map[string]interface{}) + item["name"] = *env.Name + item["value"] = *env.Value + envVariables = append(envVariables, item) + } + } + + return envVariables +} + +func sourceAuthToMap(sourceAuth *codebuild.SourceAuth) map[string]interface{} { + + auth := map[string]interface{}{} + auth["type"] = *sourceAuth.Type + + if sourceAuth.Type != nil { + auth["resource"] = *sourceAuth.Resource + } + + return auth +} + +func validateAwsCodeBuildArifactsType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "CODEPIPELINE": true, + "NO_ARTIFACTS": true, + "S3": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Arifacts Type can only be CODEPIPELINE / NO_ARTIFACTS / S3")) + } + return +} + +func validateAwsCodeBuildArifactsNamespaceType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "NONE": true, + "BUILD_ID": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Arifacts Namespace Type can only be NONE / BUILD_ID")) + } + return +} + +func validateAwsCodeBuildProjectName(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if !regexp.MustCompile(`^[A-Za-z0-9]`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "first character of %q must be a letter or number", value)) + } + + if !regexp.MustCompile(`^[A-Za-z0-9\-_]+$`).MatchString(value) { + errors = append(errors, fmt.Errorf( + "only alphanumeric characters, hyphens and underscores allowed in %q", value)) + } + + if len(value) > 255 { + errors = append(errors, fmt.Errorf( + "%q cannot be greater than 255 characters", value)) + } + + return +} + +func validateAwsCodeBuildProjectDescription(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > 255 { + errors = append(errors, fmt.Errorf("%q cannot be greater than 255 characters", value)) + } + return +} + +func validateAwsCodeBuildEnvironmentComputeType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "BUILD_GENERAL1_SMALL": true, + "BUILD_GENERAL1_MEDIUM": true, + "BUILD_GENERAL1_LARGE": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Environment Compute Type can only be BUILD_GENERAL1_SMALL / BUILD_GENERAL1_MEDIUM / BUILD_GENERAL1_LARGE")) + } + return +} + +func validateAwsCodeBuildEnvironmentType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "LINUX_CONTAINER": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Environment Type can only be LINUX_CONTAINER")) + } + return +} + +func validateAwsCodeBuildSourceType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "CODECOMMIT": true, + "CODEPIPELINE": true, + "GITHUB": true, + "S3": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Source Type can only be CODECOMMIT / CODEPIPELINE / GITHUB / S3")) + } + return +} + +func validateAwsCodeBuildSourceAuthType(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + types := map[string]bool{ + "OAUTH": true, + } + + if !types[value] { + errors = append(errors, fmt.Errorf("CodeBuild: Source Auth Type can only be OAUTH")) + } + return +} + +func validateAwsCodeBuildTimeout(v interface{}, k string) (ws []string, errors []error) { + value := v.(int) + + if value < 5 || value > 480 { + errors = append(errors, fmt.Errorf("%q must be greater than 5 minutes and less than 480 minutes (8 hours)", value)) + } + return +} diff --git a/builtin/providers/aws/resource_aws_codebuild_project_test.go b/builtin/providers/aws/resource_aws_codebuild_project_test.go new file mode 100644 index 000000000..a21fe0f2a --- /dev/null +++ b/builtin/providers/aws/resource_aws_codebuild_project_test.go @@ -0,0 +1,367 @@ +package aws + +import ( + "fmt" + "strings" + "testing" + "unicode" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/codebuild" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSCodeBuildProject_basic(t *testing.T) { + name := acctest.RandString(10) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeBuildProjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccAWSCodeBuildProjectConfig_basic(name), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"), + ), + }, + }, + }) +} + +func TestAccAWSCodeBuildProject_artifactsTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "CODEPIPELINE", ErrCount: 0}, + {Value: "NO_ARTIFACTS", ErrCount: 0}, + {Value: "S3", ErrCount: 0}, + {Value: "XYZ", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildArifactsType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project artifacts type to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_artifactsNamespaceTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "NONE", ErrCount: 0}, + {Value: "BUILD_ID", ErrCount: 0}, + {Value: "XYZ", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildArifactsNamespaceType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project artifacts namepsace_type to trigger a validation error") + } + } +} + +func longTestData() string { + data := ` + test-test-test-test-test-test-test-test-test-test- + test-test-test-test-test-test-test-test-test-test- + test-test-test-test-test-test-test-test-test-test- + test-test-test-test-test-test-test-test-test-test- + test-test-test-test-test-test-test-test-test-test- + test-test-test-test-test-test-test-test-test-test- + ` + + return strings.Map(func(r rune) rune { + if unicode.IsSpace(r) { + return -1 + } + return r + }, data) +} + +func TestAccAWSCodeBuildProject_nameValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "_test", ErrCount: 1}, + {Value: "test", ErrCount: 0}, + {Value: "1_test", ErrCount: 0}, + {Value: "test**1", ErrCount: 1}, + {Value: longTestData(), ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildProjectName(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project name to trigger a validation error - %s", errors) + } + } +} + +func TestAccAWSCodeBuildProject_descriptionValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "test", ErrCount: 0}, + {Value: longTestData(), ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildProjectDescription(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project description to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_environmentComputeTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "BUILD_GENERAL1_SMALL", ErrCount: 0}, + {Value: "BUILD_GENERAL1_MEDIUM", ErrCount: 0}, + {Value: "BUILD_GENERAL1_LARGE", ErrCount: 0}, + {Value: "BUILD_GENERAL1_VERYLARGE", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildEnvironmentComputeType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project environment compute_type to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_environmentTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "LINUX_CONTAINER", ErrCount: 0}, + {Value: "WINDOWS_CONTAINER", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildEnvironmentType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project environment type to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_sourceTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "CODECOMMIT", ErrCount: 0}, + {Value: "CODEPIPELINE", ErrCount: 0}, + {Value: "GITHUB", ErrCount: 0}, + {Value: "S3", ErrCount: 0}, + {Value: "GITLAB", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildSourceType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project source type to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_sourceAuthTypeValidation(t *testing.T) { + cases := []struct { + Value string + ErrCount int + }{ + {Value: "OAUTH", ErrCount: 0}, + {Value: "PASSWORD", ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildSourceAuthType(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project source auth to trigger a validation error") + } + } +} + +func TestAccAWSCodeBuildProject_timeoutValidation(t *testing.T) { + cases := []struct { + Value int + ErrCount int + }{ + {Value: 10, ErrCount: 0}, + {Value: 200, ErrCount: 0}, + {Value: 1, ErrCount: 1}, + {Value: 500, ErrCount: 1}, + } + + for _, tc := range cases { + _, errors := validateAwsCodeBuildTimeout(tc.Value, "aws_codebuild_project") + + if len(errors) != tc.ErrCount { + t.Fatalf("Expected the AWS CodeBuild project timeout to trigger a validation error") + } + } +} + +func testAccCheckAWSCodeBuildProjectExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No CodeBuild Project ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).codebuildconn + + out, err := conn.BatchGetProjects(&codebuild.BatchGetProjectsInput{ + Names: []*string{ + aws.String(rs.Primary.ID), + }, + }) + + if err != nil { + return err + } + + if len(out.Projects) < 1 { + return fmt.Errorf("No project found") + } + + return nil + } +} + +func testAccCheckAWSCodeBuildProjectDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).codebuildconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_codebuild_project" { + continue + } + + out, err := conn.BatchGetProjects(&codebuild.BatchGetProjectsInput{ + Names: []*string{ + aws.String(rs.Primary.ID), + }, + }) + + if err != nil { + return err + } + + if out != nil && len(out.Projects) > 0 { + return fmt.Errorf("Expected AWS CodeBuild Project to be gone, but was still found") + } + + return nil + } + + return fmt.Errorf("Default error in CodeBuild Test") +} + +func testAccAWSCodeBuildProjectConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_iam_role" "codebuild_role" { + name = "codebuild-role-%s" + assume_role_policy = <