diff --git a/aws/resource_aws_codebuild_project.go b/aws/resource_aws_codebuild_project.go index ac1a15326cf..3a9dd619010 100644 --- a/aws/resource_aws_codebuild_project.go +++ b/aws/resource_aws_codebuild_project.go @@ -610,6 +610,24 @@ func resourceAwsCodeBuildProject() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "secondary_source_version": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 12, + Set: resourceAwsCodeBuildProjectArtifactsHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source_identifier": { + Type: schema.TypeString, + Required: true, + }, + "source_version": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, "build_timeout": { Type: schema.TypeInt, Optional: true, @@ -754,6 +772,10 @@ func resourceAwsCodeBuildProjectCreate(d *schema.ResourceData, meta interface{}) params.BadgeEnabled = aws.Bool(v.(bool)) } + if v, ok := d.GetOk("secondary_source_version"); ok && v.(*schema.Set).Len() > 0 { + params.SecondarySourceVersions = expandProjectSecondarySourceVersions(v.(*schema.Set)) + } + var resp *codebuild.CreateProjectOutput // Handle IAM eventual consistency err := resource.Retry(5*time.Minute, func() *resource.RetryError { @@ -777,7 +799,7 @@ func resourceAwsCodeBuildProjectCreate(d *schema.ResourceData, meta interface{}) resp, err = conn.CreateProject(params) } if err != nil { - return fmt.Errorf("Error creating CodeBuild project: %s", err) + return fmt.Errorf("Error creating CodeBuild project: %w", err) } d.SetId(aws.StringValue(resp.Project.Arn)) @@ -843,6 +865,22 @@ func expandProjectSecondaryArtifacts(d *schema.ResourceData) []*codebuild.Projec return artifacts } +func expandProjectSecondarySourceVersions(ssv *schema.Set) []*codebuild.ProjectSourceVersion { + sourceVersions := make([]*codebuild.ProjectSourceVersion, 0) + + rawSourceVersions := ssv.List() + if len(rawSourceVersions) == 0 { + return nil + } + + for _, config := range rawSourceVersions { + sourceVersion := expandProjectSourceVersion(config.(map[string]interface{})) + sourceVersions = append(sourceVersions, &sourceVersion) + } + + return sourceVersions +} + func expandProjectArtifacts(d *schema.ResourceData) codebuild.ProjectArtifacts { configs := d.Get("artifacts").([]interface{}) data := configs[0].(map[string]interface{}) @@ -894,6 +932,16 @@ func expandProjectArtifactData(data map[string]interface{}) codebuild.ProjectArt return projectArtifacts } +func expandProjectSourceVersion(data map[string]interface{}) codebuild.ProjectSourceVersion { + + sourceVersion := codebuild.ProjectSourceVersion{ + SourceIdentifier: aws.String(data["source_identifier"].(string)), + SourceVersion: aws.String(data["source_version"].(string)), + } + + return sourceVersion +} + func expandProjectCache(s []interface{}) *codebuild.ProjectCache { var projectCache *codebuild.ProjectCache @@ -1257,43 +1305,47 @@ func resourceAwsCodeBuildProjectRead(d *schema.ResourceData, meta interface{}) e project := resp.Projects[0] if err := d.Set("artifacts", flattenAwsCodeBuildProjectArtifacts(project.Artifacts)); err != nil { - return fmt.Errorf("error setting artifacts: %s", err) + return fmt.Errorf("error setting artifacts: %w", err) } if err := d.Set("environment", flattenAwsCodeBuildProjectEnvironment(project.Environment)); err != nil { - return fmt.Errorf("error setting environment: %s", err) + return fmt.Errorf("error setting environment: %w", err) } if err := d.Set("file_system_locations", flattenAwsCodeBuildProjectFileSystemLocations(project.FileSystemLocations)); err != nil { - return fmt.Errorf("error setting file_system_locations: %s", err) + return fmt.Errorf("error setting file_system_locations: %w", err) } if err := d.Set("cache", flattenAwsCodebuildProjectCache(project.Cache)); err != nil { - return fmt.Errorf("error setting cache: %s", err) + return fmt.Errorf("error setting cache: %w", err) } if err := d.Set("logs_config", flattenAwsCodeBuildLogsConfig(project.LogsConfig)); err != nil { - return fmt.Errorf("error setting logs_config: %s", err) + return fmt.Errorf("error setting logs_config: %w", err) } if err := d.Set("secondary_artifacts", flattenAwsCodeBuildProjectSecondaryArtifacts(project.SecondaryArtifacts)); err != nil { - return fmt.Errorf("error setting secondary_artifacts: %s", err) + return fmt.Errorf("error setting secondary_artifacts: %w", err) } if err := d.Set("secondary_sources", flattenAwsCodeBuildProjectSecondarySources(project.SecondarySources)); err != nil { - return fmt.Errorf("error setting secondary_sources: %s", err) + return fmt.Errorf("error setting secondary_sources: %w", err) } if err := d.Set("source", flattenAwsCodeBuildProjectSource(project.Source)); err != nil { - return fmt.Errorf("error setting source: %s", err) + return fmt.Errorf("error setting source: %w", err) } if err := d.Set("vpc_config", flattenAwsCodeBuildVpcConfig(project.VpcConfig)); err != nil { - return fmt.Errorf("error setting vpc_config: %s", err) + return fmt.Errorf("error setting vpc_config: %w", err) } if err := d.Set("build_batch_config", flattenAwsCodeBuildBuildBatchConfig(project.BuildBatchConfig)); err != nil { - return fmt.Errorf("error setting build_batch_config: %s", err) + return fmt.Errorf("error setting build_batch_config: %w", err) + } + + if err := d.Set("secondary_source_version", flattenAwsCodeBuildProjectSecondarySourceVersions(project.SecondarySourceVersions)); err != nil { + return fmt.Errorf("error setting secondary_source_version: %w", err) } d.Set("arn", project.Arn) @@ -1378,6 +1430,17 @@ func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{}) } } + if d.HasChange("secondary_source_version") { + psv := d.Get("secondary_source_version") + + if psv.(*schema.Set).Len() > 0 { + log.Printf("%#v", psv) + params.SecondarySourceVersions = expandProjectSecondarySourceVersions(psv.(*schema.Set)) + } else { + params.SecondarySourceVersions = []*codebuild.ProjectSourceVersion{} + } + } + if d.HasChange("vpc_config") { params.VpcConfig = expandCodeBuildVpcConfig(d.Get("vpc_config").([]interface{})) } @@ -1396,7 +1459,7 @@ func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{}) params.Cache = expandProjectCache(v.([]interface{})) } else { params.Cache = &codebuild.ProjectCache{ - Type: aws.String("NO_CACHE"), + Type: aws.String(codebuild.CacheTypeNoCache), } } } @@ -1459,9 +1522,7 @@ func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{}) _, err = conn.UpdateProject(params) } if err != nil { - return fmt.Errorf( - "[ERROR] Error updating CodeBuild project (%s): %s", - d.Id(), err) + return fmt.Errorf("[ERROR] Error updating CodeBuild project (%s): %w", d.Id(), err) } return resourceAwsCodeBuildProjectRead(d, meta) @@ -1581,6 +1642,17 @@ func flattenAwsCodeBuildProjectSecondaryArtifacts(artifactsList []*codebuild.Pro return &artifactSet } +func flattenAwsCodeBuildProjectSecondarySourceVersions(sourceVersions []*codebuild.ProjectSourceVersion) *schema.Set { + sourceVersionSet := schema.Set{ + F: resourceAwsCodeBuildProjectSourceVersionHash, + } + + for _, sourceVersions := range sourceVersions { + sourceVersionSet.Add(flattenAwsCodeBuildProjectsourceVersionsData(*sourceVersions)) + } + return &sourceVersionSet +} + func flattenAwsCodeBuildProjectArtifacts(artifacts *codebuild.ProjectArtifacts) []interface{} { return []interface{}{flattenAwsCodeBuildProjectArtifactsData(*artifacts)} } @@ -1624,6 +1696,20 @@ func flattenAwsCodeBuildProjectArtifactsData(artifacts codebuild.ProjectArtifact return values } +func flattenAwsCodeBuildProjectsourceVersionsData(sourceVersion codebuild.ProjectSourceVersion) map[string]interface{} { + values := map[string]interface{}{} + + if sourceVersion.SourceIdentifier != nil { + values["source_identifier"] = aws.StringValue(sourceVersion.SourceIdentifier) + } + + if sourceVersion.SourceVersion != nil { + values["source_version"] = aws.StringValue(sourceVersion.SourceVersion) + } + + return values +} + func flattenAwsCodebuildProjectCache(cache *codebuild.ProjectCache) []interface{} { if cache == nil { return []interface{}{} @@ -1826,6 +1912,21 @@ func resourceAwsCodeBuildProjectArtifactsHash(v interface{}) int { return hashcode.String(buf.String()) } +func resourceAwsCodeBuildProjectSourceVersionHash(v interface{}) int { + var buf bytes.Buffer + m := v.(map[string]interface{}) + + if v, ok := m["source_identifier"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + if v, ok := m["source_version"]; ok { + buf.WriteString(fmt.Sprintf("%s-", v.(string))) + } + + return hashcode.String(buf.String()) +} + func environmentVariablesToMap(environmentVariables []*codebuild.EnvironmentVariable) []interface{} { envVariables := []interface{}{} diff --git a/aws/resource_aws_codebuild_project_test.go b/aws/resource_aws_codebuild_project_test.go index 9fd5d51380a..26c26e0a246 100644 --- a/aws/resource_aws_codebuild_project_test.go +++ b/aws/resource_aws_codebuild_project_test.go @@ -92,6 +92,10 @@ func TestAccAWSCodeBuildProject_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "source.0.type", "GITHUB"), resource.TestCheckResourceAttr(resourceName, "vpc_config.#", "0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "secondary_source_version.#", "0"), + resource.TestCheckResourceAttr(resourceName, "build_batch_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "secondary_artifacts.#", "0"), + resource.TestCheckResourceAttr(resourceName, "secondary_sources.#", "0"), ), }, { @@ -2244,6 +2248,73 @@ func TestAccAWSCodeBuildProject_SecondarySources_CodeCommit(t *testing.T) { }) } +func TestAccAWSCodeBuildProject_SecondarySourceVersions_CodeCommit(t *testing.T) { + var project codebuild.Project + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_codebuild_project.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPreCheckAWSCodeBuild(t) }, + ErrorCheck: testAccErrorCheck(t, codebuild.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSCodeBuildProjectDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSCodeBuildProjectConfig_SecondarySourceVersions_CodeCommit1(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeBuildProjectExists(resourceName, &project), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_sources.*", map[string]string{ + "source_identifier": "secondarySource1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_source_version.*", map[string]string{ + "source_identifier": "secondarySource1", + "source_version": rName, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAWSCodeBuildProjectConfig_SecondarySourceVersions_CodeCommit2(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeBuildProjectExists(resourceName, &project), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_sources.*", map[string]string{ + "source_identifier": "secondarySource1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_sources.*", map[string]string{ + "source_identifier": "secondarySource2", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_source_version.*", map[string]string{ + "source_identifier": "secondarySource1", + "source_version": rName, + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_source_version.*", map[string]string{ + "source_identifier": "secondarySource2", + "source_version": rName, + }), + ), + }, + { + Config: testAccAWSCodeBuildProjectConfig_SecondarySources_CodeCommit(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSCodeBuildProjectExists(resourceName, &project), + resource.TestCheckResourceAttr(resourceName, "source.0.type", "CODECOMMIT"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_sources.*", map[string]string{ + "source_identifier": "secondarySource1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "secondary_sources.*", map[string]string{ + "source_identifier": "secondarySource2", + }), + resource.TestCheckResourceAttr(resourceName, "secondary_source_version.#", "0"), + ), + }, + }, + }) +} + func TestAWSCodeBuildProject_nameValidation(t *testing.T) { cases := []struct { Value string @@ -4579,6 +4650,87 @@ resource "aws_codebuild_project" "test" { `, rName)) } +func testAccAWSCodeBuildProjectConfig_SecondarySourceVersions_CodeCommit1(rName string) string { + return composeConfig(testAccAWSCodeBuildProjectConfig_Base_ServiceRole(rName), fmt.Sprintf(` +resource "aws_codebuild_project" "test" { + name = %[1]q + service_role = aws_iam_role.test.arn + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "2" + type = "LINUX_CONTAINER" + } + + source { + location = "https://git-codecommit.region-id.amazonaws.com/v1/repos/repo-name" + type = "CODECOMMIT" + } + + secondary_sources { + location = "https://git-codecommit.region-id.amazonaws.com/v1/repos/second-repo-name" + type = "CODECOMMIT" + source_identifier = "secondarySource1" + } + + secondary_source_version { + source_version = %[1]q + source_identifier = "secondarySource1" + } +} +`, rName)) +} + +func testAccAWSCodeBuildProjectConfig_SecondarySourceVersions_CodeCommit2(rName string) string { + return composeConfig(testAccAWSCodeBuildProjectConfig_Base_ServiceRole(rName), fmt.Sprintf(` +resource "aws_codebuild_project" "test" { + name = %[1]q + service_role = aws_iam_role.test.arn + + artifacts { + type = "NO_ARTIFACTS" + } + + environment { + compute_type = "BUILD_GENERAL1_SMALL" + image = "2" + type = "LINUX_CONTAINER" + } + + source { + location = "https://git-codecommit.region-id.amazonaws.com/v1/repos/repo-name" + type = "CODECOMMIT" + } + + secondary_sources { + location = "https://git-codecommit.region-id.amazonaws.com/v1/repos/second-repo-name" + type = "CODECOMMIT" + source_identifier = "secondarySource1" + } + + secondary_sources { + location = "https://git-codecommit.region-id.amazonaws.com/v1/repos/third-repo-name" + type = "CODECOMMIT" + source_identifier = "secondarySource2" + } + + secondary_source_version { + source_version = %[1]q + source_identifier = "secondarySource1" + } + + secondary_source_version { + source_version = %[1]q + source_identifier = "secondarySource2" + } +} +`, rName)) +} + func testAccAWSCodeBuildProjectConfig_Source_BuildStatusConfig_GitHubEnterprise(rName string) string { return composeConfig(testAccAWSCodeBuildProjectConfig_Base_ServiceRole(rName), fmt.Sprintf(` resource "aws_codebuild_project" "test" {