github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/alicloud/resource_alicloud_slb.go (about)

     1  package alicloud
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/denverdino/aliyungo/common"
     9  	"github.com/denverdino/aliyungo/slb"
    10  	"github.com/hashicorp/terraform/helper/hashcode"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  	"time"
    14  )
    15  
    16  func resourceAliyunSlb() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceAliyunSlbCreate,
    19  		Read:   resourceAliyunSlbRead,
    20  		Update: resourceAliyunSlbUpdate,
    21  		Delete: resourceAliyunSlbDelete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"name": &schema.Schema{
    25  				Type:         schema.TypeString,
    26  				Optional:     true,
    27  				ValidateFunc: validateSlbName,
    28  				Computed:     true,
    29  			},
    30  
    31  			"internet": &schema.Schema{
    32  				Type:     schema.TypeBool,
    33  				Optional: true,
    34  				ForceNew: true,
    35  			},
    36  
    37  			"vswitch_id": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Optional: true,
    40  				ForceNew: true,
    41  			},
    42  
    43  			"internet_charge_type": &schema.Schema{
    44  				Type:         schema.TypeString,
    45  				Optional:     true,
    46  				ForceNew:     true,
    47  				Default:      "paybytraffic",
    48  				ValidateFunc: validateSlbInternetChargeType,
    49  			},
    50  
    51  			"bandwidth": &schema.Schema{
    52  				Type:         schema.TypeInt,
    53  				Optional:     true,
    54  				ValidateFunc: validateSlbBandwidth,
    55  				Computed:     true,
    56  			},
    57  
    58  			"listener": &schema.Schema{
    59  				Type:     schema.TypeSet,
    60  				Optional: true,
    61  				Elem: &schema.Resource{
    62  					Schema: map[string]*schema.Schema{
    63  						"instance_port": &schema.Schema{
    64  							Type:         schema.TypeInt,
    65  							ValidateFunc: validateInstancePort,
    66  							Required:     true,
    67  						},
    68  
    69  						"lb_port": &schema.Schema{
    70  							Type:         schema.TypeInt,
    71  							ValidateFunc: validateInstancePort,
    72  							Required:     true,
    73  						},
    74  
    75  						"lb_protocol": &schema.Schema{
    76  							Type:         schema.TypeString,
    77  							ValidateFunc: validateInstanceProtocol,
    78  							Required:     true,
    79  						},
    80  
    81  						"bandwidth": &schema.Schema{
    82  							Type:         schema.TypeInt,
    83  							ValidateFunc: validateSlbListenerBandwidth,
    84  							Required:     true,
    85  						},
    86  						//http
    87  						"scheduler": &schema.Schema{
    88  							Type:         schema.TypeString,
    89  							ValidateFunc: validateSlbListenerScheduler,
    90  							Optional:     true,
    91  							Default:      "wrr",
    92  						},
    93  
    94  						"sticky_session": &schema.Schema{
    95  							Type:         schema.TypeString,
    96  							ValidateFunc: validateSlbListenerStickySession,
    97  							Optional:     true,
    98  						},
    99  						"sticky_session_type": &schema.Schema{
   100  							Type:         schema.TypeString,
   101  							ValidateFunc: validateSlbListenerStickySessionType,
   102  							Optional:     true,
   103  						},
   104  						"cookie": &schema.Schema{
   105  							Type:         schema.TypeString,
   106  							ValidateFunc: validateSlbListenerCookie,
   107  							Optional:     true,
   108  						},
   109  						"PersistenceTimeout": &schema.Schema{
   110  							Type:         schema.TypeInt,
   111  							ValidateFunc: validateSlbListenerPersistenceTimeout,
   112  							Optional:     true,
   113  							Default:      0,
   114  						},
   115  						//https
   116  						"ssl_certificate_id": &schema.Schema{
   117  							Type:     schema.TypeString,
   118  							Optional: true,
   119  						},
   120  					},
   121  				},
   122  				Set: resourceAliyunSlbListenerHash,
   123  			},
   124  
   125  			//deprecated
   126  			"instances": &schema.Schema{
   127  				Type:     schema.TypeSet,
   128  				Elem:     &schema.Schema{Type: schema.TypeString},
   129  				Optional: true,
   130  				Set:      schema.HashString,
   131  			},
   132  
   133  			"address": &schema.Schema{
   134  				Type:     schema.TypeString,
   135  				Computed: true,
   136  			},
   137  		},
   138  	}
   139  }
   140  
   141  func resourceAliyunSlbCreate(d *schema.ResourceData, meta interface{}) error {
   142  
   143  	slbconn := meta.(*AliyunClient).slbconn
   144  
   145  	var slbName string
   146  	if v, ok := d.GetOk("name"); ok {
   147  		slbName = v.(string)
   148  	} else {
   149  		slbName = resource.PrefixedUniqueId("tf-lb-")
   150  		d.Set("name", slbName)
   151  	}
   152  
   153  	slbArgs := &slb.CreateLoadBalancerArgs{
   154  		RegionId:         getRegion(d, meta),
   155  		LoadBalancerName: slbName,
   156  	}
   157  
   158  	if internet, ok := d.GetOk("internet"); ok && internet.(bool) {
   159  		slbArgs.AddressType = slb.InternetAddressType
   160  		d.Set("internet", true)
   161  	} else {
   162  		slbArgs.AddressType = slb.IntranetAddressType
   163  		d.Set("internet", false)
   164  	}
   165  
   166  	if v, ok := d.GetOk("internet_charge_type"); ok && v.(string) != "" {
   167  		slbArgs.InternetChargeType = slb.InternetChargeType(v.(string))
   168  	}
   169  
   170  	if v, ok := d.GetOk("bandwidth"); ok && v.(int) != 0 {
   171  		slbArgs.Bandwidth = v.(int)
   172  	}
   173  
   174  	if v, ok := d.GetOk("vswitch_id"); ok && v.(string) != "" {
   175  		slbArgs.VSwitchId = v.(string)
   176  	}
   177  	slb, err := slbconn.CreateLoadBalancer(slbArgs)
   178  	if err != nil {
   179  		return err
   180  	}
   181  
   182  	d.SetId(slb.LoadBalancerId)
   183  
   184  	return resourceAliyunSlbUpdate(d, meta)
   185  }
   186  
   187  func resourceAliyunSlbRead(d *schema.ResourceData, meta interface{}) error {
   188  	slbconn := meta.(*AliyunClient).slbconn
   189  	loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id())
   190  	if err != nil {
   191  		if notFoundError(err) {
   192  			d.SetId("")
   193  			return nil
   194  		}
   195  
   196  		return err
   197  	}
   198  
   199  	d.Set("name", loadBalancer.LoadBalancerName)
   200  
   201  	if loadBalancer.AddressType == slb.InternetAddressType {
   202  		d.Set("internal", true)
   203  	} else {
   204  		d.Set("internal", false)
   205  	}
   206  	d.Set("internet_charge_type", loadBalancer.InternetChargeType)
   207  	d.Set("bandwidth", loadBalancer.Bandwidth)
   208  	d.Set("vswitch_id", loadBalancer.VSwitchId)
   209  	d.Set("address", loadBalancer.Address)
   210  
   211  	return nil
   212  }
   213  
   214  func resourceAliyunSlbUpdate(d *schema.ResourceData, meta interface{}) error {
   215  
   216  	slbconn := meta.(*AliyunClient).slbconn
   217  
   218  	d.Partial(true)
   219  
   220  	if d.HasChange("name") {
   221  		err := slbconn.SetLoadBalancerName(d.Id(), d.Get("name").(string))
   222  		if err != nil {
   223  			return err
   224  		}
   225  
   226  		d.SetPartial("name")
   227  	}
   228  
   229  	if d.Get("internet") == true && d.Get("internet_charge_type") == "paybybandwidth" {
   230  		//don't intranet web and paybybandwidth, then can modify bandwidth
   231  		if d.HasChange("bandwidth") {
   232  			args := &slb.ModifyLoadBalancerInternetSpecArgs{
   233  				LoadBalancerId: d.Id(),
   234  				Bandwidth:      d.Get("bandwidth").(int),
   235  			}
   236  			err := slbconn.ModifyLoadBalancerInternetSpec(args)
   237  			if err != nil {
   238  				return err
   239  			}
   240  
   241  			d.SetPartial("bandwidth")
   242  		}
   243  	}
   244  
   245  	if d.HasChange("listener") {
   246  		o, n := d.GetChange("listener")
   247  		os := o.(*schema.Set)
   248  		ns := n.(*schema.Set)
   249  
   250  		remove, _ := expandListeners(os.Difference(ns).List())
   251  		add, _ := expandListeners(ns.Difference(os).List())
   252  
   253  		if len(remove) > 0 {
   254  			for _, listener := range remove {
   255  				err := slbconn.DeleteLoadBalancerListener(d.Id(), listener.LoadBalancerPort)
   256  				if err != nil {
   257  					return fmt.Errorf("Failure removing outdated SLB listeners: %#v", err)
   258  				}
   259  			}
   260  		}
   261  
   262  		if len(add) > 0 {
   263  			for _, listener := range add {
   264  				err := createListener(slbconn, d.Id(), listener)
   265  				if err != nil {
   266  					return fmt.Errorf("Failure add SLB listeners: %#v", err)
   267  				}
   268  			}
   269  		}
   270  
   271  		d.SetPartial("listener")
   272  	}
   273  
   274  	// If we currently have instances, or did have instances,
   275  	// we want to figure out what to add and remove from the load
   276  	// balancer
   277  	if d.HasChange("instances") {
   278  		o, n := d.GetChange("instances")
   279  		os := o.(*schema.Set)
   280  		ns := n.(*schema.Set)
   281  		remove := expandBackendServers(os.Difference(ns).List())
   282  		add := expandBackendServers(ns.Difference(os).List())
   283  
   284  		if len(add) > 0 {
   285  			_, err := slbconn.AddBackendServers(d.Id(), add)
   286  			if err != nil {
   287  				return err
   288  			}
   289  		}
   290  		if len(remove) > 0 {
   291  			removeBackendServers := make([]string, 0, len(remove))
   292  			for _, e := range remove {
   293  				removeBackendServers = append(removeBackendServers, e.ServerId)
   294  			}
   295  			_, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers)
   296  			if err != nil {
   297  				return err
   298  			}
   299  		}
   300  
   301  		d.SetPartial("instances")
   302  	}
   303  
   304  	d.Partial(false)
   305  
   306  	return resourceAliyunSlbRead(d, meta)
   307  }
   308  
   309  func resourceAliyunSlbDelete(d *schema.ResourceData, meta interface{}) error {
   310  	conn := meta.(*AliyunClient).slbconn
   311  
   312  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   313  		err := conn.DeleteLoadBalancer(d.Id())
   314  
   315  		if err != nil {
   316  			return resource.NonRetryableError(err)
   317  		}
   318  
   319  		loadBalancer, err := conn.DescribeLoadBalancerAttribute(d.Id())
   320  		if err != nil {
   321  			e, _ := err.(*common.Error)
   322  			if e.ErrorResponse.Code == LoadBalancerNotFound {
   323  				return nil
   324  			}
   325  			return resource.NonRetryableError(err)
   326  		}
   327  		if loadBalancer != nil {
   328  			return resource.RetryableError(fmt.Errorf("LoadBalancer in use - trying again while it deleted."))
   329  		}
   330  		return nil
   331  	})
   332  }
   333  
   334  func resourceAliyunSlbListenerHash(v interface{}) int {
   335  	var buf bytes.Buffer
   336  	m := v.(map[string]interface{})
   337  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   338  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   339  	buf.WriteString(fmt.Sprintf("%s-",
   340  		strings.ToLower(m["lb_protocol"].(string))))
   341  
   342  	buf.WriteString(fmt.Sprintf("%d-", m["bandwidth"].(int)))
   343  
   344  	if v, ok := m["ssl_certificate_id"]; ok {
   345  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   346  	}
   347  
   348  	return hashcode.String(buf.String())
   349  }
   350  
   351  func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
   352  	if listener.Protocol == strings.ToLower("tcp") {
   353  		args := &slb.CreateLoadBalancerTCPListenerArgs{
   354  			LoadBalancerId:    loadBalancerId,
   355  			ListenerPort:      listener.LoadBalancerPort,
   356  			BackendServerPort: listener.InstancePort,
   357  			Bandwidth:         listener.Bandwidth,
   358  		}
   359  		if err := conn.CreateLoadBalancerTCPListener(args); err != nil {
   360  			return err
   361  		}
   362  	}
   363  
   364  	if listener.Protocol == strings.ToLower("http") {
   365  		args := &slb.CreateLoadBalancerHTTPListenerArgs{
   366  			LoadBalancerId:    loadBalancerId,
   367  			ListenerPort:      listener.LoadBalancerPort,
   368  			BackendServerPort: listener.InstancePort,
   369  			Bandwidth:         listener.Bandwidth,
   370  			StickySession:     slb.OffFlag,
   371  			HealthCheck:       slb.OffFlag,
   372  		}
   373  
   374  		if err := conn.CreateLoadBalancerHTTPListener(args); err != nil {
   375  			return err
   376  		}
   377  	}
   378  
   379  	if listener.Protocol == strings.ToLower("https") {
   380  		args := &slb.CreateLoadBalancerHTTPSListenerArgs{
   381  
   382  			HTTPListenerType: slb.HTTPListenerType{
   383  				LoadBalancerId:    loadBalancerId,
   384  				ListenerPort:      listener.LoadBalancerPort,
   385  				BackendServerPort: listener.InstancePort,
   386  				Bandwidth:         listener.Bandwidth,
   387  				StickySession:     slb.OffFlag,
   388  				HealthCheck:       slb.OffFlag,
   389  			},
   390  		}
   391  		if listener.SSLCertificateId == "" {
   392  			return fmt.Errorf("Server Certificated Id cann't be null")
   393  		}
   394  
   395  		args.ServerCertificateId = listener.SSLCertificateId
   396  
   397  		if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
   398  			return err
   399  		}
   400  	}
   401  
   402  	if listener.Protocol == strings.ToLower("udp") {
   403  		args := &slb.CreateLoadBalancerUDPListenerArgs{
   404  			LoadBalancerId:    loadBalancerId,
   405  			ListenerPort:      listener.LoadBalancerPort,
   406  			BackendServerPort: listener.InstancePort,
   407  			Bandwidth:         listener.Bandwidth,
   408  		}
   409  
   410  		if err := conn.CreateLoadBalancerUDPListener(args); err != nil {
   411  			return err
   412  		}
   413  	}
   414  
   415  	if err := conn.StartLoadBalancerListener(loadBalancerId, listener.LoadBalancerPort); err != nil {
   416  		return err
   417  	}
   418  
   419  	return nil
   420  }