Merge branch 'kubernetes:main' into pie

This commit is contained in:
chriss-de 2023-01-19 14:20:15 +01:00 committed by GitHub
commit e87fd2273b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 1262 additions and 428 deletions

View file

@ -201,7 +201,6 @@ dev-env-stop: ## Deletes local Kubernetes cluster created by kind.
live-docs: ## Build and launch a local copy of the documentation website in http://localhost:8000
@docker build ${PLATFORM_FLAG} ${PLATFORM} \
--no-cache \
$(MAC_DOCKER_FLAGS) \
-t ingress-nginx-docs .github/actions/mkdocs
@docker run ${PLATFORM_FLAG} ${PLATFORM} --rm -it \
-p 8000:8000 \
@ -241,6 +240,7 @@ release: ensure-buildx clean
docker buildx build \
--no-cache \
$(MAC_DOCKER_FLAGS) \
--push \
--pull \
--progress plain \
@ -253,6 +253,7 @@ release: ensure-buildx clean
docker buildx build \
--no-cache \
$(MAC_DOCKER_FLAGS) \
--push \
--pull \
--progress plain \

View file

@ -38,7 +38,6 @@ the versions listed. Ingress-Nginx versions may work on older versions but the p
| Ingress-NGINX version | k8s supported version | Alpine Version | Nginx Version |
|-----------------------|------------------------------|----------------|---------------|
| v1.5.2 | 1.26, 1.25, 1.24, 1.23 | 3.17.2 | 1.21.6 |
| v1.5.1 | 1.25, 1.24, 1.23 | 3.16.2 | 1.21.6 |
| v1.4.0 | 1.25, 1.24, 1.23, 1.22 | 3.16.2 | 1.19.10† |
| v1.3.1 | 1.24, 1.23, 1.22, 1.21, 1.20 | 3.16.2 | 1.19.10† |

2
TAG
View file

@ -1 +1 @@
v1.5.1
v1.6.0

View file

@ -311,10 +311,12 @@ Kubernetes: `>=1.20.0-0`
| controller.containerPort | object | `{"http":80,"https":443}` | Configures the ports that the nginx-controller listens on |
| controller.customTemplate.configMapKey | string | `""` | |
| controller.customTemplate.configMapName | string | `""` | |
| controller.disablePathTypeValidation | bool | `false` | This configuration defines if Ingress Controller should validate pathType. If this is true, special characters will be allowed on paths of any pathType. If false, special characters are only allowed on paths with pathType = ImplementationSpecific |
| controller.dnsConfig | object | `{}` | Optionally customize the pod dnsConfig. |
| controller.dnsPolicy | string | `"ClusterFirst"` | Optionally change this to ClusterFirstWithHostNet in case you have 'hostNetwork: true'. By default, while using host network, name resolution uses the host's DNS. If you wish nginx-controller to keep resolving names inside the k8s network, use ClusterFirstWithHostNet. |
| controller.electionID | string | `""` | Election ID to use for status update, by default it uses the controller name combined with a suffix of 'leader' |
| controller.enableMimalloc | bool | `true` | Enable mimalloc as a drop-in replacement for malloc. # ref: https://github.com/microsoft/mimalloc # |
| controller.enableTopologyAwareRouting | bool | `false` | This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-aware-hints="auto" Defaults to false |
| controller.existingPsp | string | `""` | Use an existing PSP instead of creating one |
| controller.extraArgs | object | `{}` | Additional command line arguments to pass to nginx-ingress-controller E.g. to specify the default SSL certificate you can use |
| controller.extraContainers | list | `[]` | Additional containers to be added to the controller pod. See https://github.com/lemonldap-ng-controller/lemonldap-ng-controller as example. |
@ -376,6 +378,7 @@ Kubernetes: `>=1.20.0-0`
| controller.metrics.prometheusRule.rules | list | `[]` | |
| controller.metrics.service.annotations | object | `{}` | |
| controller.metrics.service.externalIPs | list | `[]` | List of IP addresses at which the stats-exporter service is available # Ref: https://kubernetes.io/docs/user-guide/services/#external-ips # |
| controller.metrics.service.labels | object | `{}` | Labels to be added to the metrics service resource |
| controller.metrics.service.loadBalancerSourceRanges | list | `[]` | |
| controller.metrics.service.servicePort | int | `10254` | |
| controller.metrics.service.type | string | `"ClusterIP"` | |

View file

@ -51,6 +51,9 @@
{{- if .Values.controller.watchIngressWithoutClass }}
- --watch-ingress-without-class=true
{{- end }}
{{- if .Values.controller.enableTopologyAwareRouting }}
- --enable-topology-aware-routing=true
{{- end }}
{{- range $key, $value := .Values.controller.extraArgs }}
{{- /* Accept keys without values or with false as value */}}
{{- if eq ($value | quote | len) 2 }}

View file

@ -14,6 +14,7 @@ metadata:
namespace: {{ .Release.Namespace }}
data:
allow-snippet-annotations: "{{ .Values.controller.allowSnippetAnnotations }}"
disable-pathtype-validation: "{{ .Values.controller.disablePathTypeValidation }}"
{{- if .Values.controller.addHeaders }}
add-headers: {{ .Release.Namespace }}/{{ include "ingress-nginx.fullname" . }}-custom-add-headers
{{- end }}

View file

@ -77,12 +77,21 @@ controller:
# -- Process IngressClass per name (additionally as per spec.controller).
ingressClassByName: false
# -- This configuration enables Topology Aware Routing feature, used together with service annotation service.kubernetes.io/topology-aware-hints="auto"
# Defaults to false
enableTopologyAwareRouting: false
# -- This configuration defines if Ingress Controller should allow users to set
# their own *-snippet annotations, otherwise this is forbidden / dropped
# when users add those annotations.
# Global snippets in ConfigMap are still respected
allowSnippetAnnotations: true
# -- This configuration defines if Ingress Controller should validate pathType.
# If this is true, special characters will be allowed on paths of any pathType. If
# false, special characters are only allowed on paths with pathType = ImplementationSpecific
disablePathTypeValidation: false
# -- Required for use with CNI based kubernetes installations (such as ones set up by kubeadm),
# since CNI and hostport don't mix yet. Can be deprecated once https://github.com/kubernetes/kubernetes/issues/23920
# is merged
@ -700,6 +709,8 @@ controller:
annotations: {}
# prometheus.io/scrape: "true"
# prometheus.io/port: "10254"
# -- Labels to be added to the metrics service resource
labels: {}
# clusterIP: ""

View file

@ -24,6 +24,7 @@ They are set in the container spec of the `ingress-nginx-controller` Deployment
| `--enable-metrics` | Enables the collection of NGINX metrics. (default true) |
| `--enable-ssl-chain-completion` | Autocomplete SSL certificate chains with missing intermediate CA certificates. Certificates uploaded to Kubernetes must have the "Authority Information Access" X.509 v3 extension for this to succeed. (default false)|
| `--enable-ssl-passthrough` | Enable SSL Passthrough. (default false) |
| `--enable-topology-aware-routing` | Enable topology aware hints feature, needs service object annotation service.kubernetes.io/topology-aware-hints sets to auto. (default false) |
| `--health-check-path` | URL path of the health check endpoint. Configured inside the NGINX status server. All requests received on the port defined by the healthz-port parameter are forwarded internally to this path. (default "/healthz") |
| `--health-check-timeout` | Time limit, in seconds, for a probe to health-check-path to succeed. (default 10) |
| `--healthz-port` | Port to use for the healthz endpoint. (default 10254) |

View file

@ -355,56 +355,40 @@ Prometheus metrics are exposed on port 10254.
### Request metrics
* `nginx_ingress_controller_request_duration_seconds` Histogram
The request processing time in seconds (affected by client speed)
* `nginx_ingress_controller_request_duration_seconds` Histogram\
The request processing (time elapsed between the first bytes were read from the client and the log write after the last bytes were sent to the client) time in seconds (affected by client speed).\
nginx var: `request_time`
* `nginx_ingress_controller_response_duration_seconds` Histogram
The time spent on receiving the response from the upstream server (affected by client speed)
* `nginx_ingress_controller_response_duration_seconds` Histogram\
The time spent on receiving the response from the upstream server in seconds (affected by client speed when the response is bigger than proxy buffers).\
Note: can be up to several millis bigger than the `nginx_ingress_controller_request_duration_seconds` because of the different measuring method.
nginx var: `upstream_response_time`
* `nginx_ingress_controller_header_duration_seconds` Histogram
The time spent on receiving first header from the upstream server
* `nginx_ingress_controller_header_duration_seconds` Histogram\
The time spent on receiving first header from the upstream server\
nginx var: `upstream_header_time`
* `nginx_ingress_controller_connect_duration_seconds` Histogram
The time spent on establishing a connection with the upstream server
* `nginx_ingress_controller_connect_duration_seconds` Histogram\
The time spent on establishing a connection with the upstream server\
nginx var: `upstream_connect_time`
* `nginx_ingress_controller_response_size` Histogram
The response length (including request line, header, and request body)
* `nginx_ingress_controller_response_size` Histogram\
The response length (including request line, header, and request body)\
nginx var: `bytes_sent`
* `nginx_ingress_controller_request_size` Histogram
The request length (including request line, header, and request body)
* `nginx_ingress_controller_request_size` Histogram\
The request length (including request line, header, and request body)\
nginx var: `request_length`
* `nginx_ingress_controller_requests` Counter
* `nginx_ingress_controller_requests` Counter\
The total number of client requests
* `nginx_ingress_controller_bytes_sent` Histogram
The number of bytes sent to a client. **Deprecated**, use `nginx_ingress_controller_response_size`
* `nginx_ingress_controller_bytes_sent` Histogram\
The number of bytes sent to a client. **Deprecated**, use `nginx_ingress_controller_response_size`\
nginx var: `bytes_sent`
* `nginx_ingress_controller_ingress_upstream_latency_seconds` Summary
Upstream service latency per Ingress. **Deprecated**, use `nginx_ingress_controller_connect_duration_seconds`
* `nginx_ingress_controller_ingress_upstream_latency_seconds` Summary\
Upstream service latency per Ingress. **Deprecated**, use `nginx_ingress_controller_connect_duration_seconds`\
nginx var: `upstream_connect_time`
```
@ -469,6 +453,8 @@ Prometheus metrics are exposed on port 10254.
# TYPE nginx_ingress_controller_ssl_certificate_info gauge
# HELP nginx_ingress_controller_success Cumulative number of Ingress controller reload operations
# TYPE nginx_ingress_controller_success counter
# HELP nginx_ingress_controller_orphan_ingress Gauge reporting status of ingress orphanity, 1 indicates orphaned ingress. 'namespace' is the string used to identify namespace of ingress, 'ingress' for ingress name and 'type' for 'no-service' or 'no-endpoint' of orphanity
# TYPE nginx_ingress_controller_orphan_ingress gauge
```
### Admission metrics

View file

@ -62,7 +62,7 @@ The following table shows a configuration option's name, type, and the default v
|[hsts-max-age](#hsts-max-age)|string|"15724800"|
|[hsts-preload](#hsts-preload)|bool|"false"|
|[keep-alive](#keep-alive)|int|75|
|[keep-alive-requests](#keep-alive-requests)|int|100|
|[keep-alive-requests](#keep-alive-requests)|int|1000|
|[large-client-header-buffers](#large-client-header-buffers)|string|"4 8k"|
|[log-format-escape-none](#log-format-escape-none)|bool|"false"|
|[log-format-escape-json](#log-format-escape-json)|bool|"false"|
@ -103,7 +103,9 @@ The following table shows a configuration option's name, type, and the default v
|[brotli-min-length](#brotli-min-length)|int|20|
|[brotli-types](#brotli-types)|string|"application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"|
|[use-http2](#use-http2)|bool|"true"|
|[gzip-disable](#gzip-disable)|string|""|
|[gzip-level](#gzip-level)|int|1|
|[gzip-min-length](#gzip-min-length)|int|256|
|[gzip-types](#gzip-types)|string|"application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/javascript text/plain text/x-component"|
|[worker-processes](#worker-processes)|string|`<Number of CPUs>`|
|[worker-cpu-affinity](#worker-cpu-affinity)|string|""|
@ -215,6 +217,8 @@ The following table shows a configuration option's name, type, and the default v
|[service-upstream](#service-upstream)|bool|"false"|
|[ssl-reject-handshake](#ssl-reject-handshake)|bool|"false"|
|[debug-connections](#debug-connections)|[]string|"127.0.0.1,1.1.1.1/24"|
|[disable-pathtype-validation](#disable-pathtype-validation)|bool|"false"|
|[path-additional-allowed-chars](#path-additional-allowed-chars)|string|"^%$[](){}*+?"|
## add-headers
@ -694,7 +698,8 @@ _**default:**_ false
## enable-brotli
Enables or disables compression of HTTP responses using the ["brotli" module](https://github.com/google/ngx_brotli).
The default mime type list to compress is: `application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component`. _**default:**_ is disabled
The default mime type list to compress is: `application/xml+rss application/atom+xml application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component`.
_**default:**_ false
> __Note:__ Brotli does not works in Safari < 11. For more information see [https://caniuse.com/#feat=brotli](https://caniuse.com/#feat=brotli)
@ -715,6 +720,10 @@ _**default:**_ `application/xml+rss application/atom+xml application/javascript
Enables or disables [HTTP/2](https://nginx.org/en/docs/http/ngx_http_v2_module.html) support in secure connections.
## gzip-disable
Disables [gzipping](http://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_disable) of responses for requests with "User-Agent" header fields matching any of the specified regular expressions.
## gzip-level
Sets the gzip Compression Level that will be used. _**default:**_ 1
@ -1326,3 +1335,24 @@ _**default:**_ ""
_References:_
[http://nginx.org/en/docs/ngx_core_module.html#debug_connection](http://nginx.org/en/docs/ngx_core_module.html#debug_connection)
## disable-pathtype-validation
Ingress Controller validates the pathType, and only allows special characters on "path" if pathType is
ImplementationSpecific.
The only characters allowed on ingresses with pathType not ImplementationSpecific
will be 0-9, a-z, A-Z, "-", ".", "_", "~", "/".
If the validation is disabled, the [#path-additional-allowed-chars](#path-additional-allowed-chars) will
be allowed on any pathType.
This behavior can be disabled, so special characters are accepted regardless of pathType
_**default:**_ "false"
## path-additional-allowed-chars
When validating path on Ingress resources, defines the additional set of special characters that
will be allowed.
See also [#disable-pathtype-validation](#disable-pathtype-validation).
_**default:**_ "^%$[](){}*+?|"

35
go.mod
View file

@ -30,14 +30,14 @@ require (
google.golang.org/grpc/examples v0.0.0-20221220003428-4f16fbe410f7
gopkg.in/go-playground/pool.v3 v3.1.1
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
k8s.io/api v0.25.4
k8s.io/apiextensions-apiserver v0.25.0
k8s.io/apimachinery v0.25.4
k8s.io/apiserver v0.25.0
k8s.io/cli-runtime v0.25.0
k8s.io/client-go v0.25.4
k8s.io/code-generator v0.25.0
k8s.io/component-base v0.25.4
k8s.io/api v0.26.0
k8s.io/apiextensions-apiserver v0.26.0
k8s.io/apimachinery v0.26.0
k8s.io/apiserver v0.26.0
k8s.io/cli-runtime v0.26.0
k8s.io/client-go v0.26.0
k8s.io/code-generator v0.26.0
k8s.io/component-base v0.26.0
k8s.io/klog/v2 v2.80.1
pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732
sigs.k8s.io/controller-runtime v0.13.1
@ -45,17 +45,7 @@ require (
)
require (
cloud.google.com/go/compute v1.12.1 // indirect
cloud.google.com/go/compute/metadata v0.2.1 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest v0.11.27 // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.20 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/BurntSushi/toml v1.0.0 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
@ -70,11 +60,10 @@ require (
github.com/go-errors/errors v1.0.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/swag v0.19.14 // indirect
github.com/godbus/dbus/v5 v5.0.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gomarkdown/markdown v0.0.0-20210514010506-3b9f47219fe7 // indirect
@ -123,9 +112,9 @@ require (
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 // indirect
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 // indirect
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
sigs.k8s.io/kustomize/api v0.12.1 // indirect
sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect

83
go.sum
View file

@ -19,10 +19,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/compute v1.12.1 h1:gKVJMEyqV5c/UnpzjjQbo3Rjvvqpr9B1DFSbJC4OXr0=
cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU=
cloud.google.com/go/compute/metadata v0.2.1 h1:efOwf5ymceDhK6PKMnnrTHP4pppY5L22mle96M1yP48=
cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@ -35,30 +31,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.27 h1:F3R3q42aWytozkV8ihzcgMO4OA4cuqr3bNlsEuF6//A=
github.com/Azure/go-autorest/autorest v0.11.27/go.mod h1:7l8ybrIdUmGqZMTD0sRtAr8NvbHjfofbf8RSP2q7w7U=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.20 h1:gJ3E98kMpFB1MFqQCvA1yFab8vthOeD4VlFRQULxahg=
github.com/Azure/go-autorest/autorest/adal v0.9.20/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=
github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -126,7 +102,6 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
@ -135,8 +110,8 @@ github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
@ -147,9 +122,6 @@ github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -362,7 +334,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
@ -421,8 +392,6 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@ -471,7 +440,6 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -490,7 +458,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -511,7 +478,6 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -750,32 +716,31 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.25.4 h1:3YO8J4RtmG7elEgaWMb4HgmpS2CfY1QlaOz9nwB+ZSs=
k8s.io/api v0.25.4/go.mod h1:IG2+RzyPQLllQxnhzD8KQNEu4c4YvyDTpSMztf4A0OQ=
k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY=
k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E=
k8s.io/apimachinery v0.25.4 h1:CtXsuaitMESSu339tfhVXhQrPET+EiWnIY1rcurKnAc=
k8s.io/apimachinery v0.25.4/go.mod h1:jaF9C/iPNM1FuLl7Zuy5b9v+n35HGSh6AQ4HYRkCqwo=
k8s.io/apiserver v0.25.0 h1:8kl2ifbNffD440MyvHtPaIz1mw4mGKVgWqM0nL+oyu4=
k8s.io/apiserver v0.25.0/go.mod h1:BKwsE+PTC+aZK+6OJQDPr0v6uS91/HWxX7evElAH6xo=
k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q=
k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw=
k8s.io/client-go v0.25.4 h1:3RNRDffAkNU56M/a7gUfXaEzdhZlYhoW8dgViGy5fn8=
k8s.io/client-go v0.25.4/go.mod h1:8trHCAC83XKY0wsBIpbirZU4NTUpbuhc2JnI7OruGZw=
k8s.io/code-generator v0.25.0 h1:QP8fJuXu882ztf6dsqJsso/Btm94pMd68TAZC1rE6KI=
k8s.io/code-generator v0.25.0/go.mod h1:B6jZgI3DvDFAualltPitbYMQ74NjaCFxum3YeKZZ+3w=
k8s.io/component-base v0.25.4 h1:n1bjg9Yt+G1C0WnIDJmg2fo6wbEU1UGMRiQSjmj7hNQ=
k8s.io/component-base v0.25.4/go.mod h1:nnZJU8OP13PJEm6/p5V2ztgX2oyteIaAGKGMYb2L2cY=
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185 h1:TT1WdmqqXareKxZ/oNXEUSwKlLiHzPMyB0t8BaFeBYI=
k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/api v0.26.0 h1:IpPlZnxBpV1xl7TGk/X6lFtpgjgntCg8PJ+qrPHAC7I=
k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
k8s.io/apiextensions-apiserver v0.26.0 h1:Gy93Xo1eg2ZIkNX/8vy5xviVSxwQulsnUdQ00nEdpDo=
k8s.io/apiextensions-apiserver v0.26.0/go.mod h1:7ez0LTiyW5nq3vADtK6C3kMESxadD51Bh6uz3JOlqWQ=
k8s.io/apimachinery v0.26.0 h1:1feANjElT7MvPqp0JT6F3Ss6TWDwmcjLypwoPpEf7zg=
k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
k8s.io/apiserver v0.26.0 h1:q+LqIK5EZwdznGZb8bq0+a+vCqdeEEe4Ux3zsOjbc4o=
k8s.io/apiserver v0.26.0/go.mod h1:aWhlLD+mU+xRo+zhkvP/gFNbShI4wBDHS33o0+JGI84=
k8s.io/cli-runtime v0.26.0 h1:aQHa1SyUhpqxAw1fY21x2z2OS5RLtMJOCj7tN4oq8mw=
k8s.io/cli-runtime v0.26.0/go.mod h1:o+4KmwHzO/UK0wepE1qpRk6l3o60/txUZ1fEXWGIKTY=
k8s.io/client-go v0.26.0 h1:lT1D3OfO+wIi9UFolCrifbjUUgu7CpLca0AD8ghRLI8=
k8s.io/client-go v0.26.0/go.mod h1:I2Sh57A79EQsDmn7F7ASpmru1cceh3ocVT9KlX2jEZg=
k8s.io/code-generator v0.26.0 h1:ZDY+7Gic9p/lACgD1G72gQg2CvNGeAYZTPIncv+iALM=
k8s.io/code-generator v0.26.0/go.mod h1:OMoJ5Dqx1wgaQzKgc+ZWaZPfGjdRq/Y3WubFrZmeI3I=
k8s.io/component-base v0.26.0 h1:0IkChOCohtDHttmKuz+EP3j3+qKmV55rM9gIFTXA7Vs=
k8s.io/component-base v0.26.0/go.mod h1:lqHwlfV1/haa14F/Z5Zizk5QmzaVf23nQzCwVOQpfC8=
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08=
k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4=
k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1 h1:MQ8BAZPZlWk3S9K4a9NCkIFQtZShWqoha7snGixVgEA=
k8s.io/kube-openapi v0.0.0-20220803162953-67bda5d908f1/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4=
k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E=
k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs=
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732 h1:SAElp8THCfmBdM+4lmWX5gebiSSkEr7PAYDVF91qpfg=
pault.ag/go/sniff v0.0.0-20200207005214-cf7e4d167732/go.mod h1:lpvCfhqEHNJSSpG5R5A2EgsVzG8RTt4RfPoQuRAcDmg=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

View file

@ -438,6 +438,11 @@ type Configuration struct {
// Default: true
UseHTTP2 bool `json:"use-http2,omitempty"`
// Disables gzipping of responses for requests with "User-Agent" header fields matching any of
// the specified regular expressions.
// http://nginx.org/en/docs/http/ngx_http_gzip_module.html#gzip_disable
GzipDisable string `json:"gzip-disable,omitempty"`
// gzip Compression Level that will be used
GzipLevel int `json:"gzip-level,omitempty"`
@ -781,6 +786,17 @@ type Configuration struct {
// http://nginx.org/en/docs/ngx_core_module.html#debug_connection
// Default: ""
DebugConnections []string `json:"debug-connections"`
// DisablePathTypeValidation allows the admin to disable the pathType validation.
// If PathTypeValidation is enabled, the Controller will only allow alphanumeric
// characters on path (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
DisablePathTypeValidation bool `json:"disable-pathtype-validation"`
// PathAdditionalAllowedChars allows the admin to specify what are the additional
// characters allowed in case of pathType=ImplementationSpecific.
// Case disable-pathtype-validation=true, this characters will be allowed on any path.
// Defaults to: "^%$[](){}*+?"
PathAdditionalAllowedChars string `json:"path-additional-allowed-chars"`
}
// NewDefault returns the default nginx configuration
@ -798,7 +814,6 @@ func NewDefault() Configuration {
defGlobalExternalAuth := GlobalExternalAuth{"", "", "", "", "", append(defResponseHeaders, ""), "", "", "", []string{}, map[string]string{}, false}
cfg := Configuration{
AllowSnippetAnnotations: true,
AllowBackendServerHeader: false,
AnnotationValueWordBlocklist: "",
@ -817,6 +832,8 @@ func NewDefault() Configuration {
ClientHeaderTimeout: 60,
ClientBodyBufferSize: "8k",
ClientBodyTimeout: 60,
DisablePathTypeValidation: false,
PathAdditionalAllowedChars: "^%$[](){}*+?|",
EnableUnderscoresInHeaders: false,
ErrorLogLevel: errorLevel,
UseForwardedHeaders: false,
@ -839,7 +856,7 @@ func NewDefault() Configuration {
GzipMinLength: 256,
GzipTypes: gzipTypes,
KeepAlive: 75,
KeepAliveRequests: 100,
KeepAliveRequests: 1000,
LargeClientHeaderBuffers: "4 8k",
LogFormatEscapeJSON: false,
LogFormatStream: logFormatStream,

View file

@ -50,9 +50,12 @@ import (
)
const (
defUpstreamName = "upstream-default-backend"
defServerName = "_"
rootLocation = "/"
defUpstreamName = "upstream-default-backend"
defServerName = "_"
rootLocation = "/"
emptyZone = ""
orphanMetricLabelNoService = "no-service"
orphanMetricLabelNoEndpoint = "no-endpoint"
)
// Configuration contains all the settings required by an Ingress controller
@ -131,6 +134,21 @@ type Configuration struct {
DynamicConfigurationRetries int
DisableSyncEvents bool
EnableTopologyAwareRouting bool
}
func getIngressPodZone(svc *apiv1.Service) string {
svcKey := k8s.MetaNamespaceKey(svc)
if svcZoneAnnotation, ok := svc.ObjectMeta.GetAnnotations()[apiv1.AnnotationTopologyAwareHints]; ok {
if strings.ToLower(svcZoneAnnotation) == "auto" {
if foundZone, ok := k8s.IngressNodeDetails.GetLabels()[apiv1.LabelTopologyZone]; ok {
klog.V(3).Infof("Svc has topology aware annotation enabled, try to use zone %q where controller pod is running for Service %q ", foundZone, svcKey)
return foundZone
}
}
}
return emptyZone
}
// GetPublishService returns the Service used to set the load-balancer status of Ingresses.
@ -307,6 +325,10 @@ func (n *NGINXController) CheckIngress(ing *networking.Ingress) error {
k8s.SetDefaultNGINXPathType(ing)
if err := utilingress.ValidateIngressPath(ing, cfg.DisablePathTypeValidation, cfg.PathAdditionalAllowedChars); err != nil {
return fmt.Errorf("ingress contains invalid characters: %s", err)
}
allIngresses := n.store.ListIngresses()
filter := func(toCheck *ingress.Ingress) bool {
@ -429,6 +451,13 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
var endps []ingress.Endpoint
/* #nosec */
targetPort, err := strconv.Atoi(svcPort) // #nosec
var zone string
if n.cfg.EnableTopologyAwareRouting {
zone = getIngressPodZone(svc)
} else {
zone = emptyZone
}
if err != nil {
// not a port number, fall back to using port name
klog.V(3).Infof("Searching Endpoints with %v port name %q for Service %q", proto, svcPort, nsName)
@ -436,7 +465,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
sp := svc.Spec.Ports[i]
if sp.Name == svcPort {
if sp.Protocol == proto {
endps = getEndpointsFromSlices(svc, &sp, proto, n.store.GetServiceEndpointsSlices)
endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices)
break
}
}
@ -447,7 +476,7 @@ func (n *NGINXController) getStreamServices(configmapName string, proto apiv1.Pr
sp := svc.Spec.Ports[i]
if sp.Port == int32(targetPort) {
if sp.Protocol == proto {
endps = getEndpointsFromSlices(svc, &sp, proto, n.store.GetServiceEndpointsSlices)
endps = getEndpointsFromSlices(svc, &sp, proto, zone, n.store.GetServiceEndpointsSlices)
break
}
}
@ -498,8 +527,13 @@ func (n *NGINXController) getDefaultUpstream() *ingress.Backend {
upstream.Endpoints = append(upstream.Endpoints, n.DefaultEndpoint())
return upstream
}
endps := getEndpointsFromSlices(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
var zone string
if n.cfg.EnableTopologyAwareRouting {
zone = getIngressPodZone(svc)
} else {
zone = emptyZone
}
endps := getEndpointsFromSlices(svc, &svc.Spec.Ports[0], apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
if len(endps) == 0 {
klog.Warningf("Service %q does not have any active Endpoint", svcKey)
endps = []ingress.Endpoint{n.DefaultEndpoint()}
@ -827,7 +861,13 @@ func (n *NGINXController) getBackendServers(ingresses []*ingress.Ingress) ([]*in
}
sp := location.DefaultBackend.Spec.Ports[0]
endps := getEndpointsFromSlices(location.DefaultBackend, &sp, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
var zone string
if n.cfg.EnableTopologyAwareRouting {
zone = getIngressPodZone(location.DefaultBackend)
} else {
zone = emptyZone
}
endps := getEndpointsFromSlices(location.DefaultBackend, &sp, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
// custom backend is valid only if contains at least one endpoint
if len(endps) > 0 {
name := fmt.Sprintf("custom-default-backend-%v-%v", location.DefaultBackend.GetNamespace(), location.DefaultBackend.GetName())
@ -1018,8 +1058,16 @@ func (n *NGINXController) createUpstreams(data []*ingress.Ingress, du *ingress.B
endp, err := n.serviceEndpoints(svcKey, port.String())
if err != nil {
klog.Warningf("Error obtaining Endpoints for Service %q: %v", svcKey, err)
n.metricCollector.IncOrphanIngress(ing.Namespace, ing.Name, orphanMetricLabelNoService)
continue
}
n.metricCollector.DecOrphanIngress(ing.Namespace, ing.Name, orphanMetricLabelNoService)
if len(endp) == 0 {
n.metricCollector.IncOrphanIngress(ing.Namespace, ing.Name, orphanMetricLabelNoEndpoint)
} else {
n.metricCollector.DecOrphanIngress(ing.Namespace, ing.Name, orphanMetricLabelNoEndpoint)
}
upstreams[name].Endpoints = endp
}
@ -1083,7 +1131,12 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
if err != nil {
return upstreams, err
}
var zone string
if n.cfg.EnableTopologyAwareRouting {
zone = getIngressPodZone(svc)
} else {
zone = emptyZone
}
klog.V(3).Infof("Obtaining ports information for Service %q", svcKey)
// Ingress with an ExternalName Service and no port defined for that Service
if svc.Spec.Type == apiv1.ServiceTypeExternalName {
@ -1092,7 +1145,7 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
return upstreams, nil
}
servicePort := externalNamePorts(backendPort, svc)
endps := getEndpointsFromSlices(svc, servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
endps := getEndpointsFromSlices(svc, servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
if len(endps) == 0 {
klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
return upstreams, nil
@ -1109,7 +1162,7 @@ func (n *NGINXController) serviceEndpoints(svcKey, backendPort string) ([]ingres
servicePort.TargetPort.String() == backendPort ||
servicePort.Name == backendPort {
endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, n.store.GetServiceEndpointsSlices)
endps := getEndpointsFromSlices(svc, &servicePort, apiv1.ProtocolTCP, zone, n.store.GetServiceEndpointsSlices)
if len(endps) == 0 {
klog.Warningf("Service %q does not have any active Endpoint.", svcKey)
}

View file

@ -201,6 +201,97 @@ func TestCheckIngress(t *testing.T) {
},
},
}
t.Run("when validating pathType", func(t *testing.T) {
t.Run("When ingress contains invalid path and pathType validation is not disabled", func(t *testing.T) {
nginx.store = fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
DisablePathTypeValidation: false,
},
}
nginx.command = testNginxTestCommand{
t: t,
err: nil,
expected: "",
}
nginx.cfg.IngressClassConfiguration = &ingressclass.IngressClassConfiguration{
WatchWithoutClass: true,
}
ingPath := &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ingress1",
Namespace: "user-namespace1",
Annotations: map[string]string{
"kubernetes.io/ingress.class": "nginx",
},
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/xpto/(a+)",
PathType: &pathTypePrefix,
},
},
},
},
},
},
},
}
if nginx.CheckIngress(ingPath) == nil {
t.Errorf("invalid path on pathTypePrefix and validation enabled should return an error")
}
})
t.Run("When ingress contains invalid path and pathType validation is disabled", func(t *testing.T) {
nginx.store = fakeIngressStore{
ingresses: []*ingress.Ingress{},
configuration: ngx_config.Configuration{
DisablePathTypeValidation: true,
PathAdditionalAllowedChars: "^%$[](){}*+?|",
},
}
nginx.command = testNginxTestCommand{
t: t,
err: nil,
expected: "_,example.com",
}
ingPath := &networking.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "test-ingress2",
Namespace: "user-namespace2",
},
Spec: networking.IngressSpec{
Rules: []networking.IngressRule{
{
Host: "example.com",
IngressRuleValue: networking.IngressRuleValue{
HTTP: &networking.HTTPIngressRuleValue{
Paths: []networking.HTTPIngressPath{
{
Path: "/example(/|$)(.*)",
PathType: &pathTypePrefix,
},
},
},
},
},
},
},
}
if nginx.CheckIngress(ingPath) != nil {
t.Errorf("invalid path on pathTypePrefix and validation disabled should not return an error: %s", nginx.CheckIngress(ingPath))
}
})
})
t.Run("when the class is the nginx one", func(t *testing.T) {
ing.ObjectMeta.Annotations["kubernetes.io/ingress.class"] = "nginx"
nginx.command = testNginxTestCommand{
@ -2481,8 +2572,9 @@ func newDynamicNginxController(t *testing.T, setConfigMap func(string) *v1.Confi
}
return &NGINXController{
store: storer,
cfg: config,
command: NewNginxCommand(),
store: storer,
cfg: config,
command: NewNginxCommand(),
metricCollector: metric.DummyCollector{},
}
}

View file

@ -35,7 +35,7 @@ import (
)
// getEndpoints returns a list of Endpoint structs for a given service/target port combination.
func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol,
func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto corev1.Protocol, zoneForHints string,
getServiceEndpointsSlices func(string) ([]*discoveryv1.EndpointSlice, error)) []ingress.Endpoint {
upsServers := []ingress.Endpoint{}
@ -49,6 +49,7 @@ func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto c
processedUpstreamServers := make(map[string]struct{})
svcKey := k8s.MetaNamespaceKey(s)
var useTopologyHints bool
// ExternalName services
if s.Spec.Type == corev1.ServiceTypeExternalName {
@ -111,12 +112,38 @@ func getEndpointsFromSlices(s *corev1.Service, port *corev1.ServicePort, proto c
ports = append(ports, targetPort)
}
}
useTopologyHints = false
if zoneForHints != emptyZone {
useTopologyHints = true
// check if all endpointslices has zone hints
for _, ep := range eps.Endpoints {
if ep.Hints == nil || len(ep.Hints.ForZones) == 0 {
useTopologyHints = false
break
}
}
if useTopologyHints {
klog.V(3).Infof("All endpoint slices has zone hint, using zone %q for Service %q", zoneForHints, svcKey)
}
}
for _, ep := range eps.Endpoints {
if !(*ep.Conditions.Ready) {
continue
}
epHasZone := false
if useTopologyHints {
for _, epzone := range ep.Hints.ForZones {
if epzone.Name == zoneForHints {
epHasZone = true
break
}
}
}
// ep.Hints
if useTopologyHints && !epHasZone {
continue
}
for _, epPort := range ports {
for _, epAddress := range ep.Addresses {

View file

@ -33,6 +33,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
svc *corev1.Service
port *corev1.ServicePort
proto corev1.Protocol
zone string
fn func(string) ([]*discoveryv1.EndpointSlice, error)
result []ingress.Endpoint
}{
@ -41,6 +42,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
nil,
nil,
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return nil, nil
},
@ -51,6 +53,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
&corev1.Service{},
nil,
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return nil, nil
},
@ -61,6 +64,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
&corev1.Service{},
&corev1.ServicePort{Name: "default"},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -75,6 +79,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
},
&corev1.ServicePort{Name: "default"},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -99,6 +104,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -123,6 +129,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -147,6 +154,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -176,6 +184,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -205,6 +214,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{}, nil
},
@ -229,6 +239,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return nil, fmt.Errorf("unexpected error")
},
@ -253,6 +264,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -296,6 +308,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -339,6 +352,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -382,6 +396,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -430,6 +445,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromInt(80),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -478,6 +494,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{
{
@ -552,6 +569,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{
{
@ -622,6 +640,7 @@ func TestGetEndpointsFromSlices(t *testing.T) {
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
@ -656,11 +675,251 @@ func TestGetEndpointsFromSlices(t *testing.T) {
},
},
},
{
"should return one endpoint which belongs to zone",
&corev1.Service{
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeClusterIP,
ClusterIP: "1.1.1.1",
Ports: []corev1.ServicePort{
{
Name: "default",
TargetPort: intstr.FromString("port-1"),
},
},
},
},
&corev1.ServicePort{
Name: "port-1",
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"eu-west-1b",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
},
Endpoints: []discoveryv1.Endpoint{
{
Addresses: []string{"1.1.1.1"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1b",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.2"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1a",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.3"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1c",
}},
}}[0],
},
},
Ports: []discoveryv1.EndpointPort{
{
Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
Port: &[]int32{80}[0],
Name: &[]string{"port-1"}[0],
},
},
}}, nil
},
[]ingress.Endpoint{
{
Address: "1.1.1.1",
Port: "80",
},
},
},
{
"should return all endpoints because one is missing zone hint",
&corev1.Service{
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeClusterIP,
ClusterIP: "1.1.1.1",
Ports: []corev1.ServicePort{
{
Name: "default",
TargetPort: intstr.FromString("port-1"),
},
},
},
},
&corev1.ServicePort{
Name: "port-1",
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"eu-west-1b",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
},
Endpoints: []discoveryv1.Endpoint{
{
Addresses: []string{"1.1.1.1"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1b",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.2"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1b",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.3"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{}}[0],
},
},
Ports: []discoveryv1.EndpointPort{
{
Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
Port: &[]int32{80}[0],
Name: &[]string{"port-1"}[0],
},
},
}}, nil
},
[]ingress.Endpoint{
{
Address: "1.1.1.1",
Port: "80",
},
{
Address: "1.1.1.2",
Port: "80",
},
{
Address: "1.1.1.3",
Port: "80",
},
},
},
{
"should return all endpoints because no zone from controller node",
&corev1.Service{
Spec: corev1.ServiceSpec{
Type: corev1.ServiceTypeClusterIP,
ClusterIP: "1.1.1.1",
Ports: []corev1.ServicePort{
{
Name: "default",
TargetPort: intstr.FromString("port-1"),
},
},
},
},
&corev1.ServicePort{
Name: "port-1",
TargetPort: intstr.FromString("port-1"),
},
corev1.ProtocolTCP,
"",
func(string) ([]*discoveryv1.EndpointSlice, error) {
return []*discoveryv1.EndpointSlice{{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{discoveryv1.LabelServiceName: "default"},
},
Endpoints: []discoveryv1.Endpoint{
{
Addresses: []string{"1.1.1.1"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1a",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.2"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1b",
}},
}}[0],
},
{
Addresses: []string{"1.1.1.3"},
Conditions: discoveryv1.EndpointConditions{
Ready: &[]bool{true}[0],
},
Hints: &[]discoveryv1.EndpointHints{{
ForZones: []discoveryv1.ForZone{{
Name: "eu-west-1c",
}},
}}[0],
},
},
Ports: []discoveryv1.EndpointPort{
{
Protocol: &[]corev1.Protocol{corev1.ProtocolTCP}[0],
Port: &[]int32{80}[0],
Name: &[]string{"port-1"}[0],
},
},
}}, nil
},
[]ingress.Endpoint{
{
Address: "1.1.1.1",
Port: "80",
},
{
Address: "1.1.1.2",
Port: "80",
},
{
Address: "1.1.1.3",
Port: "80",
},
},
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
result := getEndpointsFromSlices(testCase.svc, testCase.port, testCase.proto, testCase.fn)
result := getEndpointsFromSlices(testCase.svc, testCase.port, testCase.proto, testCase.zone, testCase.fn)
if len(testCase.result) != len(result) {
t.Errorf("Expected %d Endpoints but got %d", len(testCase.result), len(result))
}

View file

@ -846,6 +846,11 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
copyIng := &networkingv1.Ingress{}
ing.ObjectMeta.DeepCopyInto(&copyIng.ObjectMeta)
if err := ingressutils.ValidateIngressPath(ing, s.backendConfig.DisablePathTypeValidation, s.backendConfig.PathAdditionalAllowedChars); err != nil {
klog.Errorf("ingress %s contains invalid path and will be skipped: %s", key, err)
return
}
if s.backendConfig.AnnotationValueWordBlocklist != "" {
if err := checkBadAnnotationValue(copyIng.Annotations, s.backendConfig.AnnotationValueWordBlocklist); err != nil {
klog.Warningf("skipping ingress %s: %s", key, err)
@ -865,10 +870,6 @@ func (s *k8sStore) syncIngress(ing *networkingv1.Ingress) {
if path.Path == "" {
copyIng.Spec.Rules[ri].HTTP.Paths[pi].Path = "/"
}
if !ingressutils.IsSafePath(copyIng, path.Path) {
klog.Warningf("ingress %s contains invalid path %s", key, path.Path)
return
}
}
}

View file

@ -64,6 +64,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
"access-log-path": "/var/log/test/access.log",
"error-log-path": "/var/log/test/error.log",
"use-gzip": "false",
"gzip-disable": "msie6",
"gzip-level": "9",
"gzip-min-length": "1024",
"gzip-types": "text/html",
@ -87,6 +88,7 @@ func TestMergeConfigMapToStruct(t *testing.T) {
def.ProxyReadTimeout = 1
def.ProxySendTimeout = 2
def.UseProxyProtocol = true
def.GzipDisable = "msie6"
def.GzipLevel = 9
def.GzipMinLength = 1024
def.GzipTypes = "text/html"

View file

@ -32,6 +32,7 @@ var (
ingressOperation = []string{"controller_namespace", "controller_class", "controller_pod", "namespace", "ingress"}
sslLabelHost = []string{"namespace", "class", "host", "secret_name"}
sslInfoLabels = []string{"namespace", "class", "host", "secret_name", "identifier", "issuer_organization", "issuer_common_name", "serial_number", "public_key_algorithm"}
orphanityLabels = []string{"controller_namespace", "controller_class", "controller_pod", "namespace", "ingress", "type"}
)
// Controller defines base metrics about the ingress controller
@ -48,6 +49,7 @@ type Controller struct {
checkIngressOperationErrors *prometheus.CounterVec
sslExpireTime *prometheus.GaugeVec
sslInfo *prometheus.GaugeVec
OrphanIngress *prometheus.GaugeVec
constLabels prometheus.Labels
labels prometheus.Labels
@ -171,6 +173,15 @@ func NewController(pod, namespace, class string) *Controller {
},
[]string{"name"},
),
OrphanIngress: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: PrometheusNamespace,
Name: "orphan_ingress",
Help: `Gauge reporting status of ingress orphanity, 1 indicates orphaned ingress.
'namespace' is the string used to identify namespace of ingress, 'ingress' for ingress name and 'type' for 'no-service' or 'no-endpoint' of orphanity`,
},
orphanityLabels,
),
}
return cm
@ -214,6 +225,26 @@ func (cm *Controller) IncCheckErrorCount(namespace, name string) {
cm.checkIngressOperationErrors.MustCurryWith(cm.constLabels).With(labels).Inc()
}
// IncOrphanIngress sets the the orphaned ingress gauge to one
func (cm *Controller) IncOrphanIngress(namespace string, name string, orphanityType string) {
labels := prometheus.Labels{
"namespace": namespace,
"ingress": name,
"type": orphanityType,
}
cm.OrphanIngress.MustCurryWith(cm.constLabels).With(labels).Set(1.0)
}
// DecOrphanIngress sets the the orphaned ingress gauge to zero (all services has their endpoints)
func (cm *Controller) DecOrphanIngress(namespace string, name string, orphanityType string) {
labels := prometheus.Labels{
"namespace": namespace,
"ingress": name,
"type": orphanityType,
}
cm.OrphanIngress.MustCurryWith(cm.constLabels).With(labels).Set(0.0)
}
// ConfigSuccess set a boolean flag according to the output of the controller configuration reload
func (cm *Controller) ConfigSuccess(hash uint64, success bool) {
if success {
@ -242,6 +273,7 @@ func (cm Controller) Describe(ch chan<- *prometheus.Desc) {
cm.sslInfo.Describe(ch)
cm.leaderElection.Describe(ch)
cm.buildInfo.Describe(ch)
cm.OrphanIngress.Describe(ch)
}
// Collect implements the prometheus.Collector interface.
@ -257,6 +289,7 @@ func (cm Controller) Collect(ch chan<- prometheus.Metric) {
cm.sslInfo.Collect(ch)
cm.leaderElection.Collect(ch)
cm.buildInfo.Collect(ch)
cm.OrphanIngress.Collect(ch)
}
// SetSSLExpireTime sets the expiration time of SSL Certificates

View file

@ -41,6 +41,12 @@ func (dc DummyCollector) IncReloadCount() {}
// IncReloadErrorCount ...
func (dc DummyCollector) IncReloadErrorCount() {}
// IncOrphanIngress ...
func (dc DummyCollector) IncOrphanIngress(string, string, string) {}
// DecOrphanIngress ...
func (dc DummyCollector) DecOrphanIngress(string, string, string) {}
// IncCheckCount ...
func (dc DummyCollector) IncCheckCount(string, string) {}

View file

@ -43,6 +43,8 @@ type Collector interface {
IncCheckCount(string, string)
IncCheckErrorCount(string, string)
IncOrphanIngress(string, string, string)
DecOrphanIngress(string, string, string)
RemoveMetrics(ingresses, endpoints, certificates []string)
@ -181,6 +183,14 @@ func (c *collector) SetSSLInfo(servers []*ingress.Server) {
c.ingressController.SetSSLInfo(servers)
}
func (c *collector) IncOrphanIngress(namespace string, name string, orphanityType string) {
c.ingressController.IncOrphanIngress(namespace, name, orphanityType)
}
func (c *collector) DecOrphanIngress(namespace string, name string, orphanityType string) {
c.ingressController.DecOrphanIngress(namespace, name, orphanityType)
}
func (c *collector) SetHosts(hosts sets.String) {
c.socket.SetHosts(hosts)
}

View file

@ -19,6 +19,7 @@ package status
import (
"context"
"fmt"
v1 "k8s.io/api/networking/v1"
"net"
"regexp"
"sort"
@ -128,7 +129,7 @@ func (s statusSync) Shutdown() {
}
klog.InfoS("removing value from ingress status", "address", addrs)
s.updateStatus([]apiv1.LoadBalancerIngress{})
s.updateStatus([]v1.IngressLoadBalancerIngress{})
}
func (s *statusSync) sync(key interface{}) error {
@ -160,21 +161,21 @@ func NewStatusSyncer(config Config) Syncer {
return st
}
func nameOrIPToLoadBalancerIngress(nameOrIP string) apiv1.LoadBalancerIngress {
func nameOrIPToLoadBalancerIngress(nameOrIP string) v1.IngressLoadBalancerIngress {
if net.ParseIP(nameOrIP) != nil {
return apiv1.LoadBalancerIngress{IP: nameOrIP}
return v1.IngressLoadBalancerIngress{IP: nameOrIP}
}
return apiv1.LoadBalancerIngress{Hostname: nameOrIP}
return v1.IngressLoadBalancerIngress{Hostname: nameOrIP}
}
// runningAddresses returns a list of IP addresses and/or FQDN where the
// ingress controller is currently running
func (s *statusSync) runningAddresses() ([]apiv1.LoadBalancerIngress, error) {
func (s *statusSync) runningAddresses() ([]v1.IngressLoadBalancerIngress, error) {
if s.PublishStatusAddress != "" {
re := regexp.MustCompile(`,\s*`)
multipleAddrs := re.Split(s.PublishStatusAddress, -1)
addrs := make([]apiv1.LoadBalancerIngress, len(multipleAddrs))
addrs := make([]v1.IngressLoadBalancerIngress, len(multipleAddrs))
for i, addr := range multipleAddrs {
addrs[i] = nameOrIPToLoadBalancerIngress(addr)
}
@ -193,7 +194,7 @@ func (s *statusSync) runningAddresses() ([]apiv1.LoadBalancerIngress, error) {
return nil, err
}
addrs := make([]apiv1.LoadBalancerIngress, 0)
addrs := make([]v1.IngressLoadBalancerIngress, 0)
for i := range pods.Items {
pod := pods.Items[i]
// only Running pods are valid
@ -250,7 +251,7 @@ func (s *statusSync) isRunningMultiplePods() bool {
// standardizeLoadBalancerIngresses sorts the list of loadbalancer by
// IP
func standardizeLoadBalancerIngresses(lbi []apiv1.LoadBalancerIngress) []apiv1.LoadBalancerIngress {
func standardizeLoadBalancerIngresses(lbi []v1.IngressLoadBalancerIngress) []v1.IngressLoadBalancerIngress {
sort.SliceStable(lbi, func(a, b int) bool {
return lbi[a].IP < lbi[b].IP
})
@ -259,7 +260,7 @@ func standardizeLoadBalancerIngresses(lbi []apiv1.LoadBalancerIngress) []apiv1.L
}
// updateStatus changes the status information of Ingress rules
func (s *statusSync) updateStatus(newIngressPoint []apiv1.LoadBalancerIngress) {
func (s *statusSync) updateStatus(newIngressPoint []v1.IngressLoadBalancerIngress) {
ings := s.IngressLister.ListIngresses()
p := pool.NewLimited(10)
@ -283,7 +284,7 @@ func (s *statusSync) updateStatus(newIngressPoint []apiv1.LoadBalancerIngress) {
batch.WaitAll()
}
func runUpdate(ing *ingress.Ingress, status []apiv1.LoadBalancerIngress,
func runUpdate(ing *ingress.Ingress, status []v1.IngressLoadBalancerIngress,
client clientset.Interface) pool.WorkFunc {
return func(wu pool.WorkUnit) (interface{}, error) {
if wu.IsCancelled() {
@ -307,7 +308,7 @@ func runUpdate(ing *ingress.Ingress, status []apiv1.LoadBalancerIngress,
}
}
func lessLoadBalancerIngress(addrs []apiv1.LoadBalancerIngress) func(int, int) bool {
func lessLoadBalancerIngress(addrs []v1.IngressLoadBalancerIngress) func(int, int) bool {
return func(a, b int) bool {
switch strings.Compare(addrs[a].Hostname, addrs[b].Hostname) {
case -1:
@ -319,7 +320,7 @@ func lessLoadBalancerIngress(addrs []apiv1.LoadBalancerIngress) func(int, int) b
}
}
func ingressSliceEqual(lhs, rhs []apiv1.LoadBalancerIngress) bool {
func ingressSliceEqual(lhs, rhs []v1.IngressLoadBalancerIngress) bool {
if len(lhs) != len(rhs) {
return false
}
@ -336,7 +337,7 @@ func ingressSliceEqual(lhs, rhs []apiv1.LoadBalancerIngress) bool {
return true
}
func statusAddressFromService(service string, kubeClient clientset.Interface) ([]apiv1.LoadBalancerIngress, error) {
func statusAddressFromService(service string, kubeClient clientset.Interface) ([]v1.IngressLoadBalancerIngress, error) {
ns, name, _ := k8s.ParseNameNS(service)
svc, err := kubeClient.CoreV1().Services(ns).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
@ -345,28 +346,28 @@ func statusAddressFromService(service string, kubeClient clientset.Interface) ([
switch svc.Spec.Type {
case apiv1.ServiceTypeExternalName:
return []apiv1.LoadBalancerIngress{{
return []v1.IngressLoadBalancerIngress{{
Hostname: svc.Spec.ExternalName,
}}, nil
case apiv1.ServiceTypeClusterIP:
return []apiv1.LoadBalancerIngress{{
return []v1.IngressLoadBalancerIngress{{
IP: svc.Spec.ClusterIP,
}}, nil
case apiv1.ServiceTypeNodePort:
if svc.Spec.ExternalIPs == nil {
return []apiv1.LoadBalancerIngress{{
return []v1.IngressLoadBalancerIngress{{
IP: svc.Spec.ClusterIP,
}}, nil
}
addrs := make([]apiv1.LoadBalancerIngress, len(svc.Spec.ExternalIPs))
addrs := make([]v1.IngressLoadBalancerIngress, len(svc.Spec.ExternalIPs))
for i, ip := range svc.Spec.ExternalIPs {
addrs[i] = apiv1.LoadBalancerIngress{IP: ip}
addrs[i] = v1.IngressLoadBalancerIngress{IP: ip}
}
return addrs, nil
case apiv1.ServiceTypeLoadBalancer:
addrs := make([]apiv1.LoadBalancerIngress, len(svc.Status.LoadBalancer.Ingress))
addrs := make([]v1.IngressLoadBalancerIngress, len(svc.Status.LoadBalancer.Ingress))
for i, ingress := range svc.Status.LoadBalancer.Ingress {
addrs[i] = apiv1.LoadBalancerIngress{}
addrs[i] = v1.IngressLoadBalancerIngress{}
if ingress.Hostname != "" {
addrs[i].Hostname = ingress.Hostname
}
@ -376,7 +377,7 @@ func statusAddressFromService(service string, kubeClient clientset.Interface) ([
}
for _, ip := range svc.Spec.ExternalIPs {
if !stringInIngresses(ip, addrs) {
addrs = append(addrs, apiv1.LoadBalancerIngress{IP: ip})
addrs = append(addrs, v1.IngressLoadBalancerIngress{IP: ip})
}
}
return addrs, nil
@ -386,7 +387,7 @@ func statusAddressFromService(service string, kubeClient clientset.Interface) ([
}
// stringInSlice returns true if s is in list
func stringInIngresses(s string, list []apiv1.LoadBalancerIngress) bool {
func stringInIngresses(s string, list []v1.IngressLoadBalancerIngress) bool {
for _, v := range list {
if v.IP == s || v.Hostname == s {
return true

View file

@ -34,8 +34,8 @@ import (
"k8s.io/ingress-nginx/pkg/apis/ingress"
)
func buildLoadBalancerIngressByIP() []apiv1.LoadBalancerIngress {
return []apiv1.LoadBalancerIngress{
func buildLoadBalancerIngressByIP() []networking.IngressLoadBalancerIngress {
return []networking.IngressLoadBalancerIngress{
{
IP: "10.0.0.1",
Hostname: "foo1",
@ -123,17 +123,20 @@ func buildSimpleClientSet() *testclient.Clientset {
},
}},
&apiv1.ServiceList{Items: []apiv1.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: apiv1.NamespaceDefault,
},
Status: apiv1.ServiceStatus{
LoadBalancer: apiv1.LoadBalancerStatus{
Ingress: buildLoadBalancerIngressByIP(),
},
},
},
// This is commented out as the ServiceStatus.LoadBalancer field expects a LoadBalancerStatus object
// which is incompatible with the current Ingress struct which expects a IngressLoadBalancerStatus object
// TODO: update this service when the ServiceStatus struct gets updated
//{
// ObjectMeta: metav1.ObjectMeta{
// Name: "foo",
// Namespace: apiv1.NamespaceDefault,
// },
// Status: apiv1.ServiceStatus{
// LoadBalancer: apiv1.LoadBalancerStatus{
// Ingress: buildLoadBalancerIngressByIP(),
// },
// },
//},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo_non_exist",
@ -199,8 +202,8 @@ func buildExtensionsIngresses() []networking.Ingress {
Namespace: apiv1.NamespaceDefault,
},
Status: networking.IngressStatus{
LoadBalancer: apiv1.LoadBalancerStatus{
Ingress: []apiv1.LoadBalancerIngress{
LoadBalancer: networking.IngressLoadBalancerStatus{
Ingress: []networking.IngressLoadBalancerIngress{
{
IP: "10.0.0.1",
Hostname: "foo1",
@ -218,8 +221,8 @@ func buildExtensionsIngresses() []networking.Ingress {
},
},
Status: networking.IngressStatus{
LoadBalancer: apiv1.LoadBalancerStatus{
Ingress: []apiv1.LoadBalancerIngress{
LoadBalancer: networking.IngressLoadBalancerStatus{
Ingress: []networking.IngressLoadBalancerIngress{
{
IP: "0.0.0.0",
Hostname: "foo.bar.com",
@ -234,8 +237,8 @@ func buildExtensionsIngresses() []networking.Ingress {
Namespace: apiv1.NamespaceDefault,
},
Status: networking.IngressStatus{
LoadBalancer: apiv1.LoadBalancerStatus{
Ingress: []apiv1.LoadBalancerIngress{},
LoadBalancer: networking.IngressLoadBalancerStatus{
Ingress: []networking.IngressLoadBalancerIngress{},
},
},
},
@ -261,7 +264,7 @@ func (til *testIngressLister) ListIngresses() []*ingress.Ingress {
Namespace: apiv1.NamespaceDefault,
},
Status: networking.IngressStatus{
LoadBalancer: apiv1.LoadBalancerStatus{
LoadBalancer: networking.IngressLoadBalancerStatus{
Ingress: buildLoadBalancerIngressByIP(),
},
},
@ -325,7 +328,7 @@ func TestStatusActions(t *testing.T) {
fk.sync("just-test")
// PublishService is empty, so the running address is: ["11.0.0.2"]
// after updated, the ingress's ip should only be "11.0.0.2"
newIPs := []apiv1.LoadBalancerIngress{{
newIPs := []networking.IngressLoadBalancerIngress{{
IP: "11.0.0.2",
}}
fooIngress1, err1 := fk.Client.NetworkingV1().Ingresses(apiv1.NamespaceDefault).Get(context.TODO(), "foo_ingress_1", metav1.GetOptions{})
@ -342,7 +345,7 @@ func TestStatusActions(t *testing.T) {
// execute shutdown
fk.Shutdown()
// ingress should be empty
newIPs2 := []apiv1.LoadBalancerIngress{}
var newIPs2 []networking.IngressLoadBalancerIngress
fooIngress2, err2 := fk.Client.NetworkingV1().Ingresses(apiv1.NamespaceDefault).Get(context.TODO(), "foo_ingress_1", metav1.GetOptions{})
if err2 != nil {
t.Fatalf("unexpected error")
@ -382,7 +385,7 @@ func TestKeyfunc(t *testing.T) {
func TestRunningAddressesWithPublishService(t *testing.T) {
testCases := map[string]struct {
fakeClient *testclient.Clientset
expected []apiv1.LoadBalancerIngress
expected []networking.IngressLoadBalancerIngress
errExpected bool
}{
"service type ClusterIP": {
@ -416,7 +419,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
},
},
),
[]apiv1.LoadBalancerIngress{
[]networking.IngressLoadBalancerIngress{
{IP: "1.1.1.1"},
},
false,
@ -437,7 +440,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
},
},
),
[]apiv1.LoadBalancerIngress{
[]networking.IngressLoadBalancerIngress{
{IP: "1.1.1.1"},
},
false,
@ -458,7 +461,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
},
},
),
[]apiv1.LoadBalancerIngress{
[]networking.IngressLoadBalancerIngress{
{Hostname: "foo.bar"},
},
false,
@ -495,7 +498,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
},
},
),
[]apiv1.LoadBalancerIngress{
[]networking.IngressLoadBalancerIngress{
{IP: "10.0.0.1"},
{Hostname: "foo"},
{
@ -530,7 +533,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
},
},
),
[]apiv1.LoadBalancerIngress{
[]networking.IngressLoadBalancerIngress{
{IP: "10.0.0.1"},
},
false,
@ -568,7 +571,7 @@ func TestRunningAddressesWithPublishService(t *testing.T) {
}
if ra == nil {
t.Fatalf("returned nil but expected valid []apiv1.LoadBalancerIngress")
t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress")
}
if !reflect.DeepEqual(tc.expected, ra) {
@ -584,7 +587,7 @@ func TestRunningAddressesWithPods(t *testing.T) {
r, _ := fk.runningAddresses()
if r == nil {
t.Fatalf("returned nil but expected valid []apiv1.LoadBalancerIngress")
t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress")
}
rl := len(r)
if len(r) != 1 {
@ -592,7 +595,7 @@ func TestRunningAddressesWithPods(t *testing.T) {
}
rv := r[0]
if rv.IP != "11.0.0.2" {
t.Errorf("returned %v but expected %v", rv, apiv1.LoadBalancerIngress{IP: "11.0.0.2"})
t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "11.0.0.2"})
}
}
@ -602,7 +605,7 @@ func TestRunningAddressesWithPublishStatusAddress(t *testing.T) {
ra, _ := fk.runningAddresses()
if ra == nil {
t.Fatalf("returned nil but expected valid []apiv1.LoadBalancerIngress")
t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress")
}
rl := len(ra)
if len(ra) != 1 {
@ -610,7 +613,7 @@ func TestRunningAddressesWithPublishStatusAddress(t *testing.T) {
}
rv := ra[0]
if rv.IP != "127.0.0.1" {
t.Errorf("returned %v but expected %v", rv, apiv1.LoadBalancerIngress{IP: "127.0.0.1"})
t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"})
}
}
@ -620,7 +623,7 @@ func TestRunningAddressesWithPublishStatusAddresses(t *testing.T) {
ra, _ := fk.runningAddresses()
if ra == nil {
t.Fatalf("returned nil but expected valid []apiv1.LoadBalancerIngress")
t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngress")
}
rl := len(ra)
if len(ra) != 2 {
@ -629,10 +632,10 @@ func TestRunningAddressesWithPublishStatusAddresses(t *testing.T) {
rv := ra[0]
rv2 := ra[1]
if rv.IP != "127.0.0.1" {
t.Errorf("returned %v but expected %v", rv, apiv1.LoadBalancerIngress{IP: "127.0.0.1"})
t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"})
}
if rv2.IP != "1.1.1.1" {
t.Errorf("returned %v but expected %v", rv2, apiv1.LoadBalancerIngress{IP: "1.1.1.1"})
t.Errorf("returned %v but expected %v", rv2, networking.IngressLoadBalancerIngress{IP: "1.1.1.1"})
}
}
@ -642,7 +645,7 @@ func TestRunningAddressesWithPublishStatusAddressesAndSpaces(t *testing.T) {
ra, _ := fk.runningAddresses()
if ra == nil {
t.Fatalf("returned nil but expected valid []apiv1.LoadBalancerIngresst")
t.Fatalf("returned nil but expected valid []networking.IngressLoadBalancerIngresst")
}
rl := len(ra)
if len(ra) != 2 {
@ -651,15 +654,15 @@ func TestRunningAddressesWithPublishStatusAddressesAndSpaces(t *testing.T) {
rv := ra[0]
rv2 := ra[1]
if rv.IP != "127.0.0.1" {
t.Errorf("returned %v but expected %v", rv, apiv1.LoadBalancerIngress{IP: "127.0.0.1"})
t.Errorf("returned %v but expected %v", rv, networking.IngressLoadBalancerIngress{IP: "127.0.0.1"})
}
if rv2.IP != "1.1.1.1" {
t.Errorf("returned %v but expected %v", rv2, apiv1.LoadBalancerIngress{IP: "1.1.1.1"})
t.Errorf("returned %v but expected %v", rv2, networking.IngressLoadBalancerIngress{IP: "1.1.1.1"})
}
}
func TestStandardizeLoadBalancerIngresses(t *testing.T) {
fkEndpoints := []apiv1.LoadBalancerIngress{
fkEndpoints := []networking.IngressLoadBalancerIngress{
{IP: "2001:db8::68"},
{IP: "10.0.0.1"},
{Hostname: "opensource-k8s-ingress"},
@ -668,7 +671,7 @@ func TestStandardizeLoadBalancerIngresses(t *testing.T) {
r := standardizeLoadBalancerIngresses(fkEndpoints)
if r == nil {
t.Fatalf("returned nil but expected a valid []apiv1.LoadBalancerIngress")
t.Fatalf("returned nil but expected a valid []networking.IngressLoadBalancerIngress")
}
rl := len(r)
if rl != 3 {
@ -676,21 +679,21 @@ func TestStandardizeLoadBalancerIngresses(t *testing.T) {
}
re1 := r[0]
if re1.Hostname != "opensource-k8s-ingress" {
t.Fatalf("returned %v but expected %v", re1, apiv1.LoadBalancerIngress{Hostname: "opensource-k8s-ingress"})
t.Fatalf("returned %v but expected %v", re1, networking.IngressLoadBalancerIngress{Hostname: "opensource-k8s-ingress"})
}
re2 := r[1]
if re2.IP != "10.0.0.1" {
t.Fatalf("returned %v but expected %v", re2, apiv1.LoadBalancerIngress{IP: "10.0.0.1"})
t.Fatalf("returned %v but expected %v", re2, networking.IngressLoadBalancerIngress{IP: "10.0.0.1"})
}
re3 := r[2]
if re3.IP != "2001:db8::68" {
t.Fatalf("returned %v but expected %v", re3, apiv1.LoadBalancerIngress{IP: "2001:db8::68"})
t.Fatalf("returned %v but expected %v", re3, networking.IngressLoadBalancerIngress{IP: "2001:db8::68"})
}
}
func TestIngressSliceEqual(t *testing.T) {
fk1 := buildLoadBalancerIngressByIP()
fk2 := append(buildLoadBalancerIngressByIP(), apiv1.LoadBalancerIngress{
fk2 := append(buildLoadBalancerIngressByIP(), networking.IngressLoadBalancerIngress{
IP: "10.0.0.5",
Hostname: "foo5",
})
@ -700,8 +703,8 @@ func TestIngressSliceEqual(t *testing.T) {
fk4[2].IP = "11.0.0.3"
fooTests := []struct {
lhs []apiv1.LoadBalancerIngress
rhs []apiv1.LoadBalancerIngress
lhs []networking.IngressLoadBalancerIngress
rhs []networking.IngressLoadBalancerIngress
er bool
}{
{fk1, fk1, true},
@ -710,7 +713,7 @@ func TestIngressSliceEqual(t *testing.T) {
{fk4, fk1, false},
{fk1, nil, false},
{nil, nil, true},
{[]apiv1.LoadBalancerIngress{}, []apiv1.LoadBalancerIngress{}, true},
{[]networking.IngressLoadBalancerIngress{}, []networking.IngressLoadBalancerIngress{}, true},
}
for _, fooTest := range fooTests {

View file

@ -78,6 +78,8 @@ func GetNodeIPOrName(kubeClient clientset.Interface, name string, useInternalIP
var (
// IngressPodDetails hold information about the ingress-nginx pod
IngressPodDetails *PodInfo
// IngressNodeDetails old information about the node running ingress-nginx pod
IngressNodeDetails *NodeInfo
)
// PodInfo contains runtime information about the pod running the Ingres controller
@ -87,6 +89,12 @@ type PodInfo struct {
metav1.ObjectMeta
}
// NodeInfo contains runtime information about the node pod running the Ingres controller, eg. zone where pod is running
type NodeInfo struct {
metav1.TypeMeta
metav1.ObjectMeta
}
// GetIngressPod load the ingress-nginx pod
func GetIngressPod(kubeClient clientset.Interface) error {
podName := os.Getenv("POD_NAME")
@ -108,6 +116,18 @@ func GetIngressPod(kubeClient clientset.Interface) error {
pod.ObjectMeta.DeepCopyInto(&IngressPodDetails.ObjectMeta)
IngressPodDetails.SetLabels(pod.GetLabels())
IngressNodeDetails = &NodeInfo{
TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Node"},
}
// Try to get node info/labels to determine topology zone where pod is running
node, err := kubeClient.CoreV1().Nodes().Get(context.TODO(), pod.Spec.NodeName, metav1.GetOptions{})
if err != nil {
klog.Warningf("Unable to get NODE information: %v", err)
} else {
node.ObjectMeta.DeepCopyInto(&IngressNodeDetails.ObjectMeta)
IngressNodeDetails.SetLabels(node.GetLabels())
}
return nil
}
@ -160,9 +180,6 @@ func SetDefaultNGINXPathType(ing *networkingv1.Ingress) {
p.PathType = &defaultPathType
}
if *p.PathType == networkingv1.PathTypeImplementationSpecific {
p.PathType = &defaultPathType
}
}
}
}

View file

@ -216,6 +216,8 @@ Takes the form "<host>:port". If not provided, no admission controller is starte
dynamicConfigurationRetries = flags.Int("dynamic-configuration-retries", 15, "Number of times to retry failed dynamic configuration before failing to sync an ingress.")
disableSyncEvents = flags.Bool("disable-sync-events", false, "Disables the creation of 'Sync' event resources")
enableTopologyAwareRouting = flags.Bool("enable-topology-aware-routing", false, "Enable topology aware hints feature, needs service object annotation service.kubernetes.io/topology-aware-hints sets to auto.")
)
flags.StringVar(&nginx.MaxmindMirror, "maxmind-mirror", "", `Maxmind mirror url (example: http://geoip.local/databases.`)
@ -348,6 +350,7 @@ https://blog.maxmind.com/2019/12/18/significant-changes-to-accessing-and-using-g
SyncRateLimit: *syncRateLimit,
HealthCheckHost: *healthzHost,
DynamicConfigurationRetries: *dynamicConfigurationRetries,
EnableTopologyAwareRouting: *enableTopologyAwareRouting,
ListenPorts: &ngx_config.ListenPorts{
Default: *defServerPort,
Health: *healthzPort,

View file

@ -23,7 +23,6 @@ import (
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/k8s"
"k8s.io/ingress-nginx/internal/net/ssl"
"k8s.io/ingress-nginx/pkg/apis/ingress"
@ -31,15 +30,15 @@ import (
)
const (
alphaNumericChars = `\-\.\_\~a-zA-Z0-9/`
regexEnabledChars = `\^\$\[\]\(\)\{\}\*\+`
alphaNumericChars = `A-Za-z0-9\-\.\_\~\/` // This is the default allowed set on paths
)
var (
// pathAlphaNumeric is a regex validation of something like "^/[a-zA-Z]+$" on path
pathAlphaNumeric = regexp.MustCompile("^/[" + alphaNumericChars + "]*$").MatchString
// pathRegexEnabled is a regex validation of paths that may contain regex.
pathRegexEnabled = regexp.MustCompile("^/[" + alphaNumericChars + regexEnabledChars + "]*$").MatchString
// pathAlphaNumeric is a regex validation that allows only (0-9, a-z, A-Z, "-", ".", "_", "~", "/")
pathAlphaNumericRegex = regexp.MustCompile("^[" + alphaNumericChars + "]*$").MatchString
// default path type is Prefix to not break existing definitions
defaultPathType = networkingv1.PathTypePrefix
)
func GetRemovedHosts(rucfg, newcfg *ingress.Configuration) []string {
@ -247,12 +246,45 @@ func BuildRedirects(servers []*ingress.Server) []*redirect {
return redirectServers
}
// IsSafePath verifies if the path used in ingress object contains only valid characters.
// It will behave differently if regex is enabled or not
func IsSafePath(copyIng *networkingv1.Ingress, path string) bool {
isRegex, _ := parser.GetBoolAnnotation("use-regex", copyIng)
if isRegex {
return pathRegexEnabled(path)
func ValidateIngressPath(copyIng *networkingv1.Ingress, disablePathTypeValidation bool, additionalChars string) error {
if copyIng == nil {
return nil
}
return pathAlphaNumeric(path)
escapedAdditionalChars := regexp.QuoteMeta(additionalChars)
regexPath, err := regexp.Compile("^[" + alphaNumericChars + escapedAdditionalChars + "]*$")
if err != nil {
return fmt.Errorf("ingress has misconfigured validation regex on configmap: %s - %w", additionalChars, err)
}
for _, rule := range copyIng.Spec.Rules {
if rule.HTTP == nil {
continue
}
if err := checkPath(rule.HTTP.Paths, disablePathTypeValidation, regexPath); err != nil {
return fmt.Errorf("error validating ingressPath: %w", err)
}
}
return nil
}
func checkPath(paths []networkingv1.HTTPIngressPath, disablePathTypeValidation bool, regexSpecificChars *regexp.Regexp) error {
for _, path := range paths {
if path.PathType == nil {
path.PathType = &defaultPathType
}
if disablePathTypeValidation || *path.PathType == networkingv1.PathTypeImplementationSpecific {
if !regexSpecificChars.MatchString(path.Path) {
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
}
continue
}
if !pathAlphaNumericRegex(path.Path) {
return fmt.Errorf("path %s of type %s contains invalid characters", path.Path, *path.PathType)
}
}
return nil
}

View file

@ -17,13 +17,10 @@ limitations under the License.
package ingress
import (
"fmt"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
networkingv1 "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/pkg/apis/ingress"
)
@ -136,81 +133,168 @@ func TestIsDynamicConfigurationEnough(t *testing.T) {
}
}
func generateDumbIngressforPathTest(regexEnabled bool) *networkingv1.Ingress {
var annotations = make(map[string]string)
regexAnnotation := fmt.Sprintf("%s/use-regex", parser.AnnotationsPrefix)
if regexEnabled {
annotations[regexAnnotation] = "true"
}
func generateDumbIngressforPathTest(pathType *networkingv1.PathType, path string) *networkingv1.Ingress {
return &networkingv1.Ingress{
ObjectMeta: metav1.ObjectMeta{
Name: "dumb",
Namespace: "default",
Annotations: annotations,
Name: "dumb",
Namespace: "default",
},
Spec: networkingv1.IngressSpec{
Rules: []networkingv1.IngressRule{
{
Host: "test.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: pathType,
Path: path,
},
},
},
},
},
},
},
}
}
func TestIsSafePath(t *testing.T) {
func generateComplexIngress(ing *networkingv1.Ingress) *networkingv1.Ingress {
oldRules := ing.Spec.DeepCopy().Rules
ing.Spec.Rules = []networkingv1.IngressRule{
{
Host: "test1.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: &pathTypeExact,
Path: "/xpto",
},
},
},
},
},
{
Host: "test2.com",
IngressRuleValue: networkingv1.IngressRuleValue{
HTTP: &networkingv1.HTTPIngressRuleValue{
Paths: []networkingv1.HTTPIngressPath{
{
PathType: &pathTypeExact,
Path: "/someotherpath",
},
{
PathType: &pathTypePrefix,
Path: "/someprefix/~xpto/lala123",
},
},
},
},
},
}
// we want to invert the order to test better :)
ing.Spec.Rules = append(ing.Spec.Rules, oldRules...)
return ing
}
var (
pathTypeExact = networkingv1.PathTypeExact
pathTypePrefix = networkingv1.PathTypePrefix
pathTypeImplSpecific = networkingv1.PathTypeImplementationSpecific
)
const (
defaultAdditionalChars = "^%$[](){}*+?"
)
func TestValidateIngressPath(t *testing.T) {
tests := []struct {
name string
copyIng *networkingv1.Ingress
path string
want bool
name string
copyIng *networkingv1.Ingress
disablePathTypeValidation bool
additionalChars string
wantErr bool
}{
{
name: "should accept valid path with regex disabled",
want: true,
copyIng: generateDumbIngressforPathTest(false),
path: "/xpto/~user/t-e_st.exe",
name: "should return nil when ingress = nil",
wantErr: false,
copyIng: nil,
},
{
name: "should accept valid path / with regex disabled",
want: true,
copyIng: generateDumbIngressforPathTest(false),
path: "/",
name: "should accept valid path on pathType Exact",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypeExact, "/xpto/~user9/t-e_st.exe"),
},
{
name: "should reject invalid path with invalid chars",
want: false,
copyIng: generateDumbIngressforPathTest(false),
path: "/foo/bar/;xpto",
name: "should accept valid path on pathType Prefix",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypePrefix, "/xpto/~user9/t-e_st.exe"),
},
{
name: "should reject regex path when regex is disabled",
want: false,
copyIng: generateDumbIngressforPathTest(false),
path: "/foo/bar/(.+)",
name: "should accept valid simple path on pathType Impl Specific",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/xpto/~user9/t-e_st.exe"),
},
{
name: "should accept valid path / with regex enabled",
want: true,
copyIng: generateDumbIngressforPathTest(true),
path: "/",
name: "should accept valid path on pathType nil",
wantErr: false,
copyIng: generateDumbIngressforPathTest(nil, "/xpto/~user/t-e_st.exe"),
},
{
name: "should accept regex path when regex is enabled",
want: true,
copyIng: generateDumbIngressforPathTest(true),
path: "/foo/bar/(.+)",
name: "should accept empty path",
wantErr: false,
copyIng: generateDumbIngressforPathTest(&pathTypePrefix, ""),
},
{
name: "should reject regex path when regex is enabled but the path is invalid",
want: false,
copyIng: generateDumbIngressforPathTest(true),
path: "/foo/bar/;xpto",
name: "should deny path with bad characters and pathType not implementationSpecific",
wantErr: true,
additionalChars: defaultAdditionalChars,
copyIng: generateDumbIngressforPathTest(&pathTypeExact, "/foo/bar/(.+)"),
},
{
name: "should reject regex path when regex is enabled but the path is invalid",
want: false,
copyIng: generateDumbIngressforPathTest(true),
path: ";xpto",
name: "should accept path with regex characters and pathType implementationSpecific",
wantErr: false,
additionalChars: defaultAdditionalChars,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.+)"),
},
{
name: "should accept path with regex characters and pathType exact, but pathType validation disabled",
wantErr: false,
additionalChars: defaultAdditionalChars,
disablePathTypeValidation: true,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.+)"),
},
{
name: "should reject path when the allowed additional set does not match",
wantErr: true,
additionalChars: "().?",
disablePathTypeValidation: false,
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.+)"),
},
{
name: "should accept path when the allowed additional set does match",
wantErr: false,
additionalChars: "().?",
copyIng: generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.?)"),
},
{
name: "should block if at least one path is bad",
wantErr: true,
copyIng: generateComplexIngress(generateDumbIngressforPathTest(&pathTypeExact, "/foo/bar/(.?)")),
},
{
name: "should block if at least one path is bad",
wantErr: true,
copyIng: generateComplexIngress(generateDumbIngressforPathTest(&pathTypeImplSpecific, "/foo/bar/(.?)")),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsSafePath(tt.copyIng, tt.path); got != tt.want {
t.Errorf("IsSafePath() = %v, want %v", got, tt.want)
if err := ValidateIngressPath(tt.copyIng, tt.disablePathTypeValidation, tt.additionalChars); (err != nil) != tt.wantErr {
t.Errorf("ValidateIngressPath() error = %v, wantErr %v", err, tt.wantErr)
}
})
}

View file

@ -336,6 +336,9 @@ http {
{{ if $cfg.UseGzip }}
gzip on;
gzip_comp_level {{ $cfg.GzipLevel }};
{{- if $cfg.GzipDisable }}
gzip_disable "{{ $cfg.GzipDisable }}";
{{- end }}
gzip_http_version 1.1;
gzip_min_length {{ $cfg.GzipMinLength}};
gzip_types {{ $cfg.GzipTypes }};

View file

@ -41,12 +41,22 @@ reportFileNamePrefix="report-e2e-test-suite"
echo -e "${BGREEN}Running e2e test suite (FOCUS=${FOCUS})...${NC}"
ginkgo "${ginkgo_args[@]}" \
-focus="${FOCUS}" \
-skip="\[Serial\]|\[MemoryLeak\]" \
-skip="\[Serial\]|\[MemoryLeak\]|\[TopologyHints\]" \
-nodes="${E2E_NODES}" \
--junit-report=$reportFileNamePrefix.xml \
/e2e.test
# Create configMap out of a compressed report file for extraction later
# Must be isolated, there is a collision if multiple helms tries to install same clusterRole at same time
echo -e "${BGREEN}Running e2e test for topology aware hints...${NC}"
ginkgo "${ginkgo_args[@]}" \
-focus="\[TopologyHints\]" \
-skip="\[Serial\]|\[MemoryLeak\]]" \
-nodes="${E2E_NODES}" \
--junit-report=$reportFileNamePrefix-topology.xml \
/e2e.test
# Create configMap out of a compressed report file for extraction later
echo -e "${BGREEN}Running e2e test suite with tests that require serial execution...${NC}"
ginkgo "${ginkgo_args[@]}" \
-focus="\[Serial\]" \

View file

@ -0,0 +1,51 @@
# TODO: remove the need to use fullnameOverride
fullnameOverride: nginx-ingress
controller:
image:
repository: ingress-controller/controller
chroot: true
tag: 1.0.0-dev
digest:
digestChroot:
scope:
enabled: false
config:
worker-processes: "1"
readinessProbe:
initialDelaySeconds: 3
periodSeconds: 1
livenessProbe:
initialDelaySeconds: 3
periodSeconds: 1
service:
type: NodePort
electionID: ingress-controller-leader
ingressClassResource:
# We will create and remove each IC/ClusterRole/ClusterRoleBinding per test so there's no conflict
enabled: false
extraArgs:
tcp-services-configmap: $NAMESPACE/tcp-services
# e2e tests do not require information about ingress status
update-status: "false"
terminationGracePeriodSeconds: 1
admissionWebhooks:
enabled: false
enableTopologyAwareRouting: true
# ulimit -c unlimited
# mkdir -p /tmp/coredump
# chmod a+rwx /tmp/coredump
# echo "/tmp/coredump/core.%e.%p.%h.%t" > /proc/sys/kernel/core_pattern
extraVolumeMounts:
- name: coredump
mountPath: /tmp/coredump
extraVolumes:
- name: coredump
hostPath:
path: /tmp/coredump
rbac:
create: true
scope: false

View file

@ -30,6 +30,14 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
networkingv1 "k8s.io/api/networking/v1"
)
var (
pathExact = networkingv1.PathTypeExact
pathPrefix = networkingv1.PathTypePrefix
pathImplSpecific = networkingv1.PathTypeImplementationSpecific
)
var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
@ -152,6 +160,32 @@ var _ = framework.IngressNginxDescribe("[Serial] admission controller", func() {
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid annotation value should return an error")
})
ginkgo.It("should reject ingress with bad characters and pathType != ImplementationSpecific", func() {
host := "admission-test"
firstIngress := framework.NewSingleIngress("first-ingress", "/xpto*", host, f.Namespace, framework.EchoService, 80, nil)
firstIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathPrefix
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{})
assert.NotNil(ginkgo.GinkgoT(), err, "creating an ingress with invalid path value should return an error")
secondIngress := framework.NewSingleIngress("second-ingress", "/abc123*", host, f.Namespace, framework.EchoService, 80, nil)
secondIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImplSpecific
_, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), secondIngress, metav1.CreateOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with regex on path and pathType ImplementationSpecific should not return an error")
})
ginkgo.It("should not validate characters on ingress when validation of pathType is disabled", func() {
host := "admission-test"
f.UpdateNginxConfigMapData("disable-pathtype-validation", "true")
firstIngress := framework.NewSingleIngress("first-ingress", "/xpto*", host, f.Namespace, framework.EchoService, 80, nil)
firstIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathPrefix
_, err := f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Create(context.TODO(), firstIngress, metav1.CreateOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "creating an ingress with regex chars on path and pathType validation disabled should be accepted")
})
ginkgo.It("should return an error if there is a forbidden value in some annotation", func() {
host := "admission-test"

View file

@ -35,6 +35,8 @@ import (
var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
f := framework.NewDefaultFramework("affinity")
pathImpl := networking.PathTypeImplementationSpecific
ginkgo.BeforeEach(func() {
f.NewEchoDeployment(framework.WithDeploymentReplicas(2))
})
@ -276,6 +278,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
annotations["nginx.ingress.kubernetes.io/session-cookie-path"] = "/foo/bar"
ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -299,6 +303,8 @@ var _ = framework.DescribeAnnotation("affinity session-cookie-name", func() {
annotations["nginx.ingress.kubernetes.io/use-regex"] = "true"
ing := framework.NewSingleIngress(host, "/foo/.*", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,

View file

@ -24,12 +24,15 @@ import (
"github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"
networking "k8s.io/api/networking/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-log", func() {
f := framework.NewDefaultFramework("rewrite")
pathImpl := networking.PathTypeImplementationSpecific
ginkgo.BeforeEach(func() {
f.NewEchoDeployment()
})
@ -126,6 +129,7 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend",
}
ing = framework.NewSingleIngress("regex", "/foo.+", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -168,6 +172,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend",
}
ing = framework.NewSingleIngress("regex", "/foo/bar/[a-z]{3}", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
@ -196,6 +202,8 @@ var _ = framework.DescribeAnnotation("rewrite-target use-regex enable-rewrite-lo
"nginx.ingress.kubernetes.io/rewrite-target": "/new/backend/$1",
}
ing := framework.NewSingleIngress("regex", "/foo/bar/(.+)", host, f.Namespace, framework.EchoService, 80, annotations)
ing.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0].PathType = &pathImpl
f.EnsureIngress(ing)
f.WaitForNginxServer(host,

View file

@ -0,0 +1,112 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package endpointslices
import (
"context"
"encoding/json"
"fmt"
"net/http"
"os/exec"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"
"k8s.io/ingress-nginx/internal/nginx"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.IngressNginxDescribe("[TopologyHints] topology aware routing", func() {
f := framework.NewDefaultFramework("topology")
host := "topology-svc.foo.com"
ginkgo.BeforeEach(func() {
f.NewEchoDeployment(framework.WithDeploymentReplicas(2), framework.WithSvcTopologyAnnotations())
})
ginkgo.AfterEach(func() {
// we need to uninstall chart because of clusterRole which is not destroyed with namespace
err := uninstallChart(f)
assert.Nil(ginkgo.GinkgoT(), err, "uninstalling helm chart")
})
ginkgo.It("should return 200 when service has topology hints", func() {
annotations := make(map[string]string)
ing := framework.NewSingleIngress(host, "/", host, f.Namespace, framework.EchoService, 80, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host, func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s", host))
})
ginkgo.By("checking if the service is reached")
f.HTTPTestClient().
GET("/").
WithHeader("Host", host).
Expect().
Status(http.StatusOK)
slices, err := f.KubeClientSet.DiscoveryV1().EndpointSlices(f.Namespace).List(context.TODO(), metav1.ListOptions{
LabelSelector: "kubernetes.io/service-name=echo",
Limit: 1,
})
assert.Nil(ginkgo.GinkgoT(), err)
// check if we have hints, really depends on k8s endpoint slice controller
gotHints := true
for _, ep := range slices.Items[0].Endpoints {
if ep.Hints == nil || len(ep.Hints.ForZones) == 0 {
gotHints = false
break
}
}
curlCmd := fmt.Sprintf("curl --fail --silent http://localhost:%v/configuration/backends", nginx.StatusPort)
status, err := f.ExecIngressPod(curlCmd)
assert.Nil(ginkgo.GinkgoT(), err)
var backends []map[string]interface{}
json.Unmarshal([]byte(status), &backends)
gotBackends := 0
for _, bck := range backends {
if strings.Contains(bck["name"].(string), "topology") {
gotBackends = len(bck["endpoints"].([]interface{}))
}
}
if gotHints {
//we have 2 replics, if there is just one backend it means that we are routing according slices hints to same zone as controller is
assert.Equal(ginkgo.GinkgoT(), 1, gotBackends)
} else {
// two replicas should have two endpoints without topology hints
assert.Equal(ginkgo.GinkgoT(), 2, gotBackends)
}
})
})
func uninstallChart(f *framework.Framework) error {
cmd := exec.Command("helm", "uninstall", "--namespace", f.Namespace, "nginx-ingress")
_, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("unexpected error uninstalling ingress-nginx release: %v", err)
}
return nil
}

View file

@ -40,10 +40,10 @@ const SlowEchoService = "slow-echo"
const HTTPBinService = "httpbin"
type deploymentOptions struct {
namespace string
name string
replicas int
image string
namespace string
name string
replicas int
svcAnnotations map[string]string
}
// WithDeploymentNamespace allows configuring the deployment's namespace
@ -53,6 +53,15 @@ func WithDeploymentNamespace(n string) func(*deploymentOptions) {
}
}
// WithSvcTopologyAnnotations create svc with topology aware hints sets to auto
func WithSvcTopologyAnnotations() func(*deploymentOptions) {
return func(o *deploymentOptions) {
o.svcAnnotations = map[string]string{
"service.kubernetes.io/topology-aware-hints": "auto",
}
}
}
// WithDeploymentName allows configuring the deployment's names
func WithDeploymentName(n string) func(*deploymentOptions) {
return func(o *deploymentOptions) {
@ -95,8 +104,9 @@ func (f *Framework) NewEchoDeployment(opts ...func(*deploymentOptions)) {
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: options.name,
Namespace: options.namespace,
Name: options.name,
Namespace: options.namespace,
Annotations: options.svcAnnotations,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{

View file

@ -2,8 +2,14 @@ kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
topology.kubernetes.io/zone: zone-1
- role: worker
labels:
topology.kubernetes.io/zone: zone-1
- role: worker
labels:
topology.kubernetes.io/zone: zone-2
kubeadmConfigPatches:
- |
kind: ClusterConfiguration

View file

@ -1,134 +0,0 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package security
import (
"fmt"
"net/http"
"strings"
"github.com/onsi/ginkgo/v2"
"k8s.io/ingress-nginx/test/e2e/framework"
)
const (
validPath = "/xpto/~user/t-e_st.exe"
invalidPath = "/foo/bar/;xpto"
regexPath = "/foo/bar/(.+)"
host = "securitytest.com"
)
var (
annotationRegex = map[string]string{
"nginx.ingress.kubernetes.io/use-regex": "true",
}
)
var _ = framework.IngressNginxDescribe("[Security] validate path fields", func() {
f := framework.NewDefaultFramework("validate-path")
ginkgo.BeforeEach(func() {
f.NewEchoDeployment()
})
ginkgo.It("should accept an ingress with valid path", func() {
ing := framework.NewSingleIngress(host, validPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(validPath).
WithHeader("Host", host).
Expect().
Status(http.StatusOK)
})
ginkgo.It("should drop an ingress with invalid path", func() {
ing := framework.NewSingleIngress(host, invalidPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(invalidPath).
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
ginkgo.It("should drop an ingress with regex path and regex disabled", func() {
ing := framework.NewSingleIngress(host, regexPath, host, f.Namespace, framework.EchoService, 80, nil)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET("/foo/bar/lalala").
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
ginkgo.It("should accept an ingress with regex path and regex enabled", func() {
ing := framework.NewSingleIngress(host, regexPath, host, f.Namespace, framework.EchoService, 80, annotationRegex)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET("/foo/bar/lalala").
WithHeader("Host", host).
Expect().
Status(http.StatusOK)
})
ginkgo.It("should reject an ingress with invalid path and regex enabled", func() {
ing := framework.NewSingleIngress(host, invalidPath, host, f.Namespace, framework.EchoService, 80, annotationRegex)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return !strings.Contains(server, fmt.Sprintf("server_name %s ;", host))
})
f.HTTPTestClient().
GET(invalidPath).
WithHeader("Host", host).
Expect().
Status(http.StatusNotFound)
})
})

99
test/e2e/settings/gzip.go Normal file
View file

@ -0,0 +1,99 @@
/*
Copyright 2023 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package settings
import (
"fmt"
"strings"
"github.com/onsi/ginkgo/v2"
"k8s.io/ingress-nginx/internal/ingress/controller/config"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.DescribeSetting("gzip", func() {
f := framework.NewDefaultFramework("gzip")
ginkgo.It("should be disabled by default", func() {
f.WaitForNginxConfiguration(
func(cfg string) bool {
return !strings.Contains(cfg, "gzip on;")
})
})
ginkgo.It("should be enabled with default settings", func() {
f.UpdateNginxConfigMapData("use-gzip", "true")
f.WaitForNginxConfiguration(
func(cfg string) bool {
defaultCfg := config.NewDefault()
return strings.Contains(cfg, "gzip on;") &&
strings.Contains(cfg, fmt.Sprintf("gzip_comp_level %d;", defaultCfg.GzipLevel)) &&
!strings.Contains(cfg, "gzip_disable") &&
strings.Contains(cfg, "gzip_http_version 1.1;") &&
strings.Contains(cfg, fmt.Sprintf("gzip_min_length %d;", defaultCfg.GzipMinLength)) &&
strings.Contains(cfg, fmt.Sprintf("gzip_types %s;", defaultCfg.GzipTypes)) &&
strings.Contains(cfg, "gzip_proxied any;") &&
strings.Contains(cfg, "gzip_vary on;")
})
})
ginkgo.It("should set gzip_comp_level to 4", func() {
f.UpdateNginxConfigMapData("use-gzip", "true")
f.UpdateNginxConfigMapData("gzip-level", "4")
f.WaitForNginxConfiguration(
func(cfg string) bool {
return strings.Contains(cfg, "gzip on;") &&
strings.Contains(cfg, "gzip_comp_level 4;")
})
})
ginkgo.It("should set gzip_disable to msie6", func() {
f.UpdateNginxConfigMapData("use-gzip", "true")
f.UpdateNginxConfigMapData("gzip-disable", "msie6")
f.WaitForNginxConfiguration(
func(cfg string) bool {
return strings.Contains(cfg, "gzip on;") &&
strings.Contains(cfg, `gzip_disable "msie6";`)
})
})
ginkgo.It("should set gzip_min_length to 100", func() {
f.UpdateNginxConfigMapData("use-gzip", "true")
f.UpdateNginxConfigMapData("gzip-min-length", "100")
f.WaitForNginxConfiguration(
func(cfg string) bool {
return strings.Contains(cfg, "gzip on;") &&
strings.Contains(cfg, "gzip_min_length 100;")
})
})
ginkgo.It("should set gzip_types to application/javascript", func() {
f.UpdateNginxConfigMapData("use-gzip", "true")
f.UpdateNginxConfigMapData("gzip-types", "application/javascript")
f.WaitForNginxConfiguration(
func(cfg string) bool {
return strings.Contains(cfg, "gzip on;") &&
strings.Contains(cfg, "gzip_types application/javascript;")
})
})
})

View file

@ -19,6 +19,7 @@ package settings
import (
"context"
"fmt"
v1 "k8s.io/api/networking/v1"
"log"
"net"
"strings"
@ -28,7 +29,6 @@ import (
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
@ -87,7 +87,7 @@ var _ = framework.IngressNginxDescribe("[Status] status update", func() {
ing, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).Get(context.TODO(), host, metav1.GetOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "unexpected error getting %s/%v Ingress", f.Namespace, host)
ing.Status.LoadBalancer.Ingress = []apiv1.LoadBalancerIngress{}
ing.Status.LoadBalancer.Ingress = []v1.IngressLoadBalancerIngress{}
_, err = f.KubeClientSet.NetworkingV1().Ingresses(f.Namespace).UpdateStatus(context.TODO(), ing, metav1.UpdateOptions{})
assert.Nil(ginkgo.GinkgoT(), err, "unexpected error cleaning Ingress status")
framework.Sleep(10 * time.Second)
@ -121,9 +121,9 @@ var _ = framework.IngressNginxDescribe("[Status] status update", func() {
return true, nil
})
assert.Nil(ginkgo.GinkgoT(), err, "unexpected error waiting for ingress status")
assert.Equal(ginkgo.GinkgoT(), ing.Status.LoadBalancer.Ingress, ([]apiv1.LoadBalancerIngress{
assert.Equal(ginkgo.GinkgoT(), ing.Status.LoadBalancer.Ingress, []v1.IngressLoadBalancerIngress{
{IP: "1.1.0.0"},
}))
})
})
})