terraform/builtin/providers/alicloud/resource_alicloud_slb.go

421 lines
11 KiB
Go

package alicloud
import (
"bytes"
"fmt"
"strings"
"github.com/denverdino/aliyungo/common"
"github.com/denverdino/aliyungo/slb"
"github.com/hashicorp/terraform/helper/hashcode"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"time"
)
func resourceAliyunSlb() *schema.Resource {
return &schema.Resource{
Create: resourceAliyunSlbCreate,
Read: resourceAliyunSlbRead,
Update: resourceAliyunSlbUpdate,
Delete: resourceAliyunSlbDelete,
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateSlbName,
Computed: true,
},
"internet": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
},
"vswitch_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"internet_charge_type": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: "paybytraffic",
ValidateFunc: validateSlbInternetChargeType,
},
"bandwidth": &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validateSlbBandwidth,
Computed: true,
},
"listener": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"instance_port": &schema.Schema{
Type: schema.TypeInt,
ValidateFunc: validateInstancePort,
Required: true,
},
"lb_port": &schema.Schema{
Type: schema.TypeInt,
ValidateFunc: validateInstancePort,
Required: true,
},
"lb_protocol": &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateInstanceProtocol,
Required: true,
},
"bandwidth": &schema.Schema{
Type: schema.TypeInt,
ValidateFunc: validateSlbListenerBandwidth,
Required: true,
},
//http
"scheduler": &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateSlbListenerScheduler,
Optional: true,
Default: "wrr",
},
"sticky_session": &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateSlbListenerStickySession,
Optional: true,
},
"sticky_session_type": &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateSlbListenerStickySessionType,
Optional: true,
},
"cookie": &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validateSlbListenerCookie,
Optional: true,
},
"PersistenceTimeout": &schema.Schema{
Type: schema.TypeInt,
ValidateFunc: validateSlbListenerPersistenceTimeout,
Optional: true,
Default: 0,
},
//https
"ssl_certificate_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
},
},
Set: resourceAliyunSlbListenerHash,
},
//deprecated
"instances": &schema.Schema{
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Optional: true,
Set: schema.HashString,
},
"address": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAliyunSlbCreate(d *schema.ResourceData, meta interface{}) error {
slbconn := meta.(*AliyunClient).slbconn
var slbName string
if v, ok := d.GetOk("name"); ok {
slbName = v.(string)
} else {
slbName = resource.PrefixedUniqueId("tf-lb-")
d.Set("name", slbName)
}
slbArgs := &slb.CreateLoadBalancerArgs{
RegionId: getRegion(d, meta),
LoadBalancerName: slbName,
}
if internet, ok := d.GetOk("internet"); ok && internet.(bool) {
slbArgs.AddressType = slb.InternetAddressType
d.Set("internet", true)
} else {
slbArgs.AddressType = slb.IntranetAddressType
d.Set("internet", false)
}
if v, ok := d.GetOk("internet_charge_type"); ok && v.(string) != "" {
slbArgs.InternetChargeType = slb.InternetChargeType(v.(string))
}
if v, ok := d.GetOk("bandwidth"); ok && v.(int) != 0 {
slbArgs.Bandwidth = v.(int)
}
if v, ok := d.GetOk("vswitch_id"); ok && v.(string) != "" {
slbArgs.VSwitchId = v.(string)
}
slb, err := slbconn.CreateLoadBalancer(slbArgs)
if err != nil {
return err
}
d.SetId(slb.LoadBalancerId)
return resourceAliyunSlbUpdate(d, meta)
}
func resourceAliyunSlbRead(d *schema.ResourceData, meta interface{}) error {
slbconn := meta.(*AliyunClient).slbconn
loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id())
if err != nil {
if notFoundError(err) {
d.SetId("")
return nil
}
return err
}
d.Set("name", loadBalancer.LoadBalancerName)
if loadBalancer.AddressType == slb.InternetAddressType {
d.Set("internal", true)
} else {
d.Set("internal", false)
}
d.Set("internet_charge_type", loadBalancer.InternetChargeType)
d.Set("bandwidth", loadBalancer.Bandwidth)
d.Set("vswitch_id", loadBalancer.VSwitchId)
d.Set("address", loadBalancer.Address)
return nil
}
func resourceAliyunSlbUpdate(d *schema.ResourceData, meta interface{}) error {
slbconn := meta.(*AliyunClient).slbconn
d.Partial(true)
if d.HasChange("name") {
err := slbconn.SetLoadBalancerName(d.Id(), d.Get("name").(string))
if err != nil {
return err
}
d.SetPartial("name")
}
if d.Get("internet") == true && d.Get("internet_charge_type") == "paybybandwidth" {
//don't intranet web and paybybandwidth, then can modify bandwidth
if d.HasChange("bandwidth") {
args := &slb.ModifyLoadBalancerInternetSpecArgs{
LoadBalancerId: d.Id(),
Bandwidth: d.Get("bandwidth").(int),
}
err := slbconn.ModifyLoadBalancerInternetSpec(args)
if err != nil {
return err
}
d.SetPartial("bandwidth")
}
}
if d.HasChange("listener") {
o, n := d.GetChange("listener")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove, _ := expandListeners(os.Difference(ns).List())
add, _ := expandListeners(ns.Difference(os).List())
if len(remove) > 0 {
for _, listener := range remove {
err := slbconn.DeleteLoadBalancerListener(d.Id(), listener.LoadBalancerPort)
if err != nil {
return fmt.Errorf("Failure removing outdated SLB listeners: %#v", err)
}
}
}
if len(add) > 0 {
for _, listener := range add {
err := createListener(slbconn, d.Id(), listener)
if err != nil {
return fmt.Errorf("Failure add SLB listeners: %#v", err)
}
}
}
d.SetPartial("listener")
}
// If we currently have instances, or did have instances,
// we want to figure out what to add and remove from the load
// balancer
if d.HasChange("instances") {
o, n := d.GetChange("instances")
os := o.(*schema.Set)
ns := n.(*schema.Set)
remove := expandBackendServers(os.Difference(ns).List())
add := expandBackendServers(ns.Difference(os).List())
if len(add) > 0 {
_, err := slbconn.AddBackendServers(d.Id(), add)
if err != nil {
return err
}
}
if len(remove) > 0 {
removeBackendServers := make([]string, 0, len(remove))
for _, e := range remove {
removeBackendServers = append(removeBackendServers, e.ServerId)
}
_, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers)
if err != nil {
return err
}
}
d.SetPartial("instances")
}
d.Partial(false)
return resourceAliyunSlbRead(d, meta)
}
func resourceAliyunSlbDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AliyunClient).slbconn
return resource.Retry(5*time.Minute, func() *resource.RetryError {
err := conn.DeleteLoadBalancer(d.Id())
if err != nil {
return resource.NonRetryableError(err)
}
loadBalancer, err := conn.DescribeLoadBalancerAttribute(d.Id())
if err != nil {
e, _ := err.(*common.Error)
if e.ErrorResponse.Code == LoadBalancerNotFound {
return nil
}
return resource.NonRetryableError(err)
}
if loadBalancer != nil {
return resource.RetryableError(fmt.Errorf("LoadBalancer in use - trying again while it deleted."))
}
return nil
})
}
func resourceAliyunSlbListenerHash(v interface{}) int {
var buf bytes.Buffer
m := v.(map[string]interface{})
buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
buf.WriteString(fmt.Sprintf("%s-",
strings.ToLower(m["lb_protocol"].(string))))
buf.WriteString(fmt.Sprintf("%d-", m["bandwidth"].(int)))
if v, ok := m["ssl_certificate_id"]; ok {
buf.WriteString(fmt.Sprintf("%s-", v.(string)))
}
return hashcode.String(buf.String())
}
func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
if listener.Protocol == strings.ToLower("tcp") {
args := &slb.CreateLoadBalancerTCPListenerArgs{
LoadBalancerId: loadBalancerId,
ListenerPort: listener.LoadBalancerPort,
BackendServerPort: listener.InstancePort,
Bandwidth: listener.Bandwidth,
}
if err := conn.CreateLoadBalancerTCPListener(args); err != nil {
return err
}
}
if listener.Protocol == strings.ToLower("http") {
args := &slb.CreateLoadBalancerHTTPListenerArgs{
LoadBalancerId: loadBalancerId,
ListenerPort: listener.LoadBalancerPort,
BackendServerPort: listener.InstancePort,
Bandwidth: listener.Bandwidth,
StickySession: slb.OffFlag,
HealthCheck: slb.OffFlag,
}
if err := conn.CreateLoadBalancerHTTPListener(args); err != nil {
return err
}
}
if listener.Protocol == strings.ToLower("https") {
args := &slb.CreateLoadBalancerHTTPSListenerArgs{
HTTPListenerType: slb.HTTPListenerType{
LoadBalancerId: loadBalancerId,
ListenerPort: listener.LoadBalancerPort,
BackendServerPort: listener.InstancePort,
Bandwidth: listener.Bandwidth,
StickySession: slb.OffFlag,
HealthCheck: slb.OffFlag,
},
}
if listener.SSLCertificateId == "" {
return fmt.Errorf("Server Certificated Id cann't be null")
}
args.ServerCertificateId = listener.SSLCertificateId
if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
return err
}
}
if listener.Protocol == strings.ToLower("udp") {
args := &slb.CreateLoadBalancerUDPListenerArgs{
LoadBalancerId: loadBalancerId,
ListenerPort: listener.LoadBalancerPort,
BackendServerPort: listener.InstancePort,
Bandwidth: listener.Bandwidth,
}
if err := conn.CreateLoadBalancerUDPListener(args); err != nil {
return err
}
}
if err := conn.StartLoadBalancerListener(loadBalancerId, listener.LoadBalancerPort); err != nil {
return err
}
return nil
}