Support proxy_next_upstream_timeout

This commit is contained in:
Alex Kursell 2019-04-15 11:08:57 -04:00
parent 11539da09d
commit ffeb1fe348
9 changed files with 113 additions and 60 deletions

View file

@ -58,6 +58,7 @@ You can add these Kubernetes annotations to specific Ingress objects to customiz
|[nginx.ingress.kubernetes.io/proxy-send-timeout](#custom-timeouts)|number| |[nginx.ingress.kubernetes.io/proxy-send-timeout](#custom-timeouts)|number|
|[nginx.ingress.kubernetes.io/proxy-read-timeout](#custom-timeouts)|number| |[nginx.ingress.kubernetes.io/proxy-read-timeout](#custom-timeouts)|number|
|[nginx.ingress.kubernetes.io/proxy-next-upstream](#custom-timeouts)|string| |[nginx.ingress.kubernetes.io/proxy-next-upstream](#custom-timeouts)|string|
|[nginx.ingress.kubernetes.io/proxy-next-upstream-timeout](#custom-timeouts)|number|
|[nginx.ingress.kubernetes.io/proxy-next-upstream-tries](#custom-timeouts)|number| |[nginx.ingress.kubernetes.io/proxy-next-upstream-tries](#custom-timeouts)|number|
|[nginx.ingress.kubernetes.io/proxy-request-buffering](#custom-timeouts)|string| |[nginx.ingress.kubernetes.io/proxy-request-buffering](#custom-timeouts)|string|
|[nginx.ingress.kubernetes.io/proxy-redirect-from](#proxy-redirect)|string| |[nginx.ingress.kubernetes.io/proxy-redirect-from](#proxy-redirect)|string|
@ -489,6 +490,7 @@ In some scenarios is required to have different values. To allow this we provide
- `nginx.ingress.kubernetes.io/proxy-send-timeout` - `nginx.ingress.kubernetes.io/proxy-send-timeout`
- `nginx.ingress.kubernetes.io/proxy-read-timeout` - `nginx.ingress.kubernetes.io/proxy-read-timeout`
- `nginx.ingress.kubernetes.io/proxy-next-upstream` - `nginx.ingress.kubernetes.io/proxy-next-upstream`
- `nginx.ingress.kubernetes.io/proxy-next-upstream-timeout`
- `nginx.ingress.kubernetes.io/proxy-next-upstream-tries` - `nginx.ingress.kubernetes.io/proxy-next-upstream-tries`
- `nginx.ingress.kubernetes.io/proxy-request-buffering` - `nginx.ingress.kubernetes.io/proxy-request-buffering`

View file

@ -138,6 +138,7 @@ The following table shows a configuration option's name, type, and the default v
|[proxy-cookie-path](#proxy-cookie-path)|string|"off"| |[proxy-cookie-path](#proxy-cookie-path)|string|"off"|
|[proxy-cookie-domain](#proxy-cookie-domain)|string|"off"| |[proxy-cookie-domain](#proxy-cookie-domain)|string|"off"|
|[proxy-next-upstream](#proxy-next-upstream)|string|"error timeout"| |[proxy-next-upstream](#proxy-next-upstream)|string|"error timeout"|
|[proxy-next-upstream-timeout](#proxy-next-upstream-timeout)|int|0|
|[proxy-next-upstream-tries](#proxy-next-upstream-tries)|int|3| |[proxy-next-upstream-tries](#proxy-next-upstream-tries)|int|3|
|[proxy-redirect-from](#proxy-redirect-from)|string|"off"| |[proxy-redirect-from](#proxy-redirect-from)|string|"off"|
|[proxy-request-buffering](#proxy-request-buffering)|string|"on"| |[proxy-request-buffering](#proxy-request-buffering)|string|"on"|
@ -789,6 +790,10 @@ Sets a text that [should be changed in the domain attribute](http://nginx.org/en
Specifies in [which cases](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream) a request should be passed to the next server. Specifies in [which cases](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream) a request should be passed to the next server.
## proxy-next-upstream-timeout
[Limits the time](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_timeout) in seconds during which a request can be passed to the next server.
## proxy-next-upstream-tries ## proxy-next-upstream-tries
Limit the number of [possible tries](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries) a request should be passed to the next server. Limit the number of [possible tries](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries) a request should be passed to the next server.

View file

@ -25,20 +25,21 @@ import (
// Config returns the proxy timeout to use in the upstream server/s // Config returns the proxy timeout to use in the upstream server/s
type Config struct { type Config struct {
BodySize string `json:"bodySize"` BodySize string `json:"bodySize"`
ConnectTimeout int `json:"connectTimeout"` ConnectTimeout int `json:"connectTimeout"`
SendTimeout int `json:"sendTimeout"` SendTimeout int `json:"sendTimeout"`
ReadTimeout int `json:"readTimeout"` ReadTimeout int `json:"readTimeout"`
BuffersNumber int `json:"buffersNumber"` BuffersNumber int `json:"buffersNumber"`
BufferSize string `json:"bufferSize"` BufferSize string `json:"bufferSize"`
CookieDomain string `json:"cookieDomain"` CookieDomain string `json:"cookieDomain"`
CookiePath string `json:"cookiePath"` CookiePath string `json:"cookiePath"`
NextUpstream string `json:"nextUpstream"` NextUpstream string `json:"nextUpstream"`
NextUpstreamTries int `json:"nextUpstreamTries"` NextUpstreamTimeout int `json:"nextUpstreamTimeout"`
ProxyRedirectFrom string `json:"proxyRedirectFrom"` NextUpstreamTries int `json:"nextUpstreamTries"`
ProxyRedirectTo string `json:"proxyRedirectTo"` ProxyRedirectFrom string `json:"proxyRedirectFrom"`
RequestBuffering string `json:"requestBuffering"` ProxyRedirectTo string `json:"proxyRedirectTo"`
ProxyBuffering string `json:"proxyBuffering"` RequestBuffering string `json:"requestBuffering"`
ProxyBuffering string `json:"proxyBuffering"`
} }
// Equal tests for equality between two Configuration types // Equal tests for equality between two Configuration types
@ -76,6 +77,9 @@ func (l1 *Config) Equal(l2 *Config) bool {
if l1.NextUpstream != l2.NextUpstream { if l1.NextUpstream != l2.NextUpstream {
return false return false
} }
if l1.NextUpstreamTimeout != l2.NextUpstreamTimeout {
return false
}
if l1.NextUpstreamTries != l2.NextUpstreamTries { if l1.NextUpstreamTries != l2.NextUpstreamTries {
return false return false
} }
@ -157,6 +161,11 @@ func (a proxy) Parse(ing *extensions.Ingress) (interface{}, error) {
config.NextUpstream = defBackend.ProxyNextUpstream config.NextUpstream = defBackend.ProxyNextUpstream
} }
config.NextUpstreamTimeout, err = parser.GetIntAnnotation("proxy-next-upstream-timeout", ing)
if err != nil {
config.NextUpstreamTimeout = defBackend.ProxyNextUpstreamTimeout
}
config.NextUpstreamTries, err = parser.GetIntAnnotation("proxy-next-upstream-tries", ing) config.NextUpstreamTries, err = parser.GetIntAnnotation("proxy-next-upstream-tries", ing)
if err != nil { if err != nil {
config.NextUpstreamTries = defBackend.ProxyNextUpstreamTries config.NextUpstreamTries = defBackend.ProxyNextUpstreamTries

View file

@ -70,16 +70,17 @@ type mockBackend struct {
func (m mockBackend) GetDefaultBackend() defaults.Backend { func (m mockBackend) GetDefaultBackend() defaults.Backend {
return defaults.Backend{ return defaults.Backend{
ProxyConnectTimeout: 10, ProxyConnectTimeout: 10,
ProxySendTimeout: 15, ProxySendTimeout: 15,
ProxyReadTimeout: 20, ProxyReadTimeout: 20,
ProxyBuffersNumber: 4, ProxyBuffersNumber: 4,
ProxyBufferSize: "10k", ProxyBufferSize: "10k",
ProxyBodySize: "3k", ProxyBodySize: "3k",
ProxyNextUpstream: "error", ProxyNextUpstream: "error",
ProxyNextUpstreamTries: 3, ProxyNextUpstreamTimeout: 0,
ProxyRequestBuffering: "on", ProxyNextUpstreamTries: 3,
ProxyBuffering: "off", ProxyRequestBuffering: "on",
ProxyBuffering: "off",
} }
} }
@ -94,6 +95,7 @@ func TestProxy(t *testing.T) {
data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k" data[parser.GetAnnotationWithPrefix("proxy-buffer-size")] = "1k"
data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k" data[parser.GetAnnotationWithPrefix("proxy-body-size")] = "2k"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off" data[parser.GetAnnotationWithPrefix("proxy-next-upstream")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-timeout")] = "5"
data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3" data[parser.GetAnnotationWithPrefix("proxy-next-upstream-tries")] = "3"
data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off" data[parser.GetAnnotationWithPrefix("proxy-request-buffering")] = "off"
data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on" data[parser.GetAnnotationWithPrefix("proxy-buffering")] = "on"
@ -128,6 +130,9 @@ func TestProxy(t *testing.T) {
if p.NextUpstream != "off" { if p.NextUpstream != "off" {
t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream) t.Errorf("expected off as next-upstream but returned %v", p.NextUpstream)
} }
if p.NextUpstreamTimeout != 5 {
t.Errorf("expected 5 as next-upstream-timeout but returned %v", p.NextUpstreamTimeout)
}
if p.NextUpstreamTries != 3 { if p.NextUpstreamTries != 3 {
t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries) t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries)
} }
@ -174,6 +179,9 @@ func TestProxyWithNoAnnotation(t *testing.T) {
if p.NextUpstream != "error" { if p.NextUpstream != "error" {
t.Errorf("expected error as next-upstream but returned %v", p.NextUpstream) t.Errorf("expected error as next-upstream but returned %v", p.NextUpstream)
} }
if p.NextUpstreamTimeout != 0 {
t.Errorf("expected 0 as next-upstream-timeout but returned %v", p.NextUpstreamTimeout)
}
if p.NextUpstreamTries != 3 { if p.NextUpstreamTries != 3 {
t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries) t.Errorf("expected 3 as next-upstream-tries but returned %v", p.NextUpstreamTries)
} }

View file

@ -672,26 +672,27 @@ func NewDefault() Configuration {
UseHTTP2: true, UseHTTP2: true,
ProxyStreamTimeout: "600s", ProxyStreamTimeout: "600s",
Backend: defaults.Backend{ Backend: defaults.Backend{
ProxyBodySize: bodySize, ProxyBodySize: bodySize,
ProxyConnectTimeout: 5, ProxyConnectTimeout: 5,
ProxyReadTimeout: 60, ProxyReadTimeout: 60,
ProxySendTimeout: 60, ProxySendTimeout: 60,
ProxyBuffersNumber: 4, ProxyBuffersNumber: 4,
ProxyBufferSize: "4k", ProxyBufferSize: "4k",
ProxyCookieDomain: "off", ProxyCookieDomain: "off",
ProxyCookiePath: "off", ProxyCookiePath: "off",
ProxyNextUpstream: "error timeout", ProxyNextUpstream: "error timeout",
ProxyNextUpstreamTries: 3, ProxyNextUpstreamTimeout: 0,
ProxyRequestBuffering: "on", ProxyNextUpstreamTries: 3,
ProxyRedirectFrom: "off", ProxyRequestBuffering: "on",
ProxyRedirectTo: "off", ProxyRedirectFrom: "off",
SSLRedirect: true, ProxyRedirectTo: "off",
CustomHTTPErrors: []int{}, SSLRedirect: true,
WhitelistSourceRange: []string{}, CustomHTTPErrors: []int{},
SkipAccessLogURLs: []string{}, WhitelistSourceRange: []string{},
LimitRate: 0, SkipAccessLogURLs: []string{},
LimitRateAfter: 0, LimitRate: 0,
ProxyBuffering: "off", LimitRateAfter: 0,
ProxyBuffering: "off",
}, },
UpstreamKeepaliveConnections: 32, UpstreamKeepaliveConnections: 32,
UpstreamKeepaliveTimeout: 60, UpstreamKeepaliveTimeout: 60,

View file

@ -18,12 +18,13 @@ package controller
import ( import (
"fmt" "fmt"
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
"github.com/mitchellh/hashstructure" "github.com/mitchellh/hashstructure"
"k8s.io/klog" "k8s.io/klog"
@ -841,19 +842,20 @@ func (n *NGINXController) createServers(data []*ingress.Ingress,
bdef := n.store.GetDefaultBackend() bdef := n.store.GetDefaultBackend()
ngxProxy := proxy.Config{ ngxProxy := proxy.Config{
BodySize: bdef.ProxyBodySize, BodySize: bdef.ProxyBodySize,
ConnectTimeout: bdef.ProxyConnectTimeout, ConnectTimeout: bdef.ProxyConnectTimeout,
SendTimeout: bdef.ProxySendTimeout, SendTimeout: bdef.ProxySendTimeout,
ReadTimeout: bdef.ProxyReadTimeout, ReadTimeout: bdef.ProxyReadTimeout,
BuffersNumber: bdef.ProxyBuffersNumber, BuffersNumber: bdef.ProxyBuffersNumber,
BufferSize: bdef.ProxyBufferSize, BufferSize: bdef.ProxyBufferSize,
CookieDomain: bdef.ProxyCookieDomain, CookieDomain: bdef.ProxyCookieDomain,
CookiePath: bdef.ProxyCookiePath, CookiePath: bdef.ProxyCookiePath,
NextUpstream: bdef.ProxyNextUpstream, NextUpstream: bdef.ProxyNextUpstream,
NextUpstreamTries: bdef.ProxyNextUpstreamTries, NextUpstreamTimeout: bdef.ProxyNextUpstreamTimeout,
RequestBuffering: bdef.ProxyRequestBuffering, NextUpstreamTries: bdef.ProxyNextUpstreamTries,
ProxyRedirectFrom: bdef.ProxyRedirectFrom, RequestBuffering: bdef.ProxyRequestBuffering,
ProxyBuffering: bdef.ProxyBuffering, ProxyRedirectFrom: bdef.ProxyRedirectFrom,
ProxyBuffering: bdef.ProxyBuffering,
} }
// generated on Start() with createDefaultSSLCertificate() // generated on Start() with createDefaultSSLCertificate()

View file

@ -73,6 +73,10 @@ type Backend struct {
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream // http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream
ProxyNextUpstream string `json:"proxy-next-upstream"` ProxyNextUpstream string `json:"proxy-next-upstream"`
// Limits the time during which a request can be passed to the next server.
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_timeout
ProxyNextUpstreamTimeout int `json:"proxy-next-upstream-timeout"`
// Limits the number of possible tries for passing a request to the next server. // Limits the number of possible tries for passing a request to the next server.
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries // https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream_tries
ProxyNextUpstreamTries int `json:"proxy-next-upstream-tries"` ProxyNextUpstreamTries int `json:"proxy-next-upstream-tries"`

View file

@ -1237,6 +1237,7 @@ stream {
# In case of errors try the next upstream server before returning an error # In case of errors try the next upstream server before returning an error
proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }}; proxy_next_upstream {{ buildNextUpstream $location.Proxy.NextUpstream $all.Cfg.RetryNonIdempotent }};
proxy_next_upstream_timeout {{ $location.Proxy.NextUpstreamTimeout }};
proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }}; proxy_next_upstream_tries {{ $location.Proxy.NextUpstreamTries }};
{{/* Add any additional configuration defined */}} {{/* Add any additional configuration defined */}}

View file

@ -180,8 +180,9 @@ var _ = framework.IngressNginxDescribe("Annotations - Proxy", func() {
It("should build proxy next upstream", func() { It("should build proxy next upstream", func() {
annotations := map[string]string{ annotations := map[string]string{
"nginx.ingress.kubernetes.io/proxy-next-upstream": "error timeout http_502", "nginx.ingress.kubernetes.io/proxy-next-upstream": "error timeout http_502",
"nginx.ingress.kubernetes.io/proxy-next-upstream-tries": "5", "nginx.ingress.kubernetes.io/proxy-next-upstream-timeout": "10",
"nginx.ingress.kubernetes.io/proxy-next-upstream-tries": "5",
} }
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, "http-svc", 80, &annotations) ing := framework.NewSingleIngress(host, "/", host, f.Namespace, "http-svc", 80, &annotations)
@ -190,10 +191,30 @@ var _ = framework.IngressNginxDescribe("Annotations - Proxy", func() {
f.WaitForNginxServer(host, f.WaitForNginxServer(host,
func(server string) bool { func(server string) bool {
return strings.Contains(server, "proxy_next_upstream error timeout http_502;") && return strings.Contains(server, "proxy_next_upstream error timeout http_502;") &&
strings.Contains(server, "proxy_next_upstream_timeout 10;") &&
strings.Contains(server, "proxy_next_upstream_tries 5;") strings.Contains(server, "proxy_next_upstream_tries 5;")
}) })
}) })
It("should build proxy next upstream using configmap values", func() {
annotations := map[string]string{}
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, "http-svc", 80, &annotations)
f.EnsureIngress(ing)
f.SetNginxConfigMapData(map[string]string{
"proxy-next-upstream": "timeout http_502",
"proxy-next-upstream-timeout": "53",
"proxy-next-upstream-tries": "44",
})
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "proxy_next_upstream timeout http_502;") &&
strings.Contains(server, "proxy_next_upstream_timeout 53;") &&
strings.Contains(server, "proxy_next_upstream_tries 44;")
})
})
It("should setup proxy cookies", func() { It("should setup proxy cookies", func() {
annotations := map[string]string{ annotations := map[string]string{
"nginx.ingress.kubernetes.io/proxy-cookie-domain": "localhost example.org", "nginx.ingress.kubernetes.io/proxy-cookie-domain": "localhost example.org",