github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/builtin/providers/aws/structure.go (about) 1 package aws 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "regexp" 8 "sort" 9 "strings" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/service/directoryservice" 13 "github.com/aws/aws-sdk-go/service/ec2" 14 "github.com/aws/aws-sdk-go/service/ecs" 15 "github.com/aws/aws-sdk-go/service/elasticache" 16 elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" 17 "github.com/aws/aws-sdk-go/service/elb" 18 "github.com/aws/aws-sdk-go/service/rds" 19 "github.com/aws/aws-sdk-go/service/route53" 20 "github.com/hashicorp/terraform/helper/schema" 21 ) 22 23 // Takes the result of flatmap.Expand for an array of listeners and 24 // returns ELB API compatible objects 25 func expandListeners(configured []interface{}) ([]*elb.Listener, error) { 26 listeners := make([]*elb.Listener, 0, len(configured)) 27 28 // Loop over our configured listeners and create 29 // an array of aws-sdk-go compatabile objects 30 for _, lRaw := range configured { 31 data := lRaw.(map[string]interface{}) 32 33 ip := int64(data["instance_port"].(int)) 34 lp := int64(data["lb_port"].(int)) 35 l := &elb.Listener{ 36 InstancePort: &ip, 37 InstanceProtocol: aws.String(data["instance_protocol"].(string)), 38 LoadBalancerPort: &lp, 39 Protocol: aws.String(data["lb_protocol"].(string)), 40 } 41 42 if v, ok := data["ssl_certificate_id"]; ok { 43 l.SSLCertificateId = aws.String(v.(string)) 44 } 45 46 listeners = append(listeners, l) 47 } 48 49 return listeners, nil 50 } 51 52 // Takes the result of flatmap. Expand for an array of listeners and 53 // returns ECS Volume compatible objects 54 func expandEcsVolumes(configured []interface{}) ([]*ecs.Volume, error) { 55 volumes := make([]*ecs.Volume, 0, len(configured)) 56 57 // Loop over our configured volumes and create 58 // an array of aws-sdk-go compatible objects 59 for _, lRaw := range configured { 60 data := lRaw.(map[string]interface{}) 61 62 l := &ecs.Volume{ 63 Name: aws.String(data["name"].(string)), 64 Host: &ecs.HostVolumeProperties{ 65 SourcePath: aws.String(data["host_path"].(string)), 66 }, 67 } 68 69 volumes = append(volumes, l) 70 } 71 72 return volumes, nil 73 } 74 75 // Takes JSON in a string. Decodes JSON into 76 // an array of ecs.ContainerDefinition compatible objects 77 func expandEcsContainerDefinitions(rawDefinitions string) ([]*ecs.ContainerDefinition, error) { 78 var definitions []*ecs.ContainerDefinition 79 80 err := json.Unmarshal([]byte(rawDefinitions), &definitions) 81 if err != nil { 82 return nil, fmt.Errorf("Error decoding JSON: %s", err) 83 } 84 85 return definitions, nil 86 } 87 88 // Takes the result of flatmap. Expand for an array of load balancers and 89 // returns ecs.LoadBalancer compatible objects 90 func expandEcsLoadBalancers(configured []interface{}) []*ecs.LoadBalancer { 91 loadBalancers := make([]*ecs.LoadBalancer, 0, len(configured)) 92 93 // Loop over our configured load balancers and create 94 // an array of aws-sdk-go compatible objects 95 for _, lRaw := range configured { 96 data := lRaw.(map[string]interface{}) 97 98 l := &ecs.LoadBalancer{ 99 ContainerName: aws.String(data["container_name"].(string)), 100 ContainerPort: aws.Int64(int64(data["container_port"].(int))), 101 LoadBalancerName: aws.String(data["elb_name"].(string)), 102 } 103 104 loadBalancers = append(loadBalancers, l) 105 } 106 107 return loadBalancers 108 } 109 110 // Takes the result of flatmap.Expand for an array of ingress/egress security 111 // group rules and returns EC2 API compatible objects. This function will error 112 // if it finds invalid permissions input, namely a protocol of "-1" with either 113 // to_port or from_port set to a non-zero value. 114 func expandIPPerms( 115 group *ec2.SecurityGroup, configured []interface{}) ([]*ec2.IpPermission, error) { 116 vpc := group.VpcId != nil 117 118 perms := make([]*ec2.IpPermission, len(configured)) 119 for i, mRaw := range configured { 120 var perm ec2.IpPermission 121 m := mRaw.(map[string]interface{}) 122 123 perm.FromPort = aws.Int64(int64(m["from_port"].(int))) 124 perm.ToPort = aws.Int64(int64(m["to_port"].(int))) 125 perm.IpProtocol = aws.String(m["protocol"].(string)) 126 127 // When protocol is "-1", AWS won't store any ports for the 128 // rule, but also won't error if the user specifies ports other 129 // than '0'. Force the user to make a deliberate '0' port 130 // choice when specifying a "-1" protocol, and tell them about 131 // AWS's behavior in the error message. 132 if *perm.IpProtocol == "-1" && (*perm.FromPort != 0 || *perm.ToPort != 0) { 133 return nil, fmt.Errorf( 134 "from_port (%d) and to_port (%d) must both be 0 to use the the 'ALL' \"-1\" protocol!", 135 *perm.FromPort, *perm.ToPort) 136 } 137 138 var groups []string 139 if raw, ok := m["security_groups"]; ok { 140 list := raw.(*schema.Set).List() 141 for _, v := range list { 142 groups = append(groups, v.(string)) 143 } 144 } 145 if v, ok := m["self"]; ok && v.(bool) { 146 if vpc { 147 groups = append(groups, *group.GroupId) 148 } else { 149 groups = append(groups, *group.GroupName) 150 } 151 } 152 153 if len(groups) > 0 { 154 perm.UserIdGroupPairs = make([]*ec2.UserIdGroupPair, len(groups)) 155 for i, name := range groups { 156 ownerId, id := "", name 157 if items := strings.Split(id, "/"); len(items) > 1 { 158 ownerId, id = items[0], items[1] 159 } 160 161 perm.UserIdGroupPairs[i] = &ec2.UserIdGroupPair{ 162 GroupId: aws.String(id), 163 } 164 165 if ownerId != "" { 166 perm.UserIdGroupPairs[i].UserId = aws.String(ownerId) 167 } 168 169 if !vpc { 170 perm.UserIdGroupPairs[i].GroupId = nil 171 perm.UserIdGroupPairs[i].GroupName = aws.String(id) 172 } 173 } 174 } 175 176 if raw, ok := m["cidr_blocks"]; ok { 177 list := raw.([]interface{}) 178 for _, v := range list { 179 perm.IpRanges = append(perm.IpRanges, &ec2.IpRange{CidrIp: aws.String(v.(string))}) 180 } 181 } 182 183 perms[i] = &perm 184 } 185 186 return perms, nil 187 } 188 189 // Takes the result of flatmap.Expand for an array of parameters and 190 // returns Parameter API compatible objects 191 func expandParameters(configured []interface{}) ([]*rds.Parameter, error) { 192 var parameters []*rds.Parameter 193 194 // Loop over our configured parameters and create 195 // an array of aws-sdk-go compatabile objects 196 for _, pRaw := range configured { 197 data := pRaw.(map[string]interface{}) 198 199 if data["name"].(string) == "" { 200 continue 201 } 202 203 p := &rds.Parameter{ 204 ApplyMethod: aws.String(data["apply_method"].(string)), 205 ParameterName: aws.String(data["name"].(string)), 206 ParameterValue: aws.String(data["value"].(string)), 207 } 208 209 parameters = append(parameters, p) 210 } 211 212 return parameters, nil 213 } 214 215 // Takes the result of flatmap.Expand for an array of parameters and 216 // returns Parameter API compatible objects 217 func expandElastiCacheParameters(configured []interface{}) ([]*elasticache.ParameterNameValue, error) { 218 parameters := make([]*elasticache.ParameterNameValue, 0, len(configured)) 219 220 // Loop over our configured parameters and create 221 // an array of aws-sdk-go compatabile objects 222 for _, pRaw := range configured { 223 data := pRaw.(map[string]interface{}) 224 225 p := &elasticache.ParameterNameValue{ 226 ParameterName: aws.String(data["name"].(string)), 227 ParameterValue: aws.String(data["value"].(string)), 228 } 229 230 parameters = append(parameters, p) 231 } 232 233 return parameters, nil 234 } 235 236 // Flattens a health check into something that flatmap.Flatten() 237 // can handle 238 func flattenHealthCheck(check *elb.HealthCheck) []map[string]interface{} { 239 result := make([]map[string]interface{}, 0, 1) 240 241 chk := make(map[string]interface{}) 242 chk["unhealthy_threshold"] = *check.UnhealthyThreshold 243 chk["healthy_threshold"] = *check.HealthyThreshold 244 chk["target"] = *check.Target 245 chk["timeout"] = *check.Timeout 246 chk["interval"] = *check.Interval 247 248 result = append(result, chk) 249 250 return result 251 } 252 253 // Flattens an array of UserSecurityGroups into a []string 254 func flattenSecurityGroups(list []*ec2.UserIdGroupPair) []string { 255 result := make([]string, 0, len(list)) 256 for _, g := range list { 257 result = append(result, *g.GroupId) 258 } 259 return result 260 } 261 262 // Flattens an array of Instances into a []string 263 func flattenInstances(list []*elb.Instance) []string { 264 result := make([]string, 0, len(list)) 265 for _, i := range list { 266 result = append(result, *i.InstanceId) 267 } 268 return result 269 } 270 271 // Expands an array of String Instance IDs into a []Instances 272 func expandInstanceString(list []interface{}) []*elb.Instance { 273 result := make([]*elb.Instance, 0, len(list)) 274 for _, i := range list { 275 result = append(result, &elb.Instance{InstanceId: aws.String(i.(string))}) 276 } 277 return result 278 } 279 280 // Flattens an array of Backend Descriptions into a a map of instance_port to policy names. 281 func flattenBackendPolicies(backends []*elb.BackendServerDescription) map[int64][]string { 282 policies := make(map[int64][]string) 283 for _, i := range backends { 284 for _, p := range i.PolicyNames { 285 policies[*i.InstancePort] = append(policies[*i.InstancePort], *p) 286 } 287 sort.Strings(policies[*i.InstancePort]) 288 } 289 return policies 290 } 291 292 // Flattens an array of Listeners into a []map[string]interface{} 293 func flattenListeners(list []*elb.ListenerDescription) []map[string]interface{} { 294 result := make([]map[string]interface{}, 0, len(list)) 295 for _, i := range list { 296 l := map[string]interface{}{ 297 "instance_port": *i.Listener.InstancePort, 298 "instance_protocol": strings.ToLower(*i.Listener.InstanceProtocol), 299 "lb_port": *i.Listener.LoadBalancerPort, 300 "lb_protocol": strings.ToLower(*i.Listener.Protocol), 301 } 302 // SSLCertificateID is optional, and may be nil 303 if i.Listener.SSLCertificateId != nil { 304 l["ssl_certificate_id"] = *i.Listener.SSLCertificateId 305 } 306 result = append(result, l) 307 } 308 return result 309 } 310 311 // Flattens an array of Volumes into a []map[string]interface{} 312 func flattenEcsVolumes(list []*ecs.Volume) []map[string]interface{} { 313 result := make([]map[string]interface{}, 0, len(list)) 314 for _, volume := range list { 315 l := map[string]interface{}{ 316 "name": *volume.Name, 317 "host_path": *volume.Host.SourcePath, 318 } 319 result = append(result, l) 320 } 321 return result 322 } 323 324 // Flattens an array of ECS LoadBalancers into a []map[string]interface{} 325 func flattenEcsLoadBalancers(list []*ecs.LoadBalancer) []map[string]interface{} { 326 result := make([]map[string]interface{}, 0, len(list)) 327 for _, loadBalancer := range list { 328 l := map[string]interface{}{ 329 "elb_name": *loadBalancer.LoadBalancerName, 330 "container_name": *loadBalancer.ContainerName, 331 "container_port": *loadBalancer.ContainerPort, 332 } 333 result = append(result, l) 334 } 335 return result 336 } 337 338 // Encodes an array of ecs.ContainerDefinitions into a JSON string 339 func flattenEcsContainerDefinitions(definitions []*ecs.ContainerDefinition) (string, error) { 340 byteArray, err := json.Marshal(definitions) 341 if err != nil { 342 return "", fmt.Errorf("Error encoding to JSON: %s", err) 343 } 344 345 n := bytes.Index(byteArray, []byte{0}) 346 return string(byteArray[:n]), nil 347 } 348 349 // Flattens an array of Parameters into a []map[string]interface{} 350 func flattenParameters(list []*rds.Parameter) []map[string]interface{} { 351 result := make([]map[string]interface{}, 0, len(list)) 352 for _, i := range list { 353 result = append(result, map[string]interface{}{ 354 "name": strings.ToLower(*i.ParameterName), 355 "value": strings.ToLower(*i.ParameterValue), 356 }) 357 } 358 return result 359 } 360 361 // Flattens an array of Parameters into a []map[string]interface{} 362 func flattenElastiCacheParameters(list []*elasticache.Parameter) []map[string]interface{} { 363 result := make([]map[string]interface{}, 0, len(list)) 364 for _, i := range list { 365 result = append(result, map[string]interface{}{ 366 "name": strings.ToLower(*i.ParameterName), 367 "value": strings.ToLower(*i.ParameterValue), 368 }) 369 } 370 return result 371 } 372 373 // Takes the result of flatmap.Expand for an array of strings 374 // and returns a []*string 375 func expandStringList(configured []interface{}) []*string { 376 vs := make([]*string, 0, len(configured)) 377 for _, v := range configured { 378 vs = append(vs, aws.String(v.(string))) 379 } 380 return vs 381 } 382 383 // Takes list of pointers to strings. Expand to an array 384 // of raw strings and returns a []interface{} 385 // to keep compatibility w/ schema.NewSetschema.NewSet 386 func flattenStringList(list []*string) []interface{} { 387 vs := make([]interface{}, 0, len(list)) 388 for _, v := range list { 389 vs = append(vs, *v) 390 } 391 return vs 392 } 393 394 //Flattens an array of private ip addresses into a []string, where the elements returned are the IP strings e.g. "192.168.0.0" 395 func flattenNetworkInterfacesPrivateIPAddresses(dtos []*ec2.NetworkInterfacePrivateIpAddress) []string { 396 ips := make([]string, 0, len(dtos)) 397 for _, v := range dtos { 398 ip := *v.PrivateIpAddress 399 ips = append(ips, ip) 400 } 401 return ips 402 } 403 404 //Flattens security group identifiers into a []string, where the elements returned are the GroupIDs 405 func flattenGroupIdentifiers(dtos []*ec2.GroupIdentifier) []string { 406 ids := make([]string, 0, len(dtos)) 407 for _, v := range dtos { 408 group_id := *v.GroupId 409 ids = append(ids, group_id) 410 } 411 return ids 412 } 413 414 //Expands an array of IPs into a ec2 Private IP Address Spec 415 func expandPrivateIPAddresses(ips []interface{}) []*ec2.PrivateIpAddressSpecification { 416 dtos := make([]*ec2.PrivateIpAddressSpecification, 0, len(ips)) 417 for i, v := range ips { 418 new_private_ip := &ec2.PrivateIpAddressSpecification{ 419 PrivateIpAddress: aws.String(v.(string)), 420 } 421 422 new_private_ip.Primary = aws.Bool(i == 0) 423 424 dtos = append(dtos, new_private_ip) 425 } 426 return dtos 427 } 428 429 //Flattens network interface attachment into a map[string]interface 430 func flattenAttachment(a *ec2.NetworkInterfaceAttachment) map[string]interface{} { 431 att := make(map[string]interface{}) 432 att["instance"] = *a.InstanceId 433 att["device_index"] = *a.DeviceIndex 434 att["attachment_id"] = *a.AttachmentId 435 return att 436 } 437 438 func flattenResourceRecords(recs []*route53.ResourceRecord) []string { 439 strs := make([]string, 0, len(recs)) 440 for _, r := range recs { 441 if r.Value != nil { 442 s := strings.Replace(*r.Value, "\"", "", 2) 443 strs = append(strs, s) 444 } 445 } 446 return strs 447 } 448 449 func expandResourceRecords(recs []interface{}, typeStr string) []*route53.ResourceRecord { 450 records := make([]*route53.ResourceRecord, 0, len(recs)) 451 for _, r := range recs { 452 s := r.(string) 453 switch typeStr { 454 case "TXT": 455 str := fmt.Sprintf("\"%s\"", s) 456 records = append(records, &route53.ResourceRecord{Value: aws.String(str)}) 457 default: 458 records = append(records, &route53.ResourceRecord{Value: aws.String(s)}) 459 } 460 } 461 return records 462 } 463 464 func validateRdsId(v interface{}, k string) (ws []string, errors []error) { 465 value := v.(string) 466 if !regexp.MustCompile(`^[0-9a-z-]+$`).MatchString(value) { 467 errors = append(errors, fmt.Errorf( 468 "only lowercase alphanumeric characters and hyphens allowed in %q", k)) 469 } 470 if !regexp.MustCompile(`^[a-z]`).MatchString(value) { 471 errors = append(errors, fmt.Errorf( 472 "first character of %q must be a letter", k)) 473 } 474 if regexp.MustCompile(`--`).MatchString(value) { 475 errors = append(errors, fmt.Errorf( 476 "%q cannot contain two consecutive hyphens", k)) 477 } 478 if regexp.MustCompile(`-$`).MatchString(value) { 479 errors = append(errors, fmt.Errorf( 480 "%q cannot end with a hyphen", k)) 481 } 482 return 483 } 484 485 func expandESClusterConfig(m map[string]interface{}) *elasticsearch.ElasticsearchClusterConfig { 486 config := elasticsearch.ElasticsearchClusterConfig{} 487 488 if v, ok := m["dedicated_master_enabled"]; ok { 489 isEnabled := v.(bool) 490 config.DedicatedMasterEnabled = aws.Bool(isEnabled) 491 492 if isEnabled { 493 if v, ok := m["dedicated_master_count"]; ok && v.(int) > 0 { 494 config.DedicatedMasterCount = aws.Int64(int64(v.(int))) 495 } 496 if v, ok := m["dedicated_master_type"]; ok && v.(string) != "" { 497 config.DedicatedMasterType = aws.String(v.(string)) 498 } 499 } 500 } 501 502 if v, ok := m["instance_count"]; ok { 503 config.InstanceCount = aws.Int64(int64(v.(int))) 504 } 505 if v, ok := m["instance_type"]; ok { 506 config.InstanceType = aws.String(v.(string)) 507 } 508 509 if v, ok := m["zone_awareness_enabled"]; ok { 510 config.ZoneAwarenessEnabled = aws.Bool(v.(bool)) 511 } 512 513 return &config 514 } 515 516 func flattenESClusterConfig(c *elasticsearch.ElasticsearchClusterConfig) []map[string]interface{} { 517 m := map[string]interface{}{} 518 519 if c.DedicatedMasterCount != nil { 520 m["dedicated_master_count"] = *c.DedicatedMasterCount 521 } 522 if c.DedicatedMasterEnabled != nil { 523 m["dedicated_master_enabled"] = *c.DedicatedMasterEnabled 524 } 525 if c.DedicatedMasterType != nil { 526 m["dedicated_master_type"] = *c.DedicatedMasterType 527 } 528 if c.InstanceCount != nil { 529 m["instance_count"] = *c.InstanceCount 530 } 531 if c.InstanceType != nil { 532 m["instance_type"] = *c.InstanceType 533 } 534 if c.ZoneAwarenessEnabled != nil { 535 m["zone_awareness_enabled"] = *c.ZoneAwarenessEnabled 536 } 537 538 return []map[string]interface{}{m} 539 } 540 541 func flattenESEBSOptions(o *elasticsearch.EBSOptions) []map[string]interface{} { 542 m := map[string]interface{}{} 543 544 if o.EBSEnabled != nil { 545 m["ebs_enabled"] = *o.EBSEnabled 546 } 547 if o.Iops != nil { 548 m["iops"] = *o.Iops 549 } 550 if o.VolumeSize != nil { 551 m["volume_size"] = *o.VolumeSize 552 } 553 if o.VolumeType != nil { 554 m["volume_type"] = *o.VolumeType 555 } 556 557 return []map[string]interface{}{m} 558 } 559 560 func expandESEBSOptions(m map[string]interface{}) *elasticsearch.EBSOptions { 561 options := elasticsearch.EBSOptions{} 562 563 if v, ok := m["ebs_enabled"]; ok { 564 options.EBSEnabled = aws.Bool(v.(bool)) 565 } 566 if v, ok := m["iops"]; ok && v.(int) > 0 { 567 options.Iops = aws.Int64(int64(v.(int))) 568 } 569 if v, ok := m["volume_size"]; ok && v.(int) > 0 { 570 options.VolumeSize = aws.Int64(int64(v.(int))) 571 } 572 if v, ok := m["volume_type"]; ok && v.(string) != "" { 573 options.VolumeType = aws.String(v.(string)) 574 } 575 576 return &options 577 } 578 579 func pointersMapToStringList(pointers map[string]*string) map[string]interface{} { 580 list := make(map[string]interface{}, len(pointers)) 581 for i, v := range pointers { 582 list[i] = *v 583 } 584 return list 585 } 586 587 func stringMapToPointers(m map[string]interface{}) map[string]*string { 588 list := make(map[string]*string, len(m)) 589 for i, v := range m { 590 list[i] = aws.String(v.(string)) 591 } 592 return list 593 } 594 595 func flattenDSVpcSettings( 596 s *directoryservice.DirectoryVpcSettingsDescription) []map[string]interface{} { 597 settings := make(map[string]interface{}, 0) 598 599 settings["subnet_ids"] = schema.NewSet(schema.HashString, flattenStringList(s.SubnetIds)) 600 settings["vpc_id"] = *s.VpcId 601 602 return []map[string]interface{}{settings} 603 }