From 37b79d75c74937ed34c3fa154512a3228c28d765 Mon Sep 17 00:00:00 2001 From: Dean Coakley Date: Wed, 19 Feb 2020 14:29:51 +0000 Subject: [PATCH 1/2] Add CRD schema validation --- .gitignore | 3 + Makefile | 8 +- .../common/custom-resource-definitions.yaml | 35 -- deployments/common/vs-definition.yaml | 377 +++++++++++++++++ deployments/common/vsr-definition.yaml | 359 +++++++++++++++++ .../controller-custom-resources.yaml | 41 -- .../templates/controller-vs-definition.yaml | 381 ++++++++++++++++++ .../templates/controller-vsr-definition.yaml | 363 +++++++++++++++++ ...server-and-virtualserverroute-resources.md | 41 +- .../installation-with-manifests.md | 15 +- hack/update-crds.sh | 15 + hack/verify-crds.sh | 39 ++ pkg/apis/configuration/v1/types.go | 11 +- tests/suite/custom_resources_utils.py | 33 -- tests/suite/fixtures.py | 28 +- tests/suite/resources_utils.py | 33 -- tests/suite/test_annotations.py | 6 +- tests/suite/test_custom_annotations.py | 4 +- tests/suite/test_hsts.py | 4 +- tests/suite/test_virtual_server.py | 6 +- tests/suite/test_virtual_server_tls.py | 4 +- tests/suite/yaml_utils.py | 8 +- 22 files changed, 1621 insertions(+), 193 deletions(-) delete mode 100644 deployments/common/custom-resource-definitions.yaml create mode 100644 deployments/common/vs-definition.yaml create mode 100644 deployments/common/vsr-definition.yaml delete mode 100644 deployments/helm-chart/templates/controller-custom-resources.yaml create mode 100644 deployments/helm-chart/templates/controller-vs-definition.yaml create mode 100644 deployments/helm-chart/templates/controller-vsr-definition.yaml create mode 100755 hack/update-crds.sh create mode 100755 hack/verify-crds.sh diff --git a/.gitignore b/.gitignore index c96973cf9f..b3d055e37e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ default.pem # IntelliJ IDEA .idea + +# Platform specific CRD schema generation scripts +hack/controller-gen-* diff --git a/Makefile b/Makefile index b3b16691dd..77dfbafa1d 100644 --- a/Makefile +++ b/Makefile @@ -30,9 +30,15 @@ ifneq ($(BUILD_IN_CONTAINER),1) ./hack/verify-codegen.sh endif +verify-crds: + ./hack/verify-crds.sh + update-codegen: ./hack/update-codegen.sh +update-crds: + ./hack/update-crds.sh + certificate-and-key: ifeq ($(GENERATE_DEFAULT_CERT_AND_KEY),1) ./build/generate_default_cert_and_key.sh @@ -43,7 +49,7 @@ ifneq ($(BUILD_IN_CONTAINER),1) CGO_ENABLED=0 GO111MODULE=on GOFLAGS='-mod=vendor' GOOS=linux go build -installsuffix cgo -ldflags "-w -X main.version=${VERSION} -X main.gitCommit=${GIT_COMMIT}" -o nginx-ingress github.com/nginxinc/kubernetes-ingress/cmd/nginx-ingress endif -container: test verify-codegen binary certificate-and-key +container: test verify-codegen verify-crds binary certificate-and-key ifeq ($(BUILD_IN_CONTAINER),1) docker build $(DOCKER_BUILD_OPTIONS) --build-arg IC_VERSION=$(VERSION)-$(GIT_COMMIT) --build-arg GIT_COMMIT=$(GIT_COMMIT) --build-arg VERSION=$(VERSION) --build-arg GOLANG_CONTAINER=$(GOLANG_CONTAINER) --target container -f $(DOCKERFILEPATH)/$(DOCKERFILE) -t $(PREFIX):$(TAG) . else diff --git a/deployments/common/custom-resource-definitions.yaml b/deployments/common/custom-resource-definitions.yaml deleted file mode 100644 index 7506ebf3b6..0000000000 --- a/deployments/common/custom-resource-definitions.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualservers.k8s.nginx.org -spec: - group: k8s.nginx.org - versions: - - name: v1 - served: true - storage: true - scope: Namespaced - names: - plural: virtualservers - singular: virtualserver - kind: VirtualServer - shortNames: - - vs ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualserverroutes.k8s.nginx.org -spec: - group: k8s.nginx.org - versions: - - name: v1 - served: true - storage: true - scope: Namespaced - names: - plural: virtualserverroutes - singular: virtualserverroute - kind: VirtualServerRoute - shortNames: - - vsr diff --git a/deployments/common/vs-definition.yaml b/deployments/common/vs-definition.yaml new file mode 100644 index 0000000000..eb87b672fe --- /dev/null +++ b/deployments/common/vs-definition.yaml @@ -0,0 +1,377 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualservers.k8s.nginx.org +spec: + group: k8s.nginx.org + versions: + - name: v1 + served: true + storage: true + scope: Namespaced + names: + kind: VirtualServer + plural: virtualservers + singular: virtualserver + shortNames: + - vs + preserveUnknownFields: false + validation: + openAPIV3Schema: + description: VirtualServer defines the VirtualServer resource. + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: VirtualServerSpec is the spec of the VirtualServer resource. + type: object + properties: + host: + type: string + routes: + type: array + items: + description: Route defines a route. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + errorPages: + type: array + items: + description: ErrorPage defines an ErrorPage in a Route. + type: object + properties: + codes: + type: array + items: + type: integer + redirect: + description: ErrorPageRedirect defines a redirect for an + ErrorPage. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ErrorPageReturn defines a return for an ErrorPage. + type: object + properties: + body: + type: string + code: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + type: + type: string + matches: + type: array + items: + description: Match defines a match. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + conditions: + type: array + items: + description: Condition defines a condition in a MatchRule. + type: object + properties: + argument: + type: string + cookie: + type: string + header: + type: string + value: + type: string + variable: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect + in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in + an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + path: + type: string + route: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + tls: + description: TLS defines TLS configuration for a VirtualServer. + type: object + properties: + redirect: + description: TLSRedirect defines a redirect for a TLS. + type: object + properties: + basedOn: + type: string + code: + type: integer + enable: + type: boolean + secret: + type: string + upstreams: + type: array + items: + description: Upstream defines an upstream. + type: object + properties: + buffer-size: + type: string + buffering: + type: boolean + buffers: + description: UpstreamBuffers defines Buffer Configuration for + an Upstream. + type: object + properties: + number: + type: integer + size: + type: string + client-max-body-size: + type: string + connect-timeout: + type: string + fail-timeout: + type: string + healthCheck: + description: HealthCheck defines the parameters for active Upstream + HealthChecks. + type: object + properties: + connect-timeout: + type: string + enable: + type: boolean + fails: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + interval: + type: string + jitter: + type: string + passes: + type: integer + path: + type: string + port: + type: integer + read-timeout: + type: string + send-timeout: + type: string + statusMatch: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an + Upstream. + type: object + properties: + enable: + type: boolean + keepalive: + type: integer + lb-method: + type: string + max-conns: + type: integer + max-fails: + type: integer + name: + type: string + next-upstream: + type: string + next-upstream-timeout: + type: string + next-upstream-tries: + type: integer + port: + type: integer + queue: + description: UpstreamQueue defines Queue Configuration for an + Upstream. + type: object + properties: + size: + type: integer + timeout: + type: string + read-timeout: + type: string + send-timeout: + type: string + service: + type: string + sessionCookie: + description: SessionCookie defines the parameters for session + persistence. + type: object + properties: + domain: + type: string + enable: + type: boolean + expires: + type: string + httpOnly: + type: boolean + name: + type: string + path: + type: string + secure: + type: boolean + slow-start: + type: string + subselector: + type: object + additionalProperties: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an Upstream. + type: object + properties: + enable: + type: boolean diff --git a/deployments/common/vsr-definition.yaml b/deployments/common/vsr-definition.yaml new file mode 100644 index 0000000000..108b8fe9e5 --- /dev/null +++ b/deployments/common/vsr-definition.yaml @@ -0,0 +1,359 @@ +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualserverroutes.k8s.nginx.org +spec: + group: k8s.nginx.org + versions: + - name: v1 + served: true + storage: true + scope: Namespaced + names: + kind: VirtualServerRoute + plural: virtualserverroutes + singular: virtualserverroute + shortNames: + - vsr + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + type: object + properties: + host: + type: string + subroutes: + type: array + items: + description: Route defines a route. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + errorPages: + type: array + items: + description: ErrorPage defines an ErrorPage in a Route. + type: object + properties: + codes: + type: array + items: + type: integer + redirect: + description: ErrorPageRedirect defines a redirect for an + ErrorPage. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ErrorPageReturn defines a return for an ErrorPage. + type: object + properties: + body: + type: string + code: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + type: + type: string + matches: + type: array + items: + description: Match defines a match. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + conditions: + type: array + items: + description: Condition defines a condition in a MatchRule. + type: object + properties: + argument: + type: string + cookie: + type: string + header: + type: string + value: + type: string + variable: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect + in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in + an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + path: + type: string + route: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + upstreams: + type: array + items: + description: Upstream defines an upstream. + type: object + properties: + buffer-size: + type: string + buffering: + type: boolean + buffers: + description: UpstreamBuffers defines Buffer Configuration for + an Upstream. + type: object + properties: + number: + type: integer + size: + type: string + client-max-body-size: + type: string + connect-timeout: + type: string + fail-timeout: + type: string + healthCheck: + description: HealthCheck defines the parameters for active Upstream + HealthChecks. + type: object + properties: + connect-timeout: + type: string + enable: + type: boolean + fails: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + interval: + type: string + jitter: + type: string + passes: + type: integer + path: + type: string + port: + type: integer + read-timeout: + type: string + send-timeout: + type: string + statusMatch: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an + Upstream. + type: object + properties: + enable: + type: boolean + keepalive: + type: integer + lb-method: + type: string + max-conns: + type: integer + max-fails: + type: integer + name: + type: string + next-upstream: + type: string + next-upstream-timeout: + type: string + next-upstream-tries: + type: integer + port: + type: integer + queue: + description: UpstreamQueue defines Queue Configuration for an + Upstream. + type: object + properties: + size: + type: integer + timeout: + type: string + read-timeout: + type: string + send-timeout: + type: string + service: + type: string + sessionCookie: + description: SessionCookie defines the parameters for session + persistence. + type: object + properties: + domain: + type: string + enable: + type: boolean + expires: + type: string + httpOnly: + type: boolean + name: + type: string + path: + type: string + secure: + type: boolean + slow-start: + type: string + subselector: + type: object + additionalProperties: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an Upstream. + type: object + properties: + enable: + type: boolean diff --git a/deployments/helm-chart/templates/controller-custom-resources.yaml b/deployments/helm-chart/templates/controller-custom-resources.yaml deleted file mode 100644 index a65df0bd1d..0000000000 --- a/deployments/helm-chart/templates/controller-custom-resources.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- if .Values.controller.enableCustomResources }} -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualservers.k8s.nginx.org - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} -spec: - group: k8s.nginx.org - versions: - - name: v1 - served: true - storage: true - scope: Namespaced - names: - plural: virtualservers - singular: virtualserver - kind: VirtualServer - shortNames: - - vs ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: virtualserverroutes.k8s.nginx.org - labels: - {{- include "nginx-ingress.labels" . | nindent 4 }} -spec: - group: k8s.nginx.org - versions: - - name: v1 - served: true - storage: true - scope: Namespaced - names: - plural: virtualserverroutes - singular: virtualserverroute - kind: VirtualServerRoute - shortNames: - - vsr -{{- end }} \ No newline at end of file diff --git a/deployments/helm-chart/templates/controller-vs-definition.yaml b/deployments/helm-chart/templates/controller-vs-definition.yaml new file mode 100644 index 0000000000..e1ef051364 --- /dev/null +++ b/deployments/helm-chart/templates/controller-vs-definition.yaml @@ -0,0 +1,381 @@ +{{- if .Values.controller.enableCustomResources }} +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualservers.k8s.nginx.org + labels: + {{- include "nginx-ingress.labels" . | nindent 4 }} +spec: + group: k8s.nginx.org + versions: + - name: v1 + served: true + storage: true + scope: Namespaced + names: + kind: VirtualServer + plural: virtualservers + singular: virtualserver + shortNames: + - vs + preserveUnknownFields: false + validation: + openAPIV3Schema: + description: VirtualServer defines the VirtualServer resource. + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: VirtualServerSpec is the spec of the VirtualServer resource. + type: object + properties: + host: + type: string + routes: + type: array + items: + description: Route defines a route. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + errorPages: + type: array + items: + description: ErrorPage defines an ErrorPage in a Route. + type: object + properties: + codes: + type: array + items: + type: integer + redirect: + description: ErrorPageRedirect defines a redirect for an + ErrorPage. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ErrorPageReturn defines a return for an ErrorPage. + type: object + properties: + body: + type: string + code: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + type: + type: string + matches: + type: array + items: + description: Match defines a match. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + conditions: + type: array + items: + description: Condition defines a condition in a MatchRule. + type: object + properties: + argument: + type: string + cookie: + type: string + header: + type: string + value: + type: string + variable: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect + in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in + an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + path: + type: string + route: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + tls: + description: TLS defines TLS configuration for a VirtualServer. + type: object + properties: + redirect: + description: TLSRedirect defines a redirect for a TLS. + type: object + properties: + basedOn: + type: string + code: + type: integer + enable: + type: boolean + secret: + type: string + upstreams: + type: array + items: + description: Upstream defines an upstream. + type: object + properties: + buffer-size: + type: string + buffering: + type: boolean + buffers: + description: UpstreamBuffers defines Buffer Configuration for + an Upstream. + type: object + properties: + number: + type: integer + size: + type: string + client-max-body-size: + type: string + connect-timeout: + type: string + fail-timeout: + type: string + healthCheck: + description: HealthCheck defines the parameters for active Upstream + HealthChecks. + type: object + properties: + connect-timeout: + type: string + enable: + type: boolean + fails: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + interval: + type: string + jitter: + type: string + passes: + type: integer + path: + type: string + port: + type: integer + read-timeout: + type: string + send-timeout: + type: string + statusMatch: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an + Upstream. + type: object + properties: + enable: + type: boolean + keepalive: + type: integer + lb-method: + type: string + max-conns: + type: integer + max-fails: + type: integer + name: + type: string + next-upstream: + type: string + next-upstream-timeout: + type: string + next-upstream-tries: + type: integer + port: + type: integer + queue: + description: UpstreamQueue defines Queue Configuration for an + Upstream. + type: object + properties: + size: + type: integer + timeout: + type: string + read-timeout: + type: string + send-timeout: + type: string + service: + type: string + sessionCookie: + description: SessionCookie defines the parameters for session + persistence. + type: object + properties: + domain: + type: string + enable: + type: boolean + expires: + type: string + httpOnly: + type: boolean + name: + type: string + path: + type: string + secure: + type: boolean + slow-start: + type: string + subselector: + type: object + additionalProperties: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an Upstream. + type: object + properties: + enable: + type: boolean +{{- end }} diff --git a/deployments/helm-chart/templates/controller-vsr-definition.yaml b/deployments/helm-chart/templates/controller-vsr-definition.yaml new file mode 100644 index 0000000000..202268a028 --- /dev/null +++ b/deployments/helm-chart/templates/controller-vsr-definition.yaml @@ -0,0 +1,363 @@ +{{- if .Values.controller.enableCustomResources }} +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: virtualserverroutes.k8s.nginx.org + labels: + {{- include "nginx-ingress.labels" . | nindent 4 }} +spec: + group: k8s.nginx.org + versions: + - name: v1 + served: true + storage: true + scope: Namespaced + names: + kind: VirtualServerRoute + plural: virtualserverroutes + singular: virtualserverroute + shortNames: + - vsr + preserveUnknownFields: false + validation: + openAPIV3Schema: + type: object + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + type: object + properties: + host: + type: string + subroutes: + type: array + items: + description: Route defines a route. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + errorPages: + type: array + items: + description: ErrorPage defines an ErrorPage in a Route. + type: object + properties: + codes: + type: array + items: + type: integer + redirect: + description: ErrorPageRedirect defines a redirect for an + ErrorPage. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ErrorPageReturn defines a return for an ErrorPage. + type: object + properties: + body: + type: string + code: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + type: + type: string + matches: + type: array + items: + description: Match defines a match. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + conditions: + type: array + items: + description: Condition defines a condition in a MatchRule. + type: object + properties: + argument: + type: string + cookie: + type: string + header: + type: string + value: + type: string + variable: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect + in an Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in + an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + path: + type: string + route: + type: string + splits: + type: array + items: + description: Split defines a split. + type: object + properties: + action: + description: Action defines an action. + type: object + properties: + pass: + type: string + redirect: + description: ActionRedirect defines a redirect in an + Action. + type: object + properties: + code: + type: integer + url: + type: string + return: + description: ActionReturn defines a return in an Action. + type: object + properties: + body: + type: string + code: + type: integer + type: + type: string + weight: + type: integer + upstreams: + type: array + items: + description: Upstream defines an upstream. + type: object + properties: + buffer-size: + type: string + buffering: + type: boolean + buffers: + description: UpstreamBuffers defines Buffer Configuration for + an Upstream. + type: object + properties: + number: + type: integer + size: + type: string + client-max-body-size: + type: string + connect-timeout: + type: string + fail-timeout: + type: string + healthCheck: + description: HealthCheck defines the parameters for active Upstream + HealthChecks. + type: object + properties: + connect-timeout: + type: string + enable: + type: boolean + fails: + type: integer + headers: + type: array + items: + description: Header defines an HTTP Header. + type: object + properties: + name: + type: string + value: + type: string + interval: + type: string + jitter: + type: string + passes: + type: integer + path: + type: string + port: + type: integer + read-timeout: + type: string + send-timeout: + type: string + statusMatch: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an + Upstream. + type: object + properties: + enable: + type: boolean + keepalive: + type: integer + lb-method: + type: string + max-conns: + type: integer + max-fails: + type: integer + name: + type: string + next-upstream: + type: string + next-upstream-timeout: + type: string + next-upstream-tries: + type: integer + port: + type: integer + queue: + description: UpstreamQueue defines Queue Configuration for an + Upstream. + type: object + properties: + size: + type: integer + timeout: + type: string + read-timeout: + type: string + send-timeout: + type: string + service: + type: string + sessionCookie: + description: SessionCookie defines the parameters for session + persistence. + type: object + properties: + domain: + type: string + enable: + type: boolean + expires: + type: string + httpOnly: + type: boolean + name: + type: string + path: + type: string + secure: + type: boolean + slow-start: + type: string + subselector: + type: object + additionalProperties: + type: string + tls: + description: UpstreamTLS defines a TLS configuration for an Upstream. + type: object + properties: + enable: + type: boolean +{{- end }} diff --git a/docs-web/configuration/virtualserver-and-virtualserverroute-resources.md b/docs-web/configuration/virtualserver-and-virtualserverroute-resources.md index 01663c9d87..972203212a 100644 --- a/docs-web/configuration/virtualserver-and-virtualserverroute-resources.md +++ b/docs-web/configuration/virtualserver-and-virtualserverroute-resources.md @@ -33,6 +33,8 @@ This document is the reference documentation for the resources. To see additiona - [ErrorPage.Return](#errorpage-return) - [Using VirtualServer and VirtualServerRoute](#using-virtualserver-and-virtualserverroute) - [Validation](#validation) + - [Structural Validation](#structural-validation) + - [Comprehensive Validation](#comprehensive-validation) - [Customization via ConfigMap](#customization-via-configmap) ## VirtualServer Specification @@ -190,7 +192,7 @@ The route defines rules for matching client requests to actions like passing a r - ``string`` - No* * - ``errorPages`` - - The custom responses for error codes. NGINX will use those responses instead of returning the error responses from the upstream servers or the default responses generated by NGINX. A custom response can be a redirect or a canned response. For example, a redirect to another URL if an upstream server responded with a 404 status code. + - The custom responses for error codes. NGINX will use those responses instead of returning the error responses from the upstream servers or the default responses generated by NGINX. A custom response can be a redirect or a canned response. For example, a redirect to another URL if an upstream server responded with a 404 status code. - `[]errorPage <#errorpage>`_ - No ``` @@ -307,7 +309,7 @@ action: - `matches <#match>`_ - No * - ``errorPages`` - - The custom responses for error codes. NGINX will use those responses instead of returning the error responses from the upstream servers or the default responses generated by NGINX. A custom response can be a redirect or a canned response. For example, a redirect to another URL if an upstream server responded with a 404 status code. + - The custom responses for error codes. NGINX will use those responses instead of returning the error responses from the upstream servers or the default responses generated by NGINX. A custom response can be a redirect or a canned response. For example, a redirect to another URL if an upstream server responded with a 404 status code. - `[]errorPage <#errorpage>`_ - No ``` @@ -980,7 +982,7 @@ errorPages: - No* ``` -\* -- an errorPage must include exactly one of the following: `return` or `redirect`. +\* -- an errorPage must include exactly one of the following: `return` or `redirect`. ### ErrorPage.Redirect @@ -1052,7 +1054,7 @@ return: - ``string`` - Yes * - ``headers`` - - The custom headers of the response. + - The custom headers of the response. - `errorPage.Redirect.Header <#errorpage-return-header>`_ - No ``` @@ -1107,7 +1109,32 @@ Working with VirtualServerRoute resources is analogous. In the kubectl commands, ### Validation -The Ingress Controller validates VirtualServer and VirtualServerRoute resources. If a resource is invalid, the Ingress Controller will reject it. +Two types of validation are available for VirtualServer and VirtualServerRoute resources: +* *Structural validation* by the `kubectl` and Kubernetes API server. +* *Comprehensive validation* by the Ingress Controller. + +#### Structural Validation + +The custom resource definitions for VirtualServer and VirtualServerRoute include structural OpenAPI schema which describes the type of every field of those resources. + +If you try to create (or update) a resource that violates the structural schema (for example, you use a string value for the port field of an upstream), `kubectl` and Kubernetes API server will reject such a resource: +* Example of `kubectl` validation: + ``` + $ kubectl apply -f cafe-virtual-server.yaml + error: error validating "cafe-virtual-server.yaml": error validating data: ValidationError(VirtualServer.spec.upstreams[0].port): invalid type for org.nginx.k8s.v1.VirtualServer.spec.upstreams.port: got "string", expected "integer"; if you choose to ignore these errors, turn validation off with --validate=false + ``` +* Example of Kubernetes API server validation: + ``` + $ kubectl apply -f cafe-virtual-server.yaml --validate=false + The VirtualServer "cafe" is invalid: []: Invalid value: map[string]interface {}{ ... }: validation failure list: + spec.upstreams.port in body must be of type integer: "string" + ``` + +If a resource is not rejected (it doesn't violate the structural schema), the Ingress Controller will validate it further. + +#### Comprehensive Validation + +The Ingress Controller validates the fields of the VirtualServer and VirtualServerRoute resources. If a resource is invalid, the Ingress Controller will reject it: the resource will continue to exist in the cluster, but the Ingress Controller will ignore it. You can check if the Ingress Controller successfully applied the configuration for a VirtualServer. For our example `cafe` VirtualServer, we can run: ``` @@ -1120,14 +1147,14 @@ Events: ``` Note how the events section includes a Normal event with the AddedOrUpdated reason that informs us that the configuration was successfully applied. -If you create an invalid resource, the Ingress Controller will reject it and emit a Rejected event. For example, if you create a VirtualServer `cafe` with an empty `host` field, you will get: +If you create an invalid resource, the Ingress Controller will reject it and emit a Rejected event. For example, if you create a VirtualServer `cafe` with two upstream with the same name `tea`, you will get: ``` $ kubectl describe vs cafe . . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- - Warning Rejected 2s nginx-ingress-controller VirtualServer default/cafe is invalid and was rejected: spec.host: Required value + Warning Rejected 12s nginx-ingress-controller VirtualServer default/cafe is invalid and was rejected: spec.upstreams[1].name: Duplicate value: "tea" ``` Note how the events section includes a Warning event with the Rejected reason. diff --git a/docs-web/installation/installation-with-manifests.md b/docs-web/installation/installation-with-manifests.md index 36420fc066..3b2c477b75 100644 --- a/docs-web/installation/installation-with-manifests.md +++ b/docs-web/installation/installation-with-manifests.md @@ -18,11 +18,11 @@ This document describes how to install the NGINX Ingress Controller in your Kube 1. Create a namespace and a service account for the Ingress controller: ``` $ kubectl apply -f common/ns-and-sa.yaml - ``` + ``` 2. Create a cluster role and cluster role binding for the service account: ``` $ kubectl apply -f rbac/rbac.yaml - ``` + ``` **Note**: To perform this step you must be a cluster admin. Follow the documentation of your Kubernetes platform to configure the admin access. For GKE, see the [Role-Based Access Control](https://cloud.google.com/kubernetes-engine/docs/how-to/role-based-access-control) doc. ## 2. Create the Default Secret, Customization ConfigMap, and Custom Resource Definitions @@ -41,7 +41,8 @@ This document describes how to install the NGINX Ingress Controller in your Kube 1. Create custom resource definitions for [VirtualServer and VirtualServerRoute](/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources) resources: ``` - $ kubectl apply -f common/custom-resource-definitions.yaml + $ kubectl apply -f common/vs-definition.yaml + $ kubectl apply -f common/vsr-definition.yaml ``` ## 3. Deploy the Ingress Controller @@ -98,8 +99,8 @@ $ kubectl get pods --namespace=nginx-ingress ### 4.1 Create a Service for the Ingress Controller Pods -* *Use a NodePort service*. - +* *Use a NodePort service*. + Create a service with the type *NodePort*: ``` $ kubectl create -f service/nodeport.yaml @@ -133,7 +134,7 @@ $ kubectl get pods --namespace=nginx-ingress kubectl apply -f common/nginx-config.yaml ``` **Note**: For AWS, additional options regarding an allocated load balancer are available, such as the type of a load balancer and SSL termination. Read the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer) to learn more. - + Kubernetes will allocate and configure a cloud load balancer for load balancing the Ingress controller pods. 2. Use the public IP of the load balancer to access the Ingress controller. To get the public IP: * For GCP or Azure, run: @@ -148,7 +149,7 @@ $ kubectl get pods --namespace=nginx-ingress ``` $ nslookup ``` - + The public IP can be reported in the status of an ingress resource. See the [Reporting Resources Status doc](/nginx-ingress-controller/configuration/global-configuration/reporting-resources-status) for more details. > Learn more about type LoadBalancer in the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/service/#type-loadbalancer). diff --git a/hack/update-crds.sh b/hack/update-crds.sh new file mode 100755 index 0000000000..886cc54932 --- /dev/null +++ b/hack/update-crds.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +# download controller-gen binary +VERSION=v0.2.5 +CONTROLLER_GEN_BASENAME=controller-gen-$(uname -s | tr '[:upper:]' '[:lower:]')-amd64 +CONTROLLER_GEN=${CONTROLLER_GEN_BASENAME}-${VERSION} +test -x "hack/${CONTROLLER_GEN}" || curl -f -L -o "hack/${CONTROLLER_GEN}" "https://github.com/openshift/kubernetes-sigs-controller-tools/releases/download/${VERSION}/${CONTROLLER_GEN_BASENAME}" +chmod +x "hack/${CONTROLLER_GEN}" + +# regenerate the schemas in the CRD manifests +hack/${CONTROLLER_GEN} schemapatch:manifests=./deployments/common/ paths="./pkg/apis/configuration/..." output:dir=./deployments/common \ No newline at end of file diff --git a/hack/verify-crds.sh b/hack/verify-crds.sh new file mode 100755 index 0000000000..c20a1aad6e --- /dev/null +++ b/hack/verify-crds.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. + +DIFFROOT="${SCRIPT_ROOT}/deployments/common/" +TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/deployments/common/" +_tmp="${SCRIPT_ROOT}/_tmp" + +cleanup() { + rm -rf "${_tmp}" +} +trap "cleanup" EXIT SIGINT + +cleanup + +mkdir -p "${TMP_DIFFROOT}" +cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" + +VERSION=v0.2.5 +CONTROLLER_GEN_BASENAME=controller-gen-$(uname -s | tr '[:upper:]' '[:lower:]')-amd64 +CONTROLLER_GEN=${CONTROLLER_GEN_BASENAME}-${VERSION} +test -x "hack/${CONTROLLER_GEN}" || curl -f -L -o "hack/${CONTROLLER_GEN}" "https://github.com/openshift/kubernetes-sigs-controller-tools/releases/download/${VERSION}/${CONTROLLER_GEN_BASENAME}" +chmod +x "hack/${CONTROLLER_GEN}" + +hack/${CONTROLLER_GEN} schemapatch:manifests=./deployments/common/ paths="./pkg/apis/configuration/..." output:dir=${TMP_DIFFROOT} +echo "diffing ${DIFFROOT} against potentially updated crds" +ret=0 +diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? +if [[ $ret -eq 0 ]] +then + echo "${DIFFROOT} up to date." +else + echo "${DIFFROOT} is out of date. Please regenerate crds" + exit 1 +fi diff --git a/pkg/apis/configuration/v1/types.go b/pkg/apis/configuration/v1/types.go index 85f419dd15..d2e134d84f 100644 --- a/pkg/apis/configuration/v1/types.go +++ b/pkg/apis/configuration/v1/types.go @@ -6,6 +6,7 @@ import ( // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:validation:Optional // VirtualServer defines the VirtualServer resource. type VirtualServer struct { @@ -51,7 +52,7 @@ type Upstream struct { SessionCookie *SessionCookie `json:"sessionCookie"` } -// UpstreamBuffers defines Buffer Configuration for an Upstream +// UpstreamBuffers defines Buffer Configuration for an Upstream. type UpstreamBuffers struct { Number int `json:"number"` Size string `json:"size"` @@ -157,13 +158,13 @@ type ErrorPage struct { // ErrorPageReturn defines a return for an ErrorPage. type ErrorPageReturn struct { - ActionReturn - Headers []Header `json:"headers"` + ActionReturn `json:",inline"` + Headers []Header `json:"headers"` } // ErrorPageRedirect defines a redirect for an ErrorPage. type ErrorPageRedirect struct { - ActionRedirect + ActionRedirect `json:",inline"` } // TLS defines TLS configuration for a VirtualServer. @@ -214,7 +215,7 @@ type VirtualServerRouteList struct { Items []VirtualServerRoute `json:"items"` } -// UpstreamQueue defines Queue Configuration for an Upstream +// UpstreamQueue defines Queue Configuration for an Upstream. type UpstreamQueue struct { Size int `json:"size"` Timeout string `json:"timeout"` diff --git a/tests/suite/custom_resources_utils.py b/tests/suite/custom_resources_utils.py index b0e6b1dfbc..2f58f43f88 100644 --- a/tests/suite/custom_resources_utils.py +++ b/tests/suite/custom_resources_utils.py @@ -29,25 +29,6 @@ def create_crd(api_extensions_v1_beta1: ApiextensionsV1beta1Api, body) -> None: pytest.fail(f"An unexpected exception {ex} occurred. Exiting...") -def create_crds_from_yaml(api_extensions_v1_beta1: ApiextensionsV1beta1Api, yaml_manifest) -> []: - """ - Create a CRD based on yaml file. - - :param api_extensions_v1_beta1: ApiextensionsV1beta1Api - :param yaml_manifest: an absolute path to file - :return: [] - """ - print("Create a CRD:") - names = [] - with open(yaml_manifest) as f: - docs = yaml.safe_load_all(f) - for dep in docs: - create_crd(api_extensions_v1_beta1, dep) - names.append(dep['metadata']['name']) - print(f"CRD was created with name '{dep['metadata']['name']}'") - return names - - def create_crd_from_yaml(api_extensions_v1_beta1: ApiextensionsV1beta1Api, name, yaml_manifest) -> None: """ Create a specific CRD based on yaml file. @@ -80,20 +61,6 @@ def delete_crd(api_extensions_v1_beta1: ApiextensionsV1beta1Api, name) -> None: print(f"CRD was removed with name '{name}'") -def delete_crds_from_yaml(api_extensions_v1_beta1: ApiextensionsV1beta1Api, yaml_manifest) -> None: - """ - Delete a CRD based on yaml file. - - :param api_extensions_v1_beta1: ApiextensionsV1beta1Api - :param yaml_manifest: an absolute path to file - :return: [] - """ - with open(yaml_manifest) as f: - docs = yaml.safe_load_all(f) - for dep in docs: - delete_crd(api_extensions_v1_beta1, dep['metadata']['name']) - - def create_virtual_server_from_yaml(custom_objects: CustomObjectsApi, yaml_manifest, namespace) -> str: """ Create a VirtualServer based on yaml file. diff --git a/tests/suite/fixtures.py b/tests/suite/fixtures.py index 194e056775..20af554467 100644 --- a/tests/suite/fixtures.py +++ b/tests/suite/fixtures.py @@ -10,17 +10,17 @@ ApiextensionsV1beta1Api, AppsV1Api from kubernetes.client.rest import ApiException -from suite.custom_resources_utils import create_crds_from_yaml, create_virtual_server_from_yaml, \ - delete_virtual_server, create_v_s_route_from_yaml, delete_v_s_route, delete_crds_from_yaml +from suite.custom_resources_utils import create_crd_from_yaml, create_virtual_server_from_yaml, \ + delete_virtual_server, create_v_s_route_from_yaml, delete_v_s_route, delete_crd from suite.kube_config_utils import ensure_context_in_config, get_current_context_name from suite.resources_utils import create_namespace_with_name_from_yaml, delete_namespace, create_ns_and_sa_from_yaml, \ patch_rbac, create_example_app, wait_until_all_pods_are_ready, delete_common_app, \ ensure_connection_to_public_endpoint, create_service_with_name, create_deployment_with_name, delete_deployment, \ - delete_service, replace_configmap_from_yaml, delete_testing_namespaces -from suite.resources_utils import create_ingress_controller, delete_ingress_controller, configure_rbac, cleanup_rbac -from suite.resources_utils import create_service_from_yaml, get_service_node_ports, wait_for_public_ip -from suite.resources_utils import create_configmap_from_yaml, create_secret_from_yaml -from suite.yaml_utils import get_first_vs_host_from_yaml, get_paths_from_vs_yaml, get_paths_from_vsr_yaml, \ + delete_service, replace_configmap_from_yaml, delete_testing_namespaces, \ + create_ingress_controller, delete_ingress_controller, configure_rbac, cleanup_rbac, \ + create_service_from_yaml, get_service_node_ports, wait_for_public_ip, \ + create_configmap_from_yaml, create_secret_from_yaml, \ + get_first_vs_host_from_yaml, get_paths_from_vs_yaml, get_paths_from_vsr_yaml, get_name_from_yaml, \ get_route_namespace_from_vs_yaml from settings import ALLOWED_SERVICE_TYPES, ALLOWED_IC_TYPES, DEPLOYMENTS, TEST_DATA, ALLOWED_DEPLOYMENT_TYPES @@ -302,13 +302,15 @@ def crd_ingress_controller(cli_arguments, kube_apis, ingress_controller_prerequi """ namespace = ingress_controller_prerequisites.namespace name = "nginx-ingress" + crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/common/vs-definition.yaml") + try: print("------------------------- Update ClusterRole -----------------------------------") if request.param['type'] == 'rbac-without-vs': patch_rbac(kube_apis.rbac_v1_beta1, f"{TEST_DATA}/virtual-server/rbac-without-vs.yaml") print("------------------------- Register CRD -----------------------------------") - create_crds_from_yaml(kube_apis.api_extensions_v1_beta1, - f"{DEPLOYMENTS}/common/custom-resource-definitions.yaml") + create_crd_from_yaml(kube_apis.api_extensions_v1_beta1, crd_name, + f"{DEPLOYMENTS}/common/vs-definition.yaml") print("------------------------- Create IC -----------------------------------") name = create_ingress_controller(kube_apis.v1, kube_apis.apps_v1_api, cli_arguments, namespace, request.param.get('extra_args', None)) @@ -318,16 +320,14 @@ def crd_ingress_controller(cli_arguments, kube_apis, ingress_controller_prerequi except ApiException as ex: # Finalizer method doesn't start if fixture creation was incomplete, ensure clean up here print(f"Failed to complete CRD IC fixture: {ex}\nClean up the cluster as much as possible.") - delete_crds_from_yaml(kube_apis.api_extensions_v1_beta1, - f"{DEPLOYMENTS}/common/custom-resource-definitions.yaml") + delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) print("Restore the ClusterRole:") patch_rbac(kube_apis.rbac_v1_beta1, f"{DEPLOYMENTS}/rbac/rbac.yaml") print("Remove the IC:") delete_ingress_controller(kube_apis.apps_v1_api, name, cli_arguments['deployment-type'], namespace) def fin(): - delete_crds_from_yaml(kube_apis.api_extensions_v1_beta1, - f"{DEPLOYMENTS}/common/custom-resource-definitions.yaml") + delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) print("Restore the ClusterRole:") patch_rbac(kube_apis.rbac_v1_beta1, f"{DEPLOYMENTS}/rbac/rbac.yaml") print("Remove the IC:") @@ -366,7 +366,7 @@ def virtual_server_setup(request, kube_apis, crd_ingress_controller, ingress_con Prepare Virtual Server Example. :param request: internal pytest fixture to parametrize this method: - {example: virtul-server|virtual-server-tls|..., app_type: simple|split|...} + {example: virtual-server|virtual-server-tls|..., app_type: simple|split|...} 'example' is a directory name in TEST_DATA, 'app_type' is a directory name in TEST_DATA/common/app :param kube_apis: client apis diff --git a/tests/suite/resources_utils.py b/tests/suite/resources_utils.py index a96820e25f..23de08014c 100644 --- a/tests/suite/resources_utils.py +++ b/tests/suite/resources_utils.py @@ -92,21 +92,6 @@ def cleanup_rbac(rbac_v1_beta1: RbacAuthorizationV1beta1Api, rbac: RBACAuthoriza rbac_v1_beta1.delete_cluster_role(rbac.role, delete_options) -def create_deployment_from_yaml(apps_v1_api: AppsV1Api, namespace, yaml_manifest) -> str: - """ - Create a deployment based on yaml file. - - :param apps_v1_api: AppsV1Api - :param namespace: namespace name - :param yaml_manifest: absolute path to file - :return: str - """ - print(f"Load {yaml_manifest}") - with open(yaml_manifest) as f: - dep = yaml.safe_load(f) - return create_deployment(apps_v1_api, namespace, dep) - - def create_deployment(apps_v1_api: AppsV1Api, namespace, body) -> str: """ Create a deployment based on a dict. @@ -544,24 +529,6 @@ def create_namespace(v1: CoreV1Api, body) -> str: return body['metadata']['name'] -def create_namespace_with_name_from_yaml(v1: CoreV1Api, name, yaml_manifest) -> str: - """ - Create a namespace with a specific name based on a yaml manifest. - - :param v1: CoreV1Api - :param name: name - :param yaml_manifest: an absolute path to file - :return: str - """ - print(f"Create a namespace with specific name:") - with open(yaml_manifest) as f: - dep = yaml.safe_load(f) - dep['metadata']['name'] = name - v1.create_namespace(dep) - print(f"Namespace created with name '{str(dep['metadata']['name'])}'") - return dep['metadata']['name'] - - def create_service_account(v1: CoreV1Api, namespace, body) -> None: """ Create a ServiceAccount based on a dict. diff --git a/tests/suite/test_annotations.py b/tests/suite/test_annotations.py index 46721907e9..7fd2f1c328 100644 --- a/tests/suite/test_annotations.py +++ b/tests/suite/test_annotations.py @@ -10,7 +10,7 @@ delete_common_app, create_items_from_yaml, delete_items_from_yaml, \ wait_before_test, replace_configmap_from_yaml, get_events, \ generate_ingresses_with_annotation, replace_ingress -from suite.yaml_utils import get_first_ingress_host_from_yaml, get_names_from_yaml +from suite.yaml_utils import get_first_ingress_host_from_yaml, get_name_from_yaml from settings import TEST_DATA, DEPLOYMENTS @@ -87,7 +87,7 @@ def annotations_setup(request, create_items_from_yaml(kube_apis, f"{TEST_DATA}/annotations/{request.param}/annotations-ingress.yaml", test_namespace) - ingress_name = get_names_from_yaml(f"{TEST_DATA}/annotations/{request.param}/annotations-ingress.yaml")[0] + ingress_name = get_name_from_yaml(f"{TEST_DATA}/annotations/{request.param}/annotations-ingress.yaml") ingress_host = get_first_ingress_host_from_yaml(f"{TEST_DATA}/annotations/{request.param}/annotations-ingress.yaml") if request.param == 'mergeable': minions_info = get_minions_info_from_yaml(f"{TEST_DATA}/annotations/{request.param}/annotations-ingress.yaml") @@ -139,7 +139,7 @@ def annotations_grpc_setup(request, create_items_from_yaml(kube_apis, f"{TEST_DATA}/annotations/grpc/annotations-ingress.yaml", test_namespace) - ingress_name = get_names_from_yaml(f"{TEST_DATA}/annotations/grpc/annotations-ingress.yaml")[0] + ingress_name = get_name_from_yaml(f"{TEST_DATA}/annotations/grpc/annotations-ingress.yaml") ingress_host = get_first_ingress_host_from_yaml(f"{TEST_DATA}/annotations/grpc/annotations-ingress.yaml") replace_configmap_from_yaml(kube_apis.v1, ingress_controller_prerequisites.config_map['metadata']['name'], diff --git a/tests/suite/test_custom_annotations.py b/tests/suite/test_custom_annotations.py index baa9f4ba6c..848d2c174f 100644 --- a/tests/suite/test_custom_annotations.py +++ b/tests/suite/test_custom_annotations.py @@ -3,7 +3,7 @@ from suite.fixtures import PublicEndpoint from suite.resources_utils import create_items_from_yaml, delete_items_from_yaml, replace_configmap_from_yaml, \ get_ingress_nginx_template_conf, get_first_pod_name, wait_before_test -from suite.yaml_utils import get_first_ingress_host_from_yaml, get_names_from_yaml +from suite.yaml_utils import get_first_ingress_host_from_yaml, get_name_from_yaml class CustomAnnotationsSetup: @@ -38,7 +38,7 @@ def custom_annotations_setup(request, kube_apis, ingress_controller_prerequisite ing_src = f"{TEST_DATA}/custom-annotations/{ing_type}/annotations-ingress.yaml" create_items_from_yaml(kube_apis, ing_src, test_namespace) host = get_first_ingress_host_from_yaml(ing_src) - ingress_name = get_names_from_yaml(ing_src)[0] + ingress_name = get_name_from_yaml(ing_src) wait_before_test(1) ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace) diff --git a/tests/suite/test_hsts.py b/tests/suite/test_hsts.py index d51c804a5d..1ca9c7d7c7 100644 --- a/tests/suite/test_hsts.py +++ b/tests/suite/test_hsts.py @@ -7,7 +7,7 @@ delete_common_app, create_items_from_yaml, delete_items_from_yaml, \ wait_before_test, ensure_response_from_backend, \ generate_ingresses_with_annotation, replace_ingress -from suite.yaml_utils import get_first_ingress_host_from_yaml, get_names_from_yaml +from suite.yaml_utils import get_first_ingress_host_from_yaml, get_name_from_yaml from settings import TEST_DATA @@ -39,7 +39,7 @@ def hsts_setup(request, create_items_from_yaml(kube_apis, f"{TEST_DATA}/hsts/{request.param}/hsts-ingress.yaml", test_namespace) - ingress_name = get_names_from_yaml(f"{TEST_DATA}/hsts/{request.param}/hsts-ingress.yaml")[0] + ingress_name = get_name_from_yaml(f"{TEST_DATA}/hsts/{request.param}/hsts-ingress.yaml") ingress_host = get_first_ingress_host_from_yaml(f"{TEST_DATA}/hsts/{request.param}/hsts-ingress.yaml") create_example_app(kube_apis, "simple", test_namespace) wait_until_all_pods_are_ready(kube_apis.v1, test_namespace) diff --git a/tests/suite/test_virtual_server.py b/tests/suite/test_virtual_server.py index 26c5f0492d..9e98858bb6 100644 --- a/tests/suite/test_virtual_server.py +++ b/tests/suite/test_virtual_server.py @@ -7,7 +7,7 @@ create_virtual_server_from_yaml, delete_virtual_server, patch_virtual_server_from_yaml from suite.resources_utils import patch_rbac, replace_service, read_service, \ wait_before_test, delete_service, create_service_from_yaml -from suite.yaml_utils import get_paths_from_vs_yaml, get_first_vs_host_from_yaml, get_names_from_yaml +from suite.yaml_utils import get_paths_from_vs_yaml, get_first_vs_host_from_yaml, get_name_from_yaml @pytest.mark.vs @@ -158,14 +158,14 @@ def test_responses_after_rbac_misconfiguration_on_the_fly(self, kube_apis, crd_i def test_responses_after_crd_removal_on_the_fly(self, kube_apis, crd_ingress_controller, virtual_server_setup): print("\nStep 12: remove CRD and check") - crd_name = get_names_from_yaml(f"{DEPLOYMENTS}/common/custom-resource-definitions.yaml")[0] + crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/common/vs-definition.yaml") delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) wait_and_assert_status_code(404, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host) wait_and_assert_status_code(404, virtual_server_setup.backend_2_url, virtual_server_setup.vs_host) print("Step 13: restore CRD and VS and check") create_crd_from_yaml(kube_apis.api_extensions_v1_beta1, crd_name, - f"{DEPLOYMENTS}/common/custom-resource-definitions.yaml") + f"{DEPLOYMENTS}/common/vs-definition.yaml") wait_before_test(1) create_virtual_server_from_yaml(kube_apis.custom_objects, f"{TEST_DATA}/virtual-server/standard/virtual-server.yaml", diff --git a/tests/suite/test_virtual_server_tls.py b/tests/suite/test_virtual_server_tls.py index 178aadd3df..84b40530ec 100644 --- a/tests/suite/test_virtual_server_tls.py +++ b/tests/suite/test_virtual_server_tls.py @@ -6,7 +6,7 @@ from suite.resources_utils import create_secret_from_yaml, wait_before_test, delete_secret, is_secret_present, \ replace_secret from suite.ssl_utils import get_server_certificate_subject -from suite.yaml_utils import get_names_from_yaml +from suite.yaml_utils import get_name_from_yaml @pytest.fixture(scope="class") @@ -19,7 +19,7 @@ def clean_up(request, kube_apis, test_namespace) -> None: :param test_namespace: str :return: """ - secret_name = get_names_from_yaml(f"{TEST_DATA}/virtual-server-tls/tls-secret.yaml")[0] + secret_name = get_name_from_yaml(f"{TEST_DATA}/virtual-server-tls/tls-secret.yaml") def fin(): print("Clean up after test:") diff --git a/tests/suite/yaml_utils.py b/tests/suite/yaml_utils.py index 00c6dc2f65..0f93a4318e 100644 --- a/tests/suite/yaml_utils.py +++ b/tests/suite/yaml_utils.py @@ -16,19 +16,17 @@ def get_first_ingress_host_from_yaml(file) -> str: return dep['spec']['rules'][0]['host'] -def get_names_from_yaml(file) -> []: +def get_name_from_yaml(file) -> str: """ Parse yaml file and return all the found metadata.name. :param file: an absolute path to file - :return: [] + :return: str """ - res = [] with open(file) as f: docs = yaml.safe_load_all(f) for dep in docs: - res.append(dep['metadata']['name']) - return res + return dep['metadata']['name'] def get_paths_from_vs_yaml(file) -> []: From d0acb215d4dae2d12fd4ff4d850e3f7a11dff257 Mon Sep 17 00:00:00 2001 From: dubrovina Date: Fri, 21 Feb 2020 12:47:15 +0000 Subject: [PATCH 2/2] Fix imports and failing tests --- .../standard/virtual-server.yaml | 2 -- .../virtual-server-invalid.yaml | 4 +-- .../standard/virtual-server.yaml | 1 - .../virtual-server-invalid.yaml | 2 +- .../virtual-server-updated.yaml | 1 - .../route-multiple-invalid.yaml | 4 +-- .../route-multiple.yaml | 2 -- .../route-multiple-invalid.yaml | 2 +- .../route-multiple-updated.yaml | 1 - .../route-multiple.yaml | 1 - .../plus-route-m-invalid-keys.yaml | 1 + .../route-multiple.yaml | 4 --- .../route-single-disable-tls.yaml | 2 -- .../virtual-server-disable-tls.yaml | 4 --- tests/suite/fixtures.py | 31 +++++++++-------- tests/suite/resources_utils.py | 33 +++++++++++++++++++ .../suite/test_v_s_route_canned_responses.py | 10 +++--- tests/suite/test_v_s_route_focused_canary.py | 2 +- tests/suite/test_v_s_route_upstream_tls.py | 15 ++++++--- tests/suite/test_virtual_server.py | 2 +- .../test_virtual_server_canned_responses.py | 12 ++++--- .../suite/test_virtual_server_upstream_tls.py | 15 ++++++--- tests/suite/yaml_utils.py | 4 ++- 23 files changed, 96 insertions(+), 59 deletions(-) diff --git a/tests/data/virtual-server-canned-responses/standard/virtual-server.yaml b/tests/data/virtual-server-canned-responses/standard/virtual-server.yaml index 4ed5dcd2c5..27e1d69a17 100644 --- a/tests/data/virtual-server-canned-responses/standard/virtual-server.yaml +++ b/tests/data/virtual-server-canned-responses/standard/virtual-server.yaml @@ -38,8 +38,6 @@ spec: - path: "/canned-defaults" action: return: - code: - type: body: | line1 line2 diff --git a/tests/data/virtual-server-canned-responses/virtual-server-invalid.yaml b/tests/data/virtual-server-canned-responses/virtual-server-invalid.yaml index 3708331ba4..ff251629c2 100644 --- a/tests/data/virtual-server-canned-responses/virtual-server-invalid.yaml +++ b/tests/data/virtual-server-canned-responses/virtual-server-invalid.yaml @@ -9,5 +9,5 @@ spec: action: return: code: 301 - type: "text/html" - body: \ No newline at end of file + type: "anything will do" + body: "Variables must be enclosed in curly brackets: $request_uri" \ No newline at end of file diff --git a/tests/data/virtual-server-redirects/standard/virtual-server.yaml b/tests/data/virtual-server-redirects/standard/virtual-server.yaml index 816e8db644..a3659a403a 100644 --- a/tests/data/virtual-server-redirects/standard/virtual-server.yaml +++ b/tests/data/virtual-server-redirects/standard/virtual-server.yaml @@ -13,5 +13,4 @@ spec: - path: "/default-redirect" action: redirect: - code: url: "${scheme}://${host}${request_uri}?arg=${http_x_forwarded_proto}" \ No newline at end of file diff --git a/tests/data/virtual-server-redirects/virtual-server-invalid.yaml b/tests/data/virtual-server-redirects/virtual-server-invalid.yaml index ba4440ba69..8381a44311 100644 --- a/tests/data/virtual-server-redirects/virtual-server-invalid.yaml +++ b/tests/data/virtual-server-redirects/virtual-server-invalid.yaml @@ -13,5 +13,5 @@ spec: - path: "/default-redirect" action: redirect: - code: + code: 204 url: "${nginx_version}" \ No newline at end of file diff --git a/tests/data/virtual-server-redirects/virtual-server-updated.yaml b/tests/data/virtual-server-redirects/virtual-server-updated.yaml index 44bc860a05..be4ce18c24 100644 --- a/tests/data/virtual-server-redirects/virtual-server-updated.yaml +++ b/tests/data/virtual-server-redirects/virtual-server-updated.yaml @@ -8,7 +8,6 @@ spec: - path: "/custom-redirect" action: redirect: - code: url: "http://demo.nginx.com" - path: "/default-redirect" action: diff --git a/tests/data/virtual-server-route-canned-responses/route-multiple-invalid.yaml b/tests/data/virtual-server-route-canned-responses/route-multiple-invalid.yaml index 3e729edabf..766c32794f 100644 --- a/tests/data/virtual-server-route-canned-responses/route-multiple-invalid.yaml +++ b/tests/data/virtual-server-route-canned-responses/route-multiple-invalid.yaml @@ -9,5 +9,5 @@ spec: action: return: code: 301 - type: "text/html" - body: \ No newline at end of file + type: "anything will do" + body: "Variables must be inclosed in curly brackets: $request_uri" \ No newline at end of file diff --git a/tests/data/virtual-server-route-canned-responses/route-multiple.yaml b/tests/data/virtual-server-route-canned-responses/route-multiple.yaml index 8509bb6dae..3b009d7a02 100644 --- a/tests/data/virtual-server-route-canned-responses/route-multiple.yaml +++ b/tests/data/virtual-server-route-canned-responses/route-multiple.yaml @@ -38,8 +38,6 @@ spec: - path: "/backends/canned-defaults" action: return: - code: - type: body: | line1 line2 diff --git a/tests/data/virtual-server-route-redirects/route-multiple-invalid.yaml b/tests/data/virtual-server-route-redirects/route-multiple-invalid.yaml index b12adcdc63..f5f1c78cac 100644 --- a/tests/data/virtual-server-route-redirects/route-multiple-invalid.yaml +++ b/tests/data/virtual-server-route-redirects/route-multiple-invalid.yaml @@ -13,5 +13,5 @@ spec: - path: "/backends/default-redirect" action: redirect: - code: + code: 204 url: "${nginx_version}" \ No newline at end of file diff --git a/tests/data/virtual-server-route-redirects/route-multiple-updated.yaml b/tests/data/virtual-server-route-redirects/route-multiple-updated.yaml index e8c90cb122..adcdc72577 100644 --- a/tests/data/virtual-server-route-redirects/route-multiple-updated.yaml +++ b/tests/data/virtual-server-route-redirects/route-multiple-updated.yaml @@ -8,7 +8,6 @@ spec: - path: "/backends/custom-redirect" action: redirect: - code: url: "http://demo.nginx.com" - path: "/backends/default-redirect" action: diff --git a/tests/data/virtual-server-route-redirects/route-multiple.yaml b/tests/data/virtual-server-route-redirects/route-multiple.yaml index 34333f5460..1e4298191c 100644 --- a/tests/data/virtual-server-route-redirects/route-multiple.yaml +++ b/tests/data/virtual-server-route-redirects/route-multiple.yaml @@ -13,5 +13,4 @@ spec: - path: "/backends/default-redirect" action: redirect: - code: url: "${scheme}://${host}${request_uri}?arg=${http_x_forwarded_proto}" \ No newline at end of file diff --git a/tests/data/virtual-server-route-upstream-options/plus-route-m-invalid-keys.yaml b/tests/data/virtual-server-route-upstream-options/plus-route-m-invalid-keys.yaml index df4164e243..c5cbde755b 100644 --- a/tests/data/virtual-server-route-upstream-options/plus-route-m-invalid-keys.yaml +++ b/tests/data/virtual-server-route-upstream-options/plus-route-m-invalid-keys.yaml @@ -57,6 +57,7 @@ spec: statusMatch: "invalid" slow-start: "1.5m" queue: + size: 0 timeout: "hour" subroutes: - path: "/backends/backend1" diff --git a/tests/data/virtual-server-route-upstream-tls/route-multiple.yaml b/tests/data/virtual-server-route-upstream-tls/route-multiple.yaml index 11ef801535..dc620fcac8 100644 --- a/tests/data/virtual-server-route-upstream-tls/route-multiple.yaml +++ b/tests/data/virtual-server-route-upstream-tls/route-multiple.yaml @@ -8,13 +8,9 @@ spec: - name: backend1 service: backend1-svc port: 80 - tls: - enable: - name: backend3 service: backend3-svc port: 80 - tls: - enable: False subroutes: - path: "/backends/backend1" action: diff --git a/tests/data/virtual-server-route-upstream-tls/route-single-disable-tls.yaml b/tests/data/virtual-server-route-upstream-tls/route-single-disable-tls.yaml index 58b590dbca..3c5d8aec87 100644 --- a/tests/data/virtual-server-route-upstream-tls/route-single-disable-tls.yaml +++ b/tests/data/virtual-server-route-upstream-tls/route-single-disable-tls.yaml @@ -8,8 +8,6 @@ spec: - name: backend2 service: backend2-svc port: 80 - tls: - enable: subroutes: - path: "/backend2" action: diff --git a/tests/data/virtual-server-upstream-tls/virtual-server-disable-tls.yaml b/tests/data/virtual-server-upstream-tls/virtual-server-disable-tls.yaml index afc50699ea..177bc6cd1f 100644 --- a/tests/data/virtual-server-upstream-tls/virtual-server-disable-tls.yaml +++ b/tests/data/virtual-server-upstream-tls/virtual-server-disable-tls.yaml @@ -8,13 +8,9 @@ spec: - name: backend2 service: backend2-svc port: 80 - tls: - enable: - name: backend1 service: backend1-svc port: 80 - tls: - enable: False routes: - path: "/backend1" action: diff --git a/tests/suite/fixtures.py b/tests/suite/fixtures.py index 20af554467..88a1a936b9 100644 --- a/tests/suite/fixtures.py +++ b/tests/suite/fixtures.py @@ -10,18 +10,18 @@ ApiextensionsV1beta1Api, AppsV1Api from kubernetes.client.rest import ApiException -from suite.custom_resources_utils import create_crd_from_yaml, create_virtual_server_from_yaml, \ - delete_virtual_server, create_v_s_route_from_yaml, delete_v_s_route, delete_crd +from suite.custom_resources_utils import create_virtual_server_from_yaml, \ + delete_virtual_server, create_v_s_route_from_yaml, delete_v_s_route, create_crd_from_yaml, delete_crd from suite.kube_config_utils import ensure_context_in_config, get_current_context_name from suite.resources_utils import create_namespace_with_name_from_yaml, delete_namespace, create_ns_and_sa_from_yaml, \ patch_rbac, create_example_app, wait_until_all_pods_are_ready, delete_common_app, \ ensure_connection_to_public_endpoint, create_service_with_name, create_deployment_with_name, delete_deployment, \ - delete_service, replace_configmap_from_yaml, delete_testing_namespaces, \ - create_ingress_controller, delete_ingress_controller, configure_rbac, cleanup_rbac, \ - create_service_from_yaml, get_service_node_ports, wait_for_public_ip, \ - create_configmap_from_yaml, create_secret_from_yaml, \ - get_first_vs_host_from_yaml, get_paths_from_vs_yaml, get_paths_from_vsr_yaml, get_name_from_yaml, \ - get_route_namespace_from_vs_yaml + delete_service, replace_configmap_from_yaml, delete_testing_namespaces +from suite.resources_utils import create_ingress_controller, delete_ingress_controller, configure_rbac, cleanup_rbac +from suite.resources_utils import create_service_from_yaml, get_service_node_ports, wait_for_public_ip +from suite.resources_utils import create_configmap_from_yaml, create_secret_from_yaml +from suite.yaml_utils import get_first_vs_host_from_yaml, get_paths_from_vs_yaml, get_paths_from_vsr_yaml, \ + get_route_namespace_from_vs_yaml, get_name_from_yaml from settings import ALLOWED_SERVICE_TYPES, ALLOWED_IC_TYPES, DEPLOYMENTS, TEST_DATA, ALLOWED_DEPLOYMENT_TYPES @@ -302,15 +302,18 @@ def crd_ingress_controller(cli_arguments, kube_apis, ingress_controller_prerequi """ namespace = ingress_controller_prerequisites.namespace name = "nginx-ingress" - crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/common/vs-definition.yaml") + vs_crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/vs-definition.yaml") + vsr_crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/vsr-definition.yaml") try: print("------------------------- Update ClusterRole -----------------------------------") if request.param['type'] == 'rbac-without-vs': patch_rbac(kube_apis.rbac_v1_beta1, f"{TEST_DATA}/virtual-server/rbac-without-vs.yaml") - print("------------------------- Register CRD -----------------------------------") - create_crd_from_yaml(kube_apis.api_extensions_v1_beta1, crd_name, + print("------------------------- Register CRDs -----------------------------------") + create_crd_from_yaml(kube_apis.api_extensions_v1_beta1, vs_crd_name, f"{DEPLOYMENTS}/common/vs-definition.yaml") + create_crd_from_yaml(kube_apis.api_extensions_v1_beta1, vsr_crd_name, + f"{DEPLOYMENTS}/common/vsr-definition.yaml") print("------------------------- Create IC -----------------------------------") name = create_ingress_controller(kube_apis.v1, kube_apis.apps_v1_api, cli_arguments, namespace, request.param.get('extra_args', None)) @@ -320,14 +323,16 @@ def crd_ingress_controller(cli_arguments, kube_apis, ingress_controller_prerequi except ApiException as ex: # Finalizer method doesn't start if fixture creation was incomplete, ensure clean up here print(f"Failed to complete CRD IC fixture: {ex}\nClean up the cluster as much as possible.") - delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) + delete_crd(kube_apis.api_extensions_v1_beta1, vs_crd_name) + delete_crd(kube_apis.api_extensions_v1_beta1, vsr_crd_name) print("Restore the ClusterRole:") patch_rbac(kube_apis.rbac_v1_beta1, f"{DEPLOYMENTS}/rbac/rbac.yaml") print("Remove the IC:") delete_ingress_controller(kube_apis.apps_v1_api, name, cli_arguments['deployment-type'], namespace) def fin(): - delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) + delete_crd(kube_apis.api_extensions_v1_beta1, vs_crd_name) + delete_crd(kube_apis.api_extensions_v1_beta1, vsr_crd_name) print("Restore the ClusterRole:") patch_rbac(kube_apis.rbac_v1_beta1, f"{DEPLOYMENTS}/rbac/rbac.yaml") print("Remove the IC:") diff --git a/tests/suite/resources_utils.py b/tests/suite/resources_utils.py index 23de08014c..a96820e25f 100644 --- a/tests/suite/resources_utils.py +++ b/tests/suite/resources_utils.py @@ -92,6 +92,21 @@ def cleanup_rbac(rbac_v1_beta1: RbacAuthorizationV1beta1Api, rbac: RBACAuthoriza rbac_v1_beta1.delete_cluster_role(rbac.role, delete_options) +def create_deployment_from_yaml(apps_v1_api: AppsV1Api, namespace, yaml_manifest) -> str: + """ + Create a deployment based on yaml file. + + :param apps_v1_api: AppsV1Api + :param namespace: namespace name + :param yaml_manifest: absolute path to file + :return: str + """ + print(f"Load {yaml_manifest}") + with open(yaml_manifest) as f: + dep = yaml.safe_load(f) + return create_deployment(apps_v1_api, namespace, dep) + + def create_deployment(apps_v1_api: AppsV1Api, namespace, body) -> str: """ Create a deployment based on a dict. @@ -529,6 +544,24 @@ def create_namespace(v1: CoreV1Api, body) -> str: return body['metadata']['name'] +def create_namespace_with_name_from_yaml(v1: CoreV1Api, name, yaml_manifest) -> str: + """ + Create a namespace with a specific name based on a yaml manifest. + + :param v1: CoreV1Api + :param name: name + :param yaml_manifest: an absolute path to file + :return: str + """ + print(f"Create a namespace with specific name:") + with open(yaml_manifest) as f: + dep = yaml.safe_load(f) + dep['metadata']['name'] = name + v1.create_namespace(dep) + print(f"Namespace created with name '{str(dep['metadata']['name'])}'") + return dep['metadata']['name'] + + def create_service_account(v1: CoreV1Api, namespace, body) -> None: """ Create a ServiceAccount based on a dict. diff --git a/tests/suite/test_v_s_route_canned_responses.py b/tests/suite/test_v_s_route_canned_responses.py index 40620d5de0..21a69f353f 100644 --- a/tests/suite/test_v_s_route_canned_responses.py +++ b/tests/suite/test_v_s_route_canned_responses.py @@ -5,7 +5,7 @@ from settings import TEST_DATA from suite.custom_assertions import assert_event_and_get_count, wait_and_assert_status_code, \ - assert_event, assert_event_count_increased + assert_event_count_increased, assert_event_starts_with_text_and_contains_errors from suite.custom_resources_utils import get_vs_nginx_template_conf, patch_v_s_route_from_yaml from suite.resources_utils import get_first_pod_name, get_events, wait_before_test @@ -95,11 +95,13 @@ def test_update(self, kube_apis, crd_ingress_controller, v_s_route_setup): assert_event_count_increased(vsr_event_text, initial_count_vsr, new_events_ns) def test_validation_flow(self, kube_apis, crd_ingress_controller, v_s_route_setup): + invalid_fields = [ + "spec.subroutes[0].action.return.code", "spec.subroutes[0].action.return.body" + ] req_host = f"{v_s_route_setup.public_endpoint.public_ip}:{v_s_route_setup.public_endpoint.port}" req_url = f"http://{req_host}{v_s_route_setup.route_s.paths[0]}" text = f"{v_s_route_setup.namespace}/{v_s_route_setup.route_m.name}" - event_text = f"VirtualServerRoute {text} is invalid and was rejected: " \ - f"spec.subroutes[0].action.return.body: Required value" + vsr_m_event_text = f"VirtualServerRoute {text} is invalid and was rejected: " vsr_src = f"{TEST_DATA}/virtual-server-route-canned-responses/route-multiple-invalid.yaml" patch_v_s_route_from_yaml(kube_apis.custom_objects, v_s_route_setup.route_m.name, vsr_src, v_s_route_setup.namespace) @@ -107,4 +109,4 @@ def test_validation_flow(self, kube_apis, crd_ingress_controller, v_s_route_setu wait_and_assert_status_code(404, req_url, v_s_route_setup.vs_host) events = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace) - assert_event(event_text, events) + assert_event_starts_with_text_and_contains_errors(vsr_m_event_text, events, invalid_fields) diff --git a/tests/suite/test_v_s_route_focused_canary.py b/tests/suite/test_v_s_route_focused_canary.py index c2f95a31d8..066bc4f3c8 100644 --- a/tests/suite/test_v_s_route_focused_canary.py +++ b/tests/suite/test_v_s_route_focused_canary.py @@ -62,7 +62,6 @@ def __init__(self, namespace, vs_host, vs_name, route: VirtualServerRoute, backe self.backends_url = backends_url -@pytest.mark.vsr @pytest.fixture(scope="class") def vsr_canary_setup(request, kube_apis, ingress_controller_prerequisites, ingress_controller_endpoint) -> VSRAdvancedRoutingSetup: @@ -109,6 +108,7 @@ def fin(): return VSRAdvancedRoutingSetup(ns_1, vs_host, vs_name, route, backends_url) +@pytest.mark.vsr @pytest.mark.parametrize('crd_ingress_controller, vsr_canary_setup', [({"type": "complete", "extra_args": [f"-enable-custom-resources"]}, {"example": "virtual-server-route-focused-canary"})], diff --git a/tests/suite/test_v_s_route_upstream_tls.py b/tests/suite/test_v_s_route_upstream_tls.py index 0284bfedbb..690e6215e8 100644 --- a/tests/suite/test_v_s_route_upstream_tls.py +++ b/tests/suite/test_v_s_route_upstream_tls.py @@ -1,5 +1,6 @@ import requests import pytest +from kubernetes.client.rest import ApiException from settings import TEST_DATA from suite.custom_assertions import assert_event_and_get_count, assert_event_count_increased, assert_response_codes, \ @@ -84,16 +85,20 @@ def test_events_after_setup(self, kube_apis, ingress_controller_prerequisites, assert_event(vsr_m_event_text, events_ns_m) assert_event(vs_event_text, events_ns_m) - def test_invalid_value_rejection(self, kube_apis, ingress_controller_prerequisites, + def test_validation_flow(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, v_s_route_setup, v_s_route_secure_app_setup): ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace) initial_events_ns_m = get_events(kube_apis.v1, v_s_route_setup.route_m.namespace) initial_events_ns_s = get_events(kube_apis.v1, v_s_route_setup.route_s.namespace) - patch_v_s_route_from_yaml(kube_apis.custom_objects, - v_s_route_setup.route_s.name, - f"{TEST_DATA}/virtual-server-route-upstream-tls/route-single-invalid.yaml", - v_s_route_setup.route_s.namespace) + try: + patch_v_s_route_from_yaml(kube_apis.custom_objects, + v_s_route_setup.route_s.name, + f"{TEST_DATA}/virtual-server-route-upstream-tls/route-single-invalid.yaml", + v_s_route_setup.route_s.namespace) + except ApiException as ex: + assert ex.status == 422 and "spec.upstreams.tls.enable: Invalid value" in ex.body + wait_before_test(1) config = get_vs_nginx_template_conf(kube_apis.v1, v_s_route_setup.namespace, diff --git a/tests/suite/test_virtual_server.py b/tests/suite/test_virtual_server.py index 9e98858bb6..0c2b1057d1 100644 --- a/tests/suite/test_virtual_server.py +++ b/tests/suite/test_virtual_server.py @@ -158,7 +158,7 @@ def test_responses_after_rbac_misconfiguration_on_the_fly(self, kube_apis, crd_i def test_responses_after_crd_removal_on_the_fly(self, kube_apis, crd_ingress_controller, virtual_server_setup): print("\nStep 12: remove CRD and check") - crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/common/vs-definition.yaml") + crd_name = get_name_from_yaml(f"{DEPLOYMENTS}/common/vs-definition.yaml") delete_crd(kube_apis.api_extensions_v1_beta1, crd_name) wait_and_assert_status_code(404, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host) wait_and_assert_status_code(404, virtual_server_setup.backend_2_url, virtual_server_setup.vs_host) diff --git a/tests/suite/test_virtual_server_canned_responses.py b/tests/suite/test_virtual_server_canned_responses.py index 932e0a2c9b..8c12a54bb9 100644 --- a/tests/suite/test_virtual_server_canned_responses.py +++ b/tests/suite/test_virtual_server_canned_responses.py @@ -3,8 +3,8 @@ import json from settings import TEST_DATA -from suite.custom_assertions import wait_and_assert_status_code, assert_event, assert_event_and_get_count, \ - assert_event_count_increased +from suite.custom_assertions import wait_and_assert_status_code, assert_event_and_get_count, \ + assert_event_count_increased, assert_event_starts_with_text_and_contains_errors from suite.custom_resources_utils import patch_virtual_server_from_yaml, get_vs_nginx_template_conf from suite.resources_utils import get_first_pod_name, get_events, wait_before_test @@ -83,9 +83,11 @@ def test_update(self, kube_apis, crd_ingress_controller, virtual_server_setup): assert_event_count_increased(vs_event_text, initial_count, vs_events) def test_validation_flow(self, kube_apis, crd_ingress_controller, virtual_server_setup): + invalid_fields = [ + "spec.routes[0].action.return.code", "spec.routes[0].action.return.body" + ] text = f"{virtual_server_setup.namespace}/{virtual_server_setup.vs_name}" - event_text = f"VirtualServer {text} is invalid and was rejected: " \ - f"spec.routes[0].action.return.body: Required value" + vs_event_text = f"VirtualServer {text} is invalid and was rejected: " vs_src = f"{TEST_DATA}/virtual-server-canned-responses/virtual-server-invalid.yaml" patch_virtual_server_from_yaml(kube_apis.custom_objects, virtual_server_setup.vs_name, vs_src, virtual_server_setup.namespace) @@ -93,4 +95,4 @@ def test_validation_flow(self, kube_apis, crd_ingress_controller, virtual_server wait_and_assert_status_code(404, virtual_server_setup.backend_1_url, virtual_server_setup.vs_host) vs_events = get_events(kube_apis.v1, virtual_server_setup.namespace) - assert_event(event_text, vs_events) + assert_event_starts_with_text_and_contains_errors(vs_event_text, vs_events, invalid_fields) diff --git a/tests/suite/test_virtual_server_upstream_tls.py b/tests/suite/test_virtual_server_upstream_tls.py index 6a2c2e24b3..c0c7616712 100644 --- a/tests/suite/test_virtual_server_upstream_tls.py +++ b/tests/suite/test_virtual_server_upstream_tls.py @@ -1,5 +1,6 @@ import requests import pytest +from kubernetes.client.rest import ApiException from settings import TEST_DATA from suite.custom_assertions import assert_event_and_get_count, assert_event_count_increased, assert_response_codes, \ @@ -39,14 +40,18 @@ def test_event_after_setup(self, kube_apis, ingress_controller_prerequisites, events_vs = get_events(kube_apis.v1, virtual_server_setup.namespace) assert_event(vs_event_text, events_vs) - def test_invalid_value_rejection(self, kube_apis, ingress_controller_prerequisites, + def test_validation_flow(self, kube_apis, ingress_controller_prerequisites, crd_ingress_controller, virtual_server_setup): ic_pod_name = get_first_pod_name(kube_apis.v1, ingress_controller_prerequisites.namespace) initial_events_vs = get_events(kube_apis.v1, virtual_server_setup.namespace) - patch_virtual_server_from_yaml(kube_apis.custom_objects, - virtual_server_setup.vs_name, - f"{TEST_DATA}/virtual-server-upstream-tls/virtual-server-invalid.yaml", - virtual_server_setup.namespace) + try: + patch_virtual_server_from_yaml(kube_apis.custom_objects, + virtual_server_setup.vs_name, + f"{TEST_DATA}/virtual-server-upstream-tls/virtual-server-invalid.yaml", + virtual_server_setup.namespace) + except ApiException as ex: + assert ex.status == 422 and "spec.upstreams.tls.enable: Invalid value" in ex.body + wait_before_test(1) config = get_vs_nginx_template_conf(kube_apis.v1, virtual_server_setup.namespace, diff --git a/tests/suite/yaml_utils.py b/tests/suite/yaml_utils.py index 0f93a4318e..2bab151b3b 100644 --- a/tests/suite/yaml_utils.py +++ b/tests/suite/yaml_utils.py @@ -18,15 +18,17 @@ def get_first_ingress_host_from_yaml(file) -> str: def get_name_from_yaml(file) -> str: """ - Parse yaml file and return all the found metadata.name. + Parse yaml file and return first metadata.name appeared. :param file: an absolute path to file :return: str """ + res = "" with open(file) as f: docs = yaml.safe_load_all(f) for dep in docs: return dep['metadata']['name'] + return res def get_paths_from_vs_yaml(file) -> []: