421 lines
11 KiB
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
|
|
}
|