Merge pull request #4344 from Nuglif/fastcgi-backend-support
Add FastCGI backend support (#2982)
This commit is contained in:
commit
c8a3710fb8
22 changed files with 938 additions and 6 deletions
117
docs/user-guide/fcgi-services.md
Normal file
117
docs/user-guide/fcgi-services.md
Normal file
|
@ -0,0 +1,117 @@
|
|||
|
||||
|
||||
# Exposing FastCGI Servers
|
||||
|
||||
> **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.
|
||||
>
|
||||
> — Wikipedia
|
||||
|
||||
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_.
|
||||
|
||||
|
||||
## 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: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: "nginx"
|
||||
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:
|
||||
rules:
|
||||
- host: app.example.com
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: example-service
|
||||
servicePort: fastcgi
|
||||
```
|
||||
|
||||
## The FastCGI Ingress Annotations
|
||||
|
||||
### The `nginx.ingress.kubernetes.io/backend-protocol` Annotation
|
||||
|
||||
To enable FastCGI, the `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 the whole _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](http://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](http://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: "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: "example-namespace/example-configmap"`
|
105
images/fastcgi-helloserver/Makefile
Normal file
105
images/fastcgi-helloserver/Makefile
Normal file
|
@ -0,0 +1,105 @@
|
|||
all: all-container
|
||||
|
||||
BUILDTAGS=
|
||||
|
||||
# Use the 0.0 tag for testing, it shouldn't clobber any release builds
|
||||
TAG?=0.1
|
||||
REGISTRY?=kubernetes-ingress-controller
|
||||
GOOS?=linux
|
||||
DOCKER?=docker
|
||||
SED_I?=sed -i
|
||||
GOHOSTOS ?= $(shell go env GOHOSTOS)
|
||||
|
||||
PKG=k8s.io/ingress-nginx/images/fastcgi-helloserver
|
||||
|
||||
ifeq ($(GOHOSTOS),darwin)
|
||||
SED_I=sed -i ''
|
||||
endif
|
||||
|
||||
REPO_INFO=$(shell git config --get remote.origin.url)
|
||||
|
||||
ARCH ?= $(shell go env GOARCH)
|
||||
GOARCH = ${ARCH}
|
||||
|
||||
BASEIMAGE?=alpine:3.9
|
||||
|
||||
ALL_ARCH = amd64 arm arm64 ppc64le
|
||||
|
||||
QEMUVERSION=v3.0.0
|
||||
|
||||
IMGNAME = fastcgi-helloserver
|
||||
IMAGE = $(REGISTRY)/$(IMGNAME)
|
||||
MULTI_ARCH_IMG = $(IMAGE)-$(ARCH)
|
||||
|
||||
ifeq ($(ARCH),arm)
|
||||
QEMUARCH=arm
|
||||
GOARCH=arm
|
||||
endif
|
||||
ifeq ($(ARCH),arm64)
|
||||
QEMUARCH=aarch64
|
||||
endif
|
||||
ifeq ($(ARCH),ppc64le)
|
||||
QEMUARCH=ppc64le
|
||||
GOARCH=ppc64le
|
||||
endif
|
||||
|
||||
TEMP_DIR := $(shell mktemp -d)
|
||||
|
||||
DOCKERFILE := $(TEMP_DIR)/rootfs/Dockerfile
|
||||
|
||||
sub-container-%:
|
||||
$(MAKE) ARCH=$* build container
|
||||
|
||||
sub-push-%:
|
||||
$(MAKE) ARCH=$* push
|
||||
|
||||
all-container: $(addprefix sub-container-,$(ALL_ARCH))
|
||||
|
||||
all-push: $(addprefix sub-push-,$(ALL_ARCH))
|
||||
|
||||
container: .container-$(ARCH)
|
||||
.container-$(ARCH):
|
||||
cp -r ./* $(TEMP_DIR)
|
||||
$(SED_I) 's|BASEIMAGE|$(BASEIMAGE)|g' $(DOCKERFILE)
|
||||
$(SED_I) "s|QEMUARCH|$(QEMUARCH)|g" $(DOCKERFILE)
|
||||
|
||||
ifeq ($(ARCH),amd64)
|
||||
# When building "normally" for amd64, remove the whole line, it has no part in the amd64 image
|
||||
$(SED_I) "/CROSS_BUILD_/d" $(DOCKERFILE)
|
||||
else
|
||||
# When cross-building, only the placeholder "CROSS_BUILD_" should be removed
|
||||
# Register /usr/bin/qemu-ARCH-static as the handler for ARM binaries in the kernel
|
||||
# $(DOCKER) run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
curl -sSL https://github.com/multiarch/qemu-user-static/releases/download/$(QEMUVERSION)/x86_64_qemu-$(QEMUARCH)-static.tar.gz | tar -xz -C $(TEMP_DIR)/rootfs
|
||||
$(SED_I) "s/CROSS_BUILD_//g" $(DOCKERFILE)
|
||||
endif
|
||||
|
||||
$(DOCKER) build -t $(MULTI_ARCH_IMG):$(TAG) $(TEMP_DIR)/rootfs
|
||||
|
||||
ifeq ($(ARCH), amd64)
|
||||
# This is for to maintain the backward compatibility
|
||||
$(DOCKER) tag $(MULTI_ARCH_IMG):$(TAG) $(IMAGE):$(TAG)
|
||||
endif
|
||||
|
||||
push: .push-$(ARCH)
|
||||
.push-$(ARCH):
|
||||
$(DOCKER) push $(MULTI_ARCH_IMG):$(TAG)
|
||||
ifeq ($(ARCH), amd64)
|
||||
$(DOCKER) push $(IMAGE):$(TAG)
|
||||
endif
|
||||
|
||||
clean:
|
||||
$(DOCKER) rmi -f $(MULTI_ARCH_IMG):$(TAG) || true
|
||||
|
||||
build: clean
|
||||
CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} go build -a -installsuffix cgo \
|
||||
-ldflags "-s -w" \
|
||||
-o ${TEMP_DIR}/rootfs/fastcgi-helloserver ${PKG}/...
|
||||
|
||||
release: all-container all-push
|
||||
echo "done"
|
||||
|
||||
.PHONY: register-qemu
|
||||
register-qemu:
|
||||
# Register /usr/bin/qemu-ARCH-static as the handler for binaries in multiple platforms
|
||||
$(DOCKER) run --rm --privileged multiarch/qemu-user-static:register --reset
|
30
images/fastcgi-helloserver/main.go
Normal file
30
images/fastcgi-helloserver/main.go
Normal 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)
|
||||
}
|
21
images/fastcgi-helloserver/rootfs/Dockerfile
Executable file
21
images/fastcgi-helloserver/rootfs/Dockerfile
Executable file
|
@ -0,0 +1,21 @@
|
|||
# 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 BASEIMAGE
|
||||
|
||||
CROSS_BUILD_COPY qemu-QEMUARCH-static /usr/bin/
|
||||
|
||||
COPY . /
|
||||
|
||||
CMD ["/fastcgi-helloserver"]
|
|
@ -38,6 +38,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/http2pushpreload"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/influxdb"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||
|
@ -82,6 +83,7 @@ type Ingress struct {
|
|||
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
|
||||
|
@ -128,6 +130,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),
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
const HTTP = "HTTP"
|
||||
|
||||
var (
|
||||
validProtocols = regexp.MustCompile(`^(HTTP|HTTPS|AJP|GRPC|GRPCS)$`)
|
||||
validProtocols = regexp.MustCompile(`^(HTTP|HTTPS|AJP|GRPC|GRPCS|FCGI)$`)
|
||||
)
|
||||
|
||||
type backendProtocol struct {
|
||||
|
|
107
internal/ingress/annotations/fastcgi/main.go
Normal file
107
internal/ingress/annotations/fastcgi/main.go
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
networking "k8s.io/api/networking/v1beta1"
|
||||
"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: errors.Wrap(err, "error reading configmap name from annotation"),
|
||||
}
|
||||
}
|
||||
|
||||
if cmns == "" {
|
||||
cmns = ing.Namespace
|
||||
}
|
||||
|
||||
cm = fmt.Sprintf("%v/%v", cmns, cmn)
|
||||
cmap, err := a.r.GetConfigMap(cm)
|
||||
if err != nil {
|
||||
return fcgiConfig, ing_errors.LocationDenied{
|
||||
Reason: errors.Wrapf(err, "unexpected error reading configmap %v", cm),
|
||||
}
|
||||
}
|
||||
|
||||
fcgiConfig.Params = cmap.Data
|
||||
|
||||
return fcgiConfig, nil
|
||||
}
|
263
internal/ingress/annotations/fastcgi/main_test.go
Normal file
263
internal/ingress/annotations/fastcgi/main_test.go
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
api "k8s.io/api/core/v1"
|
||||
networking "k8s.io/api/networking/v1beta1"
|
||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/parser"
|
||||
"k8s.io/ingress-nginx/internal/ingress/errors"
|
||||
"k8s.io/ingress-nginx/internal/ingress/resolver"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
func buildIngress() *networking.Ingress {
|
||||
return &networking.Ingress{
|
||||
ObjectMeta: meta_v1.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: networking.IngressSpec{
|
||||
Backend: &networking.IngressBackend{
|
||||
ServiceName: "fastcgi",
|
||||
ServicePort: intstr.FromInt(80),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type mockConfigMap struct {
|
||||
resolver.Mock
|
||||
}
|
||||
|
||||
func (m mockConfigMap) GetConfigMap(name string) (*api.ConfigMap, error) {
|
||||
if name != "default/demo-configmap" {
|
||||
return nil, errors.Errorf("there is no configmap with name %v", name)
|
||||
}
|
||||
|
||||
return &api.ConfigMap{
|
||||
ObjectMeta: meta_v1.ObjectMeta{
|
||||
Namespace: api.NamespaceDefault,
|
||||
Name: "demo-secret",
|
||||
},
|
||||
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 0 != len(config.Params) {
|
||||
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 0 != len(config.Params) {
|
||||
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 0 != len(config.Params) {
|
||||
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 2 != len(config.Params) {
|
||||
t.Errorf("Params should have a length of 2")
|
||||
}
|
||||
|
||||
if "200" != config.Params["REDIRECT_STATUS"] || "$server_name" != config.Params["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 2 != len(config.Params) {
|
||||
t.Errorf("Params should have a length of 2")
|
||||
}
|
||||
|
||||
if "200" != config.Params["REDIRECT_STATUS"] || "$server_name" != config.Params["SERVER_NAME"] {
|
||||
t.Errorf("Params value is not the one expected")
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
|
@ -1165,6 +1165,7 @@ func locationApplyAnnotations(loc *ingress.Location, anns *annotations.Ingress)
|
|||
loc.InfluxDB = anns.InfluxDB
|
||||
loc.DefaultBackend = anns.DefaultBackend
|
||||
loc.BackendProtocol = anns.BackendProtocol
|
||||
loc.FastCGI = anns.FastCGI
|
||||
loc.CustomHTTPErrors = anns.CustomHTTPErrors
|
||||
loc.ModSecurity = anns.ModSecurity
|
||||
loc.Satisfy = anns.Satisfy
|
||||
|
|
|
@ -497,6 +497,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"
|
||||
|
|
|
@ -874,6 +874,7 @@ func TestOpentracingPropagateContext(t *testing.T) {
|
|||
&ingress.Location{BackendProtocol: "GRPC"}: "opentracing_grpc_propagate_context",
|
||||
&ingress.Location{BackendProtocol: "GRPCS"}: "opentracing_grpc_propagate_context",
|
||||
&ingress.Location{BackendProtocol: "AJP"}: "opentracing_propagate_context",
|
||||
&ingress.Location{BackendProtocol: "FCGI"}: "opentracing_propagate_context",
|
||||
"not a location": "opentracing_propagate_context",
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ type Resolver interface {
|
|||
// GetDefaultBackend returns the backend that must be used as default
|
||||
GetDefaultBackend() defaults.Backend
|
||||
|
||||
// GetConfigMap searches for configmap containing the namespace and name usting the character /
|
||||
GetConfigMap(string) (*apiv1.ConfigMap, error)
|
||||
|
||||
// GetSecret searches for secrets containing the namespace and name using a the character /
|
||||
GetSecret(string) (*apiv1.Secret, error)
|
||||
|
||||
|
|
|
@ -31,6 +31,11 @@ func (m Mock) GetDefaultBackend() defaults.Backend {
|
|||
return defaults.Backend{}
|
||||
}
|
||||
|
||||
// GetConfigMap searches for configmap containing the namespace and name usting the character /
|
||||
func (m Mock) GetConfigMap(string) (*apiv1.ConfigMap, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetSecret searches for secrets contenating the namespace and name using a the character /
|
||||
func (m Mock) GetSecret(string) (*apiv1.Secret, error) {
|
||||
return nil, nil
|
||||
|
|
|
@ -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/influxdb"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/ipwhitelist"
|
||||
"k8s.io/ingress-nginx/internal/ingress/annotations/log"
|
||||
|
@ -308,6 +309,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"`
|
||||
|
|
|
@ -401,6 +401,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
|
||||
|
|
|
@ -54,6 +54,7 @@ nav:
|
|||
- Custom errors: "user-guide/custom-errors.md"
|
||||
- Default backend: "user-guide/default-backend.md"
|
||||
- Exposing TCP and UDP services: "user-guide/exposing-tcp-udp-services.md"
|
||||
- Exposing FCGI services: "user-guide/fcgi-services.md"
|
||||
- Regular expressions in paths: user-guide/ingress-path-matching.md
|
||||
- External Articles: "user-guide/external-articles.md"
|
||||
- Miscellaneous: "user-guide/miscellaneous.md"
|
||||
|
|
|
@ -24,7 +24,8 @@ RUN clean-install \
|
|||
|
||||
COPY --chown=www-data:www-data . /
|
||||
|
||||
RUN cp /usr/local/openresty/nginx/conf/mime.types /etc/nginx/mime.types
|
||||
RUN cp /usr/local/openresty/nginx/conf/mime.types /etc/nginx/mime.types \
|
||||
&& cp /usr/local/openresty/nginx/conf/fastcgi_params /etc/nginx/fastcgi_params
|
||||
RUN ln -s /usr/local/openresty/nginx/modules /etc/nginx/modules
|
||||
|
||||
# Add LuaRocks paths
|
||||
|
|
|
@ -1295,6 +1295,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 }}";
|
||||
{{- end -}}
|
||||
{{ range $k, $v := $location.FastCGI.Params }}
|
||||
fastcgi_param {{ $k }} "{{ $v }}";
|
||||
{{ end }}
|
||||
|
||||
{{ buildProxyPass $server.Hostname $all.Backends $location }}
|
||||
{{ if (or (eq $location.Proxy.ProxyRedirectFrom "default") (eq $location.Proxy.ProxyRedirectFrom "off")) }}
|
||||
proxy_redirect {{ $location.Proxy.ProxyRedirectFrom }};
|
||||
|
|
|
@ -32,7 +32,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Backendprotocol", func() {
|
|||
AfterEach(func() {
|
||||
})
|
||||
|
||||
It("should set backend protocol to https://", func() {
|
||||
It("should set backend protocol to https:// and use proxy_pass", func() {
|
||||
host := "backendprotocol.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/backend-protocol": "HTTPS",
|
||||
|
@ -47,7 +47,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Backendprotocol", func() {
|
|||
})
|
||||
})
|
||||
|
||||
It("should set backend protocol to grpc://", func() {
|
||||
It("should set backend protocol to grpc:// and use grpc_pass", func() {
|
||||
host := "backendprotocol.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/backend-protocol": "GRPC",
|
||||
|
@ -62,7 +62,7 @@ var _ = framework.IngressNginxDescribe("Annotations - Backendprotocol", func() {
|
|||
})
|
||||
})
|
||||
|
||||
It("should set backend protocol to grpcs://", func() {
|
||||
It("should set backend protocol to grpcs:// and use grpc_pass", func() {
|
||||
host := "backendprotocol.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/backend-protocol": "GRPCS",
|
||||
|
@ -77,7 +77,22 @@ var _ = framework.IngressNginxDescribe("Annotations - Backendprotocol", func() {
|
|||
})
|
||||
})
|
||||
|
||||
It("should set backend protocol to ''", func() {
|
||||
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, "http-svc", 80, &annotations)
|
||||
f.EnsureIngress(ing)
|
||||
|
||||
f.WaitForNginxServer(host,
|
||||
func(server string) bool {
|
||||
return Expect(server).Should(ContainSubstring("fastcgi_pass upstream_balancer;"))
|
||||
})
|
||||
})
|
||||
|
||||
It("should set backend protocol to '' and use ajp_pass", func() {
|
||||
host := "backendprotocol.foo.com"
|
||||
annotations := map[string]string{
|
||||
"nginx.ingress.kubernetes.io/backend-protocol": "AJP",
|
||||
|
|
130
test/e2e/annotations/fastcgi.go
Normal file
130
test/e2e/annotations/fastcgi.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/parnurzeal/gorequest"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/ingress-nginx/test/e2e/framework"
|
||||
)
|
||||
|
||||
var _ = framework.IngressNginxDescribe("Annotations - FastCGI", func() {
|
||||
f := framework.NewDefaultFramework("fastcgi")
|
||||
|
||||
BeforeEach(func() {
|
||||
f.NewFastCGIHelloServerDeployment()
|
||||
})
|
||||
|
||||
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 Expect(server).Should(ContainSubstring("include /etc/nginx/fastcgi_params;")) &&
|
||||
Expect(server).Should(ContainSubstring("fastcgi_pass"))
|
||||
})
|
||||
})
|
||||
|
||||
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 Expect(server).Should(ContainSubstring("fastcgi_index \"index.php\";"))
|
||||
})
|
||||
})
|
||||
|
||||
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",
|
||||
},
|
||||
}
|
||||
|
||||
cm, err := f.EnsureConfigMap(configuration)
|
||||
Expect(err).NotTo(HaveOccurred(), "failed to create an the configmap")
|
||||
Expect(cm).NotTo(BeNil(), "expected a configmap but none returned")
|
||||
|
||||
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 Expect(server).Should(ContainSubstring("fastcgi_param SCRIPT_FILENAME \"/home/www/scripts/php$fastcgi_script_name\";")) &&
|
||||
Expect(server).Should(ContainSubstring("fastcgi_param REDIRECT_STATUS \"200\";"))
|
||||
})
|
||||
})
|
||||
|
||||
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 Expect(server).Should(ContainSubstring("fastcgi_pass"))
|
||||
})
|
||||
|
||||
resp, body, errs := gorequest.New().
|
||||
Get(f.GetURL(framework.HTTP)+path).
|
||||
Set("Host", host).
|
||||
End()
|
||||
|
||||
Expect(errs).Should(BeEmpty())
|
||||
Expect(resp.StatusCode).Should(Equal(http.StatusOK))
|
||||
Expect(body).Should(ContainSubstring("Hello world!"))
|
||||
})
|
||||
})
|
106
test/e2e/framework/fastcgi_helloserver.go
Normal file
106
test/e2e/framework/fastcgi_helloserver.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
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/gomega"
|
||||
|
||||
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: "ingress-controller/fastcgi-helloserver:dev",
|
||||
Env: []corev1.EnvVar{},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "fastcgi",
|
||||
ContainerPort: 9000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
d, err := f.EnsureDeployment(deployment)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(d).NotTo(BeNil(), "expected a fastcgi-helloserver deployment")
|
||||
|
||||
err = WaitForPodsReady(f.KubeClientSet, DefaultTimeout, int(replicas), f.Namespace, metav1.ListOptions{
|
||||
LabelSelector: fields.SelectorFromSet(fields.Set(d.Spec.Template.ObjectMeta.Labels)).String(),
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred(), "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)
|
||||
}
|
|
@ -55,6 +55,7 @@ kubectl config set-context kubernetes-admin@${KIND_CLUSTER_NAME}
|
|||
echo "[dev-env] building container"
|
||||
make -C ${DIR}/../../ build container
|
||||
make -C ${DIR}/../../ e2e-test-image
|
||||
make -C ${DIR}/../../images/fastcgi-helloserver/ build container
|
||||
|
||||
# Remove after https://github.com/kubernetes/ingress-nginx/pull/4271 is merged
|
||||
docker tag ${REGISTRY}/nginx-ingress-controller-${ARCH}:${TAG} ${REGISTRY}/nginx-ingress-controller:${TAG}
|
||||
|
@ -62,6 +63,7 @@ docker tag ${REGISTRY}/nginx-ingress-controller-${ARCH}:${TAG} ${REGISTRY}/nginx
|
|||
echo "[dev-env] copying docker images to cluster..."
|
||||
kind load docker-image --name="${KIND_CLUSTER_NAME}" nginx-ingress-controller:e2e
|
||||
kind load docker-image --name="${KIND_CLUSTER_NAME}" ${REGISTRY}/nginx-ingress-controller:${TAG}
|
||||
kind load docker-image --name="${KIND_CLUSTER_NAME}" ${REGISTRY}/fastcgi-helloserver:${TAG}
|
||||
|
||||
echo "[dev-env] running e2e tests..."
|
||||
make -C ${DIR}/../../ e2e-test
|
||||
|
|
Loading…
Reference in a new issue