github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/aws/resource_aws_codepipeline.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/aws/aws-sdk-go/aws"
    10  	"github.com/aws/aws-sdk-go/aws/awserr"
    11  	"github.com/aws/aws-sdk-go/service/codepipeline"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsCodePipeline() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAwsCodePipelineCreate,
    19  		Read:   resourceAwsCodePipelineRead,
    20  		Update: resourceAwsCodePipelineUpdate,
    21  		Delete: resourceAwsCodePipelineDelete,
    22  		Importer: &schema.ResourceImporter{
    23  			State: schema.ImportStatePassthrough,
    24  		},
    25  
    26  		Schema: map[string]*schema.Schema{
    27  			"name": {
    28  				Type:     schema.TypeString,
    29  				Required: true,
    30  				ForceNew: true,
    31  			},
    32  
    33  			"role_arn": {
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  			},
    37  
    38  			"artifact_store": {
    39  				Type:     schema.TypeList,
    40  				Required: true,
    41  				MaxItems: 1,
    42  				Elem: &schema.Resource{
    43  					Schema: map[string]*schema.Schema{
    44  						"location": {
    45  							Type:     schema.TypeString,
    46  							Required: true,
    47  						},
    48  
    49  						"type": {
    50  							Type:         schema.TypeString,
    51  							Required:     true,
    52  							ValidateFunc: validateAwsCodePipelineArtifactStoreType,
    53  						},
    54  
    55  						"encryption_key": {
    56  							Type:     schema.TypeList,
    57  							MaxItems: 1,
    58  							Optional: true,
    59  							Elem: &schema.Resource{
    60  								Schema: map[string]*schema.Schema{
    61  									"id": {
    62  										Type:     schema.TypeString,
    63  										Required: true,
    64  									},
    65  
    66  									"type": {
    67  										Type:         schema.TypeString,
    68  										Required:     true,
    69  										ValidateFunc: validateAwsCodePipelineEncryptionKeyType,
    70  									},
    71  								},
    72  							},
    73  						},
    74  					},
    75  				},
    76  			},
    77  			"stage": {
    78  				Type:     schema.TypeList,
    79  				MinItems: 2,
    80  				Required: true,
    81  				Elem: &schema.Resource{
    82  					Schema: map[string]*schema.Schema{
    83  						"name": {
    84  							Type:     schema.TypeString,
    85  							Required: true,
    86  						},
    87  						"action": {
    88  							Type:     schema.TypeList,
    89  							Required: true,
    90  							Elem: &schema.Resource{
    91  								Schema: map[string]*schema.Schema{
    92  									"configuration": {
    93  										Type:     schema.TypeMap,
    94  										Optional: true,
    95  									},
    96  									"category": {
    97  										Type:         schema.TypeString,
    98  										Required:     true,
    99  										ValidateFunc: validateAwsCodePipelineStageActionCategory,
   100  									},
   101  									"owner": {
   102  										Type:         schema.TypeString,
   103  										Required:     true,
   104  										ValidateFunc: validateAwsCodePipelineStageActionOwner,
   105  									},
   106  									"provider": {
   107  										Type:     schema.TypeString,
   108  										Required: true,
   109  									},
   110  									"version": {
   111  										Type:     schema.TypeString,
   112  										Required: true,
   113  									},
   114  									"input_artifacts": {
   115  										Type:     schema.TypeList,
   116  										Optional: true,
   117  										Elem:     &schema.Schema{Type: schema.TypeString},
   118  									},
   119  									"output_artifacts": {
   120  										Type:     schema.TypeList,
   121  										Optional: true,
   122  										Elem:     &schema.Schema{Type: schema.TypeString},
   123  									},
   124  									"name": {
   125  										Type:     schema.TypeString,
   126  										Required: true,
   127  									},
   128  									"role_arn": {
   129  										Type:     schema.TypeString,
   130  										Optional: true,
   131  									},
   132  									"run_order": {
   133  										Type:     schema.TypeInt,
   134  										Optional: true,
   135  										Computed: true,
   136  									},
   137  								},
   138  							},
   139  						},
   140  					},
   141  				},
   142  			},
   143  		},
   144  	}
   145  }
   146  func validateAwsCodePipelineEncryptionKeyType(v interface{}, k string) (ws []string, errors []error) {
   147  	if v.(string) != "KMS" {
   148  		errors = append(errors, fmt.Errorf("CodePipeline: encryption_key type can only be KMS"))
   149  	}
   150  	return
   151  }
   152  
   153  func validateAwsCodePipelineArtifactStoreType(v interface{}, k string) (ws []string, errors []error) {
   154  	if v.(string) != "S3" {
   155  		errors = append(errors, fmt.Errorf("CodePipeline: artifact_store type can only be S3"))
   156  	}
   157  	return
   158  }
   159  
   160  func validateAwsCodePipelineStageActionCategory(v interface{}, k string) (ws []string, errors []error) {
   161  	value := v.(string)
   162  	types := map[string]bool{
   163  		"Source":   true,
   164  		"Build":    true,
   165  		"Deploy":   true,
   166  		"Test":     true,
   167  		"Invoke":   true,
   168  		"Approval": true,
   169  	}
   170  
   171  	if !types[value] {
   172  		errors = append(errors, fmt.Errorf("CodePipeline: category can only be one of Source | Build | Deploy | Test | Invoke | Approval"))
   173  	}
   174  	return
   175  }
   176  
   177  func validateAwsCodePipelineStageActionOwner(v interface{}, k string) (ws []string, errors []error) {
   178  	value := v.(string)
   179  	types := map[string]bool{
   180  		"AWS":        true,
   181  		"ThirdParty": true,
   182  		"Custom":     true,
   183  	}
   184  
   185  	if !types[value] {
   186  		errors = append(errors, fmt.Errorf("CodePipeline: owner can only be one of AWS | ThirdParty | Custom"))
   187  	}
   188  	return
   189  }
   190  
   191  func validateAwsCodePipelineStageActionConfiguration(v interface{}, k string) (ws []string, errors []error) {
   192  	for k := range v.(map[string]interface{}) {
   193  		if k == "OAuthToken" {
   194  			errors = append(errors, fmt.Errorf("CodePipeline: OAuthToken should be set as environment variable 'GITHUB_TOKEN'"))
   195  		}
   196  	}
   197  	return
   198  }
   199  
   200  func resourceAwsCodePipelineCreate(d *schema.ResourceData, meta interface{}) error {
   201  	conn := meta.(*AWSClient).codepipelineconn
   202  	params := &codepipeline.CreatePipelineInput{
   203  		Pipeline: expandAwsCodePipeline(d),
   204  	}
   205  
   206  	var resp *codepipeline.CreatePipelineOutput
   207  	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
   208  		var err error
   209  
   210  		resp, err = conn.CreatePipeline(params)
   211  
   212  		if err != nil {
   213  			return resource.RetryableError(err)
   214  		}
   215  
   216  		return resource.NonRetryableError(err)
   217  	})
   218  	if err != nil {
   219  		return fmt.Errorf("[ERROR] Error creating CodePipeline: %s", err)
   220  	}
   221  	if resp.Pipeline == nil {
   222  		return fmt.Errorf("[ERROR] Error creating CodePipeline: invalid response from AWS")
   223  	}
   224  
   225  	d.SetId(*resp.Pipeline.Name)
   226  	return resourceAwsCodePipelineRead(d, meta)
   227  }
   228  
   229  func expandAwsCodePipeline(d *schema.ResourceData) *codepipeline.PipelineDeclaration {
   230  	pipelineArtifactStore := expandAwsCodePipelineArtifactStore(d)
   231  	pipelineStages := expandAwsCodePipelineStages(d)
   232  
   233  	pipeline := codepipeline.PipelineDeclaration{
   234  		Name:          aws.String(d.Get("name").(string)),
   235  		RoleArn:       aws.String(d.Get("role_arn").(string)),
   236  		ArtifactStore: pipelineArtifactStore,
   237  		Stages:        pipelineStages,
   238  	}
   239  	return &pipeline
   240  }
   241  func expandAwsCodePipelineArtifactStore(d *schema.ResourceData) *codepipeline.ArtifactStore {
   242  	configs := d.Get("artifact_store").([]interface{})
   243  	data := configs[0].(map[string]interface{})
   244  	pipelineArtifactStore := codepipeline.ArtifactStore{
   245  		Location: aws.String(data["location"].(string)),
   246  		Type:     aws.String(data["type"].(string)),
   247  	}
   248  	tek := data["encryption_key"].([]interface{})
   249  	if len(tek) > 0 {
   250  		vk := tek[0].(map[string]interface{})
   251  		ek := codepipeline.EncryptionKey{
   252  			Type: aws.String(vk["type"].(string)),
   253  			Id:   aws.String(vk["id"].(string)),
   254  		}
   255  		pipelineArtifactStore.EncryptionKey = &ek
   256  	}
   257  	return &pipelineArtifactStore
   258  }
   259  
   260  func flattenAwsCodePipelineArtifactStore(artifactStore *codepipeline.ArtifactStore) []interface{} {
   261  	values := map[string]interface{}{}
   262  	values["type"] = *artifactStore.Type
   263  	values["location"] = *artifactStore.Location
   264  	if artifactStore.EncryptionKey != nil {
   265  		as := map[string]interface{}{
   266  			"id":   *artifactStore.EncryptionKey.Id,
   267  			"type": *artifactStore.EncryptionKey.Type,
   268  		}
   269  		values["encryption_key"] = []interface{}{as}
   270  	}
   271  	return []interface{}{values}
   272  }
   273  
   274  func expandAwsCodePipelineStages(d *schema.ResourceData) []*codepipeline.StageDeclaration {
   275  	configs := d.Get("stage").([]interface{})
   276  	pipelineStages := []*codepipeline.StageDeclaration{}
   277  
   278  	for _, stage := range configs {
   279  		data := stage.(map[string]interface{})
   280  		a := data["action"].([]interface{})
   281  		actions := expandAwsCodePipelineActions(a)
   282  		pipelineStages = append(pipelineStages, &codepipeline.StageDeclaration{
   283  			Name:    aws.String(data["name"].(string)),
   284  			Actions: actions,
   285  		})
   286  	}
   287  	return pipelineStages
   288  }
   289  
   290  func flattenAwsCodePipelineStages(stages []*codepipeline.StageDeclaration) []interface{} {
   291  	stagesList := []interface{}{}
   292  	for _, stage := range stages {
   293  		values := map[string]interface{}{}
   294  		values["name"] = *stage.Name
   295  		values["action"] = flattenAwsCodePipelineStageActions(stage.Actions)
   296  		stagesList = append(stagesList, values)
   297  	}
   298  	return stagesList
   299  
   300  }
   301  
   302  func expandAwsCodePipelineActions(s []interface{}) []*codepipeline.ActionDeclaration {
   303  	actions := []*codepipeline.ActionDeclaration{}
   304  	for _, config := range s {
   305  		data := config.(map[string]interface{})
   306  
   307  		conf := expandAwsCodePipelineStageActionConfiguration(data["configuration"].(map[string]interface{}))
   308  		if data["provider"].(string) == "GitHub" {
   309  			githubToken := os.Getenv("GITHUB_TOKEN")
   310  			if githubToken != "" {
   311  				conf["OAuthToken"] = aws.String(githubToken)
   312  			}
   313  
   314  		}
   315  
   316  		action := codepipeline.ActionDeclaration{
   317  			ActionTypeId: &codepipeline.ActionTypeId{
   318  				Category: aws.String(data["category"].(string)),
   319  				Owner:    aws.String(data["owner"].(string)),
   320  
   321  				Provider: aws.String(data["provider"].(string)),
   322  				Version:  aws.String(data["version"].(string)),
   323  			},
   324  			Name:          aws.String(data["name"].(string)),
   325  			Configuration: conf,
   326  		}
   327  
   328  		oa := data["output_artifacts"].([]interface{})
   329  		if len(oa) > 0 {
   330  			outputArtifacts := expandAwsCodePipelineActionsOutputArtifacts(oa)
   331  			action.OutputArtifacts = outputArtifacts
   332  
   333  		}
   334  		ia := data["input_artifacts"].([]interface{})
   335  		if len(ia) > 0 {
   336  			inputArtifacts := expandAwsCodePipelineActionsInputArtifacts(ia)
   337  			action.InputArtifacts = inputArtifacts
   338  
   339  		}
   340  		ra := data["role_arn"].(string)
   341  		if ra != "" {
   342  			action.RoleArn = aws.String(ra)
   343  		}
   344  		ro := data["run_order"].(int)
   345  		if ro > 0 {
   346  			action.RunOrder = aws.Int64(int64(ro))
   347  		}
   348  		actions = append(actions, &action)
   349  	}
   350  	return actions
   351  }
   352  
   353  func flattenAwsCodePipelineStageActions(actions []*codepipeline.ActionDeclaration) []interface{} {
   354  	actionsList := []interface{}{}
   355  	for _, action := range actions {
   356  		values := map[string]interface{}{
   357  			"category": *action.ActionTypeId.Category,
   358  			"owner":    *action.ActionTypeId.Owner,
   359  			"provider": *action.ActionTypeId.Provider,
   360  			"version":  *action.ActionTypeId.Version,
   361  			"name":     *action.Name,
   362  		}
   363  		if action.Configuration != nil {
   364  			config := flattenAwsCodePipelineStageActionConfiguration(action.Configuration)
   365  			_, ok := config["OAuthToken"]
   366  			actionProvider := *action.ActionTypeId.Provider
   367  			if ok && actionProvider == "GitHub" {
   368  				delete(config, "OAuthToken")
   369  			}
   370  			values["configuration"] = config
   371  		}
   372  
   373  		if len(action.OutputArtifacts) > 0 {
   374  			values["output_artifacts"] = flattenAwsCodePipelineActionsOutputArtifacts(action.OutputArtifacts)
   375  		}
   376  
   377  		if len(action.InputArtifacts) > 0 {
   378  			values["input_artifacts"] = flattenAwsCodePipelineActionsInputArtifacts(action.InputArtifacts)
   379  		}
   380  
   381  		if action.RoleArn != nil {
   382  			values["role_arn"] = *action.RoleArn
   383  		}
   384  
   385  		if action.RunOrder != nil {
   386  			values["run_order"] = int(*action.RunOrder)
   387  		}
   388  
   389  		actionsList = append(actionsList, values)
   390  	}
   391  	return actionsList
   392  }
   393  
   394  func expandAwsCodePipelineStageActionConfiguration(config map[string]interface{}) map[string]*string {
   395  	m := map[string]*string{}
   396  	for k, v := range config {
   397  		s := v.(string)
   398  		m[k] = &s
   399  	}
   400  	return m
   401  }
   402  
   403  func flattenAwsCodePipelineStageActionConfiguration(config map[string]*string) map[string]string {
   404  	m := map[string]string{}
   405  	for k, v := range config {
   406  		m[k] = *v
   407  	}
   408  	return m
   409  }
   410  
   411  func expandAwsCodePipelineActionsOutputArtifacts(s []interface{}) []*codepipeline.OutputArtifact {
   412  	outputArtifacts := []*codepipeline.OutputArtifact{}
   413  	for _, artifact := range s {
   414  		outputArtifacts = append(outputArtifacts, &codepipeline.OutputArtifact{
   415  			Name: aws.String(artifact.(string)),
   416  		})
   417  	}
   418  	return outputArtifacts
   419  }
   420  
   421  func flattenAwsCodePipelineActionsOutputArtifacts(artifacts []*codepipeline.OutputArtifact) []string {
   422  	values := []string{}
   423  	for _, artifact := range artifacts {
   424  		values = append(values, *artifact.Name)
   425  	}
   426  	return values
   427  }
   428  
   429  func expandAwsCodePipelineActionsInputArtifacts(s []interface{}) []*codepipeline.InputArtifact {
   430  	outputArtifacts := []*codepipeline.InputArtifact{}
   431  	for _, artifact := range s {
   432  		outputArtifacts = append(outputArtifacts, &codepipeline.InputArtifact{
   433  			Name: aws.String(artifact.(string)),
   434  		})
   435  	}
   436  	return outputArtifacts
   437  }
   438  
   439  func flattenAwsCodePipelineActionsInputArtifacts(artifacts []*codepipeline.InputArtifact) []string {
   440  	values := []string{}
   441  	for _, artifact := range artifacts {
   442  		values = append(values, *artifact.Name)
   443  	}
   444  	return values
   445  }
   446  
   447  func resourceAwsCodePipelineRead(d *schema.ResourceData, meta interface{}) error {
   448  	conn := meta.(*AWSClient).codepipelineconn
   449  	resp, err := conn.GetPipeline(&codepipeline.GetPipelineInput{
   450  		Name: aws.String(d.Id()),
   451  	})
   452  
   453  	if err != nil {
   454  		pipelineerr, ok := err.(awserr.Error)
   455  		if ok && pipelineerr.Code() == "PipelineNotFoundException" {
   456  			log.Printf("[INFO] Codepipeline %q not found", d.Id())
   457  			d.SetId("")
   458  			return nil
   459  		}
   460  		return fmt.Errorf("[ERROR] Error retreiving Pipeline: %q", err)
   461  	}
   462  	pipeline := resp.Pipeline
   463  
   464  	if err := d.Set("artifact_store", flattenAwsCodePipelineArtifactStore(pipeline.ArtifactStore)); err != nil {
   465  		return err
   466  	}
   467  
   468  	if err := d.Set("stage", flattenAwsCodePipelineStages(pipeline.Stages)); err != nil {
   469  		return err
   470  	}
   471  
   472  	d.Set("name", pipeline.Name)
   473  	d.Set("role_arn", pipeline.RoleArn)
   474  	return nil
   475  }
   476  
   477  func resourceAwsCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error {
   478  	conn := meta.(*AWSClient).codepipelineconn
   479  
   480  	pipeline := expandAwsCodePipeline(d)
   481  	params := &codepipeline.UpdatePipelineInput{
   482  		Pipeline: pipeline,
   483  	}
   484  	_, err := conn.UpdatePipeline(params)
   485  
   486  	if err != nil {
   487  		return fmt.Errorf(
   488  			"[ERROR] Error updating CodePipeline (%s): %s",
   489  			d.Id(), err)
   490  	}
   491  
   492  	return resourceAwsCodePipelineRead(d, meta)
   493  }
   494  
   495  func resourceAwsCodePipelineDelete(d *schema.ResourceData, meta interface{}) error {
   496  	conn := meta.(*AWSClient).codepipelineconn
   497  
   498  	_, err := conn.DeletePipeline(&codepipeline.DeletePipelineInput{
   499  		Name: aws.String(d.Id()),
   500  	})
   501  
   502  	if err != nil {
   503  		return err
   504  	}
   505  
   506  	d.SetId("")
   507  
   508  	return nil
   509  }