github.com/anfernee/terraform@v0.6.16-0.20160430000239-06e5085a92f2/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 if v.SshKey != nil { 470 m["ssh_key"] = *v.SshKey 471 } 472 nv = append(nv, m) 473 } 474 475 err := d.Set("app_source", nv) 476 if err != nil { 477 // should never happen 478 panic(err) 479 } 480 } 481 482 func resourceAwsOpsworksApplicationDataSources(d *schema.ResourceData) []*opsworks.DataSource { 483 arn := d.Get("data_source_arn").(string) 484 databaseName := d.Get("data_source_database_name").(string) 485 databaseType := d.Get("data_source_type").(string) 486 487 result := make([]*opsworks.DataSource, 1) 488 489 if len(arn) > 0 || len(databaseName) > 0 || len(databaseType) > 0 { 490 result[0] = &opsworks.DataSource{ 491 Arn: aws.String(arn), 492 DatabaseName: aws.String(databaseName), 493 Type: aws.String(databaseType), 494 } 495 } 496 return result 497 } 498 499 func resourceAwsOpsworksSetApplicationDataSources(d *schema.ResourceData, v []*opsworks.DataSource) { 500 d.Set("data_source_arn", nil) 501 d.Set("data_source_database_name", nil) 502 d.Set("data_source_type", nil) 503 504 if len(v) == 0 { 505 return 506 } 507 508 d.Set("data_source_arn", v[0].Arn) 509 d.Set("data_source_database_name", v[0].DatabaseName) 510 d.Set("data_source_type", v[0].Type) 511 } 512 513 func resourceAwsOpsworksApplicationSsl(d *schema.ResourceData) *opsworks.SslConfiguration { 514 count := d.Get("ssl_configuration.#").(int) 515 if count == 0 { 516 return nil 517 } 518 519 return &opsworks.SslConfiguration{ 520 PrivateKey: aws.String(d.Get("ssl_configuration.0.private_key").(string)), 521 Certificate: aws.String(d.Get("ssl_configuration.0.certificate").(string)), 522 Chain: aws.String(d.Get("ssl_configuration.0.chain").(string)), 523 } 524 } 525 526 func resourceAwsOpsworksSetApplicationSsl(d *schema.ResourceData, v *opsworks.SslConfiguration) { 527 nv := make([]interface{}, 0, 1) 528 set := false 529 if v != nil { 530 m := make(map[string]interface{}) 531 if v.PrivateKey != nil { 532 m["private_key"] = *v.PrivateKey 533 set = true 534 } 535 if v.Certificate != nil { 536 m["certificate"] = *v.Certificate 537 set = true 538 } 539 if v.Chain != nil { 540 m["chain"] = *v.Chain 541 set = true 542 } 543 if set { 544 nv = append(nv, m) 545 } 546 } 547 548 err := d.Set("ssl_configuration", nv) 549 if err != nil { 550 // should never happen 551 panic(err) 552 } 553 } 554 555 func resourceAwsOpsworksApplicationAttributes(d *schema.ResourceData) map[string]*string { 556 if d.Get("type") != opsworks.AppTypeRails { 557 return nil 558 } 559 attributes := make(map[string]*string) 560 561 if val := d.Get("document_root").(string); len(val) > 0 { 562 attributes[opsworks.AppAttributesKeysDocumentRoot] = aws.String(val) 563 } 564 if val := d.Get("aws_flow_ruby_settings").(string); len(val) > 0 { 565 attributes[opsworks.AppAttributesKeysAwsFlowRubySettings] = aws.String(val) 566 } 567 if val := d.Get("rails_env").(string); len(val) > 0 { 568 attributes[opsworks.AppAttributesKeysRailsEnv] = aws.String(val) 569 } 570 if val := d.Get("auto_bundle_on_deploy").(string); len(val) > 0 { 571 if val == "1" { 572 val = "true" 573 } else if val == "0" { 574 val = "false" 575 } 576 attributes[opsworks.AppAttributesKeysAutoBundleOnDeploy] = aws.String(val) 577 } 578 579 return attributes 580 } 581 582 func resourceAwsOpsworksSetApplicationAttributes(d *schema.ResourceData, v map[string]*string) { 583 d.Set("document_root", nil) 584 d.Set("rails_env", nil) 585 d.Set("aws_flow_ruby_settings", nil) 586 d.Set("auto_bundle_on_deploy", nil) 587 588 if d.Get("type") != opsworks.AppTypeRails { 589 return 590 } 591 if val, ok := v[opsworks.AppAttributesKeysDocumentRoot]; ok { 592 d.Set("document_root", val) 593 } 594 if val, ok := v[opsworks.AppAttributesKeysAwsFlowRubySettings]; ok { 595 d.Set("aws_flow_ruby_settings", val) 596 } 597 if val, ok := v[opsworks.AppAttributesKeysRailsEnv]; ok { 598 d.Set("rails_env", val) 599 } 600 if val, ok := v[opsworks.AppAttributesKeysAutoBundleOnDeploy]; ok { 601 d.Set("auto_bundle_on_deploy", val) 602 } 603 }