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  }