github.com/ottenhoff/terraform@v0.7.0-rc1.0.20160607213102-ac2d195cc560/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  	var resp *opsworks.UpdateAppOutput
   351  	err := resource.Retry(2*time.Minute, func() *resource.RetryError {
   352  		var cerr error
   353  		resp, cerr = client.UpdateApp(req)
   354  		if cerr != nil {
   355  			log.Printf("[INFO] client error")
   356  			if opserr, ok := cerr.(awserr.Error); ok {
   357  				// XXX: handle errors
   358  				log.Printf("[ERROR] OpsWorks error: %s message: %s", opserr.Code(), opserr.Message())
   359  				return resource.NonRetryableError(cerr)
   360  			}
   361  			return resource.RetryableError(cerr)
   362  		}
   363  		return nil
   364  	})
   365  
   366  	if err != nil {
   367  		return err
   368  	}
   369  	return resourceAwsOpsworksApplicationRead(d, meta)
   370  }
   371  
   372  func resourceAwsOpsworksApplicationDelete(d *schema.ResourceData, meta interface{}) error {
   373  	client := meta.(*AWSClient).opsworksconn
   374  
   375  	req := &opsworks.DeleteAppInput{
   376  		AppId: aws.String(d.Id()),
   377  	}
   378  
   379  	log.Printf("[DEBUG] Deleting OpsWorks application: %s", d.Id())
   380  
   381  	_, err := client.DeleteApp(req)
   382  	return err
   383  }
   384  
   385  func resourceAwsOpsworksSetApplicationEnvironmentVariable(d *schema.ResourceData, v []*opsworks.EnvironmentVariable) {
   386  	log.Printf("[DEBUG] envs: %s %d", v, len(v))
   387  	if len(v) == 0 {
   388  		d.Set("environment", nil)
   389  		return
   390  	}
   391  	newValue := make([]*map[string]interface{}, len(v))
   392  
   393  	for i := 0; i < len(v); i++ {
   394  		config := v[i]
   395  		data := make(map[string]interface{})
   396  		newValue[i] = &data
   397  
   398  		if config.Key != nil {
   399  			data["key"] = *config.Key
   400  		}
   401  		if config.Value != nil {
   402  			data["value"] = *config.Value
   403  		}
   404  		if config.Secure != nil {
   405  
   406  			if bool(*config.Secure) {
   407  				data["secure"] = &opsworksTrueString
   408  			} else {
   409  				data["secure"] = &opsworksFalseString
   410  			}
   411  		}
   412  		log.Printf("[DEBUG] v: %s", data)
   413  	}
   414  
   415  	d.Set("environment", newValue)
   416  }
   417  
   418  func resourceAwsOpsworksApplicationEnvironmentVariable(d *schema.ResourceData) []*opsworks.EnvironmentVariable {
   419  	environmentVariables := d.Get("environment").(*schema.Set).List()
   420  	result := make([]*opsworks.EnvironmentVariable, len(environmentVariables))
   421  
   422  	for i := 0; i < len(environmentVariables); i++ {
   423  		env := environmentVariables[i].(map[string]interface{})
   424  
   425  		result[i] = &opsworks.EnvironmentVariable{
   426  			Key:    aws.String(env["key"].(string)),
   427  			Value:  aws.String(env["value"].(string)),
   428  			Secure: aws.Bool(env["secure"].(bool)),
   429  		}
   430  	}
   431  	return result
   432  }
   433  
   434  func resourceAwsOpsworksApplicationSource(d *schema.ResourceData) *opsworks.Source {
   435  	count := d.Get("app_source.#").(int)
   436  	if count == 0 {
   437  		return nil
   438  	}
   439  
   440  	return &opsworks.Source{
   441  		Type:     aws.String(d.Get("app_source.0.type").(string)),
   442  		Url:      aws.String(d.Get("app_source.0.url").(string)),
   443  		Username: aws.String(d.Get("app_source.0.username").(string)),
   444  		Password: aws.String(d.Get("app_source.0.password").(string)),
   445  		Revision: aws.String(d.Get("app_source.0.revision").(string)),
   446  		SshKey:   aws.String(d.Get("app_source.0.ssh_key").(string)),
   447  	}
   448  }
   449  
   450  func resourceAwsOpsworksSetApplicationSource(d *schema.ResourceData, v *opsworks.Source) {
   451  	nv := make([]interface{}, 0, 1)
   452  	if v != nil {
   453  		m := make(map[string]interface{})
   454  		if v.Type != nil {
   455  			m["type"] = *v.Type
   456  		}
   457  		if v.Url != nil {
   458  			m["url"] = *v.Url
   459  		}
   460  		if v.Username != nil {
   461  			m["username"] = *v.Username
   462  		}
   463  		if v.Password != nil {
   464  			m["password"] = *v.Password
   465  		}
   466  		if v.Revision != nil {
   467  			m["revision"] = *v.Revision
   468  		}
   469  		nv = append(nv, m)
   470  	}
   471  
   472  	err := d.Set("app_source", nv)
   473  	if err != nil {
   474  		// should never happen
   475  		panic(err)
   476  	}
   477  }
   478  
   479  func resourceAwsOpsworksApplicationDataSources(d *schema.ResourceData) []*opsworks.DataSource {
   480  	arn := d.Get("data_source_arn").(string)
   481  	databaseName := d.Get("data_source_database_name").(string)
   482  	databaseType := d.Get("data_source_type").(string)
   483  
   484  	result := make([]*opsworks.DataSource, 1)
   485  
   486  	if len(arn) > 0 || len(databaseName) > 0 || len(databaseType) > 0 {
   487  		result[0] = &opsworks.DataSource{
   488  			Arn:          aws.String(arn),
   489  			DatabaseName: aws.String(databaseName),
   490  			Type:         aws.String(databaseType),
   491  		}
   492  	}
   493  	return result
   494  }
   495  
   496  func resourceAwsOpsworksSetApplicationDataSources(d *schema.ResourceData, v []*opsworks.DataSource) {
   497  	d.Set("data_source_arn", nil)
   498  	d.Set("data_source_database_name", nil)
   499  	d.Set("data_source_type", nil)
   500  
   501  	if len(v) == 0 {
   502  		return
   503  	}
   504  
   505  	d.Set("data_source_arn", v[0].Arn)
   506  	d.Set("data_source_database_name", v[0].DatabaseName)
   507  	d.Set("data_source_type", v[0].Type)
   508  }
   509  
   510  func resourceAwsOpsworksApplicationSsl(d *schema.ResourceData) *opsworks.SslConfiguration {
   511  	count := d.Get("ssl_configuration.#").(int)
   512  	if count == 0 {
   513  		return nil
   514  	}
   515  
   516  	return &opsworks.SslConfiguration{
   517  		PrivateKey:  aws.String(d.Get("ssl_configuration.0.private_key").(string)),
   518  		Certificate: aws.String(d.Get("ssl_configuration.0.certificate").(string)),
   519  		Chain:       aws.String(d.Get("ssl_configuration.0.chain").(string)),
   520  	}
   521  }
   522  
   523  func resourceAwsOpsworksSetApplicationSsl(d *schema.ResourceData, v *opsworks.SslConfiguration) {
   524  	nv := make([]interface{}, 0, 1)
   525  	set := false
   526  	if v != nil {
   527  		m := make(map[string]interface{})
   528  		if v.PrivateKey != nil {
   529  			m["private_key"] = *v.PrivateKey
   530  			set = true
   531  		}
   532  		if v.Certificate != nil {
   533  			m["certificate"] = *v.Certificate
   534  			set = true
   535  		}
   536  		if v.Chain != nil {
   537  			m["chain"] = *v.Chain
   538  			set = true
   539  		}
   540  		if set {
   541  			nv = append(nv, m)
   542  		}
   543  	}
   544  
   545  	err := d.Set("ssl_configuration", nv)
   546  	if err != nil {
   547  		// should never happen
   548  		panic(err)
   549  	}
   550  }
   551  
   552  func resourceAwsOpsworksApplicationAttributes(d *schema.ResourceData) map[string]*string {
   553  	if d.Get("type") != opsworks.AppTypeRails {
   554  		return nil
   555  	}
   556  	attributes := make(map[string]*string)
   557  
   558  	if val := d.Get("document_root").(string); len(val) > 0 {
   559  		attributes[opsworks.AppAttributesKeysDocumentRoot] = aws.String(val)
   560  	}
   561  	if val := d.Get("aws_flow_ruby_settings").(string); len(val) > 0 {
   562  		attributes[opsworks.AppAttributesKeysAwsFlowRubySettings] = aws.String(val)
   563  	}
   564  	if val := d.Get("rails_env").(string); len(val) > 0 {
   565  		attributes[opsworks.AppAttributesKeysRailsEnv] = aws.String(val)
   566  	}
   567  	if val := d.Get("auto_bundle_on_deploy").(string); len(val) > 0 {
   568  		if val == "1" {
   569  			val = "true"
   570  		} else if val == "0" {
   571  			val = "false"
   572  		}
   573  		attributes[opsworks.AppAttributesKeysAutoBundleOnDeploy] = aws.String(val)
   574  	}
   575  
   576  	return attributes
   577  }
   578  
   579  func resourceAwsOpsworksSetApplicationAttributes(d *schema.ResourceData, v map[string]*string) {
   580  	d.Set("document_root", nil)
   581  	d.Set("rails_env", nil)
   582  	d.Set("aws_flow_ruby_settings", nil)
   583  	d.Set("auto_bundle_on_deploy", nil)
   584  
   585  	if d.Get("type") != opsworks.AppTypeRails {
   586  		return
   587  	}
   588  	if val, ok := v[opsworks.AppAttributesKeysDocumentRoot]; ok {
   589  		d.Set("document_root", val)
   590  	}
   591  	if val, ok := v[opsworks.AppAttributesKeysAwsFlowRubySettings]; ok {
   592  		d.Set("aws_flow_ruby_settings", val)
   593  	}
   594  	if val, ok := v[opsworks.AppAttributesKeysRailsEnv]; ok {
   595  		d.Set("rails_env", val)
   596  	}
   597  	if val, ok := v[opsworks.AppAttributesKeysAutoBundleOnDeploy]; ok {
   598  		d.Set("auto_bundle_on_deploy", val)
   599  	}
   600  }