Skip to content

Commit 4c6e9c8

Browse files
committed
Configure buffering for vs/vsr
1 parent f56662d commit 4c6e9c8

File tree

7 files changed

+236
-26
lines changed

7 files changed

+236
-26
lines changed

docs/virtualserver-and-virtualserverroute.md

+18
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This document is the reference documentation for the resources. To see additiona
1818
- [VirtualServerRoute.Subroute](#virtualserverroutesubroute)
1919
- [Common Parts of the VirtualServer and VirtualServerRoute](#common-parts-of-the-virtualserver-and-virtualserverroute)
2020
- [Upstream](#upstream)
21+
- [Upstream.Buffers](#upstreambuffers)
2122
- [Upstream.TLS](#upstreamtls)
2223
- [Upstream.Healthcheck](#upstreamhealthcheck)
2324
- [Header](#header)
@@ -218,6 +219,23 @@ tls:
218219
| `tls` | The TLS configuration for the Upstream. | [`tls`](#UpstreamTLS) | No |
219220
| `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 |
220221
| `slow-start` | The slow start allows an upstream server to gradually recover its weight from 0 to its nominal value after it has been recovered or became available or when the server becomes available after a period of time it was considered unavailable. By default, the slow start is disabled. See the [slow_start](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#slow_start) parameter of the server directive. Note: The parameter cannot be used along with the `random`, `hash` or `ip_hash` load balancing methods and will be ignored. | `string` | No |
222+
| `buffering` | Enables buffering of responses from the upstream server. See the [proxy_buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering) directive. The default is set in the `proxy-buffering` ConfigMap key. | `boolean` | No |
223+
| `buffers` | Configures the buffers used for reading a response from the upstream server for a single connection. | [`buffers`](#UpstreamBuffers) | No |
224+
| `buffer-size` | Sets the size of the buffer used for reading the first part of a response received from the upstream server. See the [proxy_buffer_size](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) directive. The default is set in the `proxy-buffer-size` ConfigMap key. | `string` | No |
225+
226+
### Upstream.Buffers
227+
The buffers field configures the buffers used for reading a response from the upstream server for a single connection:
228+
229+
```yaml
230+
number: 4
231+
size: 8K
232+
```
233+
See the [proxy_buffers](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers) directive for additional information.
234+
235+
| Field | Description | Type | Required |
236+
| ----- | ----------- | ---- | -------- |
237+
| `number` | Configures the number of buffers. The default is set in the `proxy-buffers` ConfigMap key. | `int` | Yes |
238+
| `size` | Configures the size of a buffer. The default is set in the `proxy-buffers` ConfigMap key. | `string` | Yes |
221239

222240
### Upstream.TLS
223241

internal/configs/virtualserver.go

+17-3
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,20 @@ func generateString(s string, defaultS string) string {
428428
return s
429429
}
430430

431+
func generateBuffers(s *conf_v1alpha1.UpstreamBuffers, defaultS string) string {
432+
if s == nil {
433+
return defaultS
434+
}
435+
return fmt.Sprintf("%v %v", s.Number, s.Size)
436+
}
437+
438+
func generateBool(s *bool, defaultS bool) bool {
439+
if s != nil {
440+
return *s
441+
}
442+
return defaultS
443+
}
444+
431445
func generateLocation(path string, upstreamName string, upstream conf_v1alpha1.Upstream, cfgParams *ConfigParams) version2.Location {
432446
return version2.Location{
433447
Path: path,
@@ -437,9 +451,9 @@ func generateLocation(path string, upstreamName string, upstream conf_v1alpha1.U
437451
ProxySendTimeout: generateString(upstream.ProxySendTimeout, cfgParams.ProxySendTimeout),
438452
ClientMaxBodySize: generateString(upstream.ClientMaxBodySize, cfgParams.ClientMaxBodySize),
439453
ProxyMaxTempFileSize: cfgParams.ProxyMaxTempFileSize,
440-
ProxyBuffering: cfgParams.ProxyBuffering,
441-
ProxyBuffers: cfgParams.ProxyBuffers,
442-
ProxyBufferSize: cfgParams.ProxyBufferSize,
454+
ProxyBuffering: generateBool(upstream.ProxyBuffering, cfgParams.ProxyBuffering),
455+
ProxyBuffers: generateBuffers(upstream.ProxyBuffers, cfgParams.ProxyBuffers),
456+
ProxyBufferSize: generateString(upstream.ProxyBufferSize, cfgParams.ProxyBufferSize),
443457
ProxyPass: fmt.Sprintf("%v://%v", generateProxyPassProtocol(upstream.TLS.Enable), upstreamName),
444458
ProxyNextUpstream: generateString(upstream.ProxyNextUpstream, "error timeout"),
445459
ProxyNextUpstreamTimeout: generateString(upstream.ProxyNextUpstreamTimeout, "0s"),

internal/configs/virtualserver_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,29 @@ func TestGenerateString(t *testing.T) {
940940
}
941941
}
942942

943+
func TestGenerateBuffer(t *testing.T) {
944+
tests := []struct {
945+
inputS *conf_v1alpha1.UpstreamBuffers
946+
expected string
947+
}{
948+
{
949+
inputS: nil,
950+
expected: "8 4k",
951+
},
952+
{
953+
inputS: &conf_v1alpha1.UpstreamBuffers{Number: 8, Size: "16K"},
954+
expected: "8 16K",
955+
},
956+
}
957+
958+
for _, test := range tests {
959+
result := generateBuffers(test.inputS, "8 4k")
960+
if result != test.expected {
961+
t.Errorf("generateBuffer() return %v but expected %v", result, test.expected)
962+
}
963+
}
964+
}
965+
943966
func TestGenerateLocation(t *testing.T) {
944967
cfgParams := ConfigParams{
945968
ProxyConnectTimeout: "30s",

pkg/apis/configuration/v1alpha1/types.go

+27-18
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,33 @@ type VirtualServerSpec struct {
2525

2626
// Upstream defines an upstream.
2727
type Upstream struct {
28-
Name string `json:"name"`
29-
Service string `json:"service"`
30-
Port uint16 `json:"port"`
31-
LBMethod string `json:"lb-method"`
32-
FailTimeout string `json:"fail-timeout"`
33-
MaxFails *int `json:"max-fails"`
34-
MaxConns *int `json:"max-conns"`
35-
Keepalive *int `json:"keepalive"`
36-
ProxyConnectTimeout string `json:"connect-timeout"`
37-
ProxyReadTimeout string `json:"read-timeout"`
38-
ProxySendTimeout string `json:"send-timeout"`
39-
ProxyNextUpstream string `json:"next-upstream"`
40-
ProxyNextUpstreamTimeout string `json:"next-upstream-timeout"`
41-
ProxyNextUpstreamTries int `json:"next-upstream-tries"`
42-
ClientMaxBodySize string `json:"client-max-body-size"`
43-
TLS UpstreamTLS `json:"tls"`
44-
HealthCheck *HealthCheck `json:"healthCheck"`
45-
SlowStart string `json:"slow-start"`
28+
Name string `json:"name"`
29+
Service string `json:"service"`
30+
Port uint16 `json:"port"`
31+
LBMethod string `json:"lb-method"`
32+
FailTimeout string `json:"fail-timeout"`
33+
MaxFails *int `json:"max-fails"`
34+
MaxConns *int `json:"max-conns"`
35+
Keepalive *int `json:"keepalive"`
36+
ProxyConnectTimeout string `json:"connect-timeout"`
37+
ProxyReadTimeout string `json:"read-timeout"`
38+
ProxySendTimeout string `json:"send-timeout"`
39+
ProxyNextUpstream string `json:"next-upstream"`
40+
ProxyNextUpstreamTimeout string `json:"next-upstream-timeout"`
41+
ProxyNextUpstreamTries int `json:"next-upstream-tries"`
42+
ProxyBuffering *bool `json:"buffering"`
43+
ProxyBuffers *UpstreamBuffers `json:"buffers"`
44+
ProxyBufferSize string `json:"buffer-size"`
45+
ClientMaxBodySize string `json:"client-max-body-size"`
46+
TLS UpstreamTLS `json:"tls"`
47+
HealthCheck *HealthCheck `json:"healthCheck"`
48+
SlowStart string `json:"slow-start"`
49+
}
50+
51+
// UpstreamBuffers defines Buffer Configuration for an Upstream
52+
type UpstreamBuffers struct {
53+
Number int `json:"number"`
54+
Size string `json:"size"`
4655
}
4756

4857
// UpstreamTLS defines a TLS configuration for an Upstream.

pkg/apis/configuration/v1alpha1/zz_generated.deepcopy.go

+26
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/configuration/validation/validation.go

+44-3
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,28 @@ func validateTime(time string, fieldPath *field.Path) field.ErrorList {
9696
}
9797

9898
// http://nginx.org/en/docs/syntax.html
99-
const sizeFmt = `\d+[kKmMgG]?`
100-
const sizeErrMsg = "must consist of numeric characters followed by a valid size suffix. 'k|K|m|M|g|G"
99+
const offsetFmt = `\d+[kKmMgG]?`
100+
const offsetErrMsg = "must consist of numeric characters followed by a valid size suffix. 'k|K|m|M|g|G"
101+
102+
var offsetRegexp = regexp.MustCompile("^" + offsetFmt + "$")
103+
104+
func validateOffset(offset string, fieldPath *field.Path) field.ErrorList {
105+
allErrs := field.ErrorList{}
106+
107+
if offset == "" {
108+
return allErrs
109+
}
110+
111+
if !offsetRegexp.MatchString(offset) {
112+
msg := validation.RegexError(offsetErrMsg, offsetFmt, "16", "32k", "64M")
113+
return append(allErrs, field.Invalid(fieldPath, offset, msg))
114+
}
115+
116+
return allErrs
117+
}
118+
119+
const sizeFmt = `\d+[kKmM]?`
120+
const sizeErrMsg = "must consist of numeric characters followed by a valid size suffix. 'k|K|m|M"
101121

102122
var sizeRegexp = regexp.MustCompile("^" + sizeFmt + "$")
103123

@@ -112,6 +132,25 @@ func validateSize(size string, fieldPath *field.Path) field.ErrorList {
112132
msg := validation.RegexError(sizeErrMsg, sizeFmt, "16", "32k", "64M")
113133
return append(allErrs, field.Invalid(fieldPath, size, msg))
114134
}
135+
return allErrs
136+
}
137+
138+
func validateBuffer(buff *v1alpha1.UpstreamBuffers, fieldPath *field.Path) field.ErrorList {
139+
allErrs := field.ErrorList{}
140+
141+
if buff == nil {
142+
return allErrs
143+
}
144+
145+
if buff.Number <= 0 {
146+
allErrs = append(allErrs, field.Invalid(fieldPath.Child("number"), buff.Number, "must be positive"))
147+
}
148+
149+
if buff.Size == "" {
150+
allErrs = append(allErrs, field.Required(fieldPath.Child("size"), "cannot be empty"))
151+
} else {
152+
allErrs = append(allErrs, validateSize(buff.Size, fieldPath.Child("size"))...)
153+
}
115154

116155
return allErrs
117156
}
@@ -329,9 +368,11 @@ func validateUpstreams(upstreams []v1alpha1.Upstream, fieldPath *field.Path, isP
329368
allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxFails, idxPath.Child("max-fails"))...)
330369
allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.Keepalive, idxPath.Child("keepalive"))...)
331370
allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxConns, idxPath.Child("max-conns"))...)
332-
allErrs = append(allErrs, validateSize(u.ClientMaxBodySize, idxPath.Child("client-max-body-size"))...)
371+
allErrs = append(allErrs, validateOffset(u.ClientMaxBodySize, idxPath.Child("client-max-body-size"))...)
333372
allErrs = append(allErrs, validateUpstreamHealthCheck(u.HealthCheck, idxPath.Child("healthCheck"))...)
334373
allErrs = append(allErrs, validateTime(u.SlowStart, idxPath.Child("slow-start"))...)
374+
allErrs = append(allErrs, validateBuffer(u.ProxyBuffers, idxPath.Child("buffers"))...)
375+
allErrs = append(allErrs, validateSize(u.ProxyBufferSize, idxPath.Child("buffer-size"))...)
335376

336377
for _, msg := range validation.IsValidPortNum(int(u.Port)) {
337378
allErrs = append(allErrs, field.Invalid(idxPath.Child("port"), u.Port, msg))

pkg/apis/configuration/validation/validation_test.go

+81-2
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,37 @@ func TestValidateUpstreamsFails(t *testing.T) {
322322
},
323323
msg: "invalid value for ClientMaxBodySize",
324324
},
325+
{
326+
upstreams: []v1alpha1.Upstream{
327+
{
328+
Name: "upstream1",
329+
Service: "test-1",
330+
Port: 80,
331+
ProxyBuffers: &v1alpha1.UpstreamBuffers{
332+
Number: -1,
333+
Size: "1G",
334+
},
335+
},
336+
},
337+
expectedUpstreamNames: map[string]sets.Empty{
338+
"upstream1": {},
339+
},
340+
msg: "invalid value for ProxyBuffers",
341+
},
342+
{
343+
upstreams: []v1alpha1.Upstream{
344+
{
345+
Name: "upstream1",
346+
Service: "test-1",
347+
Port: 80,
348+
ProxyBufferSize: "1G",
349+
},
350+
},
351+
expectedUpstreamNames: map[string]sets.Empty{
352+
"upstream1": {},
353+
},
354+
msg: "invalid value for ProxyBufferSize",
355+
},
325356
}
326357

327358
isPlus := false
@@ -1658,16 +1689,64 @@ func TestValidateTime(t *testing.T) {
16581689
}
16591690
}
16601691

1692+
func TestValidateOffset(t *testing.T) {
1693+
var validInput = []string{"", "1", "10k", "11m", "1K", "100M", "5G"}
1694+
for _, test := range validInput {
1695+
allErrs := validateOffset(test, field.NewPath("offset-field"))
1696+
if len(allErrs) != 0 {
1697+
t.Errorf("validateOffset(%q) returned an error for valid input", test)
1698+
}
1699+
}
1700+
1701+
var invalidInput = []string{"55mm", "2mG", "6kb", "-5k", "1L", "5Gb"}
1702+
for _, test := range invalidInput {
1703+
allErrs := validateOffset(test, field.NewPath("offset-field"))
1704+
if len(allErrs) == 0 {
1705+
t.Errorf("validateOffset(%q) didn't return error for invalid input.", test)
1706+
}
1707+
}
1708+
}
1709+
1710+
func TestValidateBuffer(t *testing.T) {
1711+
validbuff := &v1alpha1.UpstreamBuffers{Number: 8, Size: "8k"}
1712+
allErrs := validateBuffer(validbuff, field.NewPath("buffers-field"))
1713+
1714+
if len(allErrs) != 0 {
1715+
t.Errorf("validateBuffer returned errors %v valid input %v", allErrs, validbuff)
1716+
}
1717+
1718+
invalidbuff := []*v1alpha1.UpstreamBuffers{
1719+
{
1720+
Number: -8,
1721+
Size: "15m",
1722+
},
1723+
{
1724+
Number: 8,
1725+
Size: "15G",
1726+
},
1727+
{
1728+
Number: 8,
1729+
Size: "",
1730+
},
1731+
}
1732+
for _, test := range invalidbuff {
1733+
allErrs = validateBuffer(test, field.NewPath("buffers-field"))
1734+
if len(allErrs) == 0 {
1735+
t.Errorf("validateBuffer didn't return error for invalid input %v.", test)
1736+
}
1737+
}
1738+
}
1739+
16611740
func TestValidateSize(t *testing.T) {
1662-
var validInput = []string{"", "1", "10k", "11m", "1K", "100M"}
1741+
var validInput = []string{"", "4k", "8K", "16m", "32M"}
16631742
for _, test := range validInput {
16641743
allErrs := validateSize(test, field.NewPath("size-field"))
16651744
if len(allErrs) != 0 {
16661745
t.Errorf("validateSize(%q) returned an error for valid input", test)
16671746
}
16681747
}
16691748

1670-
var invalidInput = []string{"55mm", "2mG", "6kb", "-5k", "1L"}
1749+
var invalidInput = []string{"55mm", "2mG", "6kb", "-5k", "1L", "5G"}
16711750
for _, test := range invalidInput {
16721751
allErrs := validateSize(test, field.NewPath("size-field"))
16731752
if len(allErrs) == 0 {

0 commit comments

Comments
 (0)