Revert "Remove fastcgi feature" (#10081)

* Revert "Remove fastcgi feature (#9864)"

This reverts commit 90ed0ccdbe.

* revert fastcgi* annotations warning

Signed-off-by: Jintao Zhang <zhangjintao9020@gmail.com>

---------

Signed-off-by: Jintao Zhang <zhangjintao9020@gmail.com>
This commit is contained in:
Jintao Zhang 2023-06-14 03:55:59 +08:00 committed by GitHub
parent f1e3f2fa3c
commit cccba35005
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 975 additions and 159 deletions

View file

@ -391,6 +391,8 @@ jobs:
- 'images/custom-error-pages/**'
cfssl:
- 'images/cfssl/**'
fastcgi-helloserver:
- 'images/fastcgi-helloserver/**'
echo:
- 'images/echo/**'
go-grpc-greeter-server:
@ -410,6 +412,10 @@ jobs:
if: ${{ steps.filter-images.outputs.cfssl == 'true' }}
run: |
cd images/cfssl && make build
- name: fastcgi-helloserver
if: ${{ steps.filter-images.outputs.fastcgi-helloserver == 'true' }}
run: |
cd images/fastcgi-helloserver && make build
- name: echo image build
if: ${{ steps.filter-images.outputs.echo == 'true' }}
run: |

View file

@ -56,6 +56,8 @@
- [cfssl](https://github.com/kubernetes/ingress-nginx/tree/main/images/cfssl)
- [fastcgi-helloserver](https://github.com/kubernetes/ingress-nginx/tree/main/images/fastcgi-helloserver)
- [httpbin](https://github.com/kubernetes/ingress-nginx/tree/main/images/httpbin)
- [kube-webhook-certgen](https://github.com/kubernetes/ingress-nginx/tree/main/images/kube-webhook-certgen)

View file

@ -108,6 +108,7 @@ Do not try to edit it manually.
- [should set backend protocol to $scheme:// and use proxy_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L49)
- [should set backend protocol to grpc:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L64)
- [should set backend protocol to grpcs:// and use grpc_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L79)
- [should set backend protocol to '' and use fastcgi_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L94)
- [should set backend protocol to '' and use ajp_pass](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/backendprotocol.go#L109)
### [canary-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L36)
@ -191,6 +192,13 @@ Do not try to edit it manually.
- [disable-http-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L53)
- [disable-stream-access-log set access_log off](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/disableaccesslog.go#L71)
### [backend-protocol - FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L30)
- [should use fastcgi_pass in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L37)
- [should add fastcgi_index in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L54)
- [should add fastcgi_param in the configuration file](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L71)
- [should return OK for service with backend protocol FastCGI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/fastcgi.go#L102)
### [force-ssl-redirect](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/forcesslredirect.go#L27)
- [should redirect to https](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/forcesslredirect.go#L34)
@ -342,101 +350,6 @@ Do not try to edit it manually.
- [should set the X-Forwarded-Prefix to the annotation value](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/xforwardedprefix.go#L35)
- [should not add X-Forwarded-Prefix if the annotation value is empty](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/xforwardedprefix.go#L57)
### [denylist-source-range](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L28)
- [only deny explicitly denied IPs, allow all others](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L35)
- [only allow explicitly allowed IPs, deny all others](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/ipdenylist.go#L86)
### [affinity session-cookie-name](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L35)
- [should set sticky cookie SERVERID](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L42)
- [should change cookie name on ingress definition change](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L64)
- [should set the path to /something on the generated cookie](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L99)
- [does not set the path to / on the generated cookie if there's more than one rule referring to the same backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L121)
- [should set cookie with expires](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L194)
- [should set cookie with domain](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L225)
- [should not set cookie without domain annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L248)
- [should work with use-regex annotation and session-cookie-path](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L270)
- [should warn user when use-regex is true and session-cookie-path is not set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L294)
- [should not set affinity across all server locations when using separate ingresses](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L320)
- [should set sticky cookie without host](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L352)
- [should work with server-alias annotation](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L372)
- [should set secure in cookie with provided true annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L412)
- [should not set secure in cookie with provided false annotation on http](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L435)
- [should set secure in cookie with provided false annotation on https](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/affinity.go#L458)
### [rewrite-target use-regex enable-rewrite-log](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L30)
- [should write rewrite logs](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L37)
- [should use correct longest path match](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L66)
- [should use ~* location modifier if regex annotation is present](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L111)
- [should fail to use longest match for documented warning](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L158)
- [should allow for custom rewrite parameters](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/rewrite.go#L190)
### [auth-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L39)
- [should return status code 200 when no authentication is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L46)
- [should return status code 503 when authentication is configured with an invalid secret](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L65)
- [should return status code 401 when authentication is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L89)
- [should return status code 401 when authentication is configured and Authorization header is sent with invalid credentials](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L116)
- [should return status code 401 and cors headers when authentication and cors is configured but Authorization header is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L144)
- [should return status code 200 when authentication is configured and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L172)
- [should return status code 200 when authentication is configured with a map and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L199)
- [should return status code 401 when authentication is configured with invalid content and Authorization header is sent](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L227)
- [ when external auth is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L266)
- [ when external auth is not configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L284)
- [ when auth-headers are set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L301)
- [should set cache_key when external auth cache is configured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L322)
- [user retains cookie by default](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L411)
- [user does not retain cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L422)
- [user with annotated ingress retains cookie if upstream returns error status code](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L433)
- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L485)
- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L494)
- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L505)
- [should overwrite Foo header with auth response](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L529)
- [should not create additional upstream block when auth-keepalive is not set](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L552)
- [should not create additional upstream block when host part of auth-url contains a variable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L570)
- [should not create additional upstream block when auth-keepalive is negative](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L590)
- [should not create additional upstream block when auth-keepalive is set with HTTP/2](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L609)
- [should create additional upstream block when auth-keepalive is set with HTTP/1.x](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L623)
- [should return status code 200 when signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L678)
- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L687)
- [keeps processing new ingresses even if one of the existing ingresses is misconfigured](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L698)
- [should return status code 200 when signed in after auth backend is deleted ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L772)
- [should deny login for different location on same server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L792)
- [should deny login for different servers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L820)
- [should redirect to signin url when not signed in](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L849)
- [should return 503 (location was denied)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L879)
- [should add error to the config](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/auth.go#L887)
### [canary-*](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L36)
- [should response with a 200 status from the mainline upstream when requests are made to the mainline ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L48)
- [should return 404 status for requests to the canary if no matching ingress is found](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L80)
- [should return the correct status codes when endpoints are unavailable](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L107)
- [should route requests to the correct upstream if mainline ingress is created before the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L161)
- [should route requests to the correct upstream if mainline ingress is created after the canary ingress](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L206)
- [should route requests to the correct upstream if the mainline ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L250)
- [should route requests to the correct upstream if the canary ingress is modified](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L307)
- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L372)
- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L426)
- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L490)
- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L532)
- [should routes to mainline upstream when the given Regex causes error](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L566)
- [should route requests to the correct upstream](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L604)
- [respects always and never values](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L643)
- [should route requests only to mainline if canary weight is 0](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L705)
- [should route requests only to canary if canary weight is 100](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L743)
- [should route requests only to canary if canary weight is equal to canary weight total](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L775)
- [should route requests split between mainline and canary if canary weight is 50](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L808)
- [should route requests split between mainline and canary if canary weight is 100 and weight total is 200](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L834)
- [should not use canary as a catch-all server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L863)
- [should not use canary with domain as a server](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L891)
- [does not crash when canary ingress has multiple paths to the same non-matching backend](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L915)
- [always routes traffic to canary if first request was affinitized to canary (default behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L943)
- [always routes traffic to canary if first request was affinitized to canary (explicit sticky behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L1000)
- [routes traffic to either mainline or canary backend (legacy behavior)](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/annotations/canary.go#L1058)
### [Debug CLI](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L29)
- [should list the backend servers](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/dbg/main.go#L37)
@ -485,6 +398,10 @@ Do not try to edit it manually.
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/exec.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/fastcgi_helloserver.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/fastcgi_helloserver.go#L)
### [[Setting] ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L194)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L)
@ -561,30 +478,6 @@ Do not try to edit it manually.
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/util.go#L)
### [[Setting] ](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L194)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/framework.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/healthz.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/healthz.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/k8s.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/k8s.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/metrics.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/metrics.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/deployment.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/deployment.go#L)
### [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/exec.go#L)
- [](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/framework/exec.go#L)
### [[Shutdown] Grace period shutdown](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/grace_period.go#L32)
- [/healthz should return status code 500 during shutdown grace period](https://github.com/kubernetes/ingress-nginx/tree/main/test/e2e/gracefulshutdown/grace_period.go#L35)

View file

@ -200,6 +200,7 @@ kubectl ingress-nginx conf -n ingress-nginx --host testaddr.local
```console
$ kubectl ingress-nginx exec -i -n ingress-nginx -- ls /etc/nginx
fastcgi_params
geoip
lua
mime.types

View file

@ -2,10 +2,118 @@
# Exposing FastCGI Servers
**This feature has been removed from Ingress NGINX**
> **FastCGI** is a [binary protocol](https://en.wikipedia.org/wiki/Binary_protocol "Binary protocol") for interfacing interactive programs with a [web server](https://en.wikipedia.org/wiki/Web_server "Web server"). [...] (It's) aim is to reduce the overhead related to interfacing between web server and CGI programs, allowing a server to handle more web page requests per unit of time.
>
> &mdash; Wikipedia
People willing to use fastcgi servers, should create an NGINX + FastCGI service and expose
this service via Ingress NGINX.
The _ingress-nginx_ ingress controller can be used to directly expose [FastCGI](https://en.wikipedia.org/wiki/FastCGI) servers. Enabling FastCGI in your Ingress only requires setting the _backend-protocol_ annotation to `FCGI`, and with a couple more annotations you can customize the way _ingress-nginx_ handles the communication with your FastCGI _server_.
We recommend using images like `cgr.dev/chainguard/nginx:latest` and expose your fast_cgi application
as another container on this Pod.
## Example Objects to Expose a FastCGI Pod
The _Pod_ example object below exposes port `9000`, which is the conventional FastCGI port.
```yaml
apiVersion: v1
kind: Pod
metadata:
name: example-app
labels:
app: example-app
spec:
containers:
- name: example-app
image: example-app:1.0
ports:
- containerPort: 9000
name: fastcgi
```
The _Service_ object example below matches port `9000` from the _Pod_ object above.
```yaml
apiVersion: v1
kind: Service
metadata:
name: example-service
spec:
selector:
app: example-app
ports:
- port: 9000
targetPort: 9000
name: fastcgi
```
And the _Ingress_ and _ConfigMap_ objects below demonstrates the supported _FastCGI_ specific annotations (NGINX actually has 50 FastCGI directives, all of which have not been exposed in the ingress yet), and matches the service `example-service`, and the port named `fastcgi` from above. The _ConfigMap_ **must** be created first for the _Ingress Controller_ to be able to find it when the _Ingress_ object is created, otherwise you will need to restart the _Ingress Controller_ pods.
```yaml
# The ConfigMap MUST be created first for the ingress controller to be able to
# find it when the Ingress object is created.
apiVersion: v1
kind: ConfigMap
metadata:
name: example-cm
data:
SCRIPT_FILENAME: "/example/index.php"
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "FCGI"
nginx.ingress.kubernetes.io/fastcgi-index: "index.php"
nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-cm"
name: example-app
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
name: fastcgi
```
## FastCGI Ingress Annotations
To enable FastCGI, the `nginx.ingress.kubernetes.io/backend-protocol` annotation needs to be set to `FCGI`, which overrides the default `HTTP` value.
> `nginx.ingress.kubernetes.io/backend-protocol: "FCGI"`
**This enables the _FastCGI_ mode for all paths defined in the _Ingress_ object**
### The `nginx.ingress.kubernetes.io/fastcgi-index` Annotation
To specify an index file, the `fastcgi-index` annotation value can optionally be set. In the example below, the value is set to `index.php`. This annotation corresponds to [the _NGINX_ `fastcgi_index` directive](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_index).
> `nginx.ingress.kubernetes.io/fastcgi-index: "index.php"`
### The `nginx.ingress.kubernetes.io/fastcgi-params-configmap` Annotation
To specify [_NGINX_ `fastcgi_param` directives](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_param), the `fastcgi-params-configmap` annotation is used, which in turn must lead to a _ConfigMap_ object containing the _NGINX_ `fastcgi_param` directives as key/values.
> `nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-configmap"`
And the _ConfigMap_ object to specify the `SCRIPT_FILENAME` and `HTTP_PROXY` _NGINX's_ `fastcgi_param` directives will look like the following:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: example-configmap
data:
SCRIPT_FILENAME: "/example/index.php"
HTTP_PROXY: ""
```
Using the _namespace/_ prefix is also supported, for example:
> `nginx.ingress.kubernetes.io/fastcgi-params-configmap: "example-namespace/example-configmap"`

View file

@ -894,7 +894,7 @@ Include /etc/nginx/owasp-modsecurity-crs/nginx-modsecurity.conf
### Backend Protocol
Using `backend-protocol` annotations is possible to indicate how NGINX should communicate with the backend service. (Replaces `secure-backends` in older versions)
Valid Values: HTTP, HTTPS, GRPC, GRPCS, AJP
Valid Values: HTTP, HTTPS, GRPC, GRPCS, AJP and FCGI
By default NGINX uses `HTTP`.

View file

@ -0,0 +1,59 @@
# Copyright 2020 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.
# Docker image for e2e testing.
# set default shell
SHELL=/bin/bash -o pipefail -o errexit
DIR:=$(strip $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))))
INIT_BUILDX=$(DIR)/../../hack/init-buildx.sh
SHORT_SHA ?=$(shell git rev-parse --short HEAD)
TAG ?=v$(shell date +%Y%m%d)-$(SHORT_SHA)
REGISTRY ?= local
IMAGE = $(REGISTRY)/e2e-test-fastcgi-helloserver
# required to enable buildx
export DOCKER_CLI_EXPERIMENTAL=enabled
# build with buildx
PLATFORMS?=linux/amd64,linux/arm,linux/arm64
OUTPUT=
PROGRESS=plain
build: ensure-buildx
docker buildx build \
--platform=${PLATFORMS} $(OUTPUT) \
--progress=$(PROGRESS) \
--pull \
-t $(IMAGE):$(TAG) rootfs
# push the cross built image
push: OUTPUT=--push
push: build
# enable buildx
ensure-buildx:
# this is required for cloudbuild
ifeq ("$(wildcard $(INIT_BUILDX))","")
@curl -sSL https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/hack/init-buildx.sh | bash
else
@exec $(INIT_BUILDX)
endif
@echo "done"
.PHONY: build push ensure-buildx

View file

@ -0,0 +1,22 @@
timeout: 1800s
options:
substitution_option: ALLOW_LOOSE
steps:
- name: gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90
entrypoint: bash
env:
- DOCKER_CLI_EXPERIMENTAL=enabled
- SHORT_SHA=$SHORT_SHA
- BASE_REF=$_PULL_BASE_REF
- REGISTRY=gcr.io/k8s-staging-ingress-nginx
# default cloudbuild has HOME=/builder/home and docker buildx is in /root/.docker/cli-plugins/docker-buildx
# set the home to /root explicitly to if using docker buildx
- HOME=/root
args:
- -c
- |
gcloud auth configure-docker \
&& cd images/fastcgi-helloserver && make push
substitutions:
_GIT_TAG: "12345"
_PULL_BASE_REF: "master"

View file

@ -0,0 +1,32 @@
# Copyright 2017 The Kubernetes Authors. All rights reserved.
#
# 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.
FROM golang:1.20.4-alpine3.18 as builder
WORKDIR /go/src/k8s.io/ingress-nginx/images/fastcgi
COPY . .
RUN CGO_ENABLED=0 go build -a -installsuffix cgo \
-ldflags "-s -w" \
-o fastcgi-helloserver main.go
# Use distroless as minimal base image to package the binary
# Refer to https://github.com/GoogleContainerTools/distroless for more details
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /go/src/k8s.io/ingress-nginx/images/fastcgi/fastcgi-helloserver /
USER nonroot:nonroot
CMD ["/fastcgi-helloserver"]

View file

@ -0,0 +1,30 @@
package main
import (
"fmt"
"net"
"net/http"
"net/http/fcgi"
)
func hello(w http.ResponseWriter, r *http.Request) {
keys, ok := r.URL.Query()["name"]
if !ok || len(keys[0]) < 1 {
fmt.Fprintf(w, "Hello world!")
return
}
key := keys[0]
fmt.Fprintf(w, "Hello "+string(key)+"!")
}
func main() {
http.HandleFunc("/hello", hello)
l, err := net.Listen("tcp", "0.0.0.0:9000")
if err != nil {
panic(err)
}
fcgi.Serve(l, nil)
}

View file

@ -41,6 +41,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
"k8s.io/ingress-nginx/internal/ingress/annotations/customhttperrors"
"k8s.io/ingress-nginx/internal/ingress/annotations/defaultbackend"
"k8s.io/ingress-nginx/internal/ingress/annotations/fastcgi"
"k8s.io/ingress-nginx/internal/ingress/annotations/globalratelimit"
"k8s.io/ingress-nginx/internal/ingress/annotations/http2pushpreload"
"k8s.io/ingress-nginx/internal/ingress/annotations/ipdenylist"
@ -85,6 +86,8 @@ type Ingress struct {
CorsConfig cors.Config
CustomHTTPErrors []int
DefaultBackend *apiv1.Service
//TODO: Change this back into an error when https://github.com/imdario/mergo/issues/100 is resolved
FastCGI fastcgi.Config
Denied *string
ExternalAuth authreq.Config
EnableGlobalAuth bool
@ -135,6 +138,7 @@ func NewAnnotationExtractor(cfg resolver.Resolver) Extractor {
"CorsConfig": cors.NewParser(cfg),
"CustomHTTPErrors": customhttperrors.NewParser(cfg),
"DefaultBackend": defaultbackend.NewParser(cfg),
"FastCGI": fastcgi.NewParser(cfg),
"ExternalAuth": authreq.NewParser(cfg),
"EnableGlobalAuth": authreqglobal.NewParser(cfg),
"HTTP2PushPreload": http2pushpreload.NewParser(cfg),

View file

@ -31,7 +31,7 @@ import (
const HTTP = "HTTP"
var (
validProtocols = regexp.MustCompile(`^(AUTO_HTTP|HTTP|HTTPS|AJP|GRPC|GRPCS)$`)
validProtocols = regexp.MustCompile(`^(AUTO_HTTP|HTTP|HTTPS|AJP|GRPC|GRPCS|FCGI)$`)
)
type backendProtocol struct {

View file

@ -0,0 +1,106 @@
/*
Copyright 2018 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 fastcgi
import (
"fmt"
"reflect"
networking "k8s.io/api/networking/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
ing_errors "k8s.io/ingress-nginx/internal/ingress/errors"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
type fastcgi struct {
r resolver.Resolver
}
// Config describes the per location fastcgi config
type Config struct {
Index string `json:"index"`
Params map[string]string `json:"params"`
}
// Equal tests for equality between two Configuration types
func (l1 *Config) Equal(l2 *Config) bool {
if l1 == l2 {
return true
}
if l1 == nil || l2 == nil {
return false
}
if l1.Index != l2.Index {
return false
}
return reflect.DeepEqual(l1.Params, l2.Params)
}
// NewParser creates a new fastcgiConfig protocol annotation parser
func NewParser(r resolver.Resolver) parser.IngressAnnotation {
return fastcgi{r}
}
// ParseAnnotations parses the annotations contained in the ingress
// rule used to indicate the fastcgiConfig.
func (a fastcgi) Parse(ing *networking.Ingress) (interface{}, error) {
fcgiConfig := Config{}
if ing.GetAnnotations() == nil {
return fcgiConfig, nil
}
index, err := parser.GetStringAnnotation("fastcgi-index", ing)
if err != nil {
index = ""
}
fcgiConfig.Index = index
cm, err := parser.GetStringAnnotation("fastcgi-params-configmap", ing)
if err != nil {
return fcgiConfig, nil
}
cmns, cmn, err := cache.SplitMetaNamespaceKey(cm)
if err != nil {
return fcgiConfig, ing_errors.LocationDenied{
Reason: fmt.Errorf("error reading configmap name from annotation: %w", err),
}
}
if cmns != "" && cmns != ing.Namespace {
return fcgiConfig, fmt.Errorf("different namespace is not supported on fast_cgi param configmap")
}
cm = fmt.Sprintf("%v/%v", ing.Namespace, cmn)
cmap, err := a.r.GetConfigMap(cm)
if err != nil {
return fcgiConfig, ing_errors.LocationDenied{
Reason: fmt.Errorf("unexpected error reading configmap %s: %w", cm, err),
}
}
fcgiConfig.Params = cmap.Data
return fcgiConfig, nil
}

View file

@ -0,0 +1,285 @@
/*
Copyright 2018 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 fastcgi
import (
"fmt"
"testing"
api "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
"k8s.io/ingress-nginx/internal/ingress/resolver"
)
func buildIngress() *networking.Ingress {
return &networking.Ingress{
ObjectMeta: meta_v1.ObjectMeta{
Name: "foo",
Namespace: api.NamespaceDefault,
},
Spec: networking.IngressSpec{
DefaultBackend: &networking.IngressBackend{
Service: &networking.IngressServiceBackend{
Name: "default-backend",
Port: networking.ServiceBackendPort{
Number: 80,
},
},
},
},
}
}
type mockConfigMap struct {
resolver.Mock
}
func (m mockConfigMap) GetConfigMap(name string) (*api.ConfigMap, error) {
if name != "default/demo-configmap" && name != "otherns/demo-configmap" {
return nil, fmt.Errorf("there is no configmap with name %v", name)
}
cmns, cmn, err := cache.SplitMetaNamespaceKey(name)
if err != nil {
return nil, fmt.Errorf("invalid configmap name")
}
return &api.ConfigMap{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: cmns,
Name: cmn,
},
Data: map[string]string{"REDIRECT_STATUS": "200", "SERVER_NAME": "$server_name"},
}, nil
}
func TestParseEmptyFastCGIAnnotations(t *testing.T) {
ing := buildIngress()
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing ingress without fastcgi")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if config.Index != "" {
t.Errorf("Index should be an empty string")
}
if len(config.Params) != 0 {
t.Errorf("Params should be an empty slice")
}
}
func TestParseFastCGIIndexAnnotation(t *testing.T) {
ing := buildIngress()
const expectedAnnotation = "index.php"
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-index")] = expectedAnnotation
ing.SetAnnotations(data)
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing ingress without fastcgi")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if config.Index != "index.php" {
t.Errorf("expected %s but %v returned", expectedAnnotation, config.Index)
}
}
func TestParseEmptyFastCGIParamsConfigMapAnnotation(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = ""
ing.SetAnnotations(data)
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing ingress without fastcgi")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if len(config.Params) != 0 {
t.Errorf("Params should be an empty slice")
}
}
func TestParseFastCGIInvalidParamsConfigMapAnnotation(t *testing.T) {
ing := buildIngress()
invalidConfigMapList := []string{"unknown/configMap", "unknown/config/map"}
for _, configmap := range invalidConfigMapList {
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = configmap
ing.SetAnnotations(data)
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err == nil {
t.Errorf("Reading an unexisting configmap should return an error")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if len(config.Params) != 0 {
t.Errorf("Params should be an empty slice")
}
}
}
func TestParseFastCGIParamsConfigMapAnnotationWithoutNS(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = "demo-configmap"
ing.SetAnnotations(data)
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing ingress without fastcgi")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if len(config.Params) != 2 {
t.Errorf("Params should have a length of 2")
}
if config.Params["REDIRECT_STATUS"] != "200" || config.Params["SERVER_NAME"] != "$server_name" {
t.Errorf("Params value is not the one expected")
}
}
func TestParseFastCGIParamsConfigMapAnnotationWithNS(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = "default/demo-configmap"
ing.SetAnnotations(data)
i, err := NewParser(&mockConfigMap{}).Parse(ing)
if err != nil {
t.Errorf("unexpected error parsing ingress without fastcgi")
}
config, ok := i.(Config)
if !ok {
t.Errorf("Parse do not return a Config object")
}
if len(config.Params) != 2 {
t.Errorf("Params should have a length of 2")
}
if config.Params["REDIRECT_STATUS"] != "200" || config.Params["SERVER_NAME"] != "$server_name" {
t.Errorf("Params value is not the one expected")
}
}
func TestParseFastCGIParamsConfigMapAnnotationWithDifferentNS(t *testing.T) {
ing := buildIngress()
data := map[string]string{}
data[parser.GetAnnotationWithPrefix("fastcgi-params-configmap")] = "otherns/demo-configmap"
ing.SetAnnotations(data)
_, err := NewParser(&mockConfigMap{}).Parse(ing)
if err == nil {
t.Errorf("Different namespace configmap should return an error")
}
}
func TestConfigEquality(t *testing.T) {
var nilConfig *Config
config := Config{
Index: "index.php",
Params: map[string]string{"REDIRECT_STATUS": "200", "SERVER_NAME": "$server_name"},
}
configCopy := Config{
Index: "index.php",
Params: map[string]string{"REDIRECT_STATUS": "200", "SERVER_NAME": "$server_name"},
}
config2 := Config{
Index: "index.php",
Params: map[string]string{"REDIRECT_STATUS": "200"},
}
config3 := Config{
Index: "index.py",
Params: map[string]string{"SERVER_NAME": "$server_name", "REDIRECT_STATUS": "200"},
}
config4 := Config{
Index: "index.php",
Params: map[string]string{"SERVER_NAME": "$server_name", "REDIRECT_STATUS": "200"},
}
if !config.Equal(&config) {
t.Errorf("config should be equal to itself")
}
if nilConfig.Equal(&config) {
t.Errorf("Foo")
}
if !config.Equal(&configCopy) {
t.Errorf("config should be equal to configCopy")
}
if config.Equal(&config2) {
t.Errorf("config2 should not be equal to config")
}
if config.Equal(&config3) {
t.Errorf("config3 should not be equal to config")
}
if !config.Equal(&config4) {
t.Errorf("config4 should be equal to config")
}
}

View file

@ -160,6 +160,7 @@ func normalizeString(input string) string {
var configmapAnnotations = sets.NewString(
"auth-proxy-set-header",
"fastcgi-params-configmap",
)
// AnnotationsReferencesConfigmap checks if at least one annotation in the Ingress rule

View file

@ -271,8 +271,6 @@ func (n *NGINXController) CheckWarning(ing *networking.Ingress) ([]string, error
"influxdb-host",
"influxdb-server-name",
"secure-verify-ca-secret",
"fastcgi-params-configmap",
"fastcgi-index",
)
// Skip checks if the ingress is marked as deleted
@ -608,7 +606,7 @@ func (n *NGINXController) getConfiguration(ingresses []*ingress.Ingress) (sets.S
for _, server := range servers {
// If a location is defined by a prefix string that ends with the slash character, and requests are processed by one of
// proxy_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed.
// proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass, or grpc_pass, then the special processing is performed.
// In response to a request with URI equal to // this string, but without the trailing slash, a permanent redirect with the
// code 301 will be returned to the requested URI with the slash appended. If this is not desired, an exact match of the
// URIand location could be defined like this:
@ -1519,6 +1517,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
loc.Logs = anns.Logs
loc.DefaultBackend = anns.DefaultBackend
loc.BackendProtocol = anns.BackendProtocol
loc.FastCGI = anns.FastCGI
loc.CustomHTTPErrors = anns.CustomHTTPErrors
loc.ModSecurity = anns.ModSecurity
loc.Satisfy = anns.Satisfy

View file

@ -431,7 +431,7 @@ func TestCheckWarning(t *testing.T) {
t.Run("adding invalid annotations increases the warning count", func(t *testing.T) {
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("enable-influxdb")] = "true"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("secure-verify-ca-secret")] = "true"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("fastcgi-index")] = "blabla"
ing.ObjectMeta.Annotations[parser.GetAnnotationWithPrefix("influxdb-host")] = "blabla"
defer func() {
ing.ObjectMeta.Annotations = map[string]string{}
}()

View file

@ -739,6 +739,9 @@ func buildProxyPass(host string, b interface{}, loc interface{}) string {
case "AJP":
proto = ""
proxyPass = "ajp_pass"
case "FCGI":
proto = ""
proxyPass = "fastcgi_pass"
}
upstreamName := "upstream_balancer"

View file

@ -1138,6 +1138,7 @@ func TestOpentracingPropagateContext(t *testing.T) {
{BackendProtocol: "GRPC"}: "opentracing_grpc_propagate_context;",
{BackendProtocol: "GRPCS"}: "opentracing_grpc_propagate_context;",
{BackendProtocol: "AJP"}: "opentracing_propagate_context;",
{BackendProtocol: "FCGI"}: "opentracing_propagate_context;",
nil: "",
}
@ -1157,6 +1158,7 @@ func TestOpentelemetryPropagateContext(t *testing.T) {
{BackendProtocol: "GRPC"}: "opentelemetry_propagate;",
{BackendProtocol: "GRPCS"}: "opentelemetry_propagate;",
{BackendProtocol: "AJP"}: "opentelemetry_propagate;",
{BackendProtocol: "FCGI"}: "opentelemetry_propagate;",
nil: "",
}

View file

@ -27,6 +27,7 @@ import (
"k8s.io/ingress-nginx/internal/ingress/annotations/authtls"
"k8s.io/ingress-nginx/internal/ingress/annotations/connection"
"k8s.io/ingress-nginx/internal/ingress/annotations/cors"
"k8s.io/ingress-nginx/internal/ingress/annotations/fastcgi"
"k8s.io/ingress-nginx/internal/ingress/annotations/globalratelimit"
"k8s.io/ingress-nginx/internal/ingress/annotations/ipdenylist"
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
@ -339,6 +340,9 @@ type Location struct {
// BackendProtocol indicates which protocol should be used to communicate with the service
// By default this is HTTP
BackendProtocol string `json:"backend-protocol"`
// FastCGI allows the ingress to act as a FastCGI client for a given location.
// +optional
FastCGI fastcgi.Config `json:"fastcgi,omitempty"`
// CustomHTTPErrors specifies the error codes that should be intercepted.
// +optional
CustomHTTPErrors []int `json:"custom-http-errors"`

View file

@ -435,6 +435,10 @@ func (l1 *Location) Equal(l2 *Location) bool {
return false
}
if !(&l1.FastCGI).Equal(&l2.FastCGI) {
return false
}
match := compareInts(l1.CustomHTTPErrors, l2.CustomHTTPErrors)
if !match {
return false

View file

@ -305,6 +305,7 @@ http {
keepalive_requests {{ $cfg.KeepAliveRequests }};
client_body_temp_path /tmp/nginx/client-body;
fastcgi_temp_path /tmp/nginx/fastcgi-temp;
proxy_temp_path /tmp/nginx/proxy-temp;
ajp_temp_path /tmp/nginx/ajp-temp;
@ -1488,6 +1489,16 @@ stream {
{{ range $errCode := $location.CustomHTTPErrors }}
error_page {{ $errCode }} = @custom_{{ $location.DefaultBackendUpstreamName }}_{{ $errCode }};{{ end }}
{{ if (eq $location.BackendProtocol "FCGI") }}
include /etc/nginx/fastcgi_params;
{{ end }}
{{- if $location.FastCGI.Index -}}
fastcgi_index {{ $location.FastCGI.Index | quote }};
{{- end -}}
{{ range $k, $v := $location.FastCGI.Params }}
fastcgi_param {{ $k }} {{ $v | quote }};
{{ end }}
{{ if not (empty $location.Redirect.URL) }}
return {{ $location.Redirect.Code }} {{ $location.Redirect.URL }};
{{ end }}

View file

@ -91,6 +91,21 @@ var _ = framework.DescribeAnnotation("backend-protocol", func() {
})
})
ginkgo.It("should set backend protocol to '' and use fastcgi_pass", func() {
host := "backendprotocol.foo.com"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI",
}
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, "fastcgi_pass upstream_balancer;")
})
})
ginkgo.It("should set backend protocol to '' and use ajp_pass", func() {
host := "backendprotocol.foo.com"
annotations := map[string]string{

View file

@ -0,0 +1,125 @@
/*
Copyright 2019 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 annotations
import (
"net/http"
"strings"
"github.com/onsi/ginkgo/v2"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/ingress-nginx/test/e2e/framework"
)
var _ = framework.DescribeAnnotation("backend-protocol - FastCGI", func() {
f := framework.NewDefaultFramework("fastcgi")
ginkgo.BeforeEach(func() {
f.NewFastCGIHelloServerDeployment()
})
ginkgo.It("should use fastcgi_pass in the configuration file", func() {
host := "fastcgi"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI",
}
ing := framework.NewSingleIngress(host, "/hello", host, f.Namespace, "fastcgi-helloserver", 9000, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "include /etc/nginx/fastcgi_params;") &&
strings.Contains(server, "fastcgi_pass")
})
})
ginkgo.It("should add fastcgi_index in the configuration file", func() {
host := "fastcgi-index"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI",
"nginx.ingress.kubernetes.io/fastcgi-index": "index.php",
}
ing := framework.NewSingleIngress(host, "/hello", host, f.Namespace, "fastcgi-helloserver", 9000, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "fastcgi_index \"index.php\";")
})
})
ginkgo.It("should add fastcgi_param in the configuration file", func() {
configuration := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "fastcgi-configmap",
Namespace: f.Namespace,
},
Data: map[string]string{
"SCRIPT_FILENAME": "/home/www/scripts/php$fastcgi_script_name",
"REDIRECT_STATUS": "200",
},
}
f.EnsureConfigMap(configuration)
host := "fastcgi-params-configmap"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI",
"nginx.ingress.kubernetes.io/fastcgi-params-configmap": "fastcgi-configmap",
}
ing := framework.NewSingleIngress(host, "/hello", host, f.Namespace, "fastcgi-helloserver", 9000, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "fastcgi_param SCRIPT_FILENAME \"/home/www/scripts/php$fastcgi_script_name\";") &&
strings.Contains(server, "fastcgi_param REDIRECT_STATUS \"200\";")
})
})
ginkgo.It("should return OK for service with backend protocol FastCGI", func() {
host := "fastcgi-helloserver"
path := "/hello"
annotations := map[string]string{
"nginx.ingress.kubernetes.io/backend-protocol": "FCGI",
}
ing := framework.NewSingleIngress(host, path, host, f.Namespace, "fastcgi-helloserver", 9000, annotations)
f.EnsureIngress(ing)
f.WaitForNginxServer(host,
func(server string) bool {
return strings.Contains(server, "fastcgi_pass")
})
f.HTTPTestClient().
GET(path).
WithHeader("Host", host).
Expect().
Status(http.StatusOK).
Body().Contains("Hello world!")
})
})

View file

@ -0,0 +1,104 @@
/*
Copyright 2017 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 framework
import (
"github.com/onsi/ginkgo/v2"
"github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/util/intstr"
)
// NewFastCGIHelloServerDeployment creates a new single replica
// deployment of the fortune teller image in a particular namespace
func (f *Framework) NewFastCGIHelloServerDeployment() {
f.NewNewFastCGIHelloServerDeploymentWithReplicas(1)
}
// NewNewFastCGIHelloServerDeploymentWithReplicas creates a new deployment of the
// fortune teller image in a particular namespace. Number of replicas is configurable
func (f *Framework) NewNewFastCGIHelloServerDeploymentWithReplicas(replicas int32) {
deployment := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Name: "fastcgi-helloserver",
Namespace: f.Namespace,
},
Spec: appsv1.DeploymentSpec{
Replicas: NewInt32(replicas),
Selector: &metav1.LabelSelector{
MatchLabels: map[string]string{
"app": "fastcgi-helloserver",
},
},
Template: corev1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{
"app": "fastcgi-helloserver",
},
},
Spec: corev1.PodSpec{
TerminationGracePeriodSeconds: NewInt64(0),
Containers: []corev1.Container{
{
Name: "fastcgi-helloserver",
Image: "registry.k8s.io/ingress-nginx/e2e-test-fastcgi-helloserver@sha256:0e08c836cc58f1ea862578de99b13bc4264fe071e816f96dc1d79857bfba7473",
Env: []corev1.EnvVar{},
Ports: []corev1.ContainerPort{
{
Name: "fastcgi",
ContainerPort: 9000,
},
},
},
},
},
},
},
}
d := f.EnsureDeployment(deployment)
err := waitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, metav1.ListOptions{
LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(),
})
assert.Nil(ginkgo.GinkgoT(), err, "failed to wait for to become ready")
service := &corev1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: "fastcgi-helloserver",
Namespace: f.Namespace,
},
Spec: corev1.ServiceSpec{
Ports: []corev1.ServicePort{
{
Name: "fastcgi",
Port: 9000,
TargetPort: intstr.FromInt(9000),
Protocol: "TCP",
},
},
Selector: map[string]string{
"app": "fastcgi-helloserver",
},
},
}
f.EnsureService(service)
}