Skip to content

Commit 20ec16f

Browse files
authored
Merge pull request #287 from isaachawley/slow-start-support
Support slow-start for Plus
2 parents 9394492 + 1fbb8f0 commit 20ec16f

File tree

10 files changed

+87
-13
lines changed

10 files changed

+87
-13
lines changed

examples/customization/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ The table below summarizes all of the options. For some of them, there are examp
6363
| `nginx.com/health-checks` | N/A | Enables active health checks. | `False` | [Support for Active Health Checks](../health-checks). |
6464
| `nginx.com/health-checks-mandatory` | N/A | Configures active health checks as mandatory. | `False` | [Support for Active Health Checks](../health-checks). |
6565
| `nginx.com/health-checks-mandatory-queue` | N/A | When active health checks are mandatory, configures a queue for temporary storing incoming requests during the time when NGINX Plus is checking the health of the endpoints after a configuration reload. | `0` | [Support for Active Health Checks](../health-checks). |
66+
| `nginx.com/slow-start` | N/A | Sets the upstream server [slow-start period](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/#server-slow-start). By default, slow-start is activated after a server becomes [available](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#passive-health-checks) or [healthy](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-health-check/#active-health-checks). To enable slow-start for newly added servers, configure [mandatory active health checks](../health-checks). | `"0s"` | |
6667

6768
## Using ConfigMaps
6869

nginx-controller/nginx/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Config struct {
3838
HealthCheckEnabled bool
3939
HealthCheckMandatory bool
4040
HealthCheckMandatoryQueue int64
41+
SlowStart string
4142

4243
// http://nginx.org/en/docs/http/ngx_http_realip_module.html
4344
RealIPHeader string

nginx-controller/nginx/configurator.go

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -349,22 +349,38 @@ func (cnf *Configurator) createConfig(ingEx *IngressEx) Config {
349349
}
350350
if cnf.isPlus() {
351351
ingCfg.HealthCheckEnabled = healthCheckEnabled
352-
if healthCheckMandatory, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory", ingEx.Ingress); exists {
353-
if err != nil {
354-
glog.Error(err)
355-
}
356-
ingCfg.HealthCheckMandatory = healthCheckMandatory
357-
if healthCheckQueue, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory-queue", ingEx.Ingress); exists {
358-
if err != nil {
359-
glog.Error(err)
360-
}
361-
ingCfg.HealthCheckMandatoryQueue = healthCheckQueue
362-
}
363-
}
364352
} else {
365353
glog.Warning("Annotation 'nginx.com/health-checks' requires NGINX Plus")
366354
}
367355
}
356+
if ingCfg.HealthCheckEnabled {
357+
if healthCheckMandatory, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory", ingEx.Ingress); exists {
358+
if err != nil {
359+
glog.Error(err)
360+
}
361+
ingCfg.HealthCheckMandatory = healthCheckMandatory
362+
}
363+
}
364+
if ingCfg.HealthCheckMandatory {
365+
if healthCheckQueue, exists, err := GetMapKeyAsInt(ingEx.Ingress.Annotations, "nginx.com/health-checks-mandatory-queue", ingEx.Ingress); exists {
366+
if err != nil {
367+
glog.Error(err)
368+
}
369+
ingCfg.HealthCheckMandatoryQueue = healthCheckQueue
370+
}
371+
}
372+
373+
if slowStart, exists := ingEx.Ingress.Annotations["nginx.com/slow-start"]; exists {
374+
if parsedSlowStart, err := ParseSlowStart(slowStart); err != nil {
375+
glog.Errorf("Ingress %s/%s: Invalid value nginx.org/slow-start: got %q: %v", ingEx.Ingress.GetNamespace(), ingEx.Ingress.GetName(), slowStart, err)
376+
} else {
377+
if cnf.isPlus() {
378+
ingCfg.SlowStart = parsedSlowStart
379+
} else {
380+
glog.Warning("Annotation 'nginx.com/slow-start' requires NGINX Plus")
381+
}
382+
}
383+
}
368384

369385
if serverTokens, exists, err := GetMapKeyAsBool(ingEx.Ingress.Annotations, "nginx.org/server-tokens", ingEx.Ingress); exists {
370386
if err != nil {
@@ -732,6 +748,7 @@ func (cnf *Configurator) createUpstream(ingEx *IngressEx, name string, backend *
732748
Port: addressport[1],
733749
MaxFails: cfg.MaxFails,
734750
FailTimeout: cfg.FailTimeout,
751+
SlowStart: cfg.SlowStart,
735752
})
736753
}
737754
if len(upsServers) > 0 {
@@ -905,6 +922,7 @@ func (cnf *Configurator) updatePlusEndpoints(ingEx *IngressEx) error {
905922
cfg := plus.ServerConfig{
906923
MaxFails: ingCfg.MaxFails,
907924
FailTimeout: ingCfg.FailTimeout,
925+
SlowStart: ingCfg.SlowStart,
908926
}
909927

910928
if ingEx.Ingress.Spec.Backend != nil {

nginx-controller/nginx/extensions.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package nginx
22

33
import (
4+
"errors"
45
"fmt"
6+
"regexp"
57
"strings"
68
)
79

@@ -59,3 +61,28 @@ func validateHashLBMethod(method string) (string, error) {
5961
}
6062
return "", fmt.Errorf("Invalid load balancing method: %q", method)
6163
}
64+
65+
// http://nginx.org/en/docs/syntax.html
66+
var validTimeSuffixes = []string{
67+
"ms",
68+
"s",
69+
"m",
70+
"h",
71+
"d",
72+
"w",
73+
"M",
74+
"y",
75+
}
76+
77+
var durationEscaped = strings.Join(validTimeSuffixes, "|")
78+
var validNginxTime = regexp.MustCompile(`^([0-9]+([` + durationEscaped + `]?){0,1} *)+$`)
79+
80+
// ParseSlowStart ensures that the slow_start value in the annotation is valid.
81+
func ParseSlowStart(s string) (string, error) {
82+
s = strings.TrimSpace(s)
83+
84+
if validNginxTime.MatchString(s) {
85+
return s, nil
86+
}
87+
return "", errors.New("Invalid time string")
88+
}

nginx-controller/nginx/extensions_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,24 @@ func TestParseLBMethodForPlus(t *testing.T) {
8181
}
8282
}
8383
}
84+
85+
86+
func TestParseSlowStart(t *testing.T) {
87+
var testsWithValidInput = []string{"1", "1m10s", "11 11", "5m 30s", "1s", "100m", "5w", "15m", "11M", "3h", "100y", "600"}
88+
var invalidInput = []string{"ss", "rM", "m0m", "s1s", "-5s", "", "1L"}
89+
for _, test := range testsWithValidInput {
90+
result, err := ParseSlowStart(test)
91+
if err != nil {
92+
t.Errorf("TestParseSlowStart(%q) returned an error for valid input", test)
93+
}
94+
if test != result {
95+
t.Errorf("TestParseSlowStart(%q) returned %q expected %q", test, result, test)
96+
}
97+
}
98+
for _, test := range invalidInput {
99+
result, err := ParseSlowStart(test)
100+
if err == nil {
101+
t.Errorf("TestParseSlowStart(%q) didn't return error. Returned: %q", test, result)
102+
}
103+
}
104+
}

nginx-controller/nginx/nginx.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type UpstreamServer struct {
5151
Port string
5252
MaxFails int64
5353
FailTimeout string
54+
SlowStart string
5455
}
5556

5657
// HealthCheck describes an active HTTP health check

nginx-controller/nginx/plus/nginx_api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type NginxAPIController struct {
1414
type ServerConfig struct {
1515
MaxFails int64
1616
FailTimeout string
17+
SlowStart string
1718
}
1819

1920
func NewNginxAPIController(httpClient *http.Client, endpoint string, local bool) (*NginxAPIController, error) {
@@ -37,6 +38,7 @@ func (nginx *NginxAPIController) UpdateServers(upstream string, servers []string
3738
Server: s,
3839
MaxFails: config.MaxFails,
3940
FailTimeout: config.FailTimeout,
41+
SlowStart: config.SlowStart,
4042
})
4143
}
4244

nginx-controller/nginx/plus/nginx_client.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type UpstreamServer struct {
2525
Server string `json:"server"`
2626
MaxFails int64 `json:"max_fails"`
2727
FailTimeout string `json:"fail_timeout,omitempty"`
28+
SlowStart string `json:"slow_start,omitempty"`
2829
}
2930

3031
type apiErrorResponse struct {

nginx-controller/nginx/templates/nginx-plus.ingress.tmpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ upstream {{$upstream.Name}} {
33
zone {{$upstream.Name}} 256k;
44
{{if $upstream.LBMethod }}{{$upstream.LBMethod}};{{end}}
55
{{range $server := $upstream.UpstreamServers}}
6-
server {{$server.Address}}:{{$server.Port}} max_fails={{$server.MaxFails}} fail_timeout={{$server.FailTimeout}};{{end}}
6+
server {{$server.Address}}:{{$server.Port}} max_fails={{$server.MaxFails}} fail_timeout={{$server.FailTimeout}}
7+
{{- if $server.SlowStart}} slow_start={{$server.SlowStart}}{{end}};{{end}}
78
{{if $upstream.StickyCookie}}
89
sticky cookie {{$upstream.StickyCookie}};
910
{{end}}

nginx-controller/nginx/templates/templates_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var testUps = nginx.Upstream{
2121
Port: "8181",
2222
MaxFails: 0,
2323
FailTimeout: "1s",
24+
SlowStart: "5s",
2425
},
2526
},
2627
}

0 commit comments

Comments
 (0)