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 }