terraform/builtin/providers/aws/resource_aws_codepipeline.go

510 lines
14 KiB
Go

package aws
import (
"fmt"
"log"
"os"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/codepipeline"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsCodePipeline() *schema.Resource {
return &schema.Resource{
Create: resourceAwsCodePipelineCreate,
Read: resourceAwsCodePipelineRead,
Update: resourceAwsCodePipelineUpdate,
Delete: resourceAwsCodePipelineDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"role_arn": {
Type: schema.TypeString,
Required: true,
},
"artifact_store": {
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"location": {
Type: schema.TypeString,
Required: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAwsCodePipelineArtifactStoreType,
},
"encryption_key": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAwsCodePipelineEncryptionKeyType,
},
},
},
},
},
},
},
"stage": {
Type: schema.TypeList,
MinItems: 2,
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"action": {
Type: schema.TypeList,
Required: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"configuration": {
Type: schema.TypeMap,
Optional: true,
},
"category": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAwsCodePipelineStageActionCategory,
},
"owner": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateAwsCodePipelineStageActionOwner,
},
"provider": {
Type: schema.TypeString,
Required: true,
},
"version": {
Type: schema.TypeString,
Required: true,
},
"input_artifacts": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"output_artifacts": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"name": {
Type: schema.TypeString,
Required: true,
},
"role_arn": {
Type: schema.TypeString,
Optional: true,
},
"run_order": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
},
},
},
},
},
},
},
}
}
func validateAwsCodePipelineEncryptionKeyType(v interface{}, k string) (ws []string, errors []error) {
if v.(string) != "KMS" {
errors = append(errors, fmt.Errorf("CodePipeline: encryption_key type can only be KMS"))
}
return
}
func validateAwsCodePipelineArtifactStoreType(v interface{}, k string) (ws []string, errors []error) {
if v.(string) != "S3" {
errors = append(errors, fmt.Errorf("CodePipeline: artifact_store type can only be S3"))
}
return
}
func validateAwsCodePipelineStageActionCategory(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
types := map[string]bool{
"Source": true,
"Build": true,
"Deploy": true,
"Test": true,
"Invoke": true,
"Approval": true,
}
if !types[value] {
errors = append(errors, fmt.Errorf("CodePipeline: category can only be one of Source | Build | Deploy | Test | Invoke | Approval"))
}
return
}
func validateAwsCodePipelineStageActionOwner(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
types := map[string]bool{
"AWS": true,
"ThirdParty": true,
"Custom": true,
}
if !types[value] {
errors = append(errors, fmt.Errorf("CodePipeline: owner can only be one of AWS | ThirdParty | Custom"))
}
return
}
func validateAwsCodePipelineStageActionConfiguration(v interface{}, k string) (ws []string, errors []error) {
for k := range v.(map[string]interface{}) {
if k == "OAuthToken" {
errors = append(errors, fmt.Errorf("CodePipeline: OAuthToken should be set as environment variable 'GITHUB_TOKEN'"))
}
}
return
}
func resourceAwsCodePipelineCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codepipelineconn
params := &codepipeline.CreatePipelineInput{
Pipeline: expandAwsCodePipeline(d),
}
var resp *codepipeline.CreatePipelineOutput
err := resource.Retry(2*time.Minute, func() *resource.RetryError {
var err error
resp, err = conn.CreatePipeline(params)
if err != nil {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
})
if err != nil {
return fmt.Errorf("[ERROR] Error creating CodePipeline: %s", err)
}
if resp.Pipeline == nil {
return fmt.Errorf("[ERROR] Error creating CodePipeline: invalid response from AWS")
}
d.SetId(*resp.Pipeline.Name)
return resourceAwsCodePipelineRead(d, meta)
}
func expandAwsCodePipeline(d *schema.ResourceData) *codepipeline.PipelineDeclaration {
pipelineArtifactStore := expandAwsCodePipelineArtifactStore(d)
pipelineStages := expandAwsCodePipelineStages(d)
pipeline := codepipeline.PipelineDeclaration{
Name: aws.String(d.Get("name").(string)),
RoleArn: aws.String(d.Get("role_arn").(string)),
ArtifactStore: pipelineArtifactStore,
Stages: pipelineStages,
}
return &pipeline
}
func expandAwsCodePipelineArtifactStore(d *schema.ResourceData) *codepipeline.ArtifactStore {
configs := d.Get("artifact_store").([]interface{})
data := configs[0].(map[string]interface{})
pipelineArtifactStore := codepipeline.ArtifactStore{
Location: aws.String(data["location"].(string)),
Type: aws.String(data["type"].(string)),
}
tek := data["encryption_key"].([]interface{})
if len(tek) > 0 {
vk := tek[0].(map[string]interface{})
ek := codepipeline.EncryptionKey{
Type: aws.String(vk["type"].(string)),
Id: aws.String(vk["id"].(string)),
}
pipelineArtifactStore.EncryptionKey = &ek
}
return &pipelineArtifactStore
}
func flattenAwsCodePipelineArtifactStore(artifactStore *codepipeline.ArtifactStore) []interface{} {
values := map[string]interface{}{}
values["type"] = *artifactStore.Type
values["location"] = *artifactStore.Location
if artifactStore.EncryptionKey != nil {
as := map[string]interface{}{
"id": *artifactStore.EncryptionKey.Id,
"type": *artifactStore.EncryptionKey.Type,
}
values["encryption_key"] = []interface{}{as}
}
return []interface{}{values}
}
func expandAwsCodePipelineStages(d *schema.ResourceData) []*codepipeline.StageDeclaration {
configs := d.Get("stage").([]interface{})
pipelineStages := []*codepipeline.StageDeclaration{}
for _, stage := range configs {
data := stage.(map[string]interface{})
a := data["action"].([]interface{})
actions := expandAwsCodePipelineActions(a)
pipelineStages = append(pipelineStages, &codepipeline.StageDeclaration{
Name: aws.String(data["name"].(string)),
Actions: actions,
})
}
return pipelineStages
}
func flattenAwsCodePipelineStages(stages []*codepipeline.StageDeclaration) []interface{} {
stagesList := []interface{}{}
for _, stage := range stages {
values := map[string]interface{}{}
values["name"] = *stage.Name
values["action"] = flattenAwsCodePipelineStageActions(stage.Actions)
stagesList = append(stagesList, values)
}
return stagesList
}
func expandAwsCodePipelineActions(s []interface{}) []*codepipeline.ActionDeclaration {
actions := []*codepipeline.ActionDeclaration{}
for _, config := range s {
data := config.(map[string]interface{})
conf := expandAwsCodePipelineStageActionConfiguration(data["configuration"].(map[string]interface{}))
if data["provider"].(string) == "GitHub" {
githubToken := os.Getenv("GITHUB_TOKEN")
if githubToken != "" {
conf["OAuthToken"] = aws.String(githubToken)
}
}
action := codepipeline.ActionDeclaration{
ActionTypeId: &codepipeline.ActionTypeId{
Category: aws.String(data["category"].(string)),
Owner: aws.String(data["owner"].(string)),
Provider: aws.String(data["provider"].(string)),
Version: aws.String(data["version"].(string)),
},
Name: aws.String(data["name"].(string)),
Configuration: conf,
}
oa := data["output_artifacts"].([]interface{})
if len(oa) > 0 {
outputArtifacts := expandAwsCodePipelineActionsOutputArtifacts(oa)
action.OutputArtifacts = outputArtifacts
}
ia := data["input_artifacts"].([]interface{})
if len(ia) > 0 {
inputArtifacts := expandAwsCodePipelineActionsInputArtifacts(ia)
action.InputArtifacts = inputArtifacts
}
ra := data["role_arn"].(string)
if ra != "" {
action.RoleArn = aws.String(ra)
}
ro := data["run_order"].(int)
if ro > 0 {
action.RunOrder = aws.Int64(int64(ro))
}
actions = append(actions, &action)
}
return actions
}
func flattenAwsCodePipelineStageActions(actions []*codepipeline.ActionDeclaration) []interface{} {
actionsList := []interface{}{}
for _, action := range actions {
values := map[string]interface{}{
"category": *action.ActionTypeId.Category,
"owner": *action.ActionTypeId.Owner,
"provider": *action.ActionTypeId.Provider,
"version": *action.ActionTypeId.Version,
"name": *action.Name,
}
if action.Configuration != nil {
config := flattenAwsCodePipelineStageActionConfiguration(action.Configuration)
_, ok := config["OAuthToken"]
actionProvider := *action.ActionTypeId.Provider
if ok && actionProvider == "GitHub" {
delete(config, "OAuthToken")
}
values["configuration"] = config
}
if len(action.OutputArtifacts) > 0 {
values["output_artifacts"] = flattenAwsCodePipelineActionsOutputArtifacts(action.OutputArtifacts)
}
if len(action.InputArtifacts) > 0 {
values["input_artifacts"] = flattenAwsCodePipelineActionsInputArtifacts(action.InputArtifacts)
}
if action.RoleArn != nil {
values["role_arn"] = *action.RoleArn
}
if action.RunOrder != nil {
values["run_order"] = int(*action.RunOrder)
}
actionsList = append(actionsList, values)
}
return actionsList
}
func expandAwsCodePipelineStageActionConfiguration(config map[string]interface{}) map[string]*string {
m := map[string]*string{}
for k, v := range config {
s := v.(string)
m[k] = &s
}
return m
}
func flattenAwsCodePipelineStageActionConfiguration(config map[string]*string) map[string]string {
m := map[string]string{}
for k, v := range config {
m[k] = *v
}
return m
}
func expandAwsCodePipelineActionsOutputArtifacts(s []interface{}) []*codepipeline.OutputArtifact {
outputArtifacts := []*codepipeline.OutputArtifact{}
for _, artifact := range s {
outputArtifacts = append(outputArtifacts, &codepipeline.OutputArtifact{
Name: aws.String(artifact.(string)),
})
}
return outputArtifacts
}
func flattenAwsCodePipelineActionsOutputArtifacts(artifacts []*codepipeline.OutputArtifact) []string {
values := []string{}
for _, artifact := range artifacts {
values = append(values, *artifact.Name)
}
return values
}
func expandAwsCodePipelineActionsInputArtifacts(s []interface{}) []*codepipeline.InputArtifact {
outputArtifacts := []*codepipeline.InputArtifact{}
for _, artifact := range s {
outputArtifacts = append(outputArtifacts, &codepipeline.InputArtifact{
Name: aws.String(artifact.(string)),
})
}
return outputArtifacts
}
func flattenAwsCodePipelineActionsInputArtifacts(artifacts []*codepipeline.InputArtifact) []string {
values := []string{}
for _, artifact := range artifacts {
values = append(values, *artifact.Name)
}
return values
}
func resourceAwsCodePipelineRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codepipelineconn
resp, err := conn.GetPipeline(&codepipeline.GetPipelineInput{
Name: aws.String(d.Id()),
})
if err != nil {
pipelineerr, ok := err.(awserr.Error)
if ok && pipelineerr.Code() == "PipelineNotFoundException" {
log.Printf("[INFO] Codepipeline %q not found", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("[ERROR] Error retreiving Pipeline: %q", err)
}
pipeline := resp.Pipeline
if err := d.Set("artifact_store", flattenAwsCodePipelineArtifactStore(pipeline.ArtifactStore)); err != nil {
return err
}
if err := d.Set("stage", flattenAwsCodePipelineStages(pipeline.Stages)); err != nil {
return err
}
d.Set("name", pipeline.Name)
d.Set("role_arn", pipeline.RoleArn)
return nil
}
func resourceAwsCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codepipelineconn
pipeline := expandAwsCodePipeline(d)
params := &codepipeline.UpdatePipelineInput{
Pipeline: pipeline,
}
_, err := conn.UpdatePipeline(params)
if err != nil {
return fmt.Errorf(
"[ERROR] Error updating CodePipeline (%s): %s",
d.Id(), err)
}
return resourceAwsCodePipelineRead(d, meta)
}
func resourceAwsCodePipelineDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).codepipelineconn
_, err := conn.DeletePipeline(&codepipeline.DeletePipelineInput{
Name: aws.String(d.Id()),
})
if err != nil {
return err
}
d.SetId("")
return nil
}