From 471ef16a13ed94abf8519cf226b83de84591768b Mon Sep 17 00:00:00 2001 From: Toni Kangas Date: Tue, 22 Oct 2024 14:48:55 +0300 Subject: [PATCH] feat(lbaas): add support for new rule actions and matchers --- CHANGELOG.md | 5 ++ upcloud/load_balancer.go | 68 +++++++++++++++--------- upcloud/load_balancer_test.go | 46 ++++++++++++++++ upcloud/request/load_balancer_helpers.go | 66 +++++++++++++++++++++++ 4 files changed, 159 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a80aee..ba06cec9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ See updating [Changelog example here](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] +### Added + +- managed load balancer: `http_status`, `request_header`, and `response_header` rule matchers +- managed load balancer: `set_request_header`, and `set_response_header` rule actions + ## [8.10.0] ### Added diff --git a/upcloud/load_balancer.go b/upcloud/load_balancer.go index 32c2c585..70c70dd4 100644 --- a/upcloud/load_balancer.go +++ b/upcloud/load_balancer.go @@ -58,18 +58,21 @@ const ( LoadBalancerCertificateBundleOperationalStateSetupChallenge LoadBalancerCertificateBundleOperationalState = "setup-challenge" LoadBalancerCertificateBundleOperationalStateCompleteChallenge LoadBalancerCertificateBundleOperationalState = "complete-challenge" - LoadBalancerMatcherTypeSrcIP LoadBalancerMatcherType = "src_ip" - LoadBalancerMatcherTypeSrcPort LoadBalancerMatcherType = "src_port" - LoadBalancerMatcherTypeBodySize LoadBalancerMatcherType = "body_size" - LoadBalancerMatcherTypePath LoadBalancerMatcherType = "path" - LoadBalancerMatcherTypeURL LoadBalancerMatcherType = "url" - LoadBalancerMatcherTypeURLQuery LoadBalancerMatcherType = "url_query" - LoadBalancerMatcherTypeHost LoadBalancerMatcherType = "host" - LoadBalancerMatcherTypeHTTPMethod LoadBalancerMatcherType = "http_method" - LoadBalancerMatcherTypeCookie LoadBalancerMatcherType = "cookie" - LoadBalancerMatcherTypeHeader LoadBalancerMatcherType = "header" - LoadBalancerMatcherTypeURLParam LoadBalancerMatcherType = "url_param" - LoadBalancerMatcherTypeNumMembersUp LoadBalancerMatcherType = "num_members_up" + LoadBalancerMatcherTypeSrcIP LoadBalancerMatcherType = "src_ip" + LoadBalancerMatcherTypeSrcPort LoadBalancerMatcherType = "src_port" + LoadBalancerMatcherTypeBodySize LoadBalancerMatcherType = "body_size" + LoadBalancerMatcherTypePath LoadBalancerMatcherType = "path" + LoadBalancerMatcherTypeURL LoadBalancerMatcherType = "url" + LoadBalancerMatcherTypeURLQuery LoadBalancerMatcherType = "url_query" + LoadBalancerMatcherTypeHost LoadBalancerMatcherType = "host" + LoadBalancerMatcherTypeHTTPMethod LoadBalancerMatcherType = "http_method" + LoadBalancerMatcherTypeHTTPStatus LoadBalancerMatcherType = "http_status" + LoadBalancerMatcherTypeCookie LoadBalancerMatcherType = "cookie" + LoadBalancerMatcherTypeHeader LoadBalancerMatcherType = "header" + LoadBalancerMatcherTypeRequestHeader LoadBalancerMatcherType = "request_header" + LoadBalancerMatcherTypeResponseHeader LoadBalancerMatcherType = "response_header" + LoadBalancerMatcherTypeURLParam LoadBalancerMatcherType = "url_param" + LoadBalancerMatcherTypeNumMembersUp LoadBalancerMatcherType = "num_members_up" LoadBalancerMatchingConditionAnd LoadBalancerMatchingCondition = "and" LoadBalancerMatchingConditionOr LoadBalancerMatchingCondition = "or" @@ -79,6 +82,8 @@ const ( LoadBalancerActionTypeHTTPReturn LoadBalancerActionType = "http_return" LoadBalancerActionTypeHTTPRedirect LoadBalancerActionType = "http_redirect" LoadBalancerActionTypeSetForwardedHeaders LoadBalancerActionType = "set_forwarded_headers" + LoadBalancerActionTypeSetRequestHeader LoadBalancerActionType = "set_request_header" + LoadBalancerActionTypeSetResponseHeader LoadBalancerActionType = "set_response_header" LoadBalancerActionHTTPRedirectSchemeHTTP LoadBalancerActionHTTPRedirectScheme = "http" LoadBalancerActionHTTPRedirectSchemeHTTPS LoadBalancerActionHTTPRedirectScheme = "https" @@ -275,20 +280,23 @@ type LoadBalancer struct { // LoadBalancerMatcher represents rule matcher type LoadBalancerMatcher struct { - Type LoadBalancerMatcherType `json:"type,omitempty"` - SrcIP *LoadBalancerMatcherSourceIP `json:"match_src_ip,omitempty"` - SrcPort *LoadBalancerMatcherInteger `json:"match_src_port,omitempty"` - BodySize *LoadBalancerMatcherInteger `json:"match_body_size,omitempty"` - Path *LoadBalancerMatcherString `json:"match_path,omitempty"` - URL *LoadBalancerMatcherString `json:"match_url,omitempty"` - URLQuery *LoadBalancerMatcherString `json:"match_url_query,omitempty"` - Host *LoadBalancerMatcherHost `json:"match_host,omitempty"` - HTTPMethod *LoadBalancerMatcherHTTPMethod `json:"match_http_method,omitempty"` - Cookie *LoadBalancerMatcherStringWithArgument `json:"match_cookie,omitempty"` - Header *LoadBalancerMatcherStringWithArgument `json:"match_header,omitempty"` - URLParam *LoadBalancerMatcherStringWithArgument `json:"match_url_param,omitempty"` - NumMembersUp *LoadBalancerMatcherNumMembersUp `json:"match_num_members_up,omitempty"` - Inverse *bool `json:"inverse,omitempty"` + Type LoadBalancerMatcherType `json:"type,omitempty"` + SrcIP *LoadBalancerMatcherSourceIP `json:"match_src_ip,omitempty"` + SrcPort *LoadBalancerMatcherInteger `json:"match_src_port,omitempty"` + BodySize *LoadBalancerMatcherInteger `json:"match_body_size,omitempty"` + Path *LoadBalancerMatcherString `json:"match_path,omitempty"` + RequestHeader *LoadBalancerMatcherStringWithArgument `json:"match_request_header,omitempty"` + ResponseHeader *LoadBalancerMatcherStringWithArgument `json:"match_response_header,omitempty"` + URL *LoadBalancerMatcherString `json:"match_url,omitempty"` + URLQuery *LoadBalancerMatcherString `json:"match_url_query,omitempty"` + Host *LoadBalancerMatcherHost `json:"match_host,omitempty"` + HTTPMethod *LoadBalancerMatcherHTTPMethod `json:"match_http_method,omitempty"` + HTTPStatus *LoadBalancerMatcherInteger `json:"match_http_status,omitempty"` + Cookie *LoadBalancerMatcherStringWithArgument `json:"match_cookie,omitempty"` + Header *LoadBalancerMatcherStringWithArgument `json:"match_header,omitempty"` // Deprecated: use RequestHeader instead + URLParam *LoadBalancerMatcherStringWithArgument `json:"match_url_param,omitempty"` + NumMembersUp *LoadBalancerMatcherNumMembersUp `json:"match_num_members_up,omitempty"` + Inverse *bool `json:"inverse,omitempty"` } // LoadBalancerMatcherStringWithArgument represents 'string with argument' matcher @@ -344,6 +352,8 @@ type LoadBalancerAction struct { HTTPReturn *LoadBalancerActionHTTPReturn `json:"action_http_return,omitempty"` HTTPRedirect *LoadBalancerActionHTTPRedirect `json:"action_http_redirect,omitempty"` SetForwardedHeaders *LoadBalancerActionSetForwardedHeaders `json:"action_set_forwarded_headers,omitempty"` + SetRequestHeader *LoadBalancerActionSetHeader `json:"action_set_request_header,omitempty"` + SetResponseHeader *LoadBalancerActionSetHeader `json:"action_set_response_header,omitempty"` } // LoadBalancerActionUseBackend represents 'use_backend' action @@ -370,6 +380,12 @@ type LoadBalancerActionHTTPRedirect struct { // LoadBalancerActionSetForwardedHeaders represents 'set_forwarded_headers' action type LoadBalancerActionSetForwardedHeaders struct{} +// LoadBalancerActionSetHeader represents 'set_request_header' and 'set_response_header' actions +type LoadBalancerActionSetHeader struct { + Header string `json:"header,omitempty"` + Value string `json:"value,omitempty"` +} + // LoadBalancerCertificateBundle represents certificate bundle type LoadBalancerCertificateBundle struct { UUID string `json:"uuid,omitempty"` diff --git a/upcloud/load_balancer_test.go b/upcloud/load_balancer_test.go index 0015505e..ae82c939 100644 --- a/upcloud/load_balancer_test.go +++ b/upcloud/load_balancer_test.go @@ -451,6 +451,29 @@ func TestLoadBalancerRule(t *testing.T) { Type: LoadBalancerActionTypeSetForwardedHeaders, SetForwardedHeaders: &LoadBalancerActionSetForwardedHeaders{}, }, + { + Type: LoadBalancerActionTypeSetRequestHeader, + SetRequestHeader: &LoadBalancerActionSetHeader{ + Header: "Test-Type", + Value: "Unit", + }, + }, + { + Type: LoadBalancerActionTypeSetResponseHeader, + SetResponseHeader: &LoadBalancerActionSetHeader{ + Header: "Test-Type", + Value: "Unit", + }, + }, + }, + Matchers: []LoadBalancerMatcher{ + { + Type: LoadBalancerMatcherTypeHTTPStatus, + HTTPStatus: &LoadBalancerMatcherInteger{ + Method: LoadBalancerIntegerMatcherMethodEqual, + Value: 200, + }, + }, }, CreatedAt: timeParse("2021-12-07T13:58:30.817272Z"), UpdatedAt: timeParse("2022-02-11T17:33:08.490581Z"), @@ -489,6 +512,29 @@ func TestLoadBalancerRule(t *testing.T) { { "type": "set_forwarded_headers", "action_set_forwarded_headers": {} + }, + { + "type": "set_request_header", + "action_set_request_header": { + "header": "Test-Type", + "value": "Unit" + } + }, + { + "type": "set_response_header", + "action_set_response_header": { + "header": "Test-Type", + "value": "Unit" + } + } + ], + "matchers": [ + { + "type": "http_status", + "match_http_status": { + "method": "equal", + "value": 200 + } } ] } diff --git a/upcloud/request/load_balancer_helpers.go b/upcloud/request/load_balancer_helpers.go index b7d16d39..cbc1f1bf 100644 --- a/upcloud/request/load_balancer_helpers.go +++ b/upcloud/request/load_balancer_helpers.go @@ -56,6 +56,26 @@ func NewLoadBalancerSetForwardedHeadersAction() upcloud.LoadBalancerAction { } } +func NewLoadBalancerSetRequestHeaderAction(header, value string) upcloud.LoadBalancerAction { + return upcloud.LoadBalancerAction{ + Type: upcloud.LoadBalancerActionTypeSetRequestHeader, + SetRequestHeader: &upcloud.LoadBalancerActionSetHeader{ + Header: header, + Value: value, + }, + } +} + +func NewLoadBalancerSetResponseHeaderAction(header, value string) upcloud.LoadBalancerAction { + return upcloud.LoadBalancerAction{ + Type: upcloud.LoadBalancerActionTypeSetResponseHeader, + SetResponseHeader: &upcloud.LoadBalancerActionSetHeader{ + Header: header, + Value: value, + }, + } +} + func NewLoadBalancerNumMembersUpMatcher(m upcloud.LoadBalancerIntegerMatcherMethod, count int, backend string) upcloud.LoadBalancerMatcher { return upcloud.LoadBalancerMatcher{ Type: upcloud.LoadBalancerMatcherTypeNumMembersUp, @@ -79,6 +99,7 @@ func NewLoadBalancerURLParamMatcher(m upcloud.LoadBalancerStringMatcherMethod, n } } +// Deprecated: Use NewLoadBalancerRequestHeaderMatcher instead func NewLoadBalancerHeaderMatcher(m upcloud.LoadBalancerStringMatcherMethod, name, value string, ignoreCase *bool) upcloud.LoadBalancerMatcher { return upcloud.LoadBalancerMatcher{ Type: upcloud.LoadBalancerMatcherTypeHeader, @@ -91,6 +112,30 @@ func NewLoadBalancerHeaderMatcher(m upcloud.LoadBalancerStringMatcherMethod, nam } } +func NewLoadBalancerRequestHeaderMatcher(m upcloud.LoadBalancerStringMatcherMethod, name, value string, ignoreCase *bool) upcloud.LoadBalancerMatcher { + return upcloud.LoadBalancerMatcher{ + Type: upcloud.LoadBalancerMatcherTypeRequestHeader, + RequestHeader: &upcloud.LoadBalancerMatcherStringWithArgument{ + Method: m, + Name: name, + Value: value, + IgnoreCase: ignoreCase, + }, + } +} + +func NewLoadBalancerResponseHeaderMatcher(m upcloud.LoadBalancerStringMatcherMethod, name, value string, ignoreCase *bool) upcloud.LoadBalancerMatcher { + return upcloud.LoadBalancerMatcher{ + Type: upcloud.LoadBalancerMatcherTypeResponseHeader, + ResponseHeader: &upcloud.LoadBalancerMatcherStringWithArgument{ + Method: m, + Name: name, + Value: value, + IgnoreCase: ignoreCase, + }, + } +} + func NewLoadBalancerCookieMatcher(m upcloud.LoadBalancerStringMatcherMethod, name, value string, ignoreCase *bool) upcloud.LoadBalancerMatcher { return upcloud.LoadBalancerMatcher{ Type: upcloud.LoadBalancerMatcherTypeCookie, @@ -112,6 +157,27 @@ func NewLoadBalancerHTTPMethodMatcher(method upcloud.LoadBalancerHTTPMatcherMeth } } +func NewLoadBalancerHTTPStatusMatcher(m upcloud.LoadBalancerIntegerMatcherMethod, status int) upcloud.LoadBalancerMatcher { + return upcloud.LoadBalancerMatcher{ + Type: upcloud.LoadBalancerMatcherTypeHTTPStatus, + HTTPStatus: &upcloud.LoadBalancerMatcherInteger{ + Method: m, + Value: status, + }, + } +} + +func NewLoadBalancerHTTPStatusRangeMatcher(start, end int) upcloud.LoadBalancerMatcher { + return upcloud.LoadBalancerMatcher{ + Type: upcloud.LoadBalancerMatcherTypeHTTPStatus, + HTTPStatus: &upcloud.LoadBalancerMatcherInteger{ + Method: upcloud.LoadBalancerIntegerMatcherMethodRange, + RangeStart: start, + RangeEnd: end, + }, + } +} + func NewLoadBalancerHostMatcher(host string) upcloud.LoadBalancerMatcher { return upcloud.LoadBalancerMatcher{ Type: upcloud.LoadBalancerMatcherTypeHost,