github.com/IBM-Cloud/terraform@v0.6.4-0.20170726051544-8872b87621df/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  		ro := data["run_order"].(int)
   341  		if ro > 0 {
   342  			action.RunOrder = aws.Int64(int64(ro))
   343  		}
   344  		actions = append(actions, &action)
   345  	}
   346  	return actions
   347  }
   348  
   349  func flattenAwsCodePipelineStageActions(actions []*codepipeline.ActionDeclaration) []interface{} {
   350  	actionsList := []interface{}{}
   351  	for _, action := range actions {
   352  		values := map[string]interface{}{
   353  			"category": *action.ActionTypeId.Category,
   354  			"owner":    *action.ActionTypeId.Owner,
   355  			"provider": *action.ActionTypeId.Provider,
   356  			"version":  *action.ActionTypeId.Version,
   357  			"name":     *action.Name,
   358  		}
   359  		if action.Configuration != nil {
   360  			config := flattenAwsCodePipelineStageActionConfiguration(action.Configuration)
   361  			_, ok := config["OAuthToken"]
   362  			actionProvider := *action.ActionTypeId.Provider
   363  			if ok && actionProvider == "GitHub" {
   364  				delete(config, "OAuthToken")
   365  			}
   366  			values["configuration"] = config
   367  		}
   368  
   369  		if len(action.OutputArtifacts) > 0 {
   370  			values["output_artifacts"] = flattenAwsCodePipelineActionsOutputArtifacts(action.OutputArtifacts)
   371  		}
   372  
   373  		if len(action.InputArtifacts) > 0 {
   374  			values["input_artifacts"] = flattenAwsCodePipelineActionsInputArtifacts(action.InputArtifacts)
   375  		}
   376  
   377  		if action.RunOrder != nil {
   378  			values["run_order"] = int(*action.RunOrder)
   379  		}
   380  
   381  		actionsList = append(actionsList, values)
   382  	}
   383  	return actionsList
   384  }
   385  
   386  func expandAwsCodePipelineStageActionConfiguration(config map[string]interface{}) map[string]*string {
   387  	m := map[string]*string{}
   388  	for k, v := range config {
   389  		s := v.(string)
   390  		m[k] = &s
   391  	}
   392  	return m
   393  }
   394  
   395  func flattenAwsCodePipelineStageActionConfiguration(config map[string]*string) map[string]string {
   396  	m := map[string]string{}
   397  	for k, v := range config {
   398  		m[k] = *v
   399  	}
   400  	return m
   401  }
   402  
   403  func expandAwsCodePipelineActionsOutputArtifacts(s []interface{}) []*codepipeline.OutputArtifact {
   404  	outputArtifacts := []*codepipeline.OutputArtifact{}
   405  	for _, artifact := range s {
   406  		outputArtifacts = append(outputArtifacts, &codepipeline.OutputArtifact{
   407  			Name: aws.String(artifact.(string)),
   408  		})
   409  	}
   410  	return outputArtifacts
   411  }
   412  
   413  func flattenAwsCodePipelineActionsOutputArtifacts(artifacts []*codepipeline.OutputArtifact) []string {
   414  	values := []string{}
   415  	for _, artifact := range artifacts {
   416  		values = append(values, *artifact.Name)
   417  	}
   418  	return values
   419  }
   420  
   421  func expandAwsCodePipelineActionsInputArtifacts(s []interface{}) []*codepipeline.InputArtifact {
   422  	outputArtifacts := []*codepipeline.InputArtifact{}
   423  	for _, artifact := range s {
   424  		outputArtifacts = append(outputArtifacts, &codepipeline.InputArtifact{
   425  			Name: aws.String(artifact.(string)),
   426  		})
   427  	}
   428  	return outputArtifacts
   429  }
   430  
   431  func flattenAwsCodePipelineActionsInputArtifacts(artifacts []*codepipeline.InputArtifact) []string {
   432  	values := []string{}
   433  	for _, artifact := range artifacts {
   434  		values = append(values, *artifact.Name)
   435  	}
   436  	return values
   437  }
   438  
   439  func resourceAwsCodePipelineRead(d *schema.ResourceData, meta interface{}) error {
   440  	conn := meta.(*AWSClient).codepipelineconn
   441  	resp, err := conn.GetPipeline(&codepipeline.GetPipelineInput{
   442  		Name: aws.String(d.Id()),
   443  	})
   444  
   445  	if err != nil {
   446  		pipelineerr, ok := err.(awserr.Error)
   447  		if ok && pipelineerr.Code() == "PipelineNotFoundException" {
   448  			log.Printf("[INFO] Codepipeline %q not found", d.Id())
   449  			d.SetId("")
   450  			return nil
   451  		}
   452  		return fmt.Errorf("[ERROR] Error retreiving Pipeline: %q", err)
   453  	}
   454  	pipeline := resp.Pipeline
   455  
   456  	if err := d.Set("artifact_store", flattenAwsCodePipelineArtifactStore(pipeline.ArtifactStore)); err != nil {
   457  		return err
   458  	}
   459  
   460  	if err := d.Set("stage", flattenAwsCodePipelineStages(pipeline.Stages)); err != nil {
   461  		return err
   462  	}
   463  
   464  	d.Set("name", pipeline.Name)
   465  	d.Set("role_arn", pipeline.RoleArn)
   466  	return nil
   467  }
   468  
   469  func resourceAwsCodePipelineUpdate(d *schema.ResourceData, meta interface{}) error {
   470  	conn := meta.(*AWSClient).codepipelineconn
   471  
   472  	pipeline := expandAwsCodePipeline(d)
   473  	params := &codepipeline.UpdatePipelineInput{
   474  		Pipeline: pipeline,
   475  	}
   476  	_, err := conn.UpdatePipeline(params)
   477  
   478  	if err != nil {
   479  		return fmt.Errorf(
   480  			"[ERROR] Error updating CodePipeline (%s): %s",
   481  			d.Id(), err)
   482  	}
   483  
   484  	return resourceAwsCodePipelineRead(d, meta)
   485  }
   486  
   487  func resourceAwsCodePipelineDelete(d *schema.ResourceData, meta interface{}) error {
   488  	conn := meta.(*AWSClient).codepipelineconn
   489  
   490  	_, err := conn.DeletePipeline(&codepipeline.DeletePipelineInput{
   491  		Name: aws.String(d.Id()),
   492  	})
   493  
   494  	if err != nil {
   495  		return err
   496  	}
   497  
   498  	d.SetId("")
   499  
   500  	return nil
   501  }