diff --git a/pkg/apis/compute/loadbalancer_const.go b/pkg/apis/compute/loadbalancer_const.go index 1acc1266a7..135bb86c8e 100644 --- a/pkg/apis/compute/loadbalancer_const.go +++ b/pkg/apis/compute/loadbalancer_const.go @@ -275,6 +275,38 @@ var LB_HEALTH_CHECK_HTTP_CODES = choices.NewChoices( LB_HEALTH_CHECK_HTTP_CODE_5xx, ) +const ( + LB_REDIRECT_OFF = "off" + LB_REDIRECT_RAW = "raw" +) + +var LB_REDIRECT_TYPES = choices.NewChoices( + LB_REDIRECT_OFF, + LB_REDIRECT_RAW, +) + +const ( + LB_REDIRECT_CODE_301 = int64(301) // Moved Permanently + LB_REDIRECT_CODE_302 = int64(302) // Found + LB_REDIRECT_CODE_307 = int64(307) // Temporary Redirect +) + +var LB_REDIRECT_CODES = []int64{ + LB_REDIRECT_CODE_301, + LB_REDIRECT_CODE_302, + LB_REDIRECT_CODE_307, +} + +const ( + LB_REDIRECT_SCHEME_HTTP = "http" + LB_REDIRECT_SCHEME_HTTPS = "https" +) + +var LB_REDIRECT_SCHEMES = choices.NewChoices( + LB_REDIRECT_SCHEME_HTTP, + LB_REDIRECT_SCHEME_HTTPS, +) + const ( LB_BOOL_ON = "on" LB_BOOL_OFF = "off" diff --git a/pkg/compute/models/loadbalancerlistenerrules.go b/pkg/compute/models/loadbalancerlistenerrules.go index 6fadb58133..3b2c1fc05b 100644 --- a/pkg/compute/models/loadbalancerlistenerrules.go +++ b/pkg/compute/models/loadbalancerlistenerrules.go @@ -72,6 +72,7 @@ type SLoadbalancerListenerRule struct { SLoadbalancerHealthCheck // 目前只有腾讯云HTTP、HTTPS类型的健康检查是和规则绑定的。 SLoadbalancerHTTPRateLimiter + SLoadbalancerHTTPRedirect } func ValidateListenerRuleConditions(condition string) error { @@ -409,16 +410,8 @@ func (man *SLoadbalancerListenerRuleManager) ValidateCreateData(ctx context.Cont data.Update(jsonutils.Marshal(input)) listenerV := validators.NewModelIdOrNameValidator("listener", "loadbalancerlistener", ownerId) - backendGroupV := validators.NewModelIdOrNameValidator("backend_group", "loadbalancerbackendgroup", ownerId) - keyV := map[string]validators.IValidator{ - "listener": listenerV, - "backend_group": backendGroupV, - } - - for _, v := range keyV { - if err := v.Validate(data); err != nil { - return nil, err - } + if err := listenerV.Validate(data); err != nil { + return nil, err } listener := listenerV.Model.(*SLoadbalancerListener) @@ -427,6 +420,15 @@ func (man *SLoadbalancerListenerRuleManager) ValidateCreateData(ctx context.Cont return nil, httperrors.NewResourceNotFoundError("failed to find region for loadbalancer listener %s", listener.Name) } + backendGroupV := validators.NewModelIdOrNameValidator("backend_group", "loadbalancerbackendgroup", ownerId) + if region.Provider == api.CLOUD_PROVIDER_ONECLOUD { + // backend group can be empty if you support redirect in rule + backendGroupV.Optional(true) + } + if err := backendGroupV.Validate(data); err != nil { + return nil, err + } + return region.GetDriver().ValidateCreateLoadbalancerListenerRuleData(ctx, userCred, ownerId, data, backendGroupV.Model) } diff --git a/pkg/compute/models/loadbalancerlisteners.go b/pkg/compute/models/loadbalancerlisteners.go index 296d790126..f13d4f5f30 100644 --- a/pkg/compute/models/loadbalancerlisteners.go +++ b/pkg/compute/models/loadbalancerlisteners.go @@ -96,6 +96,14 @@ type SLoadbalancerHTTPListener struct { Gzip bool `nullable:"true" list:"user" create:"optional" update:"user"` // Gzip数据压缩 } +type SLoadbalancerHTTPRedirect struct { + Redirect string `nullable:"true" list:"user" create:"optional" update:"user" default:"off"` // 跳转类型 + RedirectCode int `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转HTTP code + RedirectScheme string `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转uri scheme + RedirectHost string `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转时变更Host + RedirectPath string `nullable:"true" list:"user" create:"optional" update:"user"` // 跳转时变更Path +} + // TODO // // - CACertificate string @@ -145,6 +153,7 @@ type SLoadbalancerListener struct { SLoadbalancerHealthCheck SLoadbalancerHTTPRateLimiter + SLoadbalancerHTTPRedirect } func (man *SLoadbalancerListenerManager) CheckListenerUniqueness(ctx context.Context, lb *SLoadbalancer, listenerType string, listenerPort int64) error { diff --git a/pkg/compute/regiondrivers/kvm.go b/pkg/compute/regiondrivers/kvm.go index 09921c58b9..633ac4d938 100644 --- a/pkg/compute/regiondrivers/kvm.go +++ b/pkg/compute/regiondrivers/kvm.go @@ -283,6 +283,7 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx con listenerV := validators.NewModelIdOrNameValidator("listener", "loadbalancerlistener", ownerId) domainV := validators.NewHostPortValidator("domain").OptionalPort(true) pathV := validators.NewURLPathValidator("path") + redirectV := validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES) keyV := map[string]validators.IValidator{ "status": validators.NewStringChoicesValidator("status", api.LB_STATUS_SPEC).Default(api.LB_STATUS_ENABLED), @@ -292,6 +293,12 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx con "http_request_rate": validators.NewNonNegativeValidator("http_request_rate").Default(0), "http_request_rate_per_src": validators.NewNonNegativeValidator("http_request_rate_per_src").Default(0), + + "redirect": redirectV.Default(api.LB_REDIRECT_OFF), + "redirect_code": validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES).Default(api.LB_REDIRECT_CODE_302), + "redirect_scheme": validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES).Optional(true), + "redirect_host": validators.NewHostPortValidator("redirect_host").OptionalPort(true).Optional(true), + "redirect_path": validators.NewURLPathValidator("redirect_path").Optional(true), } if err := RunValidators(keyV, data, false); err != nil { @@ -303,10 +310,20 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerRuleData(ctx con if listenerType != api.LB_LISTENER_TYPE_HTTP && listenerType != api.LB_LISTENER_TYPE_HTTPS { return nil, httperrors.NewInputParameterError("listener type must be http/https, got %s", listenerType) } + if listener.Redirect != api.LB_REDIRECT_OFF { + return nil, httperrors.NewInputParameterError("do not allow adding rules for redirect listener") + } - if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != listener.LoadbalancerId { - return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", - lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, listener.LoadbalancerId) + { + if redirectV.Value == api.LB_REDIRECT_OFF { + if backendGroup == nil { + return nil, httperrors.NewInputParameterError("backend_group argument is missing") + } + } + if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != listener.LoadbalancerId { + return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", + lbbg.Name, lbbg.Id, lbbg.LoadbalancerId, listener.LoadbalancerId) + } } err := models.LoadbalancerListenerRuleCheckUniqueness(ctx, listener, domainV.Value, pathV.Value) @@ -323,12 +340,20 @@ func (self *SKVMRegionDriver) ValidateUpdateLoadbalancerListenerRuleData(ctx con lbr := ctx.Value("lbr").(*models.SLoadbalancerListenerRule) domainV := validators.NewHostPortValidator("domain").OptionalPort(true) pathV := validators.NewURLPathValidator("path") + redirectV := validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES) + redirectV.Default(lbr.Redirect) keyV := map[string]validators.IValidator{ "domain": domainV.AllowEmpty(true).Default(lbr.Domain), "path": pathV.Default(lbr.Path), "http_request_rate": validators.NewNonNegativeValidator("http_request_rate"), "http_request_rate_per_src": validators.NewNonNegativeValidator("http_request_rate_per_src"), + + "redirect": redirectV, + "redirect_code": validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES), + "redirect_scheme": validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES), + "redirect_host": validators.NewHostPortValidator("redirect_host").OptionalPort(true), + "redirect_path": validators.NewURLPathValidator("redirect_path"), } for _, v := range keyV { v.Optional(true) @@ -336,6 +361,11 @@ func (self *SKVMRegionDriver) ValidateUpdateLoadbalancerListenerRuleData(ctx con return nil, err } } + if lbr.Redirect != redirectV.Value { + // this can be relaxed to do not allow on/off + return nil, httperrors.NewInputParameterError("do not allow changing redirect type") + } + if backendGroup, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && backendGroup.Id != lbr.BackendGroupId { listenerM, err := models.LoadbalancerListenerManager.FetchById(lbr.ListenerId) if err != nil { @@ -357,6 +387,7 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerData(ctx context aclStatusV := validators.NewStringChoicesValidator("acl_status", api.LB_BOOL_VALUES) aclTypeV := validators.NewStringChoicesValidator("acl_type", api.LB_ACL_TYPES) aclV := validators.NewModelIdOrNameValidator("acl", "loadbalanceracl", ownerId) + redirectV := validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES) keyV := map[string]validators.IValidator{ "status": validators.NewStringChoicesValidator("status", api.LB_STATUS_SPEC).Default(api.LB_STATUS_ENABLED), @@ -387,6 +418,12 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerData(ctx context "http_request_rate": validators.NewNonNegativeValidator("http_request_rate").Default(0), "http_request_rate_per_src": validators.NewNonNegativeValidator("http_request_rate_per_src").Default(0), + + "redirect": redirectV.Default(api.LB_REDIRECT_OFF), + "redirect_code": validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES).Default(api.LB_REDIRECT_CODE_302), + "redirect_scheme": validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES).Optional(true), + "redirect_host": validators.NewHostPortValidator("redirect_host").OptionalPort(true).Optional(true), + "redirect_path": validators.NewURLPathValidator("redirect_path").Optional(true), } if err := RunValidators(keyV, data, false); err != nil { @@ -400,6 +437,13 @@ func (self *SKVMRegionDriver) ValidateCreateLoadbalancerListenerData(ctx context return nil, err } + redirectType := redirectV.Value + if redirectType != api.LB_REDIRECT_OFF { + if listenerType != api.LB_LISTENER_TYPE_HTTP && listenerType != api.LB_LISTENER_TYPE_HTTPS { + return nil, httperrors.NewInputParameterError("redirect can only be enabled for http/https listener") + } + } + // backendgroup check if lbbg, ok := backendGroup.(*models.SLoadbalancerBackendGroup); ok && lbbg.LoadbalancerId != lb.Id { return nil, httperrors.NewInputParameterError("backend group %s(%s) belongs to loadbalancer %s instead of %s", @@ -465,6 +509,9 @@ func (self *SKVMRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx context aclV.Default(lblis.AclId) } + redirectV := validators.NewStringChoicesValidator("redirect", api.LB_REDIRECT_TYPES) + redirectV.Default(lblis.Redirect) + certV := validators.NewModelIdOrNameValidator("certificate", "loadbalancercertificate", ownerId) tlsCipherPolicyV := validators.NewStringChoicesValidator("tls_cipher_policy", api.LB_TLS_CIPHER_POLICIES).Default(api.LB_TLS_CIPHER_POLICY_1_2) keyV := map[string]validators.IValidator{ @@ -508,12 +555,23 @@ func (self *SKVMRegionDriver) ValidateUpdateLoadbalancerListenerData(ctx context "certificate": certV, "tls_cipher_policy": tlsCipherPolicyV, "enable_http2": validators.NewBoolValidator("enable_http2"), + + "redirect": redirectV, + "redirect_code": validators.NewIntChoicesValidator("redirect_code", api.LB_REDIRECT_CODES), + "redirect_scheme": validators.NewStringChoicesValidator("redirect_scheme", api.LB_REDIRECT_SCHEMES), + "redirect_host": validators.NewHostPortValidator("redirect_host").OptionalPort(true), + "redirect_path": validators.NewURLPathValidator("redirect_path"), } if err := RunValidators(keyV, data, true); err != nil { return nil, err } + if lblis.Redirect != redirectV.Value { + // this can be relaxed to do not allow on/off + return nil, httperrors.NewInputParameterError("do not allow changing redirect type") + } + if err := models.LoadbalancerListenerManager.ValidateAcl(aclStatusV, aclTypeV, aclV, data, lblis.GetProviderName()); err != nil { return nil, err }