alibabacloudstack/resource_apsarastack_slb_rule.go (399 lines of code) (raw):
package alibabacloudstack
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"strconv"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/slb"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/connectivity"
"github.com/aliyun/terraform-provider-alibabacloudstack/alibabacloudstack/errmsgs"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
func resourceAlibabacloudStackSlbRule() *schema.Resource {
resource := &schema.Resource{
Schema: map[string]*schema.Schema{
"load_balancer_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"frontend_port": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 65535),
Required: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Optional:true,
Computed:true,
ForceNew: true,
Deprecated: "Field 'name' is deprecated and will be removed in a future release. Please use new field 'rule_name' instead.",
ConflictsWith: []string{"rule_name"},
},
"rule_name": {
Type: schema.TypeString,
Optional:true,
Computed:true,
ForceNew: true,
ConflictsWith: []string{"name"},
},
"listener_sync": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false),
Optional: true,
Default: string(OnFlag),
},
"scheduler": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"wrr", "wlc", "rr"}, false),
Optional: true,
Default: WRRScheduler,
DiffSuppressFunc: slbRuleListenerSyncDiffSuppressFunc,
},
"domain": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"url": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"server_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"cookie": {
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 200),
Optional: true,
DiffSuppressFunc: slbRuleCookieDiffSuppressFunc,
},
"cookie_timeout": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 86400),
Optional: true,
DiffSuppressFunc: slbRuleCookieTimeoutDiffSuppressFunc,
},
"health_check": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false),
Optional: true,
Default: OnFlag,
DiffSuppressFunc: slbRuleListenerSyncDiffSuppressFunc,
},
"health_check_http_code": {
Type: schema.TypeString,
ValidateFunc: validateAllowedSplitStringValue([]string{string(HTTP_2XX), string(HTTP_3XX), string(HTTP_4XX), string(HTTP_5XX)}, ","),
Optional: true,
Default: HTTP_2XX,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"health_check_interval": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 50),
Optional: true,
Default: 2,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"health_check_domain": {
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 80),
Optional: true,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"health_check_uri": {
Type: schema.TypeString,
ValidateFunc: validation.StringLenBetween(1, 80),
Optional: true,
Default: "/",
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"health_check_connect_port": {
Type: schema.TypeInt,
ValidateFunc: validation.Any(validation.IntBetween(1, 65535), validation.IntInSlice([]int{-520})),
Optional: true,
Computed: true,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"health_check_timeout": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 300),
Optional: true,
Default: 5,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"healthy_threshold": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 10),
Optional: true,
Default: 3,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
"unhealthy_threshold": {
Type: schema.TypeInt,
ValidateFunc: validation.IntBetween(1, 10),
Optional: true,
Default: 3,
DiffSuppressFunc: slbRuleHealthCheckDiffSuppressFunc,
},
//http & https
"sticky_session": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{"on", "off"}, false),
Optional: true,
Default: OffFlag,
DiffSuppressFunc: slbRuleListenerSyncDiffSuppressFunc,
},
//http & https
"sticky_session_type": {
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{string(InsertStickySessionType), string(ServerStickySessionType)}, false),
Optional: true,
DiffSuppressFunc: slbRuleStickySessionTypeDiffSuppressFunc,
},
"delete_protection_validation": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
setResourceFunc(resource, resourceAlibabacloudStackSlbRuleCreate, resourceAlibabacloudStackSlbRuleRead, resourceAlibabacloudStackSlbRuleUpdate, resourceAlibabacloudStackSlbRuleDelete)
return resource
}
func resourceAlibabacloudStackSlbRuleCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
slb_id := d.Get("load_balancer_id").(string)
port := d.Get("frontend_port").(int)
name := strings.Trim(connectivity.GetResourceData(d, "rule_name", "name").(string), " ")
if err := errmsgs.CheckEmpty(name, schema.TypeString, "rule_name", "name"); err != nil {
return errmsgs.WrapError(err)
}
group_id := strings.Trim(d.Get("server_group_id").(string), " ")
var domain, url, rule string
if v, ok := d.GetOk("domain"); ok {
domain = v.(string)
}
if v, ok := d.GetOk("url"); ok {
url = v.(string)
}
if domain == "" && url == "" {
return errmsgs.WrapError(errmsgs.Error("At least one 'domain' or 'url' must be set."))
} else if domain == "" {
rule = fmt.Sprintf("[{'RuleName':'%s','Url':'%s','VServerGroupId':'%s'}]", name, url, group_id)
} else if url == "" {
rule = fmt.Sprintf("[{'RuleName':'%s','Domain':'%s','VServerGroupId':'%s'}]", name, domain, group_id)
} else {
rule = fmt.Sprintf("[{'RuleName':'%s','Domain':'%s','Url':'%s','VServerGroupId':'%s'}]", name, domain, url, group_id)
}
request := slb.CreateCreateRulesRequest()
client.InitRpcRequest(*request.RpcRequest)
request.LoadBalancerId = slb_id
request.ListenerPort = requests.NewInteger(port)
request.RuleList = rule
var raw interface{}
var err error
if err = resource.Retry(3*time.Minute, func() *resource.RetryError {
raw, err = client.WithSlbClient(func(slbClient *slb.Client) (interface{}, error) {
return slbClient.CreateRules(request)
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"BackendServer.configuring", "OperationFailed.ListenerStatusNotSupport"}) {
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
}); err != nil {
errmsg := ""
if raw != nil {
response, ok := raw.(*slb.CreateRulesResponse)
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(response.BaseResponse)
}
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_slb_rule", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
response, _ := raw.(*slb.CreateRulesResponse)
d.SetId(response.Rules.Rule[0].RuleId)
return nil
}
func resourceAlibabacloudStackSlbRuleRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
slbService := SlbService{client}
object, err := slbService.DescribeSlbRule(d.Id())
if err != nil {
if errmsgs.NotFoundError(err) {
d.SetId("")
return nil
}
return errmsgs.WrapError(err)
}
connectivity.SetResourceData(d, object.RuleName, "rule_name", "name")
d.Set("load_balancer_id", object.LoadBalancerId)
if port, err := strconv.Atoi(object.ListenerPort); err != nil {
return errmsgs.WrapError(err)
} else {
d.Set("frontend_port", port)
}
d.Set("domain", object.Domain)
d.Set("url", object.Url)
d.Set("server_group_id", object.VServerGroupId)
d.Set("sticky_session", object.StickySession)
d.Set("sticky_session_type", object.StickySessionType)
d.Set("unhealthy_threshold", object.UnhealthyThreshold)
d.Set("healthy_threshold", object.HealthyThreshold)
d.Set("health_check_timeout", object.HealthCheckTimeout)
d.Set("health_check_connect_port", object.HealthCheckConnectPort)
d.Set("health_check_uri", object.HealthCheckURI)
d.Set("health_check", object.HealthCheck)
d.Set("health_check_http_code", object.HealthCheckHttpCode)
d.Set("health_check_interval", object.HealthCheckInterval)
d.Set("scheduler", object.Scheduler)
d.Set("listener_sync", object.ListenerSync)
d.Set("cookie_timeout", object.CookieTimeout)
d.Set("cookie", object.Cookie)
d.Set("health_check_domain", object.HealthCheckDomain)
return nil
}
func resourceAlibabacloudStackSlbRuleUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
update := false
fullUpdate := false
request := slb.CreateSetRuleRequest()
client.InitRpcRequest(*request.RpcRequest)
request.RuleId = d.Id()
if listenerSync, ok := d.GetOk("listener_sync"); ok && listenerSync == string(OffFlag) {
if stickySession := d.Get("sticky_session"); stickySession == string(OnFlag) {
if _, ok := d.GetOk("sticky_session_type"); !ok {
return errmsgs.WrapError(errmsgs.Error(`'sticky_session_type': required field is not set when the sticky_session is 'on'.`))
}
}
if stickySessionType := d.Get("sticky_session_type"); stickySessionType == string(InsertStickySessionType) {
if _, ok := d.GetOk("cookie_timeout"); !ok {
return errmsgs.WrapError(errmsgs.Error(`'cookie_timeout': required field is not set when the sticky_session_type is 'insert'.`))
}
}
if stickySessionType := d.Get("sticky_session_type"); stickySessionType == string(ServerStickySessionType) {
if _, ok := d.GetOk("cookie"); !ok {
return errmsgs.WrapError(errmsgs.Error(`'cookie': required field is not set when the sticky_session_type is 'server'.`))
}
}
}
if d.HasChange("server_group_id") {
request.VServerGroupId = d.Get("server_group_id").(string)
update = true
}
if d.HasChanges("rule_name", "name") {
request.RuleName = connectivity.GetResourceData(d, "rule_name", "name").(string)
if err := errmsgs.CheckEmpty(request.RuleName, schema.TypeString, "rule_name", "name"); err != nil {
return errmsgs.WrapError(err)
}
update = true
}
fullUpdate = d.HasChanges("listener_sync","scheduler","cookie","cookie_timeout","health_check", "health_check_http_code",
"health_check_interval","health_check_domain","health_check_uri","health_check_connect_port", "health_check_timeout",
"healthy_threshold","unhealthy_threshold", "sticky_session", "sticky_session_type")
if fullUpdate {
request.ListenerSync = d.Get("listener_sync").(string)
if listenerSync, ok := d.GetOk("listener_sync"); ok && listenerSync == string(OffFlag) {
request.Scheduler = d.Get("scheduler").(string)
request.HealthCheck = d.Get("health_check").(string)
request.StickySession = d.Get("sticky_session").(string)
if request.HealthCheck == string(OnFlag) {
request.HealthCheckTimeout = requests.NewInteger(d.Get("health_check_timeout").(int))
request.HealthCheckURI = d.Get("health_check_uri").(string)
request.HealthyThreshold = requests.NewInteger(d.Get("healthy_threshold").(int))
request.UnhealthyThreshold = requests.NewInteger(d.Get("unhealthy_threshold").(int))
request.HealthCheckInterval = requests.NewInteger(d.Get("health_check_interval").(int))
request.HealthCheckHttpCode = d.Get("health_check_http_code").(string)
if healthCheckDomain, ok := d.GetOk("health_check_domain"); ok {
request.HealthCheckDomain = healthCheckDomain.(string)
}
if healthCheckConnectPort, ok := d.GetOk("health_check_connect_port"); ok {
request.HealthCheckConnectPort = requests.NewInteger(healthCheckConnectPort.(int))
}
}
if request.StickySession == string(OnFlag) {
request.StickySessionType = d.Get("sticky_session_type").(string)
if request.StickySessionType == string(InsertStickySessionType) {
request.CookieTimeout = requests.NewInteger(d.Get("cookie_timeout").(int))
}
if request.StickySessionType == string(ServerStickySessionType) {
request.Cookie = d.Get("cookie").(string)
}
}
}
}
if update || fullUpdate {
raw, err := client.WithSlbClient(func(slbClient *slb.Client) (interface{}, error) {
return slbClient.SetRule(request)
})
if err != nil {
errmsg := ""
if raw != nil {
response, ok := raw.(*slb.SetRuleResponse)
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(response.BaseResponse)
}
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
}
return nil
}
func resourceAlibabacloudStackSlbRuleDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AlibabacloudStackClient)
slbService := SlbService{client}
if d.Get("delete_protection_validation").(bool) {
lbId := d.Get("load_balancer_id").(string)
lbInstance, err := slbService.DescribeSlb(lbId)
if err != nil {
if errmsgs.NotFoundError(err) {
return nil
}
return errmsgs.WrapError(err)
}
if lbInstance.DeleteProtection == "on" {
return errmsgs.WrapError(fmt.Errorf("Current rule's SLB Instance %s has enabled DeleteProtection. Please set delete_protection_validation to false to delete the rule.", lbId))
}
}
request := slb.CreateDeleteRulesRequest()
client.InitRpcRequest(*request.RpcRequest)
request.RuleIds = fmt.Sprintf("['%s']", d.Id())
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
raw, err := client.WithSlbClient(func(slbClient *slb.Client) (interface{}, error) {
return slbClient.DeleteRules(request)
})
response, ok := raw.(*slb.DeleteRulesResponse)
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"OperationFailed.ListenerStatusNotSupport"}) {
return resource.RetryableError(err)
}
errmsg := ""
if raw != nil {
if ok {
errmsg = errmsgs.GetBaseResponseErrorMessage(response.BaseResponse)
}
}
err = errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, "alibabacloudstack_slb_rule", request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, errmsg)
return resource.NonRetryableError(err)
}
addDebug(request.GetActionName(), raw, request.RpcRequest, request)
return nil
})
if err != nil {
if errmsgs.IsExpectedErrors(err, []string{"InvalidRuleId.NotFound"}) {
return nil
}
return errmsgs.WrapErrorf(err, errmsgs.RequestV1ErrorMsg, d.Id(), request.GetActionName(), errmsgs.AlibabacloudStackSdkGoERROR, "")
}
return errmsgs.WrapError(slbService.WaitForSlbRule(d.Id(), Deleted, DefaultTimeoutMedium))
}