New resource aws_codebuild_project

This commit is contained in:
liamjbennett 2017-01-12 16:01:15 +00:00 committed by stack72
parent 5a84991872
commit 4aa5117e82
No known key found for this signature in database
GPG Key ID: 8619A619B085CB16
8 changed files with 1254 additions and 201 deletions

View File

@ -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:

View File

@ -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)

View File

@ -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(),

View File

@ -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
}

View File

@ -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 = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_policy" "codebuild_policy" {
name = "codebuild-policy-%s"
path = "/service-role/"
description = "Policy used in trust relationship with CodeBuild"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
}
]
}
POLICY
}
resource "aws_iam_policy_attachment" "codebuild_policy_attachment" {
name = "codebuild-policy-attachment-%s"
policy_arn = "${aws_iam_policy.codebuild_policy.arn}"
roles = ["${aws_iam_role.codebuild_role.id}"]
}
resource "aws_codebuild_project" "foo" {
name = "test-project-%s"
description = "test_codebuild_project"
timeout = "5"
service_role = "${aws_iam_role.codebuild_role.arn}"
artifacts {
type = "NO_ARTIFACTS"
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "2"
type = "LINUX_CONTAINER"
environment_variable = {
"name" = "SOME_KEY"
"value" = "SOME_VALUE"
}
}
source {
type = "GITHUB"
location = "https://github.com/mitchellh/packer.git"
}
tags {
"Environment" = "Test"
}
}
`, rName, rName, rName, rName)
}

View File

@ -0,0 +1,67 @@
package aws
import (
"log"
"regexp"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/codebuild"
)
// diffTags takes our tags locally and the ones remotely and returns
// the set of tags that must be created, and the set of tags that must
// be destroyed.
func diffTagsCodeBuild(oldTags, newTags []*codebuild.Tag) ([]*codebuild.Tag, []*codebuild.Tag) {
// First, we're creating everything we have
create := make(map[string]interface{})
for _, t := range newTags {
create[*t.Key] = *t.Value
}
// Build the list of what to remove
var remove []*codebuild.Tag
for _, t := range oldTags {
old, ok := create[*t.Key]
if !ok || old != *t.Value {
// Delete it!
remove = append(remove, t)
}
}
return tagsFromMapCodeBuild(create), remove
}
func tagsFromMapCodeBuild(m map[string]interface{}) []*codebuild.Tag {
result := make([]*codebuild.Tag, 0, len(m))
for k, v := range m {
result = append(result, &codebuild.Tag{
Key: aws.String(k),
Value: aws.String(v.(string)),
})
}
return result
}
func tagsToMapCodeBuild(ts []*codebuild.Tag) map[string]string {
result := make(map[string]string)
for _, t := range ts {
result[*t.Key] = *t.Value
}
return result
}
// compare a tag against a list of strings and checks if it should
// be ignored or not
func tagIgnoredCodeBuild(t *codebuild.Tag) bool {
filter := []string{"^aws:*"}
for _, v := range filter {
log.Printf("[DEBUG] Matching %v with %v\n", v, *t.Key)
if r, _ := regexp.MatchString(v, *t.Key); r == true {
log.Printf("[DEBUG] Found AWS specific tag %s (val: %s), ignoring.\n", *t.Key, *t.Value)
return true
}
}
return false
}

View File

@ -0,0 +1,103 @@
package aws
import (
"fmt"
"reflect"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/codebuild"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestDiffTagsCodeBuild(t *testing.T) {
cases := []struct {
Old, New map[string]interface{}
Create, Remove map[string]string
}{
// Basic add/remove
{
Old: map[string]interface{}{
"foo": "bar",
},
New: map[string]interface{}{
"bar": "baz",
},
Create: map[string]string{
"bar": "baz",
},
Remove: map[string]string{
"foo": "bar",
},
},
// Modify
{
Old: map[string]interface{}{
"foo": "bar",
},
New: map[string]interface{}{
"foo": "baz",
},
Create: map[string]string{
"foo": "baz",
},
Remove: map[string]string{
"foo": "bar",
},
},
}
for i, tc := range cases {
c, r := diffTagsCodeBuild(tagsFromMapCodeBuild(tc.Old), tagsFromMapCodeBuild(tc.New))
cm := tagsToMapCodeBuild(c)
rm := tagsToMapCodeBuild(r)
if !reflect.DeepEqual(cm, tc.Create) {
t.Fatalf("%d: bad create: %#v", i, cm)
}
if !reflect.DeepEqual(rm, tc.Remove) {
t.Fatalf("%d: bad remove: %#v", i, rm)
}
}
}
func TestIgnoringTagsCodeBuild(t *testing.T) {
var ignoredTags []*codebuild.Tag
ignoredTags = append(ignoredTags, &codebuild.Tag{
Key: aws.String("aws:cloudformation:logical-id"),
Value: aws.String("foo"),
})
ignoredTags = append(ignoredTags, &codebuild.Tag{
Key: aws.String("aws:foo:bar"),
Value: aws.String("baz"),
})
for _, tag := range ignoredTags {
if !tagIgnoredCodeBuild(tag) {
t.Fatalf("Tag %v with value %v not ignored, but should be!", *tag.Key, *tag.Value)
}
}
}
// testAccCheckTags can be used to check the tags on a resource.
func testAccCheckTagsCodeBuild(
ts *[]*codebuild.Tag, key string, value string) resource.TestCheckFunc {
return func(s *terraform.State) error {
m := tagsToMapCodeBuild(*ts)
v, ok := m[key]
if value != "" && !ok {
return fmt.Errorf("Missing tag: %s", key)
} else if value == "" && ok {
return fmt.Errorf("Extra tag: %s", key)
}
if value == "" {
return nil
}
if v != value {
return fmt.Errorf("%s: bad value: %s", key, v)
}
return nil
}
}

View File

@ -1,158 +0,0 @@
---
layout: "aws"
page_title: "AWS: aws_codebuild_project"
sidebar_current: "docs-aws-resource-codebuild-project"
description: |-
Provides n CodeBuild Project resource.
---
# aws\_codebuild\_project
Provides a CodeBuild Project resource.
## Example Usage
```
resource "aws_iam_role" "codebuild_role" {
name = "codebuild-role-"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_policy" "codebuild_policy" {
name = "codebuild-policy"
path = "/service-role/"
description = "Policy used in trust relationship with CodeBuild"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
}
]
}
POLICY
}
resource "aws_iam_policy_attachment" "codebuild_policy_attachment" {
name = "codebuild-policy-attachment"
policy_arn = "${aws_iam_policy.codebuild_policy.arn}"
roles = ["${aws_iam_role.codebuild_role.id}"]
}
resource "aws_codebuild_project" "foo" {
name = "test-project"
description = "test_codebuild_project"
timeout = "5"
service_role = "${aws_iam_role.codebuild_role.arn}"
artifacts {
type = "NO_ARTIFACTS"
}
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "2"
type = "LINUX_CONTAINER"
environment_variable = {
"name" = "SOME_KEY"
"value" = "SOME_VALUE"
}
}
source {
type = "GITHUB"
location = "https://github.com/mitchellh/packer.git"
}
tags {
"Environment" = "Test"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The projects name.
* `description` - (Optional) A short description of the project.
* `encryption_key` - (Optional) The AWS Key Management Service (AWS KMS) customer master key (CMK) to be used for encrypting the build project's build output artifacts.
* `service_role` - (Optional) The Amazon Resource Name (ARN) of the AWS Identity and Access Management (IAM) role that enables AWS CodeBuild to interact with dependent AWS services on behalf of the AWS account.
* `timeout` - (Optional) How long in minutes, from 5 to 480 (8 hours), for AWS CodeBuild to wait until timing out any related build that does not get marked as completed. The default is 60 minutes.
* `tags` - (Optional) A mapping of tags to assign to the resource.
* `artifacts` - (Optional) Information about the project's build output artifacts. Artifact blocks are documented below.
* `environment` - (Optional) Information about the project's build environment. Environment blocks are documented below.
* `source` - (Optional) Information about the project's input source code. Source blocks are documented below.
`artifacts` supports the following:
* `type` - (Required) The build output artifact's type. Valid values for this parameter are: `CODEPIPELINE`, `NO_ARTIFACTS` or `S3`.
* `location` - (Optional) Information about the build output artifact location. If `type` is set to `CODEPIPELINE` or `NO_ARTIFACTS` then this value will be ignored. If `type` is set to `S3`, this is the name of the output bucket. If `path` is not also specified, then `location` can also specify the path of the output artifact in the output bucket.
* `name` - (Optional) The name of the project. If `type` is set to `S3`, this is the name of the output artifact object
* `namespace_type` - (Optional) The namespace to use in storing build artifacts. If `type` is set to `S3`, then valid values for this parameter are: `BUILD_ID` or `NONE`.
* `packaging` - (Optional) The type of build output artifact to create. If `type` is set to `S3`, valid values for this parameter are: `NONE` or `ZIP`
* `path` - (Optional) If `type` is set to `S3`, this is the path to the output artifact
`environment` supports the following:
* `compute_type` - (Required) Information about the compute resources the build project will use. Available values for this parameter are: `BUILD_GENERAL1_SMALL`, `BUILD_GENERAL1_MEDIUM` or `BUILD_GENERAL1_LARGE`
* `image` - (Required) The ID of the Docker image to use for this build project
* `type` - (Required) The type of build environment to use for related builds. The only valid value is `LINUX_CONTAINER`.
* `environment_variable` - (Optional) A set of environment variables to make available to builds for this build project.
`environment_variable` supports the following:
* `name` - (Required) The environment variable's name or key.
* `value` - (Required) The environment variable's value.
`source` supports the following:
* `type` - (Required) The type of repository that contains the source code to be built. Valid values for this parameter are: `CODECOMMIT`, `CODEPIPELINE`, `GITHUB` or `S3`.
* `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.
`auth` supports the following:
* `type` - (Required) The authorization type to use. The only valid value is `OAUTH`
* `resource` - (Required) The resource value that applies to the specified authorization type.
## Attributes Reference
The following attributes are exported:
* `id` - The ARN of the CodeBuild project.
* `description` - A short description of the project.
* `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.
## Import
CodeBuild projects can be imported using the ARN, e.g
```
$ terraform import aws_codebuild_project.test_project arn:aws:codebuild:us-east-1:123456789012:projects/test_project
```