github.com/cbroglie/terraform@v0.7.0-rc3.0.20170410193827-735dfc416d46/builtin/providers/alicloud/resource_alicloud_slb.go (about)

     1  package alicloud
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"errors"
     9  	"github.com/denverdino/aliyungo/common"
    10  	"github.com/denverdino/aliyungo/slb"
    11  	"github.com/hashicorp/terraform/helper/hashcode"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  	"time"
    15  )
    16  
    17  func resourceAliyunSlb() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceAliyunSlbCreate,
    20  		Read:   resourceAliyunSlbRead,
    21  		Update: resourceAliyunSlbUpdate,
    22  		Delete: resourceAliyunSlbDelete,
    23  
    24  		Schema: map[string]*schema.Schema{
    25  			"name": &schema.Schema{
    26  				Type:         schema.TypeString,
    27  				Optional:     true,
    28  				ValidateFunc: validateSlbName,
    29  				Computed:     true,
    30  			},
    31  
    32  			"internet": &schema.Schema{
    33  				Type:     schema.TypeBool,
    34  				Optional: true,
    35  				ForceNew: true,
    36  			},
    37  
    38  			"vswitch_id": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Optional: true,
    41  				ForceNew: true,
    42  			},
    43  
    44  			"internet_charge_type": &schema.Schema{
    45  				Type:         schema.TypeString,
    46  				Optional:     true,
    47  				ForceNew:     true,
    48  				Default:      "paybytraffic",
    49  				ValidateFunc: validateSlbInternetChargeType,
    50  			},
    51  
    52  			"bandwidth": &schema.Schema{
    53  				Type:         schema.TypeInt,
    54  				Optional:     true,
    55  				ValidateFunc: validateSlbBandwidth,
    56  				Computed:     true,
    57  			},
    58  
    59  			"listener": &schema.Schema{
    60  				Type:     schema.TypeSet,
    61  				Optional: true,
    62  				Elem: &schema.Resource{
    63  					Schema: map[string]*schema.Schema{
    64  						"instance_port": &schema.Schema{
    65  							Type:         schema.TypeInt,
    66  							ValidateFunc: validateInstancePort,
    67  							Required:     true,
    68  						},
    69  
    70  						"lb_port": &schema.Schema{
    71  							Type:         schema.TypeInt,
    72  							ValidateFunc: validateInstancePort,
    73  							Required:     true,
    74  						},
    75  
    76  						"lb_protocol": &schema.Schema{
    77  							Type:         schema.TypeString,
    78  							ValidateFunc: validateInstanceProtocol,
    79  							Required:     true,
    80  						},
    81  
    82  						"bandwidth": &schema.Schema{
    83  							Type:         schema.TypeInt,
    84  							ValidateFunc: validateSlbListenerBandwidth,
    85  							Required:     true,
    86  						},
    87  						"scheduler": &schema.Schema{
    88  							Type:         schema.TypeString,
    89  							ValidateFunc: validateSlbListenerScheduler,
    90  							Optional:     true,
    91  							Default:      slb.WRRScheduler,
    92  						},
    93  						//http & https
    94  						"sticky_session": &schema.Schema{
    95  							Type: schema.TypeString,
    96  							ValidateFunc: validateAllowedStringValue([]string{
    97  								string(slb.OnFlag),
    98  								string(slb.OffFlag)}),
    99  							Optional: true,
   100  							Default:  slb.OffFlag,
   101  						},
   102  						//http & https
   103  						"sticky_session_type": &schema.Schema{
   104  							Type: schema.TypeString,
   105  							ValidateFunc: validateAllowedStringValue([]string{
   106  								string(slb.InsertStickySessionType),
   107  								string(slb.ServerStickySessionType)}),
   108  							Optional: true,
   109  						},
   110  						//http & https
   111  						"cookie_timeout": &schema.Schema{
   112  							Type:         schema.TypeInt,
   113  							ValidateFunc: validateSlbListenerCookieTimeout,
   114  							Optional:     true,
   115  						},
   116  						//http & https
   117  						"cookie": &schema.Schema{
   118  							Type:         schema.TypeString,
   119  							ValidateFunc: validateSlbListenerCookie,
   120  							Optional:     true,
   121  						},
   122  						//tcp & udp
   123  						"persistence_timeout": &schema.Schema{
   124  							Type:         schema.TypeInt,
   125  							ValidateFunc: validateSlbListenerPersistenceTimeout,
   126  							Optional:     true,
   127  							Default:      0,
   128  						},
   129  						//http & https
   130  						"health_check": &schema.Schema{
   131  							Type: schema.TypeString,
   132  							ValidateFunc: validateAllowedStringValue([]string{
   133  								string(slb.OnFlag),
   134  								string(slb.OffFlag)}),
   135  							Optional: true,
   136  							Default:  slb.OffFlag,
   137  						},
   138  						//tcp
   139  						"health_check_type": &schema.Schema{
   140  							Type: schema.TypeString,
   141  							ValidateFunc: validateAllowedStringValue([]string{
   142  								string(slb.TCPHealthCheckType),
   143  								string(slb.HTTPHealthCheckType)}),
   144  							Optional: true,
   145  							Default:  slb.TCPHealthCheckType,
   146  						},
   147  						//http & https & tcp
   148  						"health_check_domain": &schema.Schema{
   149  							Type:         schema.TypeString,
   150  							ValidateFunc: validateSlbListenerHealthCheckDomain,
   151  							Optional:     true,
   152  						},
   153  						//http & https & tcp
   154  						"health_check_uri": &schema.Schema{
   155  							Type:         schema.TypeString,
   156  							ValidateFunc: validateSlbListenerHealthCheckUri,
   157  							Optional:     true,
   158  						},
   159  						"health_check_connect_port": &schema.Schema{
   160  							Type:         schema.TypeInt,
   161  							ValidateFunc: validateSlbListenerHealthCheckConnectPort,
   162  							Optional:     true,
   163  						},
   164  						"healthy_threshold": &schema.Schema{
   165  							Type:         schema.TypeInt,
   166  							ValidateFunc: validateIntegerInRange(1, 10),
   167  							Optional:     true,
   168  						},
   169  						"unhealthy_threshold": &schema.Schema{
   170  							Type:         schema.TypeInt,
   171  							ValidateFunc: validateIntegerInRange(1, 10),
   172  							Optional:     true,
   173  						},
   174  
   175  						"health_check_timeout": &schema.Schema{
   176  							Type:         schema.TypeInt,
   177  							ValidateFunc: validateIntegerInRange(1, 50),
   178  							Optional:     true,
   179  						},
   180  						"health_check_interval": &schema.Schema{
   181  							Type:         schema.TypeInt,
   182  							ValidateFunc: validateIntegerInRange(1, 5),
   183  							Optional:     true,
   184  						},
   185  						//http & https & tcp
   186  						"health_check_http_code": &schema.Schema{
   187  							Type: schema.TypeString,
   188  							ValidateFunc: validateAllowedSplitStringValue([]string{
   189  								string(slb.HTTP_2XX),
   190  								string(slb.HTTP_3XX),
   191  								string(slb.HTTP_4XX),
   192  								string(slb.HTTP_5XX)}, ","),
   193  							Optional: true,
   194  						},
   195  						//https
   196  						"ssl_certificate_id": &schema.Schema{
   197  							Type:     schema.TypeString,
   198  							Optional: true,
   199  						},
   200  						//https
   201  						//"ca_certificate_id": &schema.Schema{
   202  						//	Type:     schema.TypeString,
   203  						//	Optional: true,
   204  						//},
   205  					},
   206  				},
   207  				Set: resourceAliyunSlbListenerHash,
   208  			},
   209  
   210  			//deprecated
   211  			"instances": &schema.Schema{
   212  				Type:     schema.TypeSet,
   213  				Elem:     &schema.Schema{Type: schema.TypeString},
   214  				Optional: true,
   215  				Set:      schema.HashString,
   216  			},
   217  
   218  			"address": &schema.Schema{
   219  				Type:     schema.TypeString,
   220  				Computed: true,
   221  			},
   222  		},
   223  	}
   224  }
   225  
   226  func resourceAliyunSlbCreate(d *schema.ResourceData, meta interface{}) error {
   227  
   228  	slbconn := meta.(*AliyunClient).slbconn
   229  
   230  	var slbName string
   231  	if v, ok := d.GetOk("name"); ok {
   232  		slbName = v.(string)
   233  	} else {
   234  		slbName = resource.PrefixedUniqueId("tf-lb-")
   235  		d.Set("name", slbName)
   236  	}
   237  
   238  	slbArgs := &slb.CreateLoadBalancerArgs{
   239  		RegionId:         getRegion(d, meta),
   240  		LoadBalancerName: slbName,
   241  	}
   242  
   243  	if internet, ok := d.GetOk("internet"); ok && internet.(bool) {
   244  		slbArgs.AddressType = slb.InternetAddressType
   245  		d.Set("internet", true)
   246  	} else {
   247  		slbArgs.AddressType = slb.IntranetAddressType
   248  		d.Set("internet", false)
   249  	}
   250  
   251  	if v, ok := d.GetOk("internet_charge_type"); ok && v.(string) != "" {
   252  		slbArgs.InternetChargeType = slb.InternetChargeType(v.(string))
   253  	}
   254  
   255  	if v, ok := d.GetOk("bandwidth"); ok && v.(int) != 0 {
   256  		slbArgs.Bandwidth = v.(int)
   257  	}
   258  
   259  	if v, ok := d.GetOk("vswitch_id"); ok && v.(string) != "" {
   260  		slbArgs.VSwitchId = v.(string)
   261  	}
   262  	slb, err := slbconn.CreateLoadBalancer(slbArgs)
   263  	if err != nil {
   264  		return err
   265  	}
   266  
   267  	d.SetId(slb.LoadBalancerId)
   268  
   269  	return resourceAliyunSlbUpdate(d, meta)
   270  }
   271  
   272  func resourceAliyunSlbRead(d *schema.ResourceData, meta interface{}) error {
   273  	slbconn := meta.(*AliyunClient).slbconn
   274  	loadBalancer, err := slbconn.DescribeLoadBalancerAttribute(d.Id())
   275  	if err != nil {
   276  		if notFoundError(err) {
   277  			d.SetId("")
   278  			return nil
   279  		}
   280  
   281  		return err
   282  	}
   283  
   284  	d.Set("name", loadBalancer.LoadBalancerName)
   285  
   286  	if loadBalancer.AddressType == slb.InternetAddressType {
   287  		d.Set("internal", true)
   288  	} else {
   289  		d.Set("internal", false)
   290  	}
   291  	d.Set("internet_charge_type", loadBalancer.InternetChargeType)
   292  	d.Set("bandwidth", loadBalancer.Bandwidth)
   293  	d.Set("vswitch_id", loadBalancer.VSwitchId)
   294  	d.Set("address", loadBalancer.Address)
   295  
   296  	return nil
   297  }
   298  
   299  func resourceAliyunSlbUpdate(d *schema.ResourceData, meta interface{}) error {
   300  
   301  	slbconn := meta.(*AliyunClient).slbconn
   302  
   303  	d.Partial(true)
   304  
   305  	if d.HasChange("name") {
   306  		err := slbconn.SetLoadBalancerName(d.Id(), d.Get("name").(string))
   307  		if err != nil {
   308  			return err
   309  		}
   310  
   311  		d.SetPartial("name")
   312  	}
   313  
   314  	if d.Get("internet") == true && d.Get("internet_charge_type") == "paybybandwidth" {
   315  		//don't intranet web and paybybandwidth, then can modify bandwidth
   316  		if d.HasChange("bandwidth") {
   317  			args := &slb.ModifyLoadBalancerInternetSpecArgs{
   318  				LoadBalancerId: d.Id(),
   319  				Bandwidth:      d.Get("bandwidth").(int),
   320  			}
   321  			err := slbconn.ModifyLoadBalancerInternetSpec(args)
   322  			if err != nil {
   323  				return err
   324  			}
   325  
   326  			d.SetPartial("bandwidth")
   327  		}
   328  	}
   329  
   330  	if d.HasChange("listener") {
   331  		o, n := d.GetChange("listener")
   332  		os := o.(*schema.Set)
   333  		ns := n.(*schema.Set)
   334  
   335  		remove, _ := expandListeners(os.Difference(ns).List())
   336  		add, _ := expandListeners(ns.Difference(os).List())
   337  
   338  		if len(remove) > 0 {
   339  			for _, listener := range remove {
   340  				err := slbconn.DeleteLoadBalancerListener(d.Id(), listener.LoadBalancerPort)
   341  				if err != nil {
   342  					return fmt.Errorf("Failure removing outdated SLB listeners: %#v", err)
   343  				}
   344  			}
   345  		}
   346  
   347  		if len(add) > 0 {
   348  			for _, listener := range add {
   349  				err := createListener(slbconn, d.Id(), listener)
   350  				if err != nil {
   351  					return fmt.Errorf("Failure add SLB listeners: %#v", err)
   352  				}
   353  			}
   354  		}
   355  
   356  		d.SetPartial("listener")
   357  	}
   358  
   359  	// If we currently have instances, or did have instances,
   360  	// we want to figure out what to add and remove from the load
   361  	// balancer
   362  	if d.HasChange("instances") {
   363  		o, n := d.GetChange("instances")
   364  		os := o.(*schema.Set)
   365  		ns := n.(*schema.Set)
   366  		remove := expandBackendServers(os.Difference(ns).List())
   367  		add := expandBackendServers(ns.Difference(os).List())
   368  
   369  		if len(add) > 0 {
   370  			_, err := slbconn.AddBackendServers(d.Id(), add)
   371  			if err != nil {
   372  				return err
   373  			}
   374  		}
   375  		if len(remove) > 0 {
   376  			removeBackendServers := make([]string, 0, len(remove))
   377  			for _, e := range remove {
   378  				removeBackendServers = append(removeBackendServers, e.ServerId)
   379  			}
   380  			_, err := slbconn.RemoveBackendServers(d.Id(), removeBackendServers)
   381  			if err != nil {
   382  				return err
   383  			}
   384  		}
   385  
   386  		d.SetPartial("instances")
   387  	}
   388  
   389  	d.Partial(false)
   390  
   391  	return resourceAliyunSlbRead(d, meta)
   392  }
   393  
   394  func resourceAliyunSlbDelete(d *schema.ResourceData, meta interface{}) error {
   395  	conn := meta.(*AliyunClient).slbconn
   396  
   397  	return resource.Retry(5*time.Minute, func() *resource.RetryError {
   398  		err := conn.DeleteLoadBalancer(d.Id())
   399  
   400  		if err != nil {
   401  			return resource.NonRetryableError(err)
   402  		}
   403  
   404  		loadBalancer, err := conn.DescribeLoadBalancerAttribute(d.Id())
   405  		if err != nil {
   406  			e, _ := err.(*common.Error)
   407  			if e.ErrorResponse.Code == LoadBalancerNotFound {
   408  				return nil
   409  			}
   410  			return resource.NonRetryableError(err)
   411  		}
   412  		if loadBalancer != nil {
   413  			return resource.RetryableError(fmt.Errorf("LoadBalancer in use - trying again while it deleted."))
   414  		}
   415  		return nil
   416  	})
   417  }
   418  
   419  func resourceAliyunSlbListenerHash(v interface{}) int {
   420  	var buf bytes.Buffer
   421  	m := v.(map[string]interface{})
   422  	buf.WriteString(fmt.Sprintf("%d-", m["instance_port"].(int)))
   423  	buf.WriteString(fmt.Sprintf("%d-", m["lb_port"].(int)))
   424  	buf.WriteString(fmt.Sprintf("%s-",
   425  		strings.ToLower(m["lb_protocol"].(string))))
   426  
   427  	buf.WriteString(fmt.Sprintf("%d-", m["bandwidth"].(int)))
   428  
   429  	if v, ok := m["ssl_certificate_id"]; ok {
   430  		buf.WriteString(fmt.Sprintf("%s-", v.(string)))
   431  	}
   432  
   433  	return hashcode.String(buf.String())
   434  }
   435  
   436  func createListener(conn *slb.Client, loadBalancerId string, listener *Listener) error {
   437  
   438  	errTypeJudge := func(err error) error {
   439  		if err != nil {
   440  			if listenerType, ok := err.(*ListenerErr); ok {
   441  				if listenerType.ErrType == HealthCheckErrType {
   442  					return fmt.Errorf("When the HealthCheck is %s, then related HealthCheck parameter "+
   443  						"must have.", slb.OnFlag)
   444  				} else if listenerType.ErrType == StickySessionErrType {
   445  					return fmt.Errorf("When the StickySession is %s, then StickySessionType parameter "+
   446  						"must have.", slb.OnFlag)
   447  				} else if listenerType.ErrType == CookieTimeOutErrType {
   448  					return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
   449  						"then CookieTimeout parameter must have.", slb.OnFlag, slb.InsertStickySessionType)
   450  				} else if listenerType.ErrType == CookieErrType {
   451  					return fmt.Errorf("When the StickySession is %s and StickySessionType is %s, "+
   452  						"then Cookie parameter must have.", slb.OnFlag, slb.ServerStickySessionType)
   453  				}
   454  				return fmt.Errorf("slb listener check errtype not found.")
   455  			}
   456  		}
   457  		return nil
   458  	}
   459  
   460  	if listener.Protocol == strings.ToLower("tcp") {
   461  
   462  		args := getTcpListenerArgs(loadBalancerId, listener)
   463  
   464  		if err := conn.CreateLoadBalancerTCPListener(&args); err != nil {
   465  			return err
   466  		}
   467  	} else if listener.Protocol == strings.ToLower("http") {
   468  		args, argsErr := getHttpListenerArgs(loadBalancerId, listener)
   469  		if paramErr := errTypeJudge(argsErr); paramErr != nil {
   470  			return paramErr
   471  		}
   472  
   473  		if err := conn.CreateLoadBalancerHTTPListener(&args); err != nil {
   474  			return err
   475  		}
   476  	} else if listener.Protocol == strings.ToLower("https") {
   477  		listenerType, err := getHttpListenerType(loadBalancerId, listener)
   478  		if paramErr := errTypeJudge(err); paramErr != nil {
   479  			return paramErr
   480  		}
   481  
   482  		args := &slb.CreateLoadBalancerHTTPSListenerArgs{
   483  			HTTPListenerType: listenerType,
   484  		}
   485  		if listener.SSLCertificateId == "" {
   486  			return fmt.Errorf("Server Certificated Id cann't be null")
   487  		}
   488  
   489  		args.ServerCertificateId = listener.SSLCertificateId
   490  
   491  		if err := conn.CreateLoadBalancerHTTPSListener(args); err != nil {
   492  			return err
   493  		}
   494  	} else if listener.Protocol == strings.ToLower("udp") {
   495  		args := getUdpListenerArgs(loadBalancerId, listener)
   496  
   497  		if err := conn.CreateLoadBalancerUDPListener(&args); err != nil {
   498  			return err
   499  		}
   500  	}
   501  
   502  	if err := conn.StartLoadBalancerListener(loadBalancerId, listener.LoadBalancerPort); err != nil {
   503  		return err
   504  	}
   505  
   506  	return nil
   507  }
   508  
   509  func getTcpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerTCPListenerArgs {
   510  	args := slb.CreateLoadBalancerTCPListenerArgs{
   511  		LoadBalancerId:            loadBalancerId,
   512  		ListenerPort:              listener.LoadBalancerPort,
   513  		BackendServerPort:         listener.InstancePort,
   514  		Bandwidth:                 listener.Bandwidth,
   515  		Scheduler:                 listener.Scheduler,
   516  		PersistenceTimeout:        listener.PersistenceTimeout,
   517  		HealthCheckType:           listener.HealthCheckType,
   518  		HealthCheckDomain:         listener.HealthCheckDomain,
   519  		HealthCheckURI:            listener.HealthCheckURI,
   520  		HealthCheckConnectPort:    listener.HealthCheckConnectPort,
   521  		HealthyThreshold:          listener.HealthyThreshold,
   522  		UnhealthyThreshold:        listener.UnhealthyThreshold,
   523  		HealthCheckConnectTimeout: listener.HealthCheckTimeout,
   524  		HealthCheckInterval:       listener.HealthCheckInterval,
   525  		HealthCheckHttpCode:       listener.HealthCheckHttpCode,
   526  	}
   527  	return args
   528  }
   529  
   530  func getUdpListenerArgs(loadBalancerId string, listener *Listener) slb.CreateLoadBalancerUDPListenerArgs {
   531  	args := slb.CreateLoadBalancerUDPListenerArgs{
   532  		LoadBalancerId:            loadBalancerId,
   533  		ListenerPort:              listener.LoadBalancerPort,
   534  		BackendServerPort:         listener.InstancePort,
   535  		Bandwidth:                 listener.Bandwidth,
   536  		PersistenceTimeout:        listener.PersistenceTimeout,
   537  		HealthCheckConnectTimeout: listener.HealthCheckTimeout,
   538  		HealthCheckInterval:       listener.HealthCheckInterval,
   539  	}
   540  	return args
   541  }
   542  
   543  func getHttpListenerType(loadBalancerId string, listener *Listener) (listenType slb.HTTPListenerType, err error) {
   544  
   545  	if listener.HealthCheck == slb.OnFlag {
   546  		if listener.HealthCheckURI == "" || listener.HealthCheckDomain == "" || listener.HealthCheckConnectPort == 0 ||
   547  			listener.HealthyThreshold == 0 || listener.UnhealthyThreshold == 0 || listener.HealthCheckTimeout == 0 ||
   548  			listener.HealthCheckHttpCode == "" || listener.HealthCheckInterval == 0 {
   549  
   550  			errMsg := errors.New("err: HealthCheck empty.")
   551  			return listenType, &ListenerErr{HealthCheckErrType, errMsg}
   552  		}
   553  	}
   554  
   555  	if listener.StickySession == slb.OnFlag {
   556  		if listener.StickySessionType == "" {
   557  			errMsg := errors.New("err: stickySession empty.")
   558  			return listenType, &ListenerErr{StickySessionErrType, errMsg}
   559  		}
   560  
   561  		if listener.StickySessionType == slb.InsertStickySessionType {
   562  			if listener.CookieTimeout == 0 {
   563  				errMsg := errors.New("err: cookieTimeout empty.")
   564  				return listenType, &ListenerErr{CookieTimeOutErrType, errMsg}
   565  			}
   566  		} else if listener.StickySessionType == slb.ServerStickySessionType {
   567  			if listener.Cookie == "" {
   568  				errMsg := errors.New("err: cookie empty.")
   569  				return listenType, &ListenerErr{CookieErrType, errMsg}
   570  			}
   571  		}
   572  	}
   573  
   574  	httpListenertType := slb.HTTPListenerType{
   575  		LoadBalancerId:         loadBalancerId,
   576  		ListenerPort:           listener.LoadBalancerPort,
   577  		BackendServerPort:      listener.InstancePort,
   578  		Bandwidth:              listener.Bandwidth,
   579  		Scheduler:              listener.Scheduler,
   580  		HealthCheck:            listener.HealthCheck,
   581  		StickySession:          listener.StickySession,
   582  		StickySessionType:      listener.StickySessionType,
   583  		CookieTimeout:          listener.CookieTimeout,
   584  		Cookie:                 listener.Cookie,
   585  		HealthCheckDomain:      listener.HealthCheckDomain,
   586  		HealthCheckURI:         listener.HealthCheckURI,
   587  		HealthCheckConnectPort: listener.HealthCheckConnectPort,
   588  		HealthyThreshold:       listener.HealthyThreshold,
   589  		UnhealthyThreshold:     listener.UnhealthyThreshold,
   590  		HealthCheckTimeout:     listener.HealthCheckTimeout,
   591  		HealthCheckInterval:    listener.HealthCheckInterval,
   592  		HealthCheckHttpCode:    listener.HealthCheckHttpCode,
   593  	}
   594  
   595  	return httpListenertType, err
   596  }
   597  
   598  func getHttpListenerArgs(loadBalancerId string, listener *Listener) (listenType slb.CreateLoadBalancerHTTPListenerArgs, err error) {
   599  	httpListenerType, err := getHttpListenerType(loadBalancerId, listener)
   600  	if err != nil {
   601  		return listenType, err
   602  	}
   603  
   604  	httpArgs := slb.CreateLoadBalancerHTTPListenerArgs(httpListenerType)
   605  	return httpArgs, err
   606  }