terraform/builtin/providers/aws/resource_aws_proxy_protocol...

271 lines
8.0 KiB
Go

package aws
import (
"fmt"
"log"
"strconv"
"strings"
"github.com/awslabs/aws-sdk-go/aws"
"github.com/awslabs/aws-sdk-go/service/elb"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/schema"
)
func resourceAwsProxyProtocolPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceAwsProxyProtocolPolicyCreate,
Read: resourceAwsProxyProtocolPolicyRead,
Update: resourceAwsProxyProtocolPolicyUpdate,
Delete: resourceAwsProxyProtocolPolicyDelete,
Schema: map[string]*schema.Schema{
"load_balancer": &schema.Schema{
Type: schema.TypeString,
Required: true,
},
"instance_ports": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Required: true,
Set: func(v interface{}) int {
return hashcode.String(v.(string))
},
},
},
}
}
func resourceAwsProxyProtocolPolicyCreate(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn
elbname := aws.String(d.Get("load_balancer").(string))
input := &elb.CreateLoadBalancerPolicyInput{
LoadBalancerName: elbname,
PolicyAttributes: []*elb.PolicyAttribute{
&elb.PolicyAttribute{
AttributeName: aws.String("ProxyProtocol"),
AttributeValue: aws.String("True"),
},
},
PolicyName: aws.String("TFEnableProxyProtocol"),
PolicyTypeName: aws.String("ProxyProtocolPolicyType"),
}
// Create a policy
log.Printf("[DEBUG] ELB create a policy %s from policy type %s",
*input.PolicyName, *input.PolicyTypeName)
if _, err := elbconn.CreateLoadBalancerPolicy(input); err != nil {
return fmt.Errorf("Error creating a policy %s: %s",
*input.PolicyName, err)
}
// Assign the policy name for use later
d.Partial(true)
d.SetId(fmt.Sprintf("%s:%s", *elbname, *input.PolicyName))
d.SetPartial("load_balancer")
log.Printf("[INFO] ELB PolicyName: %s", *input.PolicyName)
return resourceAwsProxyProtocolPolicyUpdate(d, meta)
}
func resourceAwsProxyProtocolPolicyRead(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn
elbname := aws.String(d.Get("load_balancer").(string))
// Retrieve the current ELB policies for updating the state
req := &elb.DescribeLoadBalancersInput{
LoadBalancerNames: []*string{elbname},
}
resp, err := elbconn.DescribeLoadBalancers(req)
if err != nil {
if isLoadBalancerNotFound(err) {
// The ELB is gone now, so just remove it from the state
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving ELB attributes: %s", err)
}
backends := flattenBackendPolicies(resp.LoadBalancerDescriptions[0].BackendServerDescriptions)
ports := []*string{}
for ip := range backends {
ipstr := strconv.Itoa(int(ip))
ports = append(ports, &ipstr)
}
d.Set("instance_ports", ports)
d.Set("load_balancer", *elbname)
return nil
}
func resourceAwsProxyProtocolPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn
elbname := aws.String(d.Get("load_balancer").(string))
// Retrieve the current ELB policies for updating the state
req := &elb.DescribeLoadBalancersInput{
LoadBalancerNames: []*string{elbname},
}
resp, err := elbconn.DescribeLoadBalancers(req)
if err != nil {
if isLoadBalancerNotFound(err) {
// The ELB is gone now, so just remove it from the state
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving ELB attributes: %s", err)
}
backends := flattenBackendPolicies(resp.LoadBalancerDescriptions[0].BackendServerDescriptions)
_, policyName := resourceAwsProxyProtocolPolicyParseId(d.Id())
d.Partial(true)
if d.HasChange("instance_ports") {
o, n := d.GetChange("instance_ports")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := os.Difference(ns).List()
add := ns.Difference(os).List()
inputs := []*elb.SetLoadBalancerPoliciesForBackendServerInput{}
i, err := resourceAwsProxyProtocolPolicyRemove(policyName, remove, backends)
if err != nil {
return err
}
inputs = append(inputs, i...)
i, err = resourceAwsProxyProtocolPolicyAdd(policyName, add, backends)
if err != nil {
return err
}
inputs = append(inputs, i...)
for _, input := range inputs {
input.LoadBalancerName = elbname
if _, err := elbconn.SetLoadBalancerPoliciesForBackendServer(input); err != nil {
return fmt.Errorf("Error setting policy for backend: %s", err)
}
}
d.SetPartial("instance_ports")
}
return resourceAwsProxyProtocolPolicyRead(d, meta)
}
func resourceAwsProxyProtocolPolicyDelete(d *schema.ResourceData, meta interface{}) error {
elbconn := meta.(*AWSClient).elbconn
elbname := aws.String(d.Get("load_balancer").(string))
// Retrieve the current ELB policies for updating the state
req := &elb.DescribeLoadBalancersInput{
LoadBalancerNames: []*string{elbname},
}
var err error
resp, err := elbconn.DescribeLoadBalancers(req)
if err != nil {
if isLoadBalancerNotFound(err) {
// The ELB is gone now, so just remove it from the state
d.SetId("")
return nil
}
return fmt.Errorf("Error retrieving ELB attributes: %s", err)
}
backends := flattenBackendPolicies(resp.LoadBalancerDescriptions[0].BackendServerDescriptions)
ports := d.Get("instance_ports").(*schema.Set).List()
_, policyName := resourceAwsProxyProtocolPolicyParseId(d.Id())
inputs, err := resourceAwsProxyProtocolPolicyRemove(policyName, ports, backends)
if err != nil {
return fmt.Errorf("Error detaching a policy from backend: %s", err)
}
for _, input := range inputs {
input.LoadBalancerName = elbname
if _, err := elbconn.SetLoadBalancerPoliciesForBackendServer(input); err != nil {
return fmt.Errorf("Error setting policy for backend: %s", err)
}
}
pOpt := &elb.DeleteLoadBalancerPolicyInput{
LoadBalancerName: elbname,
PolicyName: aws.String(policyName),
}
if _, err := elbconn.DeleteLoadBalancerPolicy(pOpt); err != nil {
return fmt.Errorf("Error removing a policy from load balancer: %s", err)
}
return nil
}
func resourceAwsProxyProtocolPolicyRemove(policyName string, ports []interface{}, backends map[int64][]string) ([]*elb.SetLoadBalancerPoliciesForBackendServerInput, error) {
inputs := make([]*elb.SetLoadBalancerPoliciesForBackendServerInput, 0, len(ports))
for _, p := range ports {
ip, err := strconv.ParseInt(p.(string), 10, 64)
if err != nil {
return nil, fmt.Errorf("Error detaching the policy: %s", err)
}
newPolicies := []*string{}
curPolicies, found := backends[ip]
if !found {
// No policy for this instance port found, just skip it.
continue
}
for _, policy := range curPolicies {
if policy == policyName {
// remove the policy
continue
}
newPolicies = append(newPolicies, &policy)
}
inputs = append(inputs, &elb.SetLoadBalancerPoliciesForBackendServerInput{
InstancePort: &ip,
PolicyNames: newPolicies,
})
}
return inputs, nil
}
func resourceAwsProxyProtocolPolicyAdd(policyName string, ports []interface{}, backends map[int64][]string) ([]*elb.SetLoadBalancerPoliciesForBackendServerInput, error) {
inputs := make([]*elb.SetLoadBalancerPoliciesForBackendServerInput, 0, len(ports))
for _, p := range ports {
ip, err := strconv.ParseInt(p.(string), 10, 64)
if err != nil {
return nil, fmt.Errorf("Error attaching the policy: %s", err)
}
newPolicies := []*string{}
curPolicies := backends[ip]
for _, p := range curPolicies {
if p == policyName {
// Just remove it for now. It will be back later.
continue
} else {
newPolicies = append(newPolicies, &p)
}
}
newPolicies = append(newPolicies, aws.String(policyName))
inputs = append(inputs, &elb.SetLoadBalancerPoliciesForBackendServerInput{
InstancePort: &ip,
PolicyNames: newPolicies,
})
}
return inputs, nil
}
// resourceAwsProxyProtocolPolicyParseId takes an ID and parses it into
// it's constituent parts. You need two axes (LB name, policy name)
// to create or identify a proxy protocol policy in AWS's API.
func resourceAwsProxyProtocolPolicyParseId(id string) (string, string) {
parts := strings.SplitN(id, ":", 2)
return parts[0], parts[1]
}