diff --git a/aws/resource_aws_codebuild_project.go b/aws/resource_aws_codebuild_project.go index d6025575bda..d05353ceb7b 100644 --- a/aws/resource_aws_codebuild_project.go +++ b/aws/resource_aws_codebuild_project.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "regexp" + "strconv" "time" "github.com/aws/aws-sdk-go/aws" @@ -22,6 +23,9 @@ func resourceAwsCodeBuildProject() *schema.Resource { Update: resourceAwsCodeBuildProjectUpdate, Delete: resourceAwsCodeBuildProjectDelete, + SchemaVersion: 2, + MigrateState: resourceAwsCodebuildMigrateState, + Schema: map[string]*schema.Schema{ "artifacts": { Type: schema.TypeSet, @@ -106,6 +110,15 @@ func resourceAwsCodeBuildProject() *schema.Resource { Type: schema.TypeString, Required: true, }, + "type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + codebuild.EnvironmentVariableTypePlaintext, + codebuild.EnvironmentVariableTypeParameterStore, + }, false), + Default: codebuild.EnvironmentVariableTypePlaintext, + }, }, }, }, @@ -184,6 +197,15 @@ func resourceAwsCodeBuildProject() *schema.Resource { codebuild.SourceTypeGithubEnterprise, }, false), }, + "git_clone_depth": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(0), + }, + "insecure_ssl": { + Type: schema.TypeBool, + Optional: true, + }, }, }, Required: true, @@ -373,6 +395,10 @@ func expandProjectEnvironment(d *schema.ResourceData) *codebuild.ProjectEnvironm projectEnvironmentVar.Value = &v } + if v := config["type"].(string); v != "" { + projectEnvironmentVar.Type = &v + } + projectEnvironmentVariables = append(projectEnvironmentVariables, projectEnvironmentVar) } @@ -408,11 +434,15 @@ func expandProjectSource(d *schema.ResourceData) codebuild.ProjectSource { sourceType := data["type"].(string) location := data["location"].(string) buildspec := data["buildspec"].(string) + gitCloneDepth := aws.Int64(int64(data["git_clone_depth"].(int))) + insecureSsl := aws.Bool(data["insecure_ssl"].(bool)) projectSource = codebuild.ProjectSource{ - Type: &sourceType, - Location: &location, - Buildspec: &buildspec, + Type: &sourceType, + Location: &location, + Buildspec: &buildspec, + GitCloneDepth: gitCloneDepth, + InsecureSsl: insecureSsl, } if v, ok := data["auth"]; ok { @@ -527,7 +557,22 @@ func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{}) // But its a slice of pointers so if not set for every update, they get removed. params.Tags = tagsFromMapCodeBuild(d.Get("tags").(map[string]interface{})) - _, err := conn.UpdateProject(params) + err := resource.Retry(5*time.Minute, func() *resource.RetryError { + var err error + + _, err = conn.UpdateProject(params) + if err != nil { + // Work around eventual consistency of IAM + if isAWSErr(err, "InvalidInputException", "CodeBuild is not authorized to perform") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(err) + } + + return nil + + }) if err != nil { return fmt.Errorf( @@ -623,6 +668,14 @@ func flattenAwsCodeBuildProjectSource(source *codebuild.ProjectSource) []interfa m["location"] = *source.Location } + if source.GitCloneDepth != nil { + m["git_clone_depth"] = *source.GitCloneDepth + } + + if source.InsecureSsl != nil { + m["insecure_ssl"] = *source.InsecureSsl + } + l[0] = m return l @@ -668,7 +721,7 @@ func resourceAwsCodeBuildProjectEnvironmentHash(v interface{}) int { for _, e := range environmentVariables { if e != nil { // Old statefiles might have nil values in them ev := e.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s:%s-", ev["name"].(string), ev["value"].(string))) + buf.WriteString(fmt.Sprintf("%s:%s:%s-", ev["name"].(string), ev["type"].(string), ev["value"].(string))) } } @@ -686,6 +739,12 @@ func resourceAwsCodeBuildProjectSourceHash(v interface{}) int { if v, ok := m["location"]; ok { buf.WriteString(fmt.Sprintf("%s-", v.(string))) } + if v, ok := m["git_clone_depth"]; ok { + buf.WriteString(fmt.Sprintf("%s-", strconv.Itoa(v.(int)))) + } + if v, ok := m["insecure_ssl"]; ok { + buf.WriteString(fmt.Sprintf("%s-", strconv.FormatBool(v.(bool)))) + } return hashcode.String(buf.String()) } @@ -711,6 +770,7 @@ func environmentVariablesToMap(environmentVariables []*codebuild.EnvironmentVari item := map[string]interface{}{} item["name"] = *env.Name item["value"] = *env.Value + item["type"] = *env.Type envVariables = append(envVariables, item) } } diff --git a/aws/resource_aws_codebuild_project_migrate.go b/aws/resource_aws_codebuild_project_migrate.go index 97d7a9ff245..959370ae99e 100644 --- a/aws/resource_aws_codebuild_project_migrate.go +++ b/aws/resource_aws_codebuild_project_migrate.go @@ -5,6 +5,7 @@ import ( "log" "strings" + "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/terraform" ) @@ -14,6 +15,9 @@ func resourceAwsCodebuildMigrateState( case 0: log.Println("[INFO] Found AWS Codebuild State v0; migrating to v1") return migrateCodebuildStateV0toV1(is) + case 1: + log.Println("[INFO] Found AWS Codebuild State v1; migrating to v2") + return migrateCodebuildStateV1toV2(is) default: return is, fmt.Errorf("Unexpected schema version: %d", v) } @@ -34,3 +38,53 @@ func migrateCodebuildStateV0toV1(is *terraform.InstanceState) (*terraform.Instan log.Printf("[DEBUG] Attributes after migration: %#v", is.Attributes) return is, nil } + +func migrateCodebuildStateV1toV2(is *terraform.InstanceState) (*terraform.InstanceState, error) { + if is.Empty() { + log.Println("[DEBUG] Empty InstanceState; nothing to migrate.") + return is, nil + } + + log.Printf("[DEBUG] Attributes before migration v1: %#v", is.Attributes) + + prefix := "source" + entity := resourceAwsCodeBuildProject() + + // Read old keys + reader := &schema.MapFieldReader{ + Schema: entity.Schema, + Map: schema.BasicMapReader(is.Attributes), + } + result, err := reader.ReadField([]string{prefix}) + if err != nil { + return nil, err + } + + oldKeys, ok := result.Value.(*schema.Set) + if !ok { + return nil, fmt.Errorf("Got unexpected value from state: %#v", result.Value) + } + + // Delete old keys + for k := range is.Attributes { + if strings.HasPrefix(k, fmt.Sprintf("%s.", prefix)) { + delete(is.Attributes, k) + } + } + + // We just need the defaults for the new keys, no custom values needed + + // Write new keys + writer := schema.MapFieldWriter{ + Schema: entity.Schema, + } + if err := writer.WriteField([]string{prefix}, oldKeys); err != nil { + return is, err + } + for k, v := range writer.Map() { + is.Attributes[k] = v + } + + log.Printf("[DEBUG] Attributes after migration v2: %#v", is.Attributes) + return is, nil +} diff --git a/aws/resource_aws_codebuild_project_migrate_test.go b/aws/resource_aws_codebuild_project_migrate_test.go index 2ae6b4e5363..094e8ff94b4 100644 --- a/aws/resource_aws_codebuild_project_migrate_test.go +++ b/aws/resource_aws_codebuild_project_migrate_test.go @@ -1,6 +1,7 @@ package aws import ( + "reflect" "testing" "github.com/hashicorp/terraform/terraform" @@ -11,7 +12,7 @@ func TestAWSCodebuildMigrateState(t *testing.T) { StateVersion int ID string Attributes map[string]string - Expected string + Expected map[string]string Meta interface{} }{ "v0_1": { @@ -21,7 +22,11 @@ func TestAWSCodebuildMigrateState(t *testing.T) { "description": "some description", "timeout": "5", }, - Expected: "5", + Expected: map[string]string{ + "description": "some description", + "timeout": "5", + "build_timeout": "5", + }, }, "v0_2": { StateVersion: 0, @@ -30,7 +35,65 @@ func TestAWSCodebuildMigrateState(t *testing.T) { "description": "some description", "build_timeout": "5", }, - Expected: "5", + Expected: map[string]string{ + "description": "some description", + "build_timeout": "5", + }, + }, + "v1_1": { + StateVersion: 1, + ID: "tf-testing-file", + Attributes: map[string]string{ + "description": "some description", + "source.1060593600.auth.2706882902.type": "OAUTH", + "source.1060593600.type": "GITHUB", + "source.1060593600.buildspec": "", + "source.#": "1", + "source.1060593600.location": "https://github.com/hashicorp/packer.git", + "source.1060593600.auth.2706882902.resource": "FAKERESOURCE1", + "source.1060593600.auth.#": "1", + }, + // added defaults + Expected: map[string]string{ + "description": "some description", + "source.#": "1", + "source.3680370172.type": "GITHUB", + "source.3680370172.buildspec": "", + "source.3680370172.location": "https://github.com/hashicorp/packer.git", + "source.3680370172.git_clone_depth": "0", + "source.3680370172.insecure_ssl": "false", + "source.3680370172.auth.#": "1", + "source.3680370172.auth.2706882902.resource": "FAKERESOURCE1", + "source.3680370172.auth.2706882902.type": "OAUTH", + }, + }, + "v1_noop": { + StateVersion: 1, + ID: "tf-testing-file", + Attributes: map[string]string{ + "description": "some description", + "source.#": "1", + "source.3680370172.type": "GITHUB", + "source.3680370172.buildspec": "", + "source.3680370172.location": "https://github.com/hashicorp/packer.git", + "source.3680370172.git_clone_depth": "0", + "source.3680370172.insecure_ssl": "false", + "source.3680370172.auth.#": "1", + "source.3680370172.auth.2706882902.resource": "FAKERESOURCE1", + "source.3680370172.auth.2706882902.type": "OAUTH", + }, + Expected: map[string]string{ + "description": "some description", + "source.#": "1", + "source.3680370172.type": "GITHUB", + "source.3680370172.buildspec": "", + "source.3680370172.location": "https://github.com/hashicorp/packer.git", + "source.3680370172.git_clone_depth": "0", + "source.3680370172.insecure_ssl": "false", + "source.3680370172.auth.#": "1", + "source.3680370172.auth.2706882902.resource": "FAKERESOURCE1", + "source.3680370172.auth.2706882902.type": "OAUTH", + }, }, } @@ -46,8 +109,8 @@ func TestAWSCodebuildMigrateState(t *testing.T) { t.Fatalf("bad: %s, err: %#v", tn, err) } - if is.Attributes["build_timeout"] != tc.Expected { - t.Fatalf("Bad build_timeout migration: %s\n\n expected: %s", is.Attributes["build_timeout"], tc.Expected) + if !reflect.DeepEqual(is.Attributes, tc.Expected) { + t.Fatalf("Bad migration: %s\n\n expected: %s", is.Attributes, tc.Expected) } } } diff --git a/aws/resource_aws_codebuild_project_test.go b/aws/resource_aws_codebuild_project_test.go index e4a5ee3a657..fa4204aafbc 100644 --- a/aws/resource_aws_codebuild_project_test.go +++ b/aws/resource_aws_codebuild_project_test.go @@ -28,6 +28,17 @@ func TestAccAWSCodeBuildProject_basic(t *testing.T) { testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"), resource.TestCheckResourceAttr( "aws_codebuild_project.foo", "build_timeout", "5"), + resource.TestCheckResourceAttr( + "aws_codebuild_project.foo", "environment.2427541259.environment_variable.0.name", "SOME_KEY"), + // default + resource.TestCheckResourceAttr( + "aws_codebuild_project.foo", "environment.2427541259.environment_variable.0.type", "PLAINTEXT"), + resource.TestCheckResourceAttr( + "aws_codebuild_project.foo", "environment.2427541259.environment_variable.1.type", "PLAINTEXT"), + resource.TestCheckResourceAttr( + "aws_codebuild_project.foo", "environment.2427541259.environment_variable.2.type", "PARAMETER_STORE"), + // resource.TestCheckResourceAttr( + // "aws_codebuild_project.foo", "build_timeout", "5"), ), }, }, @@ -83,6 +94,8 @@ func TestAccAWSCodeBuildProject_sourceAuth(t *testing.T) { authType := "OAUTH" name := acctest.RandString(10) resourceName := "aws_codebuild_project.foo" + gitCloneDepth := "5" + insecureSsl := "true" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -97,8 +110,10 @@ func TestAccAWSCodeBuildProject_sourceAuth(t *testing.T) { Config: testAccAWSCodeBuildProjectConfig_sourceAuth(name, authResource, authType), Check: resource.ComposeTestCheckFunc( testAccCheckAWSCodeBuildProjectExists(resourceName), - resource.TestCheckResourceAttr(resourceName, "source.1060593600.auth.2706882902.resource", authResource), - resource.TestCheckResourceAttr(resourceName, "source.1060593600.auth.2706882902.type", authType), + resource.TestCheckResourceAttr(resourceName, "source.2491592095.git_clone_depth", gitCloneDepth), + resource.TestCheckResourceAttr(resourceName, "source.2491592095.insecure_ssl", insecureSsl), + resource.TestCheckResourceAttr(resourceName, "source.2491592095.auth.2706882902.resource", authResource), + resource.TestCheckResourceAttr(resourceName, "source.2491592095.auth.2706882902.type", authType), ), }, }, @@ -313,11 +328,25 @@ resource "aws_codebuild_project" "foo" { "name" = "SOME_KEY" "value" = "SOME_VALUE" } + + environment_variable = { + "name" = "SOME_OTHER_KEY" + "value" = "SOME_OTHER_VALUE" + "type" = "PLAINTEXT" + } + + environment_variable = { + "name" = "SOME_PARAMSTORE_KEY" + "value" = "SOME_PARAMSTORE_VALUE" + "type" = "PARAMETER_STORE" + } } source { - type = "GITHUB" - location = "https://github.com/hashicorp/packer.git" + type = "GITHUB" + location = "https://github.com/hashicorp/packer.git" + git_clone_depth = 1 + insecure_ssl = true } tags { @@ -398,11 +427,24 @@ resource "aws_codebuild_project" "foo" { "name" = "SOME_OTHERKEY" "value" = "SOME_OTHERVALUE" } + + environment_variable = { + "name" = "SOME_OTHER_KEY" + "value" = "SOME_OTHER_VALUE" + "type" = "PLAINTEXT" + } + + environment_variable = { + "name" = "SOME_PARAMSTORE_KEY" + "value" = "SOME_PARAMSTORE_VALUE" + "type" = "PARAMETER_STORE" + } } source { - type = "GITHUB" - location = "https://github.com/hashicorp/packer.git" + type = "GITHUB" + location = "https://github.com/hashicorp/packer.git" + git_clone_depth = 5 } tags { @@ -567,8 +609,10 @@ resource "aws_codebuild_project" "foo" { } source { - type = "GITHUB" - location = "https://github.com/hashicorp/packer.git" + type = "GITHUB" + location = "https://github.com/hashicorp/packer.git" + git_clone_depth = "5" + insecure_ssl = "true" auth { resource = "%[2]s" diff --git a/website/docs/r/codebuild_project.html.markdown b/website/docs/r/codebuild_project.html.markdown index 33ef73c13a1..fc615527c99 100644 --- a/website/docs/r/codebuild_project.html.markdown +++ b/website/docs/r/codebuild_project.html.markdown @@ -86,12 +86,67 @@ resource "aws_codebuild_project" "foo" { environment_variable { "name" = "SOME_KEY2" "value" = "SOME_VALUE2" + "type" = "PARAMETER_STORE" } } source { - type = "GITHUB" - location = "https://github.com/mitchellh/packer.git" + type = "GITHUB" + location = "https://github.com/mitchellh/packer.git" + git_clone_depth = 1 + } + + vpc_config { + vpc_id = "vpc-725fca" + + subnets = [ + "subnet-ba35d2e0", + "subnet-ab129af1", + ] + + security_group_ids = [ + "sg-f9f27d91", + "sg-e4f48g23", + ] + } + + tags { + "Environment" = "Test" + } +} + +resource "aws_codebuild_project" "github_enterprise" { + name = "test-project" + description = "test_codebuild_project" + build_timeout = "5" + service_role = "${aws_iam_role.codebuild_role.arn}" + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "aws/codebuild/nodejs:6.3.1" + type = "LINUX_CONTAINER" + + environment_variable { + "name" = "SOME_KEY1" + "value" = "SOME_VALUE1" + } + + environment_variable { + "name" = "SOME_KEY2" + "value" = "SOME_VALUE2" + "type" = "PARAMETER_STORE" + } + } + + source { + type = "GITHUB_ENTERPRISE" + location = "https://somewhere/org/repo.git" + git_clone_depth = 1 + insecure_ssl = true } vpc_config { @@ -150,6 +205,7 @@ The following arguments are supported: * `name` - (Required) The environment variable's name or key. * `value` - (Required) The environment variable's value. +* `type` - (Optional) The environment variable's type. Valid values for this parameter are: `PLAINTEXT` or `PARAMETER_STORE`. `source` supports the following: @@ -157,6 +213,8 @@ The following arguments are supported: * `auth` - (Optional) Information about the authorization settings for AWS CodeBuild to access the source code to be built. Auth blocks are documented below. * `buildspec` - (Optional) The build spec declaration to use for this build project's related builds. * `location` - (Optional) The location of the source code from git or s3. +* `git_clone_depth` - (Optional) Truncate git history to this many commits. +* `insecure_ssl` - (Optional) Ignore SSL warnings when connecting to source control. `auth` supports the following: