From ddd8023e4e99fd0461d3b86890662a63c819e932 Mon Sep 17 00:00:00 2001 From: Dean Coakley Date: Thu, 15 Aug 2019 16:26:31 +0100 Subject: [PATCH] Add ClientBodyMaxSize support in vs/vsr --- docs/virtualserver-and-virtualserverroute.md | 8 +++-- internal/configs/virtualserver.go | 2 +- pkg/apis/configuration/v1alpha1/types.go | 1 + .../configuration/validation/validation.go | 22 +++++++++++++ .../validation/validation_test.go | 32 +++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/virtualserver-and-virtualserverroute.md b/docs/virtualserver-and-virtualserverroute.md index efd9b92ec9..28cc3a56da 100644 --- a/docs/virtualserver-and-virtualserverroute.md +++ b/docs/virtualserver-and-virtualserverroute.md @@ -191,6 +191,7 @@ send-timeout: 30s next-upstream: "error timeout non_idempotent" next-upstream-timeout: 5s next-upstream-tries: 10 +client-max-body-size: 2m tls: enable: True ``` @@ -210,9 +211,10 @@ tls: | `connect-timeout` | The timeout for establishing a connection with an upstream server. See the [proxy_connect_timeout](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_connect_timeout) directive. The default is specified in the `proxy-connect-timeout` ConfigMap key. | `string` | No | | `read-timeout` | The timeout for reading a response from an upstream server. See the [proxy_read_timeout](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout) directive. The default is specified in the `proxy-read-timeout` ConfigMap key. | `string` | No | | `send-timeout` | The timeout for transmitting a request to an upstream server. See the [proxy_send_timeout](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_send_timeout) directive. The default is specified in the `proxy-send-timeout` ConfigMap key. | `string` | No | -| `next-upstream` | Specifies in which cases a request should be passed to the next upstream server. See the [proxy_next_upstream](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream) directive. The default is `error timeout`. | `string` | No | -| `next-upstream-timeout` | The time during which a request can be passed to the next upstream server. See the [proxy_next_upstream_timeout](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_timeout) directive. The `0` value turns off the time limit. The default is `0`. | `string` | No | -| `next-upstream-tries` | The number of possible tries for passing a request to the next upstream server. See the [proxy_next_upstream_tries](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries) directive. The `0` value turns off this limit. The default is `0`. | `int` | No | +| `next-upstream` | Specifies in which cases a request should be passed to the next upstream server. See the [proxy_next_upstream](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream) directive. The default is `error timeout`. | `string` | No | +| `next-upstream-timeout` | The time during which a request can be passed to the next upstream server. See the [proxy_next_upstream_timeout](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_timeout) directive. The `0` value turns off the time limit. The default is `0`. | `string` | No | +| `next-upstream-tries` | The number of possible tries for passing a request to the next upstream server. See the [proxy_next_upstream_tries](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries) directive. The `0` value turns off this limit. The default is `0`. | `int` | No | +| `client-max-body-size` | Sets the maximum allowed size of the client request body. See the [client_max_body_size](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) directive. The default is set in the `client-max-body-size` ConfigMap key. | `string` | No | | `tls` | The TLS configuration for the Upstream. | [`tls`](#UpstreamTLS) | No | | `healthCheck` | The health check configuration for the Upstream. See the [health_check](http://nginx.org/en/docs/http/ngx_http_upstream_hc_module.html#health_check) directive. Note: this feature is supported only in NGINX Plus. | [`healthcheck`](#UpstreamHealthcheck) | No | diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index 586bbffbee..f3fc429925 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -398,7 +398,7 @@ func generateLocation(path string, upstreamName string, upstream conf_v1alpha1.U ProxyConnectTimeout: generateString(upstream.ProxyConnectTimeout, cfgParams.ProxyConnectTimeout), ProxyReadTimeout: generateString(upstream.ProxyReadTimeout, cfgParams.ProxyReadTimeout), ProxySendTimeout: generateString(upstream.ProxySendTimeout, cfgParams.ProxySendTimeout), - ClientMaxBodySize: cfgParams.ClientMaxBodySize, + ClientMaxBodySize: generateString(upstream.ClientMaxBodySize, cfgParams.ClientMaxBodySize), ProxyMaxTempFileSize: cfgParams.ProxyMaxTempFileSize, ProxyBuffering: cfgParams.ProxyBuffering, ProxyBuffers: cfgParams.ProxyBuffers, diff --git a/pkg/apis/configuration/v1alpha1/types.go b/pkg/apis/configuration/v1alpha1/types.go index 8a128c6bf2..31493c975f 100644 --- a/pkg/apis/configuration/v1alpha1/types.go +++ b/pkg/apis/configuration/v1alpha1/types.go @@ -39,6 +39,7 @@ type Upstream struct { ProxyNextUpstream string `json:"next-upstream"` ProxyNextUpstreamTimeout string `json:"next-upstream-timeout"` ProxyNextUpstreamTries int `json:"next-upstream-tries"` + ClientMaxBodySize string `json:"client-max-body-size"` TLS UpstreamTLS `json:"tls"` HealthCheck *HealthCheck `json:"healthCheck"` } diff --git a/pkg/apis/configuration/validation/validation.go b/pkg/apis/configuration/validation/validation.go index 01419881a4..b821934263 100644 --- a/pkg/apis/configuration/validation/validation.go +++ b/pkg/apis/configuration/validation/validation.go @@ -95,6 +95,27 @@ func validateTime(time string, fieldPath *field.Path) field.ErrorList { return allErrs } +// http://nginx.org/en/docs/syntax.html +const sizeFmt = `\d+[kKmMgG]?` +const sizeErrMsg = "must consist of numeric characters followed by a valid size suffix. 'k|K|m|M|g|G" + +var sizeRegexp = regexp.MustCompile("^" + sizeFmt + "$") + +func validateSize(size string, fieldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if size == "" { + return allErrs + } + + if !sizeRegexp.MatchString(size) { + msg := validation.RegexError(sizeErrMsg, sizeFmt, "16", "32k", "64M") + return append(allErrs, field.Invalid(fieldPath, size, msg)) + } + + return allErrs +} + func validateUpstreamLBMethod(lBMethod string, fieldPath *field.Path, isPlus bool) field.ErrorList { allErrs := field.ErrorList{} if lBMethod == "" { @@ -308,6 +329,7 @@ func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path, isP allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxFails, idxPath.Child("max-fails"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.Keepalive, idxPath.Child("keepalive"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxConns, idxPath.Child("max-conns"))...) + allErrs = append(allErrs, validateSize(u.ClientMaxBodySize, idxPath.Child("client-max-body-size"))...) allErrs = append(allErrs, validateUpstreamHealthCheck(u.HealthCheck, idxPath.Child("healthCheck"))...) for _, msg := range validation.IsValidPortNum(int(u.Port)) { diff --git a/pkg/apis/configuration/validation/validation_test.go b/pkg/apis/configuration/validation/validation_test.go index 010ef86f18..341061f79a 100644 --- a/pkg/apis/configuration/validation/validation_test.go +++ b/pkg/apis/configuration/validation/validation_test.go @@ -308,6 +308,20 @@ func TestValidateUpstreamsFails(t *testing.T) { }, msg: "negative value for MaxConns", }, + { + upstreams: []v1alpha1.Upstream{ + { + Name: "upstream1", + Service: "test-1", + Port: 80, + ClientMaxBodySize: "7mins", + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": {}, + }, + msg: "invalid value for ClientMaxBodySize", + }, } isPlus := false @@ -1644,6 +1658,24 @@ func TestValidateTime(t *testing.T) { } } +func TestValidateSize(t *testing.T) { + var validInput = []string{"", "1", "10k", "11m", "1K", "100M"} + for _, test := range validInput { + allErrs := validateSize(test, field.NewPath("size-field")) + if len(allErrs) != 0 { + t.Errorf("validateSize(%q) returned an error for valid input", test) + } + } + + var invalidInput = []string{"55mm", "2mG", "6kb", "-5k", "1L"} + for _, test := range invalidInput { + allErrs := validateSize(test, field.NewPath("size-field")) + if len(allErrs) == 0 { + t.Errorf("validateSize(%q) didn't return error for invalid input.", test) + } + } +} + func TestValidateTimeFails(t *testing.T) { time := "invalid" allErrs := validateTime(time, field.NewPath("time-field"))