github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/aws/resource_aws_opsworks_application.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     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/opsworks"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  func resourceAwsOpsworksApplication() *schema.Resource {
    17  	return &schema.Resource{
    18  
    19  		Create: resourceAwsOpsworksApplicationCreate,
    20  		Read:   resourceAwsOpsworksApplicationRead,
    21  		Update: resourceAwsOpsworksApplicationUpdate,
    22  		Delete: resourceAwsOpsworksApplicationDelete,
    23  		Schema: map[string]*schema.Schema{
    24  			"id": &schema.Schema{
    25  				Type:     schema.TypeString,
    26  				Computed: true,
    27  			},
    28  			"name": &schema.Schema{
    29  				Type:     schema.TypeString,
    30  				Required: true,
    31  			},
    32  			"short_name": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Computed: true,
    35  				Optional: true,
    36  			},
    37  			// aws-flow-ruby | java | rails | php | nodejs | static | other
    38  			"type": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Required: true,
    41  			},
    42  			"stack_id": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Required: true,
    45  			},
    46  			// TODO: the following 4 vals are really part of the Attributes array. We should validate that only ones relevant to the chosen type are set, perhaps. (what is the default type? how do they map?)
    47  			"document_root": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  				//Default:  "public",
    51  			},
    52  			"rails_env": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Optional: true,
    55  				//Default:  "production",
    56  			},
    57  			"auto_bundle_on_deploy": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Optional: true,
    60  				//Default:  true,
    61  			},
    62  			"aws_flow_ruby_settings": &schema.Schema{
    63  				Type:     schema.TypeString,
    64  				Optional: true,
    65  			},
    66  			"app_source": &schema.Schema{
    67  				Type:     schema.TypeList,
    68  				Optional: true,
    69  				Computed: true,
    70  				Elem: &schema.Resource{
    71  					Schema: map[string]*schema.Schema{
    72  						"type": &schema.Schema{
    73  							Type:     schema.TypeString,
    74  							Required: true,
    75  						},
    76  
    77  						"url": &schema.Schema{
    78  							Type:     schema.TypeString,
    79  							Optional: true,
    80  						},
    81  
    82  						"username": &schema.Schema{
    83  							Type:     schema.TypeString,
    84  							Optional: true,
    85  						},
    86  
    87  						"password": &schema.Schema{
    88  							Type:     schema.TypeString,
    89  							Optional: true,
    90  						},
    91  
    92  						"revision": &schema.Schema{
    93  							Type:     schema.TypeString,
    94  							Optional: true,
    95  						},
    96  
    97  						"ssh_key": &schema.Schema{
    98  							Type:     schema.TypeString,
    99  							Optional: true,
   100  						},
   101  					},
   102  				},
   103  			},
   104  			// AutoSelectOpsworksMysqlInstance, OpsworksMysqlInstance, or RdsDbInstance.
   105  			// anything beside auto select will lead into failure in case the instance doesn't exist
   106  			// XXX: validation?
   107  			"data_source_type": &schema.Schema{
   108  				Type:     schema.TypeString,
   109  				Optional: true,
   110  			},
   111  			"data_source_database_name": &schema.Schema{
   112  				Type:     schema.TypeString,
   113  				Optional: true,
   114  			},
   115  			"data_source_arn": &schema.Schema{
   116  				Type:     schema.TypeString,
   117  				Optional: true,
   118  			},
   119  			"description": &schema.Schema{
   120  				Type:     schema.TypeString,
   121  				Optional: true,
   122  			},
   123  			"domains": &schema.Schema{
   124  				Type:     schema.TypeList,
   125  				Optional: true,
   126  				Elem:     &schema.Schema{Type: schema.TypeString},
   127  			},
   128  			"environment": &schema.Schema{
   129  				Type:     schema.TypeSet,
   130  				Optional: true,
   131  				Elem: &schema.Resource{
   132  					Schema: map[string]*schema.Schema{
   133  						"key": &schema.Schema{
   134  							Type:     schema.TypeString,
   135  							Required: true,
   136  						},
   137  						"value": &schema.Schema{
   138  							Type:     schema.TypeString,
   139  							Required: true,
   140  						},
   141  						"secure": &schema.Schema{
   142  							Type:     schema.TypeBool,
   143  							Optional: true,
   144  							Default:  true,
   145  						},
   146  					},
   147  				},
   148  			},
   149  			"enable_ssl": &schema.Schema{
   150  				Type:     schema.TypeBool,
   151  				Optional: true,
   152  				Default:  false,
   153  			},
   154  			"ssl_configuration": &schema.Schema{
   155  				Type:     schema.TypeList,
   156  				Optional: true,
   157  				//Computed: true,
   158  				Elem: &schema.Resource{
   159  					Schema: map[string]*schema.Schema{
   160  						"certificate": &schema.Schema{
   161  							Type:     schema.TypeString,
   162  							Required: true,
   163  							StateFunc: func(v interface{}) string {
   164  								switch v.(type) {
   165  								case string:
   166  									return strings.TrimSpace(v.(string))
   167  								default:
   168  									return ""
   169  								}
   170  							},
   171  						},
   172  						"private_key": &schema.Schema{
   173  							Type:     schema.TypeString,
   174  							Required: true,
   175  							StateFunc: func(v interface{}) string {
   176  								switch v.(type) {
   177  								case string:
   178  									return strings.TrimSpace(v.(string))
   179  								default:
   180  									return ""
   181  								}
   182  							},
   183  						},
   184  						"chain": &schema.Schema{
   185  							Type:     schema.TypeString,
   186  							Optional: true,
   187  							StateFunc: func(v interface{}) string {
   188  								switch v.(type) {
   189  								case string:
   190  									return strings.TrimSpace(v.(string))
   191  								default:
   192  									return ""
   193  								}
   194  							},
   195  						},
   196  					},
   197  				},
   198  			},
   199  		},
   200  	}
   201  }
   202  
   203  func resourceAwsOpsworksApplicationValidate(d *schema.ResourceData) error {
   204  	appSourceCount := d.Get("app_source.#").(int)
   205  	if appSourceCount > 1 {
   206  		return fmt.Errorf("Only one app_source is permitted.")
   207  	}
   208  
   209  	sslCount := d.Get("ssl_configuration.#").(int)
   210  	if sslCount > 1 {
   211  		return fmt.Errorf("Only one ssl_configuration is permitted.")
   212  	}
   213  
   214  	if d.Get("type").(string) == opsworks.AppTypeRails {
   215  		if _, ok := d.GetOk("rails_env"); !ok {
   216  			return fmt.Errorf("Set rails_env must be set if type is set to rails.")
   217  		}
   218  	}
   219  	switch d.Get("type").(string) {
   220  	case opsworks.AppTypeStatic:
   221  	case opsworks.AppTypeRails:
   222  	case opsworks.AppTypePhp:
   223  	case opsworks.AppTypeOther:
   224  	case opsworks.AppTypeNodejs:
   225  	case opsworks.AppTypeJava:
   226  	case opsworks.AppTypeAwsFlowRuby:
   227  		log.Printf("[DEBUG] type supported")
   228  	default:
   229  		return fmt.Errorf("opsworks_application.type must be one of %s, %s, %s, %s, %s, %s, %s",
   230  			opsworks.AppTypeStatic,
   231  			opsworks.AppTypeRails,
   232  			opsworks.AppTypePhp,
   233  			opsworks.AppTypeOther,
   234  			opsworks.AppTypeNodejs,
   235  			opsworks.AppTypeJava,
   236  			opsworks.AppTypeAwsFlowRuby)
   237  	}
   238  
   239  	return nil
   240  }
   241  
   242  func resourceAwsOpsworksApplicationRead(d *schema.ResourceData, meta interface{}) error {
   243  	client := meta.(*AWSClient).opsworksconn
   244  
   245  	req := &opsworks.DescribeAppsInput{
   246  		AppIds: []*string{
   247  			aws.String(d.Id()),
   248  		},
   249  	}
   250  
   251  	log.Printf("[DEBUG] Reading OpsWorks app: %s", d.Id())
   252  
   253  	resp, err := client.DescribeApps(req)
   254  	if err != nil {
   255  		if awserr, ok := err.(awserr.Error); ok {
   256  			if awserr.Code() == "ResourceNotFoundException" {
   257  				log.Printf("[INFO] App not found: %s", d.Id())
   258  				d.SetId("")
   259  				return nil
   260  			}
   261  		}
   262  		return err
   263  	}
   264  
   265  	app := resp.Apps[0]
   266  
   267  	d.Set("name", app.Name)
   268  	d.Set("stack_id", app.StackId)
   269  	d.Set("type", app.Type)
   270  	d.Set("description", app.Description)
   271  	d.Set("domains", flattenStringList(app.Domains))
   272  	d.Set("enable_ssl", app.EnableSsl)
   273  	resourceAwsOpsworksSetApplicationSsl(d, app.SslConfiguration)
   274  	resourceAwsOpsworksSetApplicationSource(d, app.AppSource)
   275  	resourceAwsOpsworksSetApplicationDataSources(d, app.DataSources)
   276  	resourceAwsOpsworksSetApplicationEnvironmentVariable(d, app.Environment)
   277  	resourceAwsOpsworksSetApplicationAttributes(d, app.Attributes)
   278  	return nil
   279  }
   280  
   281  func resourceAwsOpsworksApplicationCreate(d *schema.ResourceData, meta interface{}) error {
   282  	client := meta.(*AWSClient).opsworksconn
   283  
   284  	err := resourceAwsOpsworksApplicationValidate(d)
   285  	if err != nil {
   286  		return err
   287  	}
   288  
   289  	req := &opsworks.CreateAppInput{
   290  		Name:             aws.String(d.Get("name").(string)),
   291  		Shortname:        aws.String(d.Get("short_name").(string)),
   292  		StackId:          aws.String(d.Get("stack_id").(string)),
   293  		Type:             aws.String(d.Get("type").(string)),
   294  		Description:      aws.String(d.Get("description").(string)),
   295  		Domains:          expandStringList(d.Get("domains").([]interface{})),
   296  		EnableSsl:        aws.Bool(d.Get("enable_ssl").(bool)),
   297  		SslConfiguration: resourceAwsOpsworksApplicationSsl(d),
   298  		AppSource:        resourceAwsOpsworksApplicationSource(d),
   299  		DataSources:      resourceAwsOpsworksApplicationDataSources(d),
   300  		Environment:      resourceAwsOpsworksApplicationEnvironmentVariable(d),
   301  		Attributes:       resourceAwsOpsworksApplicationAttributes(d),
   302  	}
   303  
   304  	var resp *opsworks.CreateAppOutput
   305  	err = resource.Retry(2*time.Minute, func() *resource.RetryError {
   306  		var cerr error
   307  		resp, cerr = client.CreateApp(req)
   308  		if cerr != nil {
   309  			log.Printf("[INFO] client error")
   310  			if opserr, ok := cerr.(awserr.Error); ok {
   311  				// XXX: handle errors
   312  				log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message())
   313  				return resource.RetryableError(cerr)
   314  			}
   315  			return resource.NonRetryableError(cerr)
   316  		}
   317  		return nil
   318  	})
   319  
   320  	if err != nil {
   321  		return err
   322  	}
   323  
   324  	appID := *resp.AppId
   325  	d.SetId(appID)
   326  	d.Set("id", appID)
   327  
   328  	return resourceAwsOpsworksApplicationRead(d, meta)
   329  }
   330  
   331  func resourceAwsOpsworksApplicationUpdate(d *schema.ResourceData, meta interface{}) error {
   332  	client := meta.(*AWSClient).opsworksconn
   333  
   334  	req := &opsworks.UpdateAppInput{
   335  		AppId:            aws.String(d.Id()),
   336  		Name:             aws.String(d.Get("name").(string)),
   337  		Type:             aws.String(d.Get("type").(string)),
   338  		Description:      aws.String(d.Get("description").(string)),
   339  		Domains:          expandStringList(d.Get("domains").([]interface{})),
   340  		EnableSsl:        aws.Bool(d.Get("enable_ssl").(bool)),
   341  		SslConfiguration: resourceAwsOpsworksApplicationSsl(d),
   342  		AppSource:        resourceAwsOpsworksApplicationSource(d),
   343  		DataSources:      resourceAwsOpsworksApplicationDataSources(d),
   344  		Environment:      resourceAwsOpsworksApplicationEnvironmentVariable(d),
   345  		Attributes:       resourceAwsOpsworksApplicationAttributes(d),
   346  	}
   347  
   348  	log.Printf("[DEBUG] Updating OpsWorks layer: %s", d.Id())
   349  
   350  	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
   351  		_, cerr := client.UpdateApp(req)
   352  		if cerr != nil {
   353  			log.Printf("[INFO] client error")
   354  			if opserr, ok := cerr.(awserr.Error); ok {
   355  				// XXX: handle errors
   356  				log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message())
   357  				return resource.NonRetryableError(cerr)
   358  			}
   359  			return resource.RetryableError(cerr)
   360  		}
   361  		return nil
   362  	})
   363  
   364  	if err != nil {
   365  		return err
   366  	}
   367  	return resourceAwsOpsworksApplicationRead(d, meta)
   368  }
   369  
   370  func resourceAwsOpsworksApplicationDelete(d *schema.ResourceData, meta interface{}) error {
   371  	client := meta.(*AWSClient).opsworksconn
   372  
   373  	req := &opsworks.DeleteAppInput{
   374  		AppId: aws.String(d.Id()),
   375  	}
   376  
   377  	log.Printf("[DEBUG] Deleting OpsWorks application: %s", d.Id())
   378  
   379  	_, err := client.DeleteApp(req)
   380  	return err
   381  }
   382  
   383  func resourceAwsOpsworksSetApplicationEnvironmentVariable(d *schema.ResourceData, v []*opsworks.EnvironmentVariable) {
   384  	log.Printf("[DEBUG] envs: %s %d", v, len(v))
   385  	if len(v) == 0 {
   386  		d.Set("environment", nil)
   387  		return
   388  	}
   389  	newValue := make([]*map[string]interface{}, len(v))
   390  
   391  	for i := 0; i < len(v); i++ {
   392  		config := v[i]
   393  		data := make(map[string]interface{})
   394  		newValue[i] = &data
   395  
   396  		if config.Key != nil {
   397  			data["key"] = *config.Key
   398  		}
   399  		if config.Value != nil {
   400  			data["value"] = *config.Value
   401  		}
   402  		if config.Secure != nil {
   403  
   404  			if bool(*config.Secure) {
   405  				data["secure"] = &opsworksTrueString
   406  			} else {
   407  				data["secure"] = &opsworksFalseString
   408  			}
   409  		}
   410  		log.Printf("[DEBUG] v: %s", data)
   411  	}
   412  
   413  	d.Set("environment", newValue)
   414  }
   415  
   416  func resourceAwsOpsworksApplicationEnvironmentVariable(d *schema.ResourceData) []*opsworks.EnvironmentVariable {
   417  	environmentVariables := d.Get("environment").(*schema.Set).List()
   418  	result := make([]*opsworks.EnvironmentVariable, len(environmentVariables))
   419  
   420  	for i := 0; i < len(environmentVariables); i++ {
   421  		env := environmentVariables[i].(map[string]interface{})
   422  
   423  		result[i] = &opsworks.EnvironmentVariable{
   424  			Key:    aws.String(env["key"].(string)),
   425  			Value:  aws.String(env["value"].(string)),
   426  			Secure: aws.Bool(env["secure"].(bool)),
   427  		}
   428  	}
   429  	return result
   430  }
   431  
   432  func resourceAwsOpsworksApplicationSource(d *schema.ResourceData) *opsworks.Source {
   433  	count := d.Get("app_source.#").(int)
   434  	if count == 0 {
   435  		return nil
   436  	}
   437  
   438  	return &opsworks.Source{
   439  		Type:     aws.String(d.Get("app_source.0.type").(string)),
   440  		Url:      aws.String(d.Get("app_source.0.url").(string)),
   441  		Username: aws.String(d.Get("app_source.0.username").(string)),
   442  		Password: aws.String(d.Get("app_source.0.password").(string)),
   443  		Revision: aws.String(d.Get("app_source.0.revision").(string)),
   444  		SshKey:   aws.String(d.Get("app_source.0.ssh_key").(string)),
   445  	}
   446  }
   447  
   448  func resourceAwsOpsworksSetApplicationSource(d *schema.ResourceData, v *opsworks.Source) {
   449  	nv := make([]interface{}, 0, 1)
   450  	if v != nil {
   451  		m := make(map[string]interface{})
   452  		if v.Type != nil {
   453  			m["type"] = *v.Type
   454  		}
   455  		if v.Url != nil {
   456  			m["url"] = *v.Url
   457  		}
   458  		if v.Username != nil {
   459  			m["username"] = *v.Username
   460  		}
   461  		if v.Password != nil {
   462  			m["password"] = *v.Password
   463  		}
   464  		if v.Revision != nil {
   465  			m["revision"] = *v.Revision
   466  		}
   467  		nv = append(nv, m)
   468  	}
   469  
   470  	err := d.Set("app_source", nv)
   471  	if err != nil {
   472  		// should never happen
   473  		panic(err)
   474  	}
   475  }
   476  
   477  func resourceAwsOpsworksApplicationDataSources(d *schema.ResourceData) []*opsworks.DataSource {
   478  	arn := d.Get("data_source_arn").(string)
   479  	databaseName := d.Get("data_source_database_name").(string)
   480  	databaseType := d.Get("data_source_type").(string)
   481  
   482  	result := make([]*opsworks.DataSource, 1)
   483  
   484  	if len(arn) > 0 || len(databaseName) > 0 || len(databaseType) > 0 {
   485  		result[0] = &opsworks.DataSource{
   486  			Arn:          aws.String(arn),
   487  			DatabaseName: aws.String(databaseName),
   488  			Type:         aws.String(databaseType),
   489  		}
   490  	}
   491  	return result
   492  }
   493  
   494  func resourceAwsOpsworksSetApplicationDataSources(d *schema.ResourceData, v []*opsworks.DataSource) {
   495  	d.Set("data_source_arn", nil)
   496  	d.Set("data_source_database_name", nil)
   497  	d.Set("data_source_type", nil)
   498  
   499  	if len(v) == 0 {
   500  		return
   501  	}
   502  
   503  	d.Set("data_source_arn", v[0].Arn)
   504  	d.Set("data_source_database_name", v[0].DatabaseName)
   505  	d.Set("data_source_type", v[0].Type)
   506  }
   507  
   508  func resourceAwsOpsworksApplicationSsl(d *schema.ResourceData) *opsworks.SslConfiguration {
   509  	count := d.Get("ssl_configuration.#").(int)
   510  	if count == 0 {
   511  		return nil
   512  	}
   513  
   514  	return &opsworks.SslConfiguration{
   515  		PrivateKey:  aws.String(d.Get("ssl_configuration.0.private_key").(string)),
   516  		Certificate: aws.String(d.Get("ssl_configuration.0.certificate").(string)),
   517  		Chain:       aws.String(d.Get("ssl_configuration.0.chain").(string)),
   518  	}
   519  }
   520  
   521  func resourceAwsOpsworksSetApplicationSsl(d *schema.ResourceData, v *opsworks.SslConfiguration) {
   522  	nv := make([]interface{}, 0, 1)
   523  	set := false
   524  	if v != nil {
   525  		m := make(map[string]interface{})
   526  		if v.PrivateKey != nil {
   527  			m["private_key"] = *v.PrivateKey
   528  			set = true
   529  		}
   530  		if v.Certificate != nil {
   531  			m["certificate"] = *v.Certificate
   532  			set = true
   533  		}
   534  		if v.Chain != nil {
   535  			m["chain"] = *v.Chain
   536  			set = true
   537  		}
   538  		if set {
   539  			nv = append(nv, m)
   540  		}
   541  	}
   542  
   543  	err := d.Set("ssl_configuration", nv)
   544  	if err != nil {
   545  		// should never happen
   546  		panic(err)
   547  	}
   548  }
   549  
   550  func resourceAwsOpsworksApplicationAttributes(d *schema.ResourceData) map[string]*string {
   551  	if d.Get("type") != opsworks.AppTypeRails {
   552  		return nil
   553  	}
   554  	attributes := make(map[string]*string)
   555  
   556  	if val := d.Get("document_root").(string); len(val) > 0 {
   557  		attributes[opsworks.AppAttributesKeysDocumentRoot] = aws.String(val)
   558  	}
   559  	if val := d.Get("aws_flow_ruby_settings").(string); len(val) > 0 {
   560  		attributes[opsworks.AppAttributesKeysAwsFlowRubySettings] = aws.String(val)
   561  	}
   562  	if val := d.Get("rails_env").(string); len(val) > 0 {
   563  		attributes[opsworks.AppAttributesKeysRailsEnv] = aws.String(val)
   564  	}
   565  	if val := d.Get("auto_bundle_on_deploy").(string); len(val) > 0 {
   566  		if val == "1" {
   567  			val = "true"
   568  		} else if val == "0" {
   569  			val = "false"
   570  		}
   571  		attributes[opsworks.AppAttributesKeysAutoBundleOnDeploy] = aws.String(val)
   572  	}
   573  
   574  	return attributes
   575  }
   576  
   577  func resourceAwsOpsworksSetApplicationAttributes(d *schema.ResourceData, v map[string]*string) {
   578  	d.Set("document_root", nil)
   579  	d.Set("rails_env", nil)
   580  	d.Set("aws_flow_ruby_settings", nil)
   581  	d.Set("auto_bundle_on_deploy", nil)
   582  
   583  	if d.Get("type") != opsworks.AppTypeRails {
   584  		return
   585  	}
   586  	if val, ok := v[opsworks.AppAttributesKeysDocumentRoot]; ok {
   587  		d.Set("document_root", val)
   588  	}
   589  	if val, ok := v[opsworks.AppAttributesKeysAwsFlowRubySettings]; ok {
   590  		d.Set("aws_flow_ruby_settings", val)
   591  	}
   592  	if val, ok := v[opsworks.AppAttributesKeysRailsEnv]; ok {
   593  		d.Set("rails_env", val)
   594  	}
   595  	if val, ok := v[opsworks.AppAttributesKeysAutoBundleOnDeploy]; ok {
   596  		d.Set("auto_bundle_on_deploy", val)
   597  	}
   598  }