Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CodeBuild in VPC continuing Issue #2547 #3324

Merged
merged 2 commits into from
Feb 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 77 additions & 6 deletions aws/resource_aws_codebuild_project.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,33 @@ func resourceAwsCodeBuildProject() *schema.Resource {
ValidateFunc: validateAwsCodeBuildTimeout,
},
"tags": tagsSchema(),
"vpc_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"vpc_id": {
Type: schema.TypeString,
Required: true,
},
"subnets": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
// Set: schema.HashString,
MaxItems: 16,
},
"security_group_ids": {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
// Set: schema.HashString,
MaxItems: 5,
},
},
},
},
},
}
}
Expand Down Expand Up @@ -215,6 +242,10 @@ func resourceAwsCodeBuildProjectCreate(d *schema.ResourceData, meta interface{})
params.TimeoutInMinutes = aws.Int64(int64(v.(int)))
}

if v, ok := d.GetOk("vpc_config"); ok {
params.VpcConfig = expandCodeBuildVpcConfig(v.([]interface{}))
}

if v, ok := d.GetOk("tags"); ok {
params.Tags = tagsFromMapCodeBuild(v.(map[string]interface{}))
}
Expand All @@ -230,6 +261,10 @@ func resourceAwsCodeBuildProjectCreate(d *schema.ResourceData, meta interface{})
return resource.RetryableError(err)
}

if isAWSErr(err, "InvalidInputException", "Not authorized to perform DescribeSecurityGroups") {
return resource.RetryableError(err)
}

return resource.NonRetryableError(err)
}

Expand Down Expand Up @@ -326,6 +361,21 @@ func expandProjectEnvironment(d *schema.ResourceData) *codebuild.ProjectEnvironm
return projectEnv
}

func expandCodeBuildVpcConfig(rawVpcConfig []interface{}) *codebuild.VpcConfig {
vpcConfig := codebuild.VpcConfig{}
if len(rawVpcConfig) == 0 {
return &vpcConfig
} else {

data := rawVpcConfig[0].(map[string]interface{})
vpcConfig.VpcId = aws.String(data["vpc_id"].(string))
vpcConfig.Subnets = expandStringList(data["subnets"].(*schema.Set).List())
vpcConfig.SecurityGroupIds = expandStringList(data["security_group_ids"].(*schema.Set).List())

return &vpcConfig
}
}

func expandProjectSource(d *schema.ResourceData) codebuild.ProjectSource {
configs := d.Get("source").(*schema.Set).List()
projectSource := codebuild.ProjectSource{}
Expand Down Expand Up @@ -380,15 +430,19 @@ func resourceAwsCodeBuildProjectRead(d *schema.ResourceData, meta interface{}) e

project := resp.Projects[0]

if err := d.Set("artifacts", flattenAwsCodebuildProjectArtifacts(project.Artifacts)); err != nil {
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("environment", schema.NewSet(resourceAwsCodeBuildProjectEnvironmentHash, flattenAwsCodebuildProjectEnvironment(project.Environment))); err != nil {
if err := d.Set("source", flattenAwsCodeBuildProjectSource(project.Source)); err != nil {
return err
}

if err := d.Set("source", flattenAwsCodebuildProjectSource(project.Source)); err != nil {
if err := d.Set("vpc_config", flattenAwsCodeBuildVpcConfig(project.VpcConfig)); err != nil {
return err
}

Expand Down Expand Up @@ -427,6 +481,10 @@ func resourceAwsCodeBuildProjectUpdate(d *schema.ResourceData, meta interface{})
params.Artifacts = &projectArtifacts
}

if d.HasChange("vpc_config") {
params.VpcConfig = expandCodeBuildVpcConfig(d.Get("vpc_config").([]interface{}))
}

if d.HasChange("description") {
params.Description = aws.String(d.Get("description").(string))
}
Expand Down Expand Up @@ -474,7 +532,7 @@ func resourceAwsCodeBuildProjectDelete(d *schema.ResourceData, meta interface{})
return nil
}

func flattenAwsCodebuildProjectArtifacts(artifacts *codebuild.ProjectArtifacts) *schema.Set {
func flattenAwsCodeBuildProjectArtifacts(artifacts *codebuild.ProjectArtifacts) *schema.Set {

artifactSet := schema.Set{
F: resourceAwsCodeBuildProjectArtifactsHash,
Expand Down Expand Up @@ -509,7 +567,7 @@ func flattenAwsCodebuildProjectArtifacts(artifacts *codebuild.ProjectArtifacts)
return &artifactSet
}

func flattenAwsCodebuildProjectEnvironment(environment *codebuild.ProjectEnvironment) []interface{} {
func flattenAwsCodeBuildProjectEnvironment(environment *codebuild.ProjectEnvironment) []interface{} {
envConfig := map[string]interface{}{}

envConfig["type"] = *environment.Type
Expand All @@ -525,7 +583,7 @@ func flattenAwsCodebuildProjectEnvironment(environment *codebuild.ProjectEnviron

}

func flattenAwsCodebuildProjectSource(source *codebuild.ProjectSource) []interface{} {
func flattenAwsCodeBuildProjectSource(source *codebuild.ProjectSource) []interface{} {
l := make([]interface{}, 1)
m := map[string]interface{}{}

Expand All @@ -548,6 +606,19 @@ func flattenAwsCodebuildProjectSource(source *codebuild.ProjectSource) []interfa
return l
}

func flattenAwsCodeBuildVpcConfig(vpcConfig *codebuild.VpcConfig) []interface{} {
if vpcConfig != nil {
values := map[string]interface{}{}

values["vpc_id"] = *vpcConfig.VpcId
values["subnets"] = schema.NewSet(schema.HashString, flattenStringList(vpcConfig.Subnets))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work with just values["subnets"] = flattenStringList(vpcConfig.Subnets)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        testing.go:513: Step 0 error: Error applying: 1 error(s) occurred:

                * aws_codebuild_project.foo: 1 error(s) occurred:

                * aws_codebuild_project.foo: Invalid address to set: []string{"vpc_config", "0", "subnets"}
        testing.go:573: Error destroying resource! WARNING: Dangling resources
                may exist. The full state and error is shown below.

                Error: Error refreshing: 1 error(s) occurred:

                * aws_codebuild_project.foo: 1 error(s) occurred:

                * aws_codebuild_project.foo: aws_codebuild_project.foo: Invalid address to set: []string{"vpc_config", "0", "subnets"}

                State: <nil>
FAIL

I don't think so sadly :(

Copy link
Contributor Author

@moofish32 moofish32 Feb 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears d.Set has the magic buried in there to make this work. I tried a few tricks to pull it out so it would work for subresources, but I couldn't quite make it work. I've stopped and decided that perhaps core should add some features to set and get with subresources instead of my hack-ish attempts. If you think we should invest more here let me know, but right now I didn't want to block this with that.

values["security_group_ids"] = schema.NewSet(schema.HashString, flattenStringList(vpcConfig.SecurityGroupIds))

return []interface{}{values}
}
return nil
}

func resourceAwsCodeBuildProjectArtifactsHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
Expand Down
110 changes: 99 additions & 11 deletions aws/resource_aws_codebuild_project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,55 @@ func TestAccAWSCodeBuildProject_basic(t *testing.T) {
CheckDestroy: testAccCheckAWSCodeBuildProjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCodeBuildProjectConfig_basic(name),
Config: testAccAWSCodeBuildProjectConfig_basic(name, "", ""),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"),
resource.TestCheckResourceAttr(
"aws_codebuild_project.foo", "build_timeout", "5"),
),
},
},
})
}

func TestAccAWSCodeBuildProject_vpc(t *testing.T) {
name := acctest.RandString(10)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSCodeBuildProjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSCodeBuildProjectConfig_basic(name,
hclVpcConfig("\"${aws_subnet.codebuild_subnet.id}\",\"${aws_subnet.codebuild_subnet_2.id}\""), hclVpcResources()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"),
resource.TestCheckResourceAttr(
"aws_codebuild_project.foo", "build_timeout", "5"),
resource.TestCheckResourceAttrSet("aws_codebuild_project.foo", "vpc_config.0.vpc_id"),
resource.TestCheckResourceAttr("aws_codebuild_project.foo", "vpc_config.0.subnets.#", "2"),
resource.TestCheckResourceAttr("aws_codebuild_project.foo", "vpc_config.0.security_group_ids.#", "1"),
),
},
{
Config: testAccAWSCodeBuildProjectConfig_basic(name, hclVpcConfig("\"${aws_subnet.codebuild_subnet.id}\""), hclVpcResources()),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"),
resource.TestCheckResourceAttr(
"aws_codebuild_project.foo", "build_timeout", "5"),
resource.TestCheckResourceAttrSet("aws_codebuild_project.foo", "vpc_config.0.vpc_id"),
resource.TestCheckResourceAttr("aws_codebuild_project.foo", "vpc_config.0.subnets.#", "1"),
resource.TestCheckResourceAttr("aws_codebuild_project.foo", "vpc_config.0.security_group_ids.#", "1"),
),
},
{
Config: testAccAWSCodeBuildProjectConfig_basicUpdated(name),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSCodeBuildProjectExists("aws_codebuild_project.foo"),
resource.TestCheckResourceAttr(
"aws_codebuild_project.foo", "build_timeout", "50"),
resource.TestCheckNoResourceAttr("aws_codebuild_project.foo", "vpc_config"),
),
},
},
Expand Down Expand Up @@ -350,7 +386,7 @@ func testAccCheckAWSCodeBuildProjectDestroy(s *terraform.State) error {
return fmt.Errorf("Default error in CodeBuild Test")
}

func testAccAWSCodeBuildProjectConfig_basic(rName string) string {
func testAccAWSCodeBuildProjectConfig_basic(rName, vpcConfig, vpcResources string) string {
return fmt.Sprintf(`
resource "aws_iam_role" "codebuild_role" {
name = "codebuild-role-%s"
Expand All @@ -371,10 +407,10 @@ EOF
}

resource "aws_iam_policy" "codebuild_policy" {
name = "codebuild-policy-%s"
path = "/service-role/"
description = "Policy used in trust relationship with CodeBuild"
policy = <<POLICY
name = "codebuild-policy-%s"
path = "/service-role/"
description = "Policy used in trust relationship with CodeBuild"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
Expand All @@ -388,6 +424,19 @@ resource "aws_iam_policy" "codebuild_policy" {
"logs:CreateLogStream",
"logs:PutLogEvents"
]
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeDhcpOptions",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs"
],
"Resource": "*"
}
]
}
Expand All @@ -401,10 +450,10 @@ resource "aws_iam_policy_attachment" "codebuild_policy_attachment" {
}

resource "aws_codebuild_project" "foo" {
name = "test-project-%s"
description = "test_codebuild_project"
build_timeout = "5"
service_role = "${aws_iam_role.codebuild_role.arn}"
name = "test-project-%s"
description = "test_codebuild_project"
build_timeout = "5"
service_role = "${aws_iam_role.codebuild_role.arn}"

artifacts {
type = "NO_ARTIFACTS"
Expand All @@ -429,8 +478,10 @@ resource "aws_codebuild_project" "foo" {
tags {
"Environment" = "Test"
}
%s
}
`, rName, rName, rName, rName)
%s
`, rName, rName, rName, rName, vpcConfig, vpcResources)
}

func testAccAWSCodeBuildProjectConfig_basicUpdated(rName string) string {
Expand Down Expand Up @@ -686,3 +737,40 @@ resource "aws_codebuild_project" "foo" {
}
`, rName, authResource, authType)
}

func hclVpcResources() string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI these function names are global across the entire provider so we would generally prefer sticking with the long for testAccAWSCodeBuildProjectConfig_X

return fmt.Sprintf(`
resource "aws_vpc" "codebuild_vpc" {
cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "codebuild_subnet" {
vpc_id = "${aws_vpc.codebuild_vpc.id}"
cidr_block = "10.0.0.0/24"
}

resource "aws_subnet" "codebuild_subnet_2" {
vpc_id = "${aws_vpc.codebuild_vpc.id}"
cidr_block = "10.0.1.0/24"
}


resource "aws_security_group" "codebuild_security_group" {
vpc_id = "${aws_vpc.codebuild_vpc.id}"
}
`)
}

func hclVpcConfig(subnets string) string {
return fmt.Sprintf(`
vpc_config {
vpc_id = "${aws_vpc.codebuild_vpc.id}"

subnets = [ %s ]

security_group_ids = [
"${aws_security_group.codebuild_security_group.id}"
]
}
`, subnets)
}
22 changes: 21 additions & 1 deletion website/docs/r/codebuild_project.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,20 @@ resource "aws_codebuild_project" "foo" {
location = "https://github.com/mitchellh/packer.git"
}

vpc_config {
vpc_id = "vpc-725fca"

subnets = [
"subnet-ba35d2e0",
"subnet-ab129af1",
]

security_group_ids = [
"sg-f9f27d91",
"sg-e4f48g23",
]
}

tags {
"Environment" = "Test"
}
Expand All @@ -113,6 +127,7 @@ The following arguments are supported:
* `artifacts` - (Required) Information about the project's build output artifacts. Artifact blocks are documented below.
* `environment` - (Required) Information about the project's build environment. Environment blocks are documented below.
* `source` - (Required) Information about the project's input source code. Source blocks are documented below.
* `vpc_config` - (Optional) Configuration for the builds to run inside a VPC. VPC config blocks are documented below.

`artifacts` supports the following:

Expand Down Expand Up @@ -148,6 +163,12 @@ The following arguments are supported:
* `type` - (Required) The authorization type to use. The only valid value is `OAUTH`
* `resource` - (Optional) The resource value that applies to the specified authorization type.

`vpc_config` supports the following:

* `vpc_id` - (Required) The ID of the VPC within which to run builds.
* `subnets` - (Required) The subnet IDs within which to run builds.
* `security_group_ids` - (Required) The security group IDs to assign to running builds.

## Attributes Reference

The following attributes are exported:
Expand All @@ -157,4 +178,3 @@ The following attributes are exported:
* `encryption_key` - The AWS Key Management Service (AWS KMS) customer master key (CMK) that was used for encrypting the build project's build output artifacts.
* `name` - The projects name.
* `service_role` - The ARN of the IAM service role.