diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 91f0dfe0..4ab374cc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,10 @@ jobs: - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 with: go-version: "1.22" - - run: make integration-tests + - name: Install gingko + run: go install github.com/onsi/ginkgo/v2/ginkgo + - run: make integration-tests-envtest + - run: make integration-tests-real-cluster - name: Upload integration-tests coverage to Codecov uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0 env: @@ -59,9 +62,6 @@ jobs: uses: golangci/golangci-lint-action@a4f60bb28d35aeee14e6880718e0c85ff1882e64 # v6.0.1 with: version: v1.57.2 - # Disable caching as a workaround for https://github.com/golangci/golangci-lint-action/issues/135. - # The line can be removed once the golangci-lint issue is resolved. - skip-pkg-cache: true shellcheck: name: Shellcheck diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 457275f8..0b262bd9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,22 +42,22 @@ You can use [k3d](https://k3d.io/) to create a local cluster for development pur ### Settings The `tilt-settings.yaml.example` acts as a template for the `tilt-settings.yaml` -file that you need to create in the root of this repository. Copy the example -file and edit it to match your environment. The `tilt-settings.yaml` file is +file that you need to create in the root of this repository. Copy the example +file and edit it to match your environment. The `tilt-settings.yaml` file is ignored by git, so you can edit it without concern about committing it by mistake. The following settings can be configured: - `registry`: the container registry to push the controller image to. If you -don't have a private registry, you can use `ghcr.io` provided your cluster has -access to it. + don't have a private registry, you can use `ghcr.io` provided your cluster has + access to it. - `image`: the name of the controller image. If you are using `ghcr.io` as your -registry, you need to prefix the image name with your GitHub username. + registry, you need to prefix the image name with your GitHub username. - `helm_charts_path`: the path to the `helm-charts` repository that you cloned -in the previous step. + in the previous step. Example: @@ -84,6 +84,90 @@ empty cluster: tilt up --stream ``` +## Testing + +### Run tests + +Run all tests: + +```console +make test +``` + +Run unit tests only: + +```console +make unit-tests +``` + +Run controller integration tests only: + +```console +make integration-tests +``` + +Run tests that do not require a Kubernetes cluster only: + +```console +make integration-tests-envtest +``` + +Run tests that require a Kubernetes cluster only: + +```console +make integration-tests-real-cluster +``` + +### Writing (controller) integration tests + +The controller integration tests are written using the [Ginkgo](https://onsi.github.io/ginkgo/) +and [Gomega](https://onsi.github.io/gomega/) testing frameworks. +The tests are located in the `internal/controller` package. + +By default, the tests are run by using [envtest](https://book.kubebuilder.io/reference/envtest) which +sets up an instance of etcd and the Kubernetes API server, without kubelet, controller-manager or other components. + +However, some tests require a real Kubernetes cluster to run. +These tests must be marked with the `real-cluster` ginkgo label. + +Example: + +``` +var _ = Describe("A test that requires a real cluster", Label("real cluster") func() { + Context("when running in a real cluster", func() { + It("should do something", func() { + // test code + }) + }) +}) +``` + +To run only the tests that require a real cluster, you can use the following command: + +```console +make integration-test-real-cluster +``` + +The suite setup will start a [k3s testcontainer](https://testcontainers.com/modules/k3s/) and run the tests against it. +It will also stop and remove the container when the tests finish. + +Note that the `real-cluster` tests are slower than the `envtest` tests, therefore, it is recommended to keep the number of `real-cluster` tests to a minimum. +An example of a test that requires a real cluster is the `AdmissionPolicy` test suite, since at the time of writing, we wait for the `PolicyServer` Pod to be ready before reconciling the webhook configuration. + +### Focusing + +You can focus on a specific test or spec by using a [Focused Spec](https://onsi.github.io/ginkgo/#focused-specs). + +Example: + +```go +var _ = Describe("Controller test", func() { + FIt("should do something", func() { + // This spec will be the only one executed + }) +}) +``` + ## Tagging a new release ### Make sure to update the CRD docs @@ -133,24 +217,24 @@ everything works well: - [ ] Update audit scanner code - [ ] Run audit scanner tests or check if the CI is green in the main branch - [ ] Bump policy server version in the `Cargo.toml` and update the `Cargo.lock` -file. This requires a PR in the repository to update the files in the main -branch. Update the local code after merging the PR + file. This requires a PR in the repository to update the files in the main + branch. Update the local code after merging the PR - [ ] Run policy server tests or check if the CI is green in the main branch - [ ] Bump kwctl version in the `Cargo.toml` and update the `Cargo.lock` file. -This requires a PR in the repository to update the files in the main branch. -Update the local code after merging the PR + This requires a PR in the repository to update the files in the main branch. + Update the local code after merging the PR - [ ] Run kwctl tests or check if the CI is green in the main branch - [ ] Tag audit scanner - [ ] Tag policy server - [ ] Tag controller - [ ] Tag kwctl - [ ] Wait for all CI running in all the major components (audit scanner, -controller, policy server and kwctl) to finish + controller, policy server and kwctl) to finish - [ ] Check if the Helm chart repository CI open a PR updating the Helm charts -with the correct changes. + with the correct changes. - [ ] Check if the `kubewarden-controller` chart versions are correctly bumped - [ ] Check if the `kubewarden-defaults` chart versions are correctly bumped - [ ] Check if the `kubewarden-crds` chart versions are correctly bumped - [ ] Check if kubewarden-controller, kubewarden-defaults and kubewarden-crds - charts have the same `appVersion` + charts have the same `appVersion` - [ ] Check if CI in the Helm chart PR is green. If so, merge it. diff --git a/Makefile b/Makefile index f3352e89..e14f562f 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,15 @@ - # Image URL to use all building/pushing image targets IMG ?= controller:latest # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.29 +ENVTEST_K8S_VERSION = 1.30.0 # K3S_TESTCONTAINER_VERSION refers to the version of k3s testcontainer to be used by envtest to run integration tests. -K3S_TESTCONTAINER_VERSION = v1.29.1-k3s2 +K3S_TESTCONTAINER_VERSION = v1.30.0-k3s1 # POLICY_SERVER_VERSION refers to the version of the policy server to be used by integration tests. -POLICY_SERVER_VERSION = v1.11.0 -# Binary directory -ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) -BIN_DIR := $(abspath $(ROOT_DIR)/bin) +POLICY_SERVER_VERSION = v1.14.0 + +# Let's use a generous timeout for integration tests because GitHub workers can +# be slow +TEST_TIMEOUT := 30m # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -18,40 +18,25 @@ else GOBIN=$(shell go env GOBIN) endif +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + # Setting SHELL to bash allows bash commands to be executed by recipes. -# This is a requirement for 'setup-envtest.sh' in the test target. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail .SHELLFLAGS = -ec -# Tools binaries -CONTROLLER_GEN_VER := v0.15.0 -CONTROLLER_GEN_BIN := controller-gen -CONTROLLER_GEN := $(BIN_DIR)/$(CONTROLLER_GEN_BIN) - -KUSTOMIZE_VER := v5.4.1 -KUSTOMIZE_BIN := kustomize -KUSTOMIZE := $(BIN_DIR)/$(KUSTOMIZE_BIN) - -SETUP_ENVTEST_VER := v0.0.0-20211110210527-619e6b92dab9 -SETUP_ENVTEST_BIN := setup-envtest -SETUP_ENVTEST := $(abspath $(BIN_DIR)/$(SETUP_ENVTEST_BIN)) - -GOLANGCI_LINT_VER := v1.57.2 -GOLANGCI_LINT_BIN := golangci-lint -GOLANGCI_LINT := $(BIN_DIR)/$(GOLANGCI_LINT_BIN) - -# Let's use a generous timeout for integration tests because GitHub workers can -# be slow -TEST_TIMEOUT := 30m - +.PHONY: all all: build ##@ General # The help target prints out all targets with their descriptions organized # beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the +# target descriptions by '##'. The awk command is responsible for reading the # entire set of makefiles included in this invocation, looking for lines of the # file as xyz: ## something, and then pretty-format the target and help. Then, # if there's a line with ##@ something, that gets pretty-printed as a category. @@ -60,74 +45,71 @@ all: build # More info on the awk command: # http://linuxcommand.org/lc3_adv_awk.php +.PHONY: help help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) -## @ Tools - -controller-gen: $(CONTROLLER_GEN) ## Install a local copy of controller-gen. -kustomize: $(KUSTOMIZE) ## Install a local copy of kustomize. -setup-envtest: $(SETUP_ENVTEST) ## Install a local copy of setup-envtest. -golangci-lint: $(GOLANGCI_LINT) ## Install a local copy of golang ci-lint. - -$(CONTROLLER_GEN): ## Install controller-gen. - GOBIN=$(BIN_DIR) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VER) - -$(KUSTOMIZE): ## Install kustomize. - GOBIN=$(BIN_DIR) go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VER) - -$(SETUP_ENVTEST): ## Install setup-envtest. - GOBIN=$(BIN_DIR) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(SETUP_ENVTEST_VER) - -$(GOLANGCI_LINT): ## Install golangci-lint. - GOBIN=$(BIN_DIR) go install github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_VER) - ##@ Development -manifests: $(CONTROLLER_GEN) ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases -generate: $(CONTROLLER_GEN) ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." +.PHONY: fmt fmt: ## Run go fmt against code. go fmt ./... +.PHONY: vet vet: ## Run go vet against code. go vet ./... -lint: $(GOLANGCI_LINT) - $(GOLANGCI_LINT) run - .PHONY: test test: unit-tests integration-tests ## Run tests. -.PHONY: setup-envtest -setup-envtest: $(SETUP_ENVTEST) # Build setup-envtest - @if [ $(shell go env GOOS) == "darwin" ]; then \ - $(eval KUBEBUILDER_ASSETS := $(shell $(SETUP_ENVTEST) use --use-env -p path --arch amd64 $(ENVTEST_K8S_VERSION))) \ - echo "kube-builder assets set using darwin OS"; \ - else \ - $(eval KUBEBUILDER_ASSETS := $(shell $(SETUP_ENVTEST) use --use-env -p path $(ENVTEST_K8S_VERSION))) \ - echo "kube-builder assets set using other OS"; \ - fi - .PHONY: unit-tests -unit-tests: manifests generate fmt vet setup-envtest ## Run unit tests. - KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test $$(go list ./... | grep -v /internal/controller) -race -test.v -coverprofile=coverage/unit-tests/coverage.txt -covermode=atomic - -.PHONY: setup-envtest integration-tests -integration-tests: manifests generate fmt vet setup-envtest ## Run integration tests. - ACK_GINKGO_DEPRECATIONS=2.12.0 K3S_TESTCONTAINER_VERSION="$(K3S_TESTCONTAINER_VERSION)" POLICY_SERVER_VERSION="$(POLICY_SERVER_VERSION)" \ - go test -v ./internal/controller/... -ginkgo.v -ginkgo.progress -race -test.v \ - -ginkgo.randomize-all -ginkgo.timeout=$(TEST_TIMEOUT) \ - -coverprofile=coverage/integration-tests/coverage-controllers.txt \ - -covermode=atomic -timeout=$(TEST_TIMEOUT) -coverpkg=all - -.PHONY: generate-crds -generate-crds: $(KUSTOMIZE) manifests kustomize ## generate final crds with kustomize. Normally shipped in Helm charts. - mkdir -p generated-crds - $(KUSTOMIZE) build config/crd -o generated-crds # If -o points to a folder, kustomize saves them as several files instead of 1 +unit-tests: manifests generate fmt vet ## Run unit tests. + go test $$(go list ./... | grep -v /internal/controller) -race -test.v -coverprofile=coverage/unit-tests/coverage.txt -covermode=atomic + +# Integration tests are split into two targets to allow for running tests +# that require a real cluster to be run separately from those that can be run using envtest. +.PHONY: integration-tests +integration-tests: integration-tests-envtest integration-tests-real-cluster ## Run integration tests. + +# Note that the label-filter "!real-cluster" is used to exclude tests that require a real cluster, +# otherwise ginkgo will try to run ALL tests. +.PHONY: integration-tests-envtest +integration-tests-envtest: manifests generate fmt vet envtest ## Run integration tests that do not require a real cluster only (using envtest). + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" \ + ginkgo -v -github-output -timeout=$(TEST_TIMEOUT) -label-filter="!real-cluster" \ + -output-dir=./coverage/integration-tests/ -coverprofile=coverage-envtest.txt -covermode=atomic -coverpkg=all \ + ./internal/controller/ + +.PHONY: integration-tests-real-cluster +integration-tests-real-cluster: manifests generate fmt vet ## Run integration tests that require a real cluster only. + K3S_TESTCONTAINER_VERSION="$(K3S_TESTCONTAINER_VERSION)" POLICY_SERVER_VERSION="$(POLICY_SERVER_VERSION)" \ + ginkgo -p -v -github-output -timeout=$(TEST_TIMEOUT) -label-filter="real-cluster" \ + -output-dir=./coverage/integration-tests/ -coverprofile=coverage-real-cluster.txt -covermode=atomic -coverpkg=all \ + ./internal/controller/ + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: tilt-up +tilt-up: manifests generate fmt vet ## Run a controller using Tilt. + tilt up --stream + +.PHONY: tilt-down +tilt-down: ## Stop Tilt. + tilt down ##@ Build @@ -135,44 +117,113 @@ generate-crds: $(KUSTOMIZE) manifests kustomize ## generate final crds with kust build: manifests generate fmt vet ## Build manager binary. go build -o bin/manager cmd/main.go -.PHONY: run -run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go -deployments-namespace kubewarden --default-policy-server default - -docker-build: test ## Build docker image with the manager. - docker build -t ${IMG} . +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . +.PHONY: docker-push docker-push: ## Push docker image with the manager. - docker push ${IMG} + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name kubebuild-builder + $(CONTAINER_TOOL) buildx use kubebuild-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm kubebuild-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml ##@ Deployment -install: $(KUSTOMIZE) manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - - -uninstall: $(KUSTOMIZE) manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl delete -f - - -deploy: $(KUSTOMIZE) manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - +ifndef ignore-not-found + ignore-not-found = false +endif -undeploy: $(KUSTOMIZE) ## Undeploy controller from the K8s cluster specified in ~/.kube/config. - bash -c "./scripts/removeFinalizersFromCRDs.sh" - $(KUSTOMIZE) build config/default | kubectl delete -f - +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | $(KUBECTL) apply -f - -##@ Release +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - -# go-get-tool will 'go get' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-get-tool +.PHONY: deploy +deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + +.PHONY: undeploy +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize-$(KUSTOMIZE_VERSION) +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen-$(CONTROLLER_TOOLS_VERSION) +ENVTEST ?= $(LOCALBIN)/setup-envtest-$(ENVTEST_VERSION) +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint-$(GOLANGCI_LINT_VERSION) + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.4.1 +CONTROLLER_TOOLS_VERSION ?= v0.15.0 +ENVTEST_VERSION ?= release-0.18 +GOLANGCI_LINT_VERSION ?= v1.57.2 + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint,${GOLANGCI_LINT_VERSION}) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary (ideally with version) +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool @[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ -rm -rf $$TMP_DIR ;\ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv "$$(echo "$(1)" | sed "s/-$(3)$$//")" $(1) ;\ } endef diff --git a/internal/controller/admissionpolicy_controller_test.go b/internal/controller/admissionpolicy_controller_test.go index a3c8f244..ae8f9aa3 100644 --- a/internal/controller/admissionpolicy_controller_test.go +++ b/internal/controller/admissionpolicy_controller_test.go @@ -18,6 +18,7 @@ limitations under the License. package controller import ( + "context" "fmt" . "github.com/onsi/ginkgo/v2" //nolint:revive @@ -31,15 +32,11 @@ import ( "github.com/kubewarden/kubewarden-controller/internal/constants" ) -var _ = Describe("AdmissionPolicy controller", func() { - var policyNamespace string - var policyServerName string - // let's use this constant to avoid linter errors - const PolicyServerNamePrefix = "policy-server-" +var _ = Describe("AdmissionPolicy controller", Label("real-cluster"), func() { + ctx := context.Background() + policyNamespace := "admission-policy-controller-test" BeforeEach(func() { - policyNamespace = "admission-policy-controller-test" - policyServerName = newName("policy-server") Expect( k8sClient.Create(ctx, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ @@ -49,13 +46,16 @@ var _ = Describe("AdmissionPolicy controller", func() { ).To(haveSucceededOrAlreadyExisted()) }) - When("creating a validating AdmissionPolicy", func() { + When("creating a validating AdmissionPolicy", Ordered, func() { + var policyServerName string var policyName string var policy *policiesv1.AdmissionPolicy - BeforeEach(func() { + BeforeAll(func() { + policyServerName = newName("policy-server") + createPolicyServerAndWaitForItsService(ctx, policyServerFactory(policyServerName)) + policyName = newName("validating-policy") - createPolicyServerAndWaitForItsService(policyServerFactory(policyServerName)) policy = admissionPolicyFactory(policyName, policyNamespace, policyServerName, false) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) }) @@ -63,14 +63,14 @@ var _ = Describe("AdmissionPolicy controller", func() { It("should set the AdminissionPolicy to active sometime after its creation", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) @@ -78,7 +78,7 @@ var _ = Describe("AdmissionPolicy controller", func() { It("should create the ValidatingWebhookConfiguration", func() { Eventually(func() error { - validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -88,9 +88,9 @@ var _ = Describe("AdmissionPolicy controller", func() { Expect(validatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNameAnnotationKey]).To(Equal(policyName)) Expect(validatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNamespaceAnnotationKey]).To(Equal(policyNamespace)) Expect(validatingWebhookConfiguration.Webhooks).To(HaveLen(1)) - Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal(PolicyServerNamePrefix + policyServerName)) + Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal("policy-server-" + policyServerName)) - caSecret, err := getTestCASecret() + caSecret, err := getTestCASecret(ctx) Expect(err).ToNot(HaveOccurred()) Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.CABundle).To(Equal(caSecret.Data[constants.PolicyServerCARootPemName])) @@ -98,12 +98,12 @@ var _ = Describe("AdmissionPolicy controller", func() { }, timeout, pollInterval).Should(Succeed()) }) - It("should be reconcile the ValidationWebhookConfiguration to the original state after some change", func() { + It("should reconcile the ValidationWebhookConfiguration to the original state after some change", func() { var originalValidatingWebhookConfiguration *admissionregistrationv1.ValidatingWebhookConfiguration var validatingWebhookConfiguration *admissionregistrationv1.ValidatingWebhookConfiguration Eventually(func() error { var err error - validatingWebhookConfiguration, err = getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + validatingWebhookConfiguration, err = getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -124,7 +124,7 @@ var _ = Describe("AdmissionPolicy controller", func() { By("reconciling the ValidatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { - return getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + return getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalValidatingWebhookConfiguration.Labels)), @@ -135,7 +135,7 @@ var _ = Describe("AdmissionPolicy controller", func() { // simulate uninitialized labels and annotation maps (behavior of Kubewarden <= 1.9.0), or user change By("setting the ValidatingWebhookConfiguration labels and annotation to nil") - validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) Expect(err).ToNot(HaveOccurred()) originalValidatingWebhookConfiguration = validatingWebhookConfiguration.DeepCopy() validatingWebhookConfiguration.Labels = nil @@ -146,7 +146,7 @@ var _ = Describe("AdmissionPolicy controller", func() { By("reconciling the ValidatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { - return getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + return getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalValidatingWebhookConfiguration.Labels)), @@ -157,13 +157,16 @@ var _ = Describe("AdmissionPolicy controller", func() { }) }) - When("creating a mutating AdmissionPolicy", func() { + When("creating a mutating AdmissionPolicy", Ordered, func() { + var policyServerName string var policyName string var policy *policiesv1.AdmissionPolicy - BeforeEach(func() { + BeforeAll(func() { + policyServerName = newName("policy-server") + createPolicyServerAndWaitForItsService(ctx, policyServerFactory(policyServerName)) + policyName = newName("mutating-policy") - createPolicyServerAndWaitForItsService(policyServerFactory(policyServerName)) policy = admissionPolicyFactory(policyName, policyNamespace, policyServerName, true) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) }) @@ -171,14 +174,14 @@ var _ = Describe("AdmissionPolicy controller", func() { It("should set the AdmissionPolicy to active", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) @@ -186,7 +189,7 @@ var _ = Describe("AdmissionPolicy controller", func() { It("should create the MutatingWebhookConfiguration", func() { Eventually(func() error { - mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(policy.GetUniqueName()) + mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -196,9 +199,9 @@ var _ = Describe("AdmissionPolicy controller", func() { Expect(mutatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNameAnnotationKey]).To(Equal(policyName)) Expect(mutatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNamespaceAnnotationKey]).To(Equal(policyNamespace)) Expect(mutatingWebhookConfiguration.Webhooks).To(HaveLen(1)) - Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal(PolicyServerNamePrefix + policyServerName)) + Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal("policy-server-" + policyServerName)) - caSecret, err := getTestCASecret() + caSecret, err := getTestCASecret(ctx) Expect(err).ToNot(HaveOccurred()) Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.CABundle).To(Equal(caSecret.Data[constants.PolicyServerCARootPemName])) @@ -211,7 +214,7 @@ var _ = Describe("AdmissionPolicy controller", func() { var mutatingWebhookConfiguration *admissionregistrationv1.MutatingWebhookConfiguration Eventually(func() error { var err error - mutatingWebhookConfiguration, err = getTestMutatingWebhookConfiguration(policy.GetUniqueName()) + mutatingWebhookConfiguration, err = getTestMutatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -232,7 +235,7 @@ var _ = Describe("AdmissionPolicy controller", func() { By("reconciling the MutatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.MutatingWebhookConfiguration, error) { - return getTestMutatingWebhookConfiguration(fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) + return getTestMutatingWebhookConfiguration(ctx, fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalMutatingWebhookConfiguration.Labels)), @@ -243,7 +246,7 @@ var _ = Describe("AdmissionPolicy controller", func() { // simulate unitialized labels and annotation maps (behaviour of Kubewarden <= 1.9.0), or user change By("by setting the MutatingWebhookConfiguration labels and annotation to nil") - mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) + mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(ctx, fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) Expect(err).ToNot(HaveOccurred()) originalMutatingWebhookConfiguration = mutatingWebhookConfiguration.DeepCopy() mutatingWebhookConfiguration.Labels = nil @@ -254,7 +257,7 @@ var _ = Describe("AdmissionPolicy controller", func() { By("reconciling the MutatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.MutatingWebhookConfiguration, error) { - return getTestMutatingWebhookConfiguration(fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) + return getTestMutatingWebhookConfiguration(ctx, fmt.Sprintf("namespaced-%s-%s", policyNamespace, policyName)) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalMutatingWebhookConfiguration.Labels)), @@ -272,17 +275,17 @@ var _ = Describe("AdmissionPolicy controller", func() { ).To(haveSucceededOrAlreadyExisted()) Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusUnscheduled)), ) }) - When("creating an AdmissionPolicy with a PolicyServer assigned but not running yet", func() { - var policyName string + When("creating an AdmissionPolicy with a PolicyServer assigned but not running yet", Ordered, func() { + policyName := newName("scheduled-policy") + policyServerName := newName("policy-server") - BeforeEach(func() { - policyName = newName("scheduled-policy") + BeforeAll(func() { Expect( k8sClient.Create(ctx, admissionPolicyFactory(policyName, policyNamespace, policyServerName, false)), ).To(haveSucceededOrAlreadyExisted()) @@ -290,7 +293,7 @@ var _ = Describe("AdmissionPolicy controller", func() { It("should set the policy status to scheduled", func() { Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusScheduled)), ) @@ -304,14 +307,14 @@ var _ = Describe("AdmissionPolicy controller", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.AdmissionPolicy, error) { - return getTestAdmissionPolicy(policyNamespace, policyName) + return getTestAdmissionPolicy(ctx, policyNamespace, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) diff --git a/internal/controller/clusteradmissionpolicy_controller_test.go b/internal/controller/clusteradmissionpolicy_controller_test.go index 7d7849ef..0e01b27a 100644 --- a/internal/controller/clusteradmissionpolicy_controller_test.go +++ b/internal/controller/clusteradmissionpolicy_controller_test.go @@ -18,6 +18,8 @@ limitations under the License. package controller import ( + "context" + . "github.com/onsi/ginkgo/v2" //nolint:revive . "github.com/onsi/gomega" //nolint:revive @@ -27,23 +29,19 @@ import ( "github.com/kubewarden/kubewarden-controller/internal/constants" ) -var _ = Describe("ClusterAdmissionPolicy controller", func() { - var policyServerName string - - // let's use this constant to avoid linter errors - const PolicyServerNamePrefix = "policy-server-" - - BeforeEach(func() { - policyServerName = newName("policy-server") - }) +var _ = Describe("ClusterAdmissionPolicy controller", Label("real-cluster"), func() { + ctx := context.Background() - When("creating a validating ClusterAdmissionPolicy", func() { + When("creating a validating ClusterAdmissionPolicy", Ordered, func() { + var policyServerName string var policyName string var policy *policiesv1.ClusterAdmissionPolicy - BeforeEach(func() { + BeforeAll(func() { + policyServerName = newName("policy-server") + createPolicyServerAndWaitForItsService(ctx, policyServerFactory(policyServerName)) + policyName = newName("validating-policy") - createPolicyServerAndWaitForItsService(policyServerFactory(policyServerName)) policy = clusterAdmissionPolicyFactory(policyName, policyServerName, false) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) }) @@ -51,14 +49,14 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { It("should set the ClusterAdmissionPolicy to active", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) @@ -66,7 +64,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { It("should create the ValidatingWebhookConfiguration", func() { Eventually(func() error { - validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + validatingWebhookConfiguration, err := getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -76,9 +74,9 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { Expect(validatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNameAnnotationKey]).To(Equal(policyName)) Expect(validatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNamespaceAnnotationKey]).To(BeEmpty()) Expect(validatingWebhookConfiguration.Webhooks).To(HaveLen(1)) - Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal(PolicyServerNamePrefix + policyServerName)) + Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal("policy-server-" + policyServerName)) - caSecret, err := getTestCASecret() + caSecret, err := getTestCASecret(ctx) Expect(err).ToNot(HaveOccurred()) Expect(validatingWebhookConfiguration.Webhooks[0].ClientConfig.CABundle).To(Equal(caSecret.Data[constants.PolicyServerCARootPemName])) @@ -92,7 +90,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { var validatingWebhookConfiguration *admissionregistrationv1.ValidatingWebhookConfiguration Eventually(func() error { var err error - validatingWebhookConfiguration, err = getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + validatingWebhookConfiguration, err = getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -112,7 +110,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { By("reconciling the ValidatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { - return getTestValidatingWebhookConfiguration(policy.GetUniqueName()) + return getTestValidatingWebhookConfiguration(ctx, policy.GetUniqueName()) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalValidatingWebhookConfiguration.Labels)), @@ -123,13 +121,16 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { }) }) - When("creating a mutating ClusterAdmissionPolicy", func() { + When("creating a mutating ClusterAdmissionPolicy", Ordered, func() { + var policyServerName string var policyName string var policy *policiesv1.ClusterAdmissionPolicy - BeforeEach(func() { + BeforeAll(func() { + policyServerName = newName("policy-server") + createPolicyServerAndWaitForItsService(ctx, policyServerFactory(policyServerName)) + policyName = newName("mutating-policy") - createPolicyServerAndWaitForItsService(policyServerFactory(policyServerName)) policy = clusterAdmissionPolicyFactory(policyName, policyServerName, true) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) }) @@ -137,14 +138,14 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { It("should set the AdmissionPolicy to active", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) @@ -152,7 +153,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { It("should create the MutatingWebhookConfiguration", func() { Eventually(func() error { - mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(policy.GetUniqueName()) + mutatingWebhookConfiguration, err := getTestMutatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -161,9 +162,9 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { Expect(mutatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNameAnnotationKey]).To(Equal(policyName)) Expect(mutatingWebhookConfiguration.Annotations[constants.WebhookConfigurationPolicyNamespaceAnnotationKey]).To(BeEmpty()) Expect(mutatingWebhookConfiguration.Webhooks).To(HaveLen(1)) - Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal(PolicyServerNamePrefix + policyServerName)) + Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.Service.Name).To(Equal("policy-server-" + policyServerName)) - caSecret, err := getTestCASecret() + caSecret, err := getTestCASecret(ctx) Expect(err).ToNot(HaveOccurred()) Expect(mutatingWebhookConfiguration.Webhooks[0].ClientConfig.CABundle).To(Equal(caSecret.Data[constants.PolicyServerCARootPemName])) @@ -176,7 +177,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { var mutatingWebhookConfiguration *admissionregistrationv1.MutatingWebhookConfiguration Eventually(func() error { var err error - mutatingWebhookConfiguration, err = getTestMutatingWebhookConfiguration(policy.GetUniqueName()) + mutatingWebhookConfiguration, err = getTestMutatingWebhookConfiguration(ctx, policy.GetUniqueName()) if err != nil { return err } @@ -197,7 +198,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { By("reconciling the MutatingWebhookConfiguration to its original state") Eventually(func() (*admissionregistrationv1.MutatingWebhookConfiguration, error) { - return getTestMutatingWebhookConfiguration("clusterwide-" + policyName) + return getTestMutatingWebhookConfiguration(ctx, "clusterwide-"+policyName) }, timeout, pollInterval).Should( And( HaveField("Labels", Equal(originalMutatingWebhookConfiguration.Labels)), @@ -215,17 +216,17 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { ).To(haveSucceededOrAlreadyExisted()) Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusUnscheduled)), ) }) - When("creating a ClusterAdmissionPolicy with a PolicyServer assigned but not running yet", func() { - var policyName string + When("creating a ClusterAdmissionPolicy with a PolicyServer assigned but not running yet", Ordered, func() { + policyName := newName("scheduled-policy") + policyServerName := newName("policy-server") - BeforeEach(func() { - policyName = newName("scheduled-policy") + BeforeAll(func() { Expect( k8sClient.Create(ctx, clusterAdmissionPolicyFactory(policyName, policyServerName, false)), ).To(haveSucceededOrAlreadyExisted()) @@ -237,7 +238,7 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { ).To(haveSucceededOrAlreadyExisted()) Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusScheduled)), ) @@ -251,14 +252,14 @@ var _ = Describe("ClusterAdmissionPolicy controller", func() { By("changing the policy status to pending") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusPending)), ) By("changing the policy status to active") Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) + return getTestClusterAdmissionPolicy(ctx, policyName) }, timeout, pollInterval).Should( HaveField("Status.PolicyStatus", Equal(policiesv1.PolicyStatusActive)), ) diff --git a/internal/controller/policyserver_controller_test.go b/internal/controller/policyserver_controller_test.go index e5136891..7740bcb0 100644 --- a/internal/controller/policyserver_controller_test.go +++ b/internal/controller/policyserver_controller_test.go @@ -14,9 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:godox package controller import ( + "context" "encoding/json" "errors" "fmt" @@ -37,218 +39,13 @@ import ( ) var _ = Describe("PolicyServer controller", func() { + ctx := context.Background() var policyServerName string BeforeEach(func() { policyServerName = newName("policy-server") }) - When("deleting a PolicyServer", func() { - BeforeEach(func() { - createPolicyServerAndWaitForItsService(policyServerFactory(policyServerName)) - }) - - Context("with no assigned policies", func() { - It("should get its finalizer removed", func() { - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() (*policiesv1.PolicyServer, error) { - return getTestPolicyServer(policyServerName) - }, timeout, pollInterval).ShouldNot( - HaveField("Finalizers", ContainElement(constants.KubewardenFinalizer)), - ) - }) - - It("should get its old not domain-qualified finalizer removed", func() { - Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) - if err != nil { - return err - } - controllerutil.AddFinalizer(policyServer, constants.KubewardenFinalizerPre114) - return k8sClient.Update(ctx, policyServer) - }, timeout, pollInterval).Should(Succeed()) - - Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) - if err != nil { - return err - } - if controllerutil.ContainsFinalizer(policyServer, constants.KubewardenFinalizerPre114) { - return nil - } - return errors.New("finalizer not found") - }, timeout, pollInterval).Should(Succeed()) - - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() (*policiesv1.PolicyServer, error) { - return getTestPolicyServer(policyServerName) - }, timeout, pollInterval).ShouldNot( - HaveField("Finalizers", ContainElement(constants.KubewardenFinalizerPre114)), - ) - }) - - It("policy server resources should be gone after it being deleted", func() { - // It's necessary remove the test finalizer to make the - // Kubernetes garbage collector to remove the resources - Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) - if err != nil { - return err - } - controllerutil.RemoveFinalizer(policyServer, IntegrationTestsFinalizer) - return reconciler.Client.Update(ctx, policyServer) - }).Should(Succeed()) - - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() error { - _, err := getTestPolicyServer(policyServerName) - return err - }, timeout, pollInterval).Should(notFound()) - - Eventually(func() error { - _, err := getTestPolicyServerService(policyServerName) - return err - }, timeout, pollInterval).Should(notFound()) - - Eventually(func() error { - _, err := getTestPolicyServerSecret(policyServerName) - return err - }, timeout, pollInterval).Should(notFound()) - - Eventually(func() error { - _, err := getTestPolicyServerDeployment(policyServerName) - return err - }, timeout, pollInterval).Should(notFound()) - - Eventually(func() error { - _, err := getTestPolicyServerConfigMap(policyServerName) - return err - }, timeout, pollInterval).Should(notFound()) - }) - }) - - Context("with assigned policies", func() { - var policyName string - - BeforeEach(func() { - policyName = newName("policy") - Expect( - k8sClient.Create(ctx, clusterAdmissionPolicyFactory(policyName, policyServerName, false)), - ).To(Succeed()) - Eventually(func() error { - _, err := getTestClusterAdmissionPolicy(policyName) - return err - }, timeout, pollInterval).Should(Succeed()) - Expect( - getTestPolicyServerService(policyServerName), - ).To( - HaveField("DeletionTimestamp", BeNil()), - ) - }) - - It("should delete assigned policies", func() { - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) - }, timeout, pollInterval).ShouldNot( - HaveField("DeletionTimestamp", BeNil()), - ) - }) - - It("should get its old not domain-qualidied finalizer removed from policies", func() { - Eventually(func() error { - policy, err := getTestClusterAdmissionPolicy(policyName) - if err != nil { - return err - } - controllerutil.AddFinalizer(policy, constants.KubewardenFinalizerPre114) - return k8sClient.Update(ctx, policy) - }, timeout, pollInterval).Should(Succeed()) - Eventually(func() error { - policy, err := getTestClusterAdmissionPolicy(policyName) - if err != nil { - return err - } - if controllerutil.ContainsFinalizer(policy, constants.KubewardenFinalizerPre114) { - return nil - } - return errors.New("old finalizer not found") - }, timeout, pollInterval).Should(Succeed()) - - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) - }, timeout, pollInterval).Should(And( - HaveField("DeletionTimestamp", Not(BeNil())), - HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizer))), - HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizerPre114))), - HaveField("Finalizers", ContainElement(IntegrationTestsFinalizer)), - )) - }) - - It("should not delete its managed resources until all the scheduled policies are gone", func() { - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { - return getTestClusterAdmissionPolicy(policyName) - }).Should(And( - HaveField("DeletionTimestamp", Not(BeNil())), - HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizer))), - HaveField("Finalizers", ContainElement(IntegrationTestsFinalizer)), - )) - - Eventually(func() error { - _, err := getTestPolicyServerService(policyServerName) - return err - }).Should(Succeed()) - }) - - It(fmt.Sprintf("should get its %q finalizer removed", constants.KubewardenFinalizer), func() { - Eventually(func() error { - policy, err := getTestClusterAdmissionPolicy(policyName) - if err != nil { - return err - } - controllerutil.RemoveFinalizer(policy, IntegrationTestsFinalizer) - return reconciler.Client.Update(ctx, policy) - }).Should(Succeed()) - - Expect( - k8sClient.Delete(ctx, policyServerFactory(policyServerName)), - ).To(Succeed()) - - // wait for the reconciliation loop of the ClusterAdmissionPolicy to remove the resource - Eventually(func() error { - _, err := getTestClusterAdmissionPolicy(policyName) - return err - }, timeout, pollInterval).ShouldNot(Succeed()) - - Eventually(func() (*policiesv1.PolicyServer, error) { - return getTestPolicyServer(policyServerName) - }, timeout, pollInterval).ShouldNot( - HaveField("Finalizers", ContainElement(constants.KubewardenFinalizer)), - ) - }) - }) - }) - When("creating a PolicyServer", func() { It("should use the policy server tolerations configuration in the policy server deployment", func() { tolerationSeconds := int64(10) @@ -266,9 +63,11 @@ var _ = Describe("PolicyServer controller", func() { Effect: corev1.TaintEffectNoExecute, TolerationSeconds: &tolerationSeconds, }} - createPolicyServerAndWaitForItsService(policyServer) - deployment, err := getTestPolicyServerDeployment(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Expect(deployment.Spec.Template.Spec.Tolerations).To(MatchAllElements(func(element interface{}) string { toleration, _ := element.(corev1.Toleration) return toleration.Key @@ -309,9 +108,11 @@ var _ = Describe("PolicyServer controller", func() { }, }, } - createPolicyServerAndWaitForItsService(policyServer) - deployment, err := getTestPolicyServerDeployment(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Expect(deployment.Spec.Template.Spec.Affinity).To(PointTo(MatchFields(IgnoreExtras, Fields{ "NodeAffinity": PointTo(MatchFields(IgnoreExtras, Fields{ "RequiredDuringSchedulingIgnoredDuringExecution": PointTo(MatchFields(IgnoreExtras, Fields{ @@ -329,9 +130,11 @@ var _ = Describe("PolicyServer controller", func() { It("should create policy server deployment with some default configuration", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) - deployment, err := getTestPolicyServerDeployment(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + By("checking the deployment container security context") Expect(deployment.Spec.Template.Spec.Containers).Should(ContainElement(MatchFields(IgnoreExtras, Fields{ "SecurityContext": PointTo(MatchFields(IgnoreExtras, Fields{ @@ -351,6 +154,7 @@ var _ = Describe("PolicyServer controller", func() { "SeccompProfile": BeNil(), })), }))) + By("checking the deployment spec") Expect(deployment.Spec.Template.Spec).To(MatchFields(IgnoreExtras, Fields{ "Tolerations": BeEmpty(), @@ -390,9 +194,11 @@ var _ = Describe("PolicyServer controller", func() { RunAsNonRoot: &runAsNonRoot, }, } - createPolicyServerAndWaitForItsService(policyServer) - deployment, err := getTestPolicyServerDeployment(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Expect(deployment.Spec.Template.Spec.Containers).Should(ContainElement(MatchFields(IgnoreExtras, Fields{ "SecurityContext": PointTo(MatchFields(IgnoreExtras, Fields{ "RunAsNonRoot": PointTo(BeFalse()), @@ -408,6 +214,7 @@ var _ = Describe("PolicyServer controller", func() { "SeccompProfile": BeNil(), })), }))) + Expect(deployment.Spec.Template.Spec.SecurityContext).To(PointTo(MatchFields(IgnoreExtras, Fields{ "SELinuxOptions": BeNil(), "WindowsOptions": BeNil(), @@ -424,9 +231,11 @@ var _ = Describe("PolicyServer controller", func() { It("should create the policy server configmap empty if no policies are assigned ", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) - configmap, err := getTestPolicyServerConfigMap(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + configmap, err := getTestPolicyServerConfigMap(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Expect(configmap).To(PointTo(MatchFields(IgnoreExtras, Fields{ "Data": MatchAllKeys(Keys{ constants.PolicyServerConfigPoliciesEntry: Equal("{}"), @@ -437,7 +246,8 @@ var _ = Describe("PolicyServer controller", func() { It("should create the policy server configmap with the assigned policies", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + policyName := newName("policy") policy := clusterAdmissionPolicyFactory(policyName, policyServerName, false) Expect(k8sClient.Create(ctx, policy)).To(Succeed()) @@ -458,7 +268,7 @@ var _ = Describe("PolicyServer controller", func() { Expect(err).ToNot(HaveOccurred()) Eventually(func() *corev1.ConfigMap { - configMap, _ := getTestPolicyServerConfigMap(policyServerName) + configMap, _ := getTestPolicyServerConfigMap(ctx, policyServerName) return configMap }, timeout, pollInterval).Should(PointTo(MatchFields(IgnoreExtras, Fields{ "Data": MatchAllKeys(Keys{ @@ -474,7 +284,8 @@ var _ = Describe("PolicyServer controller", func() { policyServer.Spec.SourceAuthorities = map[string][]string{ "myprivateregistry:5000": {"cert1", "cert2"}, } - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + sourceAuthoriries := map[string][]map[string]string{} for uri, certificates := range policyServer.Spec.SourceAuthorities { certs := []map[string]string{} @@ -493,10 +304,11 @@ var _ = Describe("PolicyServer controller", func() { Expect(err).ToNot(HaveOccurred()) Eventually(func() error { - _, err := getTestPolicyServerConfigMap(policyServerName) + _, err := getTestPolicyServerConfigMap(ctx, policyServerName) return err }, timeout, pollInterval).Should(Succeed()) - configMap, err := getTestPolicyServerConfigMap(policyServerName) + + configMap, err := getTestPolicyServerConfigMap(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) Expect(configMap).To(PointTo(MatchFields(IgnoreExtras, Fields{ "Data": MatchAllKeys(Keys{ @@ -510,10 +322,10 @@ var _ = Describe("PolicyServer controller", func() { policyServer := policyServerFactory(policyServerName) minAvailable := intstr.FromInt(2) policyServer.Spec.MinAvailable = &minAvailable - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) Eventually(func() *k8spoliciesv1.PodDisruptionBudget { - pdb, _ := getPolicyServerPodDisruptionBudget(policyServerName) + pdb, _ := getPolicyServerPodDisruptionBudget(ctx, policyServerName) return pdb }, timeout, pollInterval).Should(policyServerPodDisruptionBudgetMatcher(policyServer, &minAvailable, nil)) }) @@ -522,74 +334,63 @@ var _ = Describe("PolicyServer controller", func() { policyServer := policyServerFactory(policyServerName) maxUnavailable := intstr.FromInt(2) policyServer.Spec.MaxUnavailable = &maxUnavailable - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) Eventually(func() *k8spoliciesv1.PodDisruptionBudget { - pdb, _ := getPolicyServerPodDisruptionBudget(policyServerName) + pdb, _ := getPolicyServerPodDisruptionBudget(ctx, policyServerName) return pdb }, timeout, pollInterval).Should(policyServerPodDisruptionBudgetMatcher(policyServer, nil, &maxUnavailable)) }) It("should not create PodDisruptionBudget when policy server has no PDB configuration", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + Consistently(func() error { - _, err := getPolicyServerPodDisruptionBudget(policyServerName) + _, err := getPolicyServerPodDisruptionBudget(ctx, policyServerName) return err }, consistencyTimeout, pollInterval).ShouldNot(Succeed()) }) - It("should create the PolicyServer pod with the limits and the requests", func() { + It("should create the PolicyServer deployment with the limits and the requests", func() { policyServer := policyServerFactory(policyServerName) policyServer.Spec.Limits = corev1.ResourceList{ "cpu": resource.MustParse("100m"), "memory": resource.MustParse("1Gi"), } - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + By("creating a deployment with limits and requests set") Eventually(func() error { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return err } Expect(deployment.Spec.Template.Spec.Containers[0].Resources.Limits).To(Equal(policyServer.Spec.Limits)) return nil }, timeout, pollInterval).Should(Succeed()) - - By("creating a pod with limit and request set") - Eventually(func() error { - pod, err := getTestPolicyServerPod(policyServerName) - if err != nil { - return err - } - - Expect(pod.Spec.Containers[0].Resources.Limits).To(Equal(policyServer.Spec.Limits)) - - By("setting the requests to the same value as the limits") - Expect(pod.Spec.Containers[0].Resources.Requests).To(Equal(policyServer.Spec.Limits)) - - return nil - }, timeout, pollInterval).Should(Succeed()) }) It("should create deployment with owner reference", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + Eventually(func() error { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return err } - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } Expect(deployment.OwnerReferences).To(ContainElement( MatchFields(IgnoreExtras, Fields{ - "UID": Equal(policyServer.GetUID()), - "Name": Equal(policyServer.GetName()), - "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), - "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), + "UID": Equal(policyServer.GetUID()), + "Name": Equal(policyServer.GetName()), + // FIXME: for some reason GroupVersionKind is not set + // "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), + // "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), }), )) return nil @@ -598,22 +399,24 @@ var _ = Describe("PolicyServer controller", func() { It("should create configmap with owner reference", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + Eventually(func() error { - configmap, err := getTestPolicyServerConfigMap(policyServerName) + configmap, err := getTestPolicyServerConfigMap(ctx, policyServerName) if err != nil { return err } - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } Expect(configmap.OwnerReferences).To(ContainElement( MatchFields(IgnoreExtras, Fields{ - "UID": Equal(policyServer.GetUID()), - "Name": Equal(policyServer.GetName()), - "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), - "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), + "UID": Equal(policyServer.GetUID()), + "Name": Equal(policyServer.GetName()), + // FIXME: for some reason GroupVersionKind is not set + // "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), + // "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), }), )) return nil @@ -622,22 +425,24 @@ var _ = Describe("PolicyServer controller", func() { It("should create service with owner reference", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) + Eventually(func() error { - service, err := getTestPolicyServerService(policyServerName) + service, err := getTestPolicyServerService(ctx, policyServerName) if err != nil { return err } - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } Expect(service.OwnerReferences).To(ContainElement( MatchFields(IgnoreExtras, Fields{ - "UID": Equal(policyServer.GetUID()), - "Name": Equal(policyServer.GetName()), - "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), - "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), + "UID": Equal(policyServer.GetUID()), + "Name": Equal(policyServer.GetName()), + // FIXME: for some reason GroupVersionKind is not set + // "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), + // "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), }), )) return nil @@ -646,10 +451,10 @@ var _ = Describe("PolicyServer controller", func() { It("should create the policy server secrets", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) Eventually(func() error { - secret, err := getTestPolicyServerCASecret() + secret, err := getTestPolicyServerCASecret(ctx) if err != nil { return err } @@ -663,11 +468,11 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() error { - secret, err := getTestPolicyServerSecret(policyServerName) + secret, err := getTestPolicyServerSecret(ctx, policyServerName) if err != nil { return err } - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -679,10 +484,11 @@ var _ = Describe("PolicyServer controller", func() { By("setting the secret owner reference") Expect(secret.OwnerReferences).To(ContainElement( MatchFields(IgnoreExtras, Fields{ - "UID": Equal(policyServer.GetUID()), - "Name": Equal(policyServer.GetName()), - "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), - "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), + "UID": Equal(policyServer.GetUID()), + "Name": Equal(policyServer.GetName()), + // FIXME: for some reason GroupVersionKind is not set + // "Kind": Equal(policyServer.GetObjectKind().GroupVersionKind().Kind), + // "APIVersion": Equal(policyServer.GetObjectKind().GroupVersionKind().GroupVersion().String()), }), )) return nil @@ -691,11 +497,13 @@ var _ = Describe("PolicyServer controller", func() { It("should set the configMap version as a deployment annotation", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) - configmap, err := getTestPolicyServerConfigMap(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + configmap, err := getTestPolicyServerConfigMap(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Eventually(func() error { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return err } @@ -711,11 +519,13 @@ var _ = Describe("PolicyServer controller", func() { It("should update the configMap version after adding a policy", func() { policyServer := policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) - initalConfigMap, err := getTestPolicyServerConfigMap(policyServerName) + createPolicyServerAndWaitForItsService(ctx, policyServer) + + initalConfigMap, err := getTestPolicyServerConfigMap(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + Eventually(func() error { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return err } @@ -733,14 +543,14 @@ var _ = Describe("PolicyServer controller", func() { Expect(k8sClient.Create(ctx, policy)).To(Succeed()) Eventually(func() error { - configmap, err := getTestPolicyServerConfigMap(policyServerName) + configmap, err := getTestPolicyServerConfigMap(ctx, policyServerName) if err != nil { return err } if configmap.GetResourceVersion() == initalConfigMap.GetResourceVersion() { return errors.New("configmap version did not change") } - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return err } @@ -760,17 +570,17 @@ var _ = Describe("PolicyServer controller", func() { BeforeEach(func() { policyServer = policyServerFactory(policyServerName) - createPolicyServerAndWaitForItsService(policyServer) + createPolicyServerAndWaitForItsService(ctx, policyServer) }) It("should create a PDB if the policy server definition is updated with a PodDisruptionBudget configuration", func() { Consistently(func() error { - _, err := getPolicyServerPodDisruptionBudget(policyServerName) + _, err := getPolicyServerPodDisruptionBudget(ctx, policyServerName) return err }, consistencyTimeout, pollInterval).ShouldNot(Succeed()) By("updating the PolicyServer with a MaxAvailable PDB configuration") - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) maxUnavailable := intstr.FromInt(2) policyServer.Spec.MaxUnavailable = &maxUnavailable @@ -779,17 +589,17 @@ var _ = Describe("PolicyServer controller", func() { By("creating a PodDisruptionBudget with a MaxUnavailable configuration") Eventually(func() *k8spoliciesv1.PodDisruptionBudget { - pdb, _ := getPolicyServerPodDisruptionBudget(policyServerName) + pdb, _ := getPolicyServerPodDisruptionBudget(ctx, policyServerName) return pdb }, timeout, pollInterval).Should(policyServerPodDisruptionBudgetMatcher(policyServer, nil, &maxUnavailable)) }) It("should update deployment when policy server image change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) oldImage := deployment.Spec.Template.Spec.Containers[0].Image Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -798,7 +608,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() string { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return "" } @@ -807,11 +617,11 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server replica size change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) oldReplica := deployment.Spec.Replicas Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -820,7 +630,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() int32 { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return 0 } @@ -829,11 +639,11 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server service account change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) oldServiceAccount := deployment.Spec.Template.Spec.ServiceAccountName Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -842,7 +652,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() string { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return "" } @@ -851,12 +661,12 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server security context change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) oldSecurityContext := deployment.Spec.Template.Spec.SecurityContext newUser := int64(1000) Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -867,7 +677,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() *corev1.PodSecurityContext { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil } @@ -878,7 +688,7 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server affinity configuration change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) oldAffinity := deployment.Spec.Template.Spec.Affinity newAffinity := corev1.Affinity{ @@ -899,7 +709,7 @@ var _ = Describe("PolicyServer controller", func() { }, } Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -908,7 +718,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() *corev1.Affinity { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil } @@ -917,14 +727,16 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server annotations change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + oldAnnotations := deployment.Spec.Template.Annotations newAnnotations := map[string]string{ "new-annotation": "new-value", } + Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -933,7 +745,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() map[string]string { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil } @@ -942,15 +754,17 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server resources limits change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + oldContainers := deployment.Spec.Template.Spec.Containers newResourceLimits := corev1.ResourceList{ "cpu": resource.MustParse("100m"), "memory": resource.MustParse("1Gi"), } + Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -959,7 +773,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() []corev1.Container { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil } @@ -972,15 +786,17 @@ var _ = Describe("PolicyServer controller", func() { }) It("should update deployment when policy server environment variables change", func() { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) Expect(err).ToNot(HaveOccurred()) + oldContainers := deployment.Spec.Template.Spec.Containers newEnvironmentVariable := corev1.EnvVar{ Name: "NEW_ENV", Value: "new-value", } + Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -989,7 +805,7 @@ var _ = Describe("PolicyServer controller", func() { }).Should(Succeed()) Eventually(func() []corev1.Container { - deployment, err := getTestPolicyServerDeployment(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil } @@ -1005,8 +821,9 @@ var _ = Describe("PolicyServer controller", func() { "cpu": resource.MustParse("50m"), "memory": resource.MustParse("500Mi"), } + Eventually(func() error { - policyServer, err := getTestPolicyServer(policyServerName) + policyServer, err := getTestPolicyServer(ctx, policyServerName) if err != nil { return err } @@ -1014,13 +831,13 @@ var _ = Describe("PolicyServer controller", func() { return k8sClient.Update(ctx, policyServer) }).Should(Succeed()) - By("updating the pod with the new requests") + By("updating the deployment with the new requests") Eventually(func() (*corev1.Container, error) { - pod, err := getTestPolicyServerPod(policyServerName) + deployment, err := getTestPolicyServerDeployment(ctx, policyServerName) if err != nil { return nil, err } - return &pod.Spec.Containers[0], nil + return &deployment.Spec.Template.Spec.Containers[0], nil }, timeout, pollInterval).Should( And( HaveField("Resources.Requests", Equal(updatedRequestsResources)), @@ -1029,4 +846,168 @@ var _ = Describe("PolicyServer controller", func() { ) }) }) + + When("deleting a PolicyServer", func() { + BeforeEach(func() { + createPolicyServerAndWaitForItsService(ctx, policyServerFactory(policyServerName)) + }) + + Context("with no assigned policies", func() { + It("should get its finalizer removed", func() { + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + Eventually(func() (*policiesv1.PolicyServer, error) { + return getTestPolicyServer(ctx, policyServerName) + }, timeout, pollInterval).ShouldNot( + HaveField("Finalizers", ContainElement(constants.KubewardenFinalizer)), + ) + }) + + It("should get its old not domain-qualified finalizer removed", func() { + Eventually(func() error { + policyServer, err := getTestPolicyServer(ctx, policyServerName) + if err != nil { + return err + } + controllerutil.AddFinalizer(policyServer, constants.KubewardenFinalizerPre114) + return k8sClient.Update(ctx, policyServer) + }, timeout, pollInterval).Should(Succeed()) + + Eventually(func() error { + policyServer, err := getTestPolicyServer(ctx, policyServerName) + if err != nil { + return err + } + if controllerutil.ContainsFinalizer(policyServer, constants.KubewardenFinalizerPre114) { + return nil + } + return errors.New("finalizer not found") + }, timeout, pollInterval).Should(Succeed()) + + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + Eventually(func() (*policiesv1.PolicyServer, error) { + return getTestPolicyServer(ctx, policyServerName) + }, timeout, pollInterval).ShouldNot( + HaveField("Finalizers", ContainElement(constants.KubewardenFinalizerPre114)), + ) + }) + }) + + Context("with assigned policies", func() { + var policyName string + + BeforeEach(func() { + policyName = newName("policy") + Expect( + k8sClient.Create(ctx, clusterAdmissionPolicyFactory(policyName, policyServerName, false)), + ).To(Succeed()) + Eventually(func() error { + _, err := getTestClusterAdmissionPolicy(ctx, policyName) + return err + }, timeout, pollInterval).Should(Succeed()) + Expect( + getTestPolicyServerService(ctx, policyServerName), + ).To( + HaveField("DeletionTimestamp", BeNil()), + ) + }) + + It("should delete assigned policies", func() { + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { + return getTestClusterAdmissionPolicy(ctx, policyName) + }, timeout, pollInterval).ShouldNot( + HaveField("DeletionTimestamp", BeNil()), + ) + }) + + It("should get its old not domain-qualidied finalizer removed from policies", func() { + Eventually(func() error { + policy, err := getTestClusterAdmissionPolicy(ctx, policyName) + if err != nil { + return err + } + controllerutil.AddFinalizer(policy, constants.KubewardenFinalizerPre114) + return k8sClient.Update(ctx, policy) + }, timeout, pollInterval).Should(Succeed()) + Eventually(func() error { + policy, err := getTestClusterAdmissionPolicy(ctx, policyName) + if err != nil { + return err + } + if controllerutil.ContainsFinalizer(policy, constants.KubewardenFinalizerPre114) { + return nil + } + return errors.New("old finalizer not found") + }, timeout, pollInterval).Should(Succeed()) + + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { + return getTestClusterAdmissionPolicy(ctx, policyName) + }, timeout, pollInterval).Should(And( + HaveField("DeletionTimestamp", Not(BeNil())), + HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizer))), + HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizerPre114))), + HaveField("Finalizers", ContainElement(integrationTestsFinalizer)), + )) + }) + + It("should not delete its managed resources until all the scheduled policies are gone", func() { + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + Eventually(func() (*policiesv1.ClusterAdmissionPolicy, error) { + return getTestClusterAdmissionPolicy(ctx, policyName) + }).Should(And( + HaveField("DeletionTimestamp", Not(BeNil())), + HaveField("Finalizers", Not(ContainElement(constants.KubewardenFinalizer))), + HaveField("Finalizers", ContainElement(integrationTestsFinalizer)), + )) + + Eventually(func() error { + _, err := getTestPolicyServerService(ctx, policyServerName) + return err + }).Should(Succeed()) + }) + + It(fmt.Sprintf("should get its %q finalizer removed", constants.KubewardenFinalizer), func() { + Eventually(func() error { + policy, err := getTestClusterAdmissionPolicy(ctx, policyName) + if err != nil { + return err + } + controllerutil.RemoveFinalizer(policy, integrationTestsFinalizer) + return k8sClient.Update(ctx, policy) + }).Should(Succeed()) + + Expect( + k8sClient.Delete(ctx, policyServerFactory(policyServerName)), + ).To(Succeed()) + + // wait for the reconciliation loop of the ClusterAdmissionPolicy to remove the resource + Eventually(func() error { + _, err := getTestClusterAdmissionPolicy(ctx, policyName) + return err + }, timeout, pollInterval).ShouldNot(Succeed()) + + Eventually(func() (*policiesv1.PolicyServer, error) { + return getTestPolicyServer(ctx, policyServerName) + }, timeout, pollInterval).ShouldNot( + HaveField("Finalizers", ContainElement(constants.KubewardenFinalizer)), + ) + }) + }) + }) }) diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 50ac48ee..dbc9c720 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -21,17 +21,18 @@ import ( "os" "path/filepath" "testing" + "time" . "github.com/onsi/ginkgo/v2" //nolint:revive . "github.com/onsi/gomega" //nolint:revive - "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/k3s" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" @@ -40,24 +41,20 @@ import ( policiesv1 "github.com/kubewarden/kubewarden-controller/api/policies/v1" "github.com/kubewarden/kubewarden-controller/internal/admission" + clientcmdapi "k8s.io/client-go/tools/clientcmd/api" //+kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var ( - cfg *rest.Config //nolint - k8sClient client.Client - testEnv *envtest.Environment - ctx context.Context - cancel context.CancelFunc - reconciler admission.Reconciler - k3sContainer *k3s.K3sContainer -) +var k8sClient client.Client const ( - DeploymentsNamespace = "kubewarden-integration-tests" + timeout = 180 * time.Second + pollInterval = 250 * time.Millisecond + consistencyTimeout = 5 * time.Second + deploymentsNamespace = "kubewarden-integration-tests" ) func TestAPIs(t *testing.T) { @@ -66,60 +63,65 @@ func TestAPIs(t *testing.T) { RunSpecs(t, "Controller Suite") } -var _ = BeforeSuite(func() { +var _ = SynchronizedBeforeSuite(func() []byte { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - ctx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - k3sTestcontainerVersion, ok := os.LookupEnv("K3S_TESTCONTAINER_VERSION") - if !ok { - k3sTestcontainerVersion = "latest" - } - - var err error - k3sContainer, err = k3s.RunContainer(ctx, - testcontainers.WithImage("docker.io/rancher/k3s:"+k3sTestcontainerVersion), - ) - Expect(err).NotTo(HaveOccurred()) + var ctx context.Context + ctx, cancel := context.WithCancel(context.TODO()) - kubeConfigYaml, err := k3sContainer.GetKubeConfig(ctx) - Expect(err).NotTo(HaveOccurred()) - - restcfg, err := clientcmd.RESTConfigFromKubeConfig(kubeConfigYaml) - Expect(err).NotTo(HaveOccurred()) - - trueValue := true - testEnv = &envtest.Environment{ + testEnv := &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, ErrorIfCRDPathMissing: true, - Config: restcfg, - UseExistingCluster: &trueValue, + } + // If the suite is being run with the "real-cluster" label, start a k3s container + // and use it as the test environment. + // See: https://github.com/onsi/ginkgo/issues/1108#issuecomment-1456637713 + if Label("real-cluster").MatchesLabelFilter(GinkgoLabelFilter()) { + By("starting the k3s test container") + k3sTestcontainerVersion, ok := os.LookupEnv("K3S_TESTCONTAINER_VERSION") + if !ok { + k3sTestcontainerVersion = "latest" + } + + k3sContainer, err := k3s.RunContainer(ctx, + testcontainers.WithImage("docker.io/rancher/k3s:"+k3sTestcontainerVersion), + ) + Expect(err).NotTo(HaveOccurred()) + + kubeConfigYaml, err := k3sContainer.GetKubeConfig(ctx) + Expect(err).NotTo(HaveOccurred()) + + kubeConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeConfigYaml) + Expect(err).NotTo(HaveOccurred()) + + By("configuting the test environment to use the k3s cluster") + testEnv.UseExistingCluster = ptr.To(true) + testEnv.Config = kubeConfig } - cfg, err := testEnv.Start() + restConfig, err := testEnv.Start() Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) + Expect(restConfig).NotTo(BeNil()) err = policiesv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + k8sClient, err = client.New(restConfig, client.Options{Scheme: scheme.Scheme}) Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) - k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + k8sManager, err := ctrl.NewManager(restConfig, ctrl.Options{ Scheme: scheme.Scheme, }) Expect(err).ToNot(HaveOccurred()) - reconciler = admission.Reconciler{ + reconciler := admission.Reconciler{ Client: k8sManager.GetClient(), APIReader: k8sManager.GetClient(), Log: ctrl.Log.WithName("reconciler"), - DeploymentsNamespace: DeploymentsNamespace, + DeploymentsNamespace: deploymentsNamespace, } err = (&AdmissionPolicyReconciler{ @@ -139,14 +141,14 @@ var _ = BeforeSuite(func() { err = (&PolicyServerReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), - DeploymentsNamespace: DeploymentsNamespace, + DeploymentsNamespace: deploymentsNamespace, }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) // Create the integration tests deployments namespace err = k8sClient.Create(ctx, &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ - Name: DeploymentsNamespace, + Name: deploymentsNamespace, }, }) Expect(err).NotTo(HaveOccurred()) @@ -156,19 +158,61 @@ var _ = BeforeSuite(func() { err = k8sManager.Start(ctx) Expect(err).ToNot(HaveOccurred(), "failed to run manager") }() -}) -var _ = AfterSuite(func() { - // When running the suite multiple times, canceling the context - // is not enough to stop the container in time. We need to terminate it. - // Otherwise, the next run may fail in the container initialization. - By("terminate the k3s container") - err := k3sContainer.Terminate(ctx) + DeferCleanup(func() { + By("tearing down the test environment") + cancel() + + err = testEnv.Stop() + Expect(err).ToNot(HaveOccurred(), "failed to tear down the test environment") + }) + + // Convert rest.Config in api.Config so we write it to bytes + config := clientcmdapi.Config{ + Clusters: map[string]*clientcmdapi.Cluster{ + "default": { + Server: restConfig.Host, + CertificateAuthorityData: restConfig.CAData, + }, + }, + AuthInfos: map[string]*clientcmdapi.AuthInfo{ + "default": { + ClientCertificateData: restConfig.CertData, + ClientKeyData: restConfig.KeyData, + Username: restConfig.Username, + Password: restConfig.Password, + }, + }, + Contexts: map[string]*clientcmdapi.Context{ + "default": { + Cluster: "default", + AuthInfo: "default", + }, + }, + CurrentContext: "default", + } + configBytes, err := clientcmd.Write(config) + Expect(err).NotTo(HaveOccurred()) + + return configBytes +}, func(configBytes []byte) { + By("connecting to the test environment") + if k8sClient != nil { + return + } + + config, err := clientcmd.Load(configBytes) + Expect(err).NotTo(HaveOccurred()) + restConfig, err := clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig() + restConfig.QPS = 1000.0 + restConfig.Burst = 2000.0 Expect(err).NotTo(HaveOccurred()) - cancel() - By("tearing down the test environment") + err = policiesv1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) - err = testEnv.Stop() + k8sClient, err = client.New(restConfig, client.Options{ + Scheme: scheme.Scheme, + }) Expect(err).NotTo(HaveOccurred()) }) diff --git a/internal/controller/utils_test.go b/internal/controller/utils_test.go index 9abcf784..66addae3 100644 --- a/internal/controller/utils_test.go +++ b/internal/controller/utils_test.go @@ -14,14 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ +//nolint:ireturn package controller import ( + "context" "errors" "fmt" "math/rand" "os" - "time" . "github.com/onsi/gomega" //nolint:revive . "github.com/onsi/gomega/gstruct" //nolint:revive @@ -40,12 +41,7 @@ import ( "github.com/kubewarden/kubewarden-controller/internal/constants" ) -const ( - timeout = 180 * time.Second - pollInterval = 250 * time.Millisecond - IntegrationTestsFinalizer = "integration-tests-safety-net-finalizer" - consistencyTimeout = 5 * time.Second -) +const integrationTestsFinalizer = "integration-tests-safety-net-finalizer" var ( templatePolicyServer = policiesv1.PolicyServer{ @@ -91,7 +87,7 @@ func policyServerFactory(name string) *policiesv1.PolicyServer { // By adding this finalizer automatically, we ensure that when // testing removal of finalizers on deleted objects, that they will // exist at all times - IntegrationTestsFinalizer, + integrationTestsFinalizer, } return policyServer } @@ -109,7 +105,7 @@ func admissionPolicyFactory(name, policyNamespace, policyServerName string, muta // By adding this finalizer automatically, we ensure that when // testing removal of finalizers on deleted objects, that they will // exist at all times - IntegrationTestsFinalizer, + integrationTestsFinalizer, } return admissionPolicy } @@ -126,75 +122,75 @@ func clusterAdmissionPolicyFactory(name, policyServerName string, mutating bool) // By adding this finalizer automatically, we ensure that when // testing removal of finalizers on deleted objects, that they will // exist at all times - IntegrationTestsFinalizer, + integrationTestsFinalizer, } return clusterAdmissionPolicy } -func getTestAdmissionPolicy(namespace, name string) (*policiesv1.AdmissionPolicy, error) { +func getTestAdmissionPolicy(ctx context.Context, namespace, name string) (*policiesv1.AdmissionPolicy, error) { admissionPolicy := policiesv1.AdmissionPolicy{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &admissionPolicy); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, &admissionPolicy); err != nil { return nil, errors.Join(errors.New("could not find AdmissionPolicy"), err) } return &admissionPolicy, nil } -func getTestClusterAdmissionPolicy(name string) (*policiesv1.ClusterAdmissionPolicy, error) { +func getTestClusterAdmissionPolicy(ctx context.Context, name string) (*policiesv1.ClusterAdmissionPolicy, error) { clusterAdmissionPolicy := policiesv1.ClusterAdmissionPolicy{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: name}, &clusterAdmissionPolicy); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, &clusterAdmissionPolicy); err != nil { return nil, errors.Join(errors.New("could not find ClusterAdmissionPolicy"), err) } return &clusterAdmissionPolicy, nil } -func getTestPolicyServer(name string) (*policiesv1.PolicyServer, error) { +func getTestPolicyServer(ctx context.Context, name string) (*policiesv1.PolicyServer, error) { policyServer := policiesv1.PolicyServer{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: name}, &policyServer); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, &policyServer); err != nil { return nil, errors.Join(errors.New("could not find PolicyServer"), err) } return &policyServer, nil } -func getTestPolicyServerService(policyServerName string) (*corev1.Service, error) { +func getTestPolicyServerService(ctx context.Context, policyServerName string) (*corev1.Service, error) { serviceName := getPolicyServerNameWithPrefix(policyServerName) service := corev1.Service{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: serviceName, Namespace: DeploymentsNamespace}, &service); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: serviceName, Namespace: deploymentsNamespace}, &service); err != nil { return nil, errors.Join(errors.New("could not find Service owned by PolicyServer"), err) } return &service, nil } -func getTestPolicyServerCASecret() (*corev1.Secret, error) { +func getTestPolicyServerCASecret(ctx context.Context) (*corev1.Secret, error) { secret := corev1.Secret{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: constants.PolicyServerCARootSecretName, Namespace: DeploymentsNamespace}, &secret); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: constants.PolicyServerCARootSecretName, Namespace: deploymentsNamespace}, &secret); err != nil { return nil, errors.Join(errors.New("could not find the PolicyServer CA secret"), err) } return &secret, nil } -func getTestPolicyServerSecret(policyServerName string) (*corev1.Secret, error) { +func getTestPolicyServerSecret(ctx context.Context, policyServerName string) (*corev1.Secret, error) { secretName := getPolicyServerNameWithPrefix(policyServerName) secret := corev1.Secret{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: secretName, Namespace: DeploymentsNamespace}, &secret); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: secretName, Namespace: deploymentsNamespace}, &secret); err != nil { return nil, errors.Join(errors.New("could not find secret owned by PolicyServer"), err) } return &secret, nil } -func getTestPolicyServerDeployment(policyServerName string) (*appsv1.Deployment, error) { +func getTestPolicyServerDeployment(ctx context.Context, policyServerName string) (*appsv1.Deployment, error) { deploymentName := getPolicyServerNameWithPrefix(policyServerName) deployment := appsv1.Deployment{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: deploymentName, Namespace: DeploymentsNamespace}, &deployment); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: deploymentName, Namespace: deploymentsNamespace}, &deployment); err != nil { return nil, errors.Join(errors.New("could not find Deployment owned by PolicyServer"), err) } return &deployment, nil } -func getTestPolicyServerConfigMap(policyServerName string) (*corev1.ConfigMap, error) { +func getTestPolicyServerConfigMap(ctx context.Context, policyServerName string) (*corev1.ConfigMap, error) { configMapName := getPolicyServerNameWithPrefix(policyServerName) configmap := corev1.ConfigMap{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: DeploymentsNamespace}, &configmap); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: configMapName, Namespace: deploymentsNamespace}, &configmap); err != nil { return nil, errors.Join(errors.New("could not find ConfigMap owned by PolicyServer"), err) } return &configmap, nil @@ -209,47 +205,32 @@ func getPolicyServerNameWithPrefix(policyServerName string) string { return policyServer.NameWithPrefix() } -func getTestPolicyServerPod(policyServerName string) (*corev1.Pod, error) { - podList := corev1.PodList{} - if err := reconciler.APIReader.List(ctx, &podList, client.MatchingLabels{ - constants.PolicyServerLabelKey: policyServerName, - }); err != nil { - return nil, errors.Join(errors.New("could not list Pods owned by PolicyServer"), err) - } - - if len(podList.Items) == 0 { - return nil, errors.New("could not find Pod owned by PolicyServer") - } - - return &podList.Items[0], nil -} - -func getTestValidatingWebhookConfiguration(name string) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { +func getTestValidatingWebhookConfiguration(ctx context.Context, name string) (*admissionregistrationv1.ValidatingWebhookConfiguration, error) { validatingWebhookConfiguration := admissionregistrationv1.ValidatingWebhookConfiguration{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: name}, &validatingWebhookConfiguration); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, &validatingWebhookConfiguration); err != nil { return nil, errors.Join(errors.New("could not find ValidatingWebhookConfiguration"), err) } return &validatingWebhookConfiguration, nil } -func getTestMutatingWebhookConfiguration(name string) (*admissionregistrationv1.MutatingWebhookConfiguration, error) { +func getTestMutatingWebhookConfiguration(ctx context.Context, name string) (*admissionregistrationv1.MutatingWebhookConfiguration, error) { mutatingWebhookConfiguration := admissionregistrationv1.MutatingWebhookConfiguration{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: name}, &mutatingWebhookConfiguration); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: name}, &mutatingWebhookConfiguration); err != nil { return nil, errors.Join(errors.New("could not find ValidatingWebhookConfiguration"), err) } return &mutatingWebhookConfiguration, nil } -func getTestCASecret() (*corev1.Secret, error) { +func getTestCASecret(ctx context.Context) (*corev1.Secret, error) { secret := corev1.Secret{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: constants.PolicyServerCARootSecretName, Namespace: DeploymentsNamespace}, &secret); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: constants.PolicyServerCARootSecretName, Namespace: deploymentsNamespace}, &secret); err != nil { return nil, errors.Join(errors.New("could not find CA secret"), err) } return &secret, nil } -func getPolicyServerPodDisruptionBudget(policyServerName string) (*k8spoliciesv1.PodDisruptionBudget, error) { +func getPolicyServerPodDisruptionBudget(ctx context.Context, policyServerName string) (*k8spoliciesv1.PodDisruptionBudget, error) { policyServer := policiesv1.PolicyServer{ ObjectMeta: metav1.ObjectMeta{ Name: policyServerName, @@ -257,7 +238,7 @@ func getPolicyServerPodDisruptionBudget(policyServerName string) (*k8spoliciesv1 } podDisruptionBudgetName := policyServer.NameWithPrefix() pdb := &k8spoliciesv1.PodDisruptionBudget{} - if err := reconciler.APIReader.Get(ctx, client.ObjectKey{Name: podDisruptionBudgetName, Namespace: DeploymentsNamespace}, pdb); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Name: podDisruptionBudgetName, Namespace: deploymentsNamespace}, pdb); err != nil { return nil, errors.Join(errors.New("could not find PodDisruptionBudget"), err) } return pdb, nil @@ -297,7 +278,7 @@ func policyServerPodDisruptionBudgetMatcher(policyServer *policiesv1.PolicyServe ) } -func alreadyExists() types.GomegaMatcher { //nolint:ireturn +func alreadyExists() types.GomegaMatcher { return WithTransform( func(err error) bool { return err != nil && apierrors.IsAlreadyExists(err) @@ -306,7 +287,7 @@ func alreadyExists() types.GomegaMatcher { //nolint:ireturn ) } -func haveSucceededOrAlreadyExisted() types.GomegaMatcher { //nolint:ireturn +func haveSucceededOrAlreadyExisted() types.GomegaMatcher { return SatisfyAny( BeNil(), alreadyExists(), @@ -328,22 +309,13 @@ func newName(prefix string) string { return fmt.Sprintf("%s-%s", prefix, randStringRunes(8)) } -func createPolicyServerAndWaitForItsService(policyServer *policiesv1.PolicyServer) { +func createPolicyServerAndWaitForItsService(ctx context.Context, policyServer *policiesv1.PolicyServer) { Expect( k8sClient.Create(ctx, policyServer), ).To(haveSucceededOrAlreadyExisted()) // Wait for the Service associated with the PolicyServer to be created Eventually(func() error { - _, err := getTestPolicyServerService(policyServer.GetName()) + _, err := getTestPolicyServerService(ctx, policyServer.GetName()) return err }, timeout, pollInterval).Should(Succeed()) } - -func notFound() types.GomegaMatcher { //nolint:ireturn - return WithTransform( - func(err error) bool { - return err != nil && apierrors.IsNotFound(err) - }, - BeTrue(), - ) -}