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