From e8168e396c8713a5a8a1c6fa38e20e5cdee74a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Guilherme=20Vanz?= Date: Thu, 22 Feb 2024 10:58:19 -0300 Subject: [PATCH] fix(test): rewrite webhooks tests as unit tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The webhook_suite_test.go originally test webhook validations with integrations test on Ginkgo. Which is slow because it setup a whole test environment to simulate Kubernetes cluster. This commit changes that rewriting the test as unit tests calling the validation function directly. Signed-off-by: José Guilherme Vanz --- Makefile | 1 - go.mod | 4 +- go.sum | 48 +- .../v1/clusteradmissionpolicy_webhook_test.go | 229 +++++++ .../policies/v1/policyserver_webhook_test.go | 47 ++ pkg/apis/policies/v1/webhook_suite_test.go | 609 ------------------ 6 files changed, 282 insertions(+), 656 deletions(-) create mode 100644 pkg/apis/policies/v1/clusteradmissionpolicy_webhook_test.go create mode 100644 pkg/apis/policies/v1/policyserver_webhook_test.go delete mode 100644 pkg/apis/policies/v1/webhook_suite_test.go diff --git a/Makefile b/Makefile index 5d6a1a5e8..2e9b9e292 100644 --- a/Makefile +++ b/Makefile @@ -115,7 +115,6 @@ unit-tests: manifests generate fmt vet setup-envtest ## Run unit tests. .PHONY: setup-envtest integration-tests integration-tests: manifests generate fmt vet setup-envtest ## Run integration tests. - ACK_GINKGO_DEPRECATIONS=2.12.0 KUBEBUILDER_ASSETS="$(KUBEBUILDER_ASSETS)" go test ./pkg/... -ginkgo.v -ginkgo.progress -race -test.v -coverprofile=coverage/integration-tests/coverage-pkg.txt -covermode=atomic ACK_GINKGO_DEPRECATIONS=2.12.0 K3S_TESTCONTAINER_VERSION="$(K3S_TESTCONTAINER_VERSION)" POLICY_SERVER_VERSION="$(POLICY_SERVER_VERSION)" go test ./controllers/... -ginkgo.v -ginkgo.progress -race -test.v -coverprofile=coverage/integration-tests/coverage-controllers.txt -covermode=atomic .PHONY: generate-crds diff --git a/go.mod b/go.mod index e61b8c4e9..d007d0f65 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/google/go-cmp v0.6.0 github.com/onsi/ginkgo/v2 v2.15.0 github.com/onsi/gomega v1.31.1 + github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.28.0 github.com/testcontainers/testcontainers-go/modules/k3s v0.28.0 go.opentelemetry.io/otel v1.23.1 @@ -32,7 +33,6 @@ require ( github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/reference v0.5.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect github.com/docker/docker v25.0.2+incompatible // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -73,8 +73,8 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc5 // indirect - github.com/opencontainers/runc v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect diff --git a/go.sum b/go.sum index 1cd41ba6d..aad66b74c 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,6 @@ github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw= -github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE= github.com/containerd/containerd v1.7.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -32,14 +30,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= -github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v25.0.2+incompatible h1:/OaKeauroa10K4Nqavw4zlhcDq/WBcPMc5DbjOGgozY= github.com/docker/docker v25.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -91,8 +83,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No= @@ -143,8 +133,6 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= -github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -161,8 +149,6 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ= -github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -183,12 +169,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/testcontainers/testcontainers-go v0.27.0 h1:IeIrJN4twonTDuMuBNQdKZ+K97yd7VrmNGu+lDpYcDk= -github.com/testcontainers/testcontainers-go v0.27.0/go.mod h1:+HgYZcd17GshBUZv9b+jKFJ198heWPQq3KQIp2+N+7U= github.com/testcontainers/testcontainers-go v0.28.0 h1:1HLm9qm+J5VikzFDYhOd+Zw12NtOl+8drH2E8nTY1r8= github.com/testcontainers/testcontainers-go v0.28.0/go.mod h1:COlDpUXbwW3owtpMkEB1zo9gwb1CoKVKlyrVPejF4AU= -github.com/testcontainers/testcontainers-go/modules/k3s v0.27.0 h1:0t0j8stcPmKiocqanccncg7082iHnK81mGSuLGA6L5Q= -github.com/testcontainers/testcontainers-go/modules/k3s v0.27.0/go.mod h1:vIWhhOmt9E46scDA2RH4n5BICu3dB0ycoqWqs43kmcI= github.com/testcontainers/testcontainers-go/modules/k3s v0.28.0 h1:Q7cRf3wo8OlM3wt67BoBG3JusNs9yCZ6VsKDsgIwRuc= github.com/testcontainers/testcontainers-go/modules/k3s v0.28.0/go.mod h1:McprNaKCjJpXLRf9mBMMmpfj+hBP25kpmoMl9ygaOHE= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= @@ -202,32 +184,22 @@ github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFi github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= -go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y= -go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI= go.opentelemetry.io/otel v1.23.1 h1:Za4UzOqJYS+MUczKI320AtqZHZb7EqxO00jAHE0jmQY= go.opentelemetry.io/otel v1.23.1/go.mod h1:Td0134eafDLcTS4y+zQ26GE8u3dEuRBiBCTUIRHaikA= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0 h1:tfil6di0PoNV7FZdsCS7A5izZoVVQ7AuXtyekbOpG/I= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.45.0/go.mod h1:AKFZIEPOnqB00P63bTjOiah4ZTaRzl1TKwUWpZdYUHI= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1 h1:ZqRWZJGHXV/1yCcEEVJ6/Uz2JtM79DNS8OZYa3vVY/A= go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.23.1/go.mod h1:D7ynngPWlGJrqyGSDOdscuv7uqttfCE3jcBvffDv9y4= -go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg= -go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= go.opentelemetry.io/otel/metric v1.23.1 h1:PQJmqJ9u2QaJLBOELl1cxIdPcpbwzbkjfEyelTl2rlo= go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/HubbC+KyH8FaCWI= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= go.opentelemetry.io/otel/sdk v1.23.1 h1:O7JmZw0h76if63LQdsBMKQDWNb5oEcOThG9IrxscV+E= go.opentelemetry.io/otel/sdk v1.23.1/go.mod h1:LzdEVR5am1uKOOwfBWFef2DCi1nu3SA8XQxx2IerWFk= -go.opentelemetry.io/otel/sdk/metric v1.22.0 h1:ARrRetm1HCVxq0cbnaZQlfwODYJHo3gFL8Z3tSmHBcI= -go.opentelemetry.io/otel/sdk/metric v1.22.0/go.mod h1:KjQGeMIDlBNEOo6HvjhxIec1p/69/kULDcp4gr0oLQQ= go.opentelemetry.io/otel/sdk/metric v1.23.1 h1:T9/8WsYg+ZqIpMWwdISVVrlGb/N0Jr1OHjR/alpKwzg= go.opentelemetry.io/otel/sdk/metric v1.23.1/go.mod h1:8WX6WnNtHCgUruJ4TJ+UssQjMtpxkpX0zveQC8JG/E0= -go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0= -go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo= go.opentelemetry.io/otel/trace v1.23.1 h1:4LrmmEd8AU2rFvU1zegmvqW7+kWarxtNOPyeL6HmYY8= go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI= go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -313,8 +285,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1 h1:gphdwh0npgs8elJ4T6J+DQJHPVF7RsuJHCfwztUb4J4= google.golang.org/genproto/googleapis/rpc v0.0.0-20240108191215-35c7eff3a6b1/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA= -google.golang.org/grpc v1.60.1 h1:26+wFr+cNqSGFcOXcabYC0lUVJVRa2Sb2ortSK7VrEU= -google.golang.org/grpc v1.60.1/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= @@ -334,18 +304,12 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw= -k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ= k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/apiextensions-apiserver v0.29.0 h1:0VuspFG7Hj+SxyF/Z/2T0uFbI5gb5LRgEyUVE3Q4lV0= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= -k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= -k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= -k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= -k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A= k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks= k8s.io/component-base v0.29.0 h1:T7rjd5wvLnPBV1vC4zWd/iWRbV8Mdxs+nGaoaFzGw3s= @@ -356,10 +320,6 @@ k8s.io/kube-openapi v0.0.0-20240105020646-a37d4de58910 h1:1Rp/XEKP5uxPs6QrsngEHA k8s.io/kube-openapi v0.0.0-20240105020646-a37d4de58910/go.mod h1:Pa1PvrP7ACSkuX6I7KYomY6cmMA0Tx86waBhDUgoKPw= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.17.0 h1:fjJQf8Ukya+VjogLO6/bNX9HE6Y2xpsO5+fyS26ur/s= -sigs.k8s.io/controller-runtime v0.17.0/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= -sigs.k8s.io/controller-runtime v0.17.1 h1:V1dQELMGVk46YVXXQUbTFujU7u4DQj6YUj9Rb6cuzz8= -sigs.k8s.io/controller-runtime v0.17.1/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= diff --git a/pkg/apis/policies/v1/clusteradmissionpolicy_webhook_test.go b/pkg/apis/policies/v1/clusteradmissionpolicy_webhook_test.go new file mode 100644 index 000000000..77a662d61 --- /dev/null +++ b/pkg/apis/policies/v1/clusteradmissionpolicy_webhook_test.go @@ -0,0 +1,229 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "testing" + + "github.com/stretchr/testify/require" + + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func buildClusterAdmissionPolicy(customRules []admissionregistrationv1.RuleWithOperations, policyServer string, policyMode PolicyMode) *ClusterAdmissionPolicy { + rules := customRules + + if rules == nil { + rules = append(rules, admissionregistrationv1.RuleWithOperations{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{"*/*"}, + }, + }) + } + + return &ClusterAdmissionPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testing-policy", + Namespace: "default", + }, + Spec: ClusterAdmissionPolicySpec{ + PolicySpec: PolicySpec{ + PolicyServer: policyServer, + Settings: runtime.RawExtension{ + Raw: []byte("{}"), + }, + Rules: rules, + Mode: policyMode, + }, + }, + } +} + +func TestValidateRulesField(t *testing.T) { + tests := []struct { + name string + policy Policy + expectedErrorMessage string // use empty string when no error is expected + }{ + {"with no operations and API groups and resources", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{}, "default", "protect"), "spec.rules: Required value: a value must be specified"}, + {"with empty objects", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{}}, "default", "protect"), "spec.rules.operations: Required value: a value must be specified"}, + {"with no operations", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules.operations: Required value: a value must be specified"}, + {"with null operations", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: nil, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules.operations: Required value: a value must be specified"}, + {"with empty operations string", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{""}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules.operations: Invalid value: \"\": field value cannot contain the empty string"}, + + {"with no apiVersion", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules: Required value: apiVersions and resources must have specified values"}, + {"with no resources", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{}, + }}}, "default", "protect"), "spec.rules: Required value: apiVersions and resources must have specified values"}, + {"with empty apiVersion string", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{""}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules.rule.apiVersions: Invalid value: \"\": rule.apiVersions value cannot contain the empty string"}, + {"with some of the apiVersion are empty string", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{""}, + Resources: []string{"*/*"}, + }}}, "default", "protect"), "spec.rules.rule.apiVersions: Invalid value: \"\": rule.apiVersions value cannot contain the empty string"}, + {"with empty resources string", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"*"}, + APIVersions: []string{"*"}, + Resources: []string{""}, + }}}, "default", "protect"), "spec.rules.rule.resources: Invalid value: \"\": rule.resources value cannot contain the empty string"}, + {"with some of the resources are string", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"", "pods"}, + }}}, "default", "protect"), "spec.rules.rule.resources: Invalid value: \"\": rule.resources value cannot contain the empty string"}, + {"with all operations and API groups and resources", buildClusterAdmissionPolicy(nil, "default", "protect"), ""}, + {"with valid APIVersion and resources. But with empty APIGroup", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"pods"}, + }}}, "default", "protect"), ""}, + {"with valid APIVersion, Resources and APIGroup", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "default", "protect"), ""}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := validateRulesField(test.policy) + if test.expectedErrorMessage != "" { + require.ErrorContains(t, err, test.expectedErrorMessage) + return + } + require.NoError(t, err) + }) + } +} + +func TestValidatePolicyUpdate(t *testing.T) { + tests := []struct { + name string + oldPolicy Policy + newPolicy Policy + expectedErrorMessage string // use empty string when no error is expected + }{ + {"update policy server", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "old-policy-server", "monitor"), buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "new-policy-server", "monitor"), "spec.policyServer: Forbidden: the field is immutable"}, + {"change from protect to monitor", buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "default", "protect"), buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "default", "monitor"), "spec.mode: Forbidden: field cannot transition from protect to monitor. Recreate instead."}, + {"adding more rules", + buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}}, "default", "protect"), + buildClusterAdmissionPolicy([]admissionregistrationv1.RuleWithOperations{ + { + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"apps"}, + APIVersions: []string{"v1"}, + Resources: []string{"deployments"}, + }}, + { + Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{""}, + APIVersions: []string{"v1"}, + Resources: []string{"pods"}, + }}, + }, "default", "protect"), ""}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + err := validatePolicyUpdate(test.oldPolicy, test.newPolicy) + if test.expectedErrorMessage != "" { + require.ErrorContains(t, err, test.expectedErrorMessage) + return + } + require.NoError(t, err) + }) + } +} diff --git a/pkg/apis/policies/v1/policyserver_webhook_test.go b/pkg/apis/policies/v1/policyserver_webhook_test.go new file mode 100644 index 000000000..832603951 --- /dev/null +++ b/pkg/apis/policies/v1/policyserver_webhook_test.go @@ -0,0 +1,47 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestValidatePolicyServerName(t *testing.T) { + name := make([]byte, 64) + for i := range name { + name[i] = 'a' + } + policyServer := &PolicyServer{ + ObjectMeta: metav1.ObjectMeta{ + Name: string(name), + Namespace: "default", + }, + Spec: PolicyServerSpec{ + Image: "image", + Replicas: 1, + }, + } + policyServerValidator := policyServerValidator{ + k8sClient: nil, + deploymentsNamespace: "default", + } + err := policyServerValidator.validate(context.Background(), policyServer) + require.ErrorContains(t, err, "the PolicyServer name cannot be longer than 63 characters") +} diff --git a/pkg/apis/policies/v1/webhook_suite_test.go b/pkg/apis/policies/v1/webhook_suite_test.go deleted file mode 100644 index 7ddc6a466..000000000 --- a/pkg/apis/policies/v1/webhook_suite_test.go +++ /dev/null @@ -1,609 +0,0 @@ -/* - - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1 - -import ( - "context" - "crypto/tls" - "fmt" - "net" - "path/filepath" - "testing" - "time" - - . "github.com/onsi/ginkgo/v2" //nolint:revive - . "github.com/onsi/gomega" //nolint:revive - - admissionv1beta1 "k8s.io/api/admission/v1beta1" - metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" - "sigs.k8s.io/controller-runtime/pkg/webhook" - - //+kubebuilder:scaffold:imports - - "github.com/kubewarden/kubewarden-controller/internal/pkg/constants" - admissionregistrationv1 "k8s.io/api/admissionregistration/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var ( - k8sClient client.Client - testEnv *envtest.Environment - ctx context.Context - cancel context.CancelFunc -) - -func TestWebhooks(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Webhook Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - ctx, cancel = context.WithCancel(context.TODO()) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: false, - WebhookInstallOptions: envtest.WebhookInstallOptions{ - Paths: []string{filepath.Join("..", "..", "..", "..", "config", "webhook")}, - }, - } - - cfg, err := testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - scheme := runtime.NewScheme() - err = AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - err = admissionv1beta1.AddToScheme(scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - - // start webhook server using Manager - webhookInstallOptions := &testEnv.WebhookInstallOptions - serverOptions := webhook.Options{ - Host: webhookInstallOptions.LocalServingHost, - Port: webhookInstallOptions.LocalServingPort, - CertDir: webhookInstallOptions.LocalServingCertDir, - } - mgr, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme, - LeaderElection: false, - WebhookServer: webhook.NewServer(serverOptions), - Metrics: metricsserver.Options{ - BindAddress: "0", - }, - }) - Expect(err).NotTo(HaveOccurred()) - - err = (&ClusterAdmissionPolicy{}).SetupWebhookWithManager(mgr) - Expect(err).NotTo(HaveOccurred()) - - err = (&PolicyServer{}).SetupWebhookWithManager(mgr, "kubewarden") - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:webhook - - go func() { - err = mgr.Start(ctx) - if err != nil { - Expect(err).NotTo(HaveOccurred()) - } - }() - - // wait for the webhook server to get ready - dialer := &net.Dialer{Timeout: time.Second} - addrPort := fmt.Sprintf("%s:%d", webhookInstallOptions.LocalServingHost, webhookInstallOptions.LocalServingPort) - Eventually(func() error { - //nolint:gosec - conn, err := tls.DialWithDialer(dialer, "tcp", addrPort, &tls.Config{InsecureSkipVerify: true}) - if err != nil { - return fmt.Errorf("failed polling webhook server: %w", err) - } - conn.Close() - return nil - }).Should(Succeed()) -}) - -var _ = AfterSuite(func() { - cancel() - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) - -func makeClusterAdmissionPolicyTemplate(name, namespace, policyServerName string, customRules []admissionregistrationv1.RuleWithOperations) *ClusterAdmissionPolicy { - rules := customRules - - if rules == nil { - rules = append(rules, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.OperationAll}, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"*"}, - APIVersions: []string{"*"}, - Resources: []string{"*/*"}, - }, - }) - } - - return &ClusterAdmissionPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: ClusterAdmissionPolicySpec{ - PolicySpec: PolicySpec{ - PolicyServer: policyServerName, - Settings: runtime.RawExtension{ - Raw: []byte("{}"), - }, - Rules: rules, - }, - }, - } -} - -//nolint:dupl -func deleteClusterAdmissionPolicy(ctx context.Context, name, namespace string) { - nsn := types.NamespacedName{ - Name: name, - Namespace: namespace, - } - pol := &ClusterAdmissionPolicy{} - err := k8sClient.Get(ctx, nsn, pol) - if apierrors.IsNotFound(err) { - return - } - Expect(err).NotTo(HaveOccurred()) - - Expect(k8sClient.Delete(ctx, pol)).To(Succeed()) - - // Remove finalizer - err = k8sClient.Get(ctx, nsn, pol) - Expect(err).NotTo(HaveOccurred()) - polUpdated := pol.DeepCopy() - controllerutil.RemoveFinalizer(polUpdated, constants.KubewardenFinalizer) - err = k8sClient.Update(ctx, polUpdated) - if err != nil { - fmt.Fprint(GinkgoWriter, err) - } - Expect(err).NotTo(HaveOccurred()) - - Eventually(func() bool { - err := k8sClient.Get(ctx, nsn, &ClusterAdmissionPolicy{}) - return apierrors.IsNotFound(err) - }).Should(BeTrue()) -} - -func makePolicyServerTemplate(name, namespace string) *PolicyServer { - return &PolicyServer{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: PolicyServerSpec{ - Image: "image", - Replicas: 1, - }, - } -} - -//nolint:dupl -func deletePolicyServer(ctx context.Context, name, namespace string) { - nsn := types.NamespacedName{ - Name: name, - Namespace: namespace, - } - pol := &PolicyServer{} - err := k8sClient.Get(ctx, nsn, pol) - if apierrors.IsNotFound(err) { - return - } - Expect(err).NotTo(HaveOccurred()) - - Expect(k8sClient.Delete(ctx, pol)).To(Succeed()) - - // Remove finalizer - err = k8sClient.Get(ctx, nsn, pol) - Expect(err).NotTo(HaveOccurred()) - polUpdated := pol.DeepCopy() - controllerutil.RemoveFinalizer(polUpdated, constants.KubewardenFinalizer) - err = k8sClient.Update(ctx, polUpdated) - if err != nil { - fmt.Fprint(GinkgoWriter, err) - } - Expect(err).NotTo(HaveOccurred()) - - Eventually(func() bool { - err := k8sClient.Get(ctx, nsn, &ClusterAdmissionPolicy{}) - return apierrors.IsNotFound(err) - }).Should(BeTrue()) -} - -// checkCreationSuccessfulWithRules checks that creating a ClusterAdmissionPolicy with the specified rules is successful -func checkCreationSuccessfulWithRules(policyName, namespace, policyServerName string, rulesArray []admissionregistrationv1.RuleWithOperations) { - pol := makeClusterAdmissionPolicyTemplate(policyName, namespace, policyServerName, rulesArray) - - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - - By("deleting the created ClusterAdmissionPolicy") - deleteClusterAdmissionPolicy(ctx, policyName, namespace) -} - -// checkUpdateSuccessfulWithRules checks that updating a created ClusterAdmissionPolicy with the specified rules is successful. -// It first creates a default ClusterAdmissionPolicy, then updates it to new rules that should succeed. -func checkUpdateSuccessfulWithRules(policyName, namespace, policyServerName string, rules []admissionregistrationv1.RuleWithOperations) { - pol := makeClusterAdmissionPolicyTemplate(policyName, namespace, policyServerName, nil) - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - - pol.Spec.Rules = rules - Expect(k8sClient.Update(ctx, pol)).To(Succeed()) - - By("deleting the created ClusterAdmissionPolicy") - deleteClusterAdmissionPolicy(ctx, policyName, namespace) -} - -// checkCreationUnsuccessfulWithRules checks that creating a ClusterAdmissionPolicy with the specified rules is unsuccessful -func checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerName string, rules []admissionregistrationv1.RuleWithOperations) { - pol := makeClusterAdmissionPolicyTemplate(policyName, namespace, policyServerName, rules) - - Expect(k8sClient.Create(ctx, pol)).ToNot(Succeed()) -} - -// checkUpdateSuccessfulWithRules checks that updating a created ClusterAdmissionPolicy with the specified rules is unsuccessful. -// It first creates a default ClusterAdmissionPolicy, then updates it to new rules that should not succeed. -func checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerName string, rules []admissionregistrationv1.RuleWithOperations) { - pol := makeClusterAdmissionPolicyTemplate(policyName, namespace, policyServerName, nil) - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - - pol.Spec.Rules = rules - Expect(k8sClient.Update(ctx, pol)).ToNot(Succeed()) - - By("deleting the created ClusterAdmissionPolicy") - deleteClusterAdmissionPolicy(ctx, policyName, namespace) -} - -var _ = Describe("validate ClusterAdmissionPolicy webhook with ", func() { - namespace := "default" - policyServerFooName := "policy-server-foo" - - It("should accept creating ClusterAdmissionPolicy", func() { - pol := makeClusterAdmissionPolicyTemplate("policy-test", namespace, policyServerFooName, nil) - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(pol), pol) - if err != nil { - fmt.Fprint(GinkgoWriter, err) - } - Expect(err).NotTo(HaveOccurred()) - - By("checking default values") - // Testing for PolicyStatus == "unscheduled" can't happen here, Status - // subresources can't be defaulted - Expect(pol.ObjectMeta.Finalizers).To(HaveLen(1)) - Expect(pol.ObjectMeta.Finalizers[0]).To(Equal(constants.KubewardenFinalizer)) - Expect(pol.Spec.PolicyServer).To(Equal(policyServerFooName)) - - By("deleting the created ClusterAdmissionPolicy") - deleteClusterAdmissionPolicy(ctx, "policy-test", namespace) - }) - - It("should deny updating ClusterAdmissionPolicy if policyServer name is changed", func() { - pol := makeClusterAdmissionPolicyTemplate("policy-test2", namespace, "policy-server-bar", nil) - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - - pol.Spec.PolicyServer = "policy-server-changed" - Expect(k8sClient.Update(ctx, pol)).NotTo(Succeed()) - - By("deleting the created ClusterAdmissionPolicy") - deleteClusterAdmissionPolicy(ctx, "policy-test2", namespace) - }) - - Context("confirm valid values for the rules field", func() { - When("an empty rules array is specified", func() { - emptyObjectsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - - policyName := "policy-test-empty-rules-array" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyObjectsRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyObjectsRulesArray) - }) - }) - - When("a rules array with empty objects is specified", func() { - emptyObjectsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyObjectsRulesArray = append(emptyObjectsRulesArray, admissionregistrationv1.RuleWithOperations{}) - - policyName := "policy-test-empty-rules-object" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyObjectsRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyObjectsRulesArray) - }) - }) - - When("a rules array with non-empty objects is specified", func() { - When("the operations field is empty", func() { - emptyOperationsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyOperationsRulesArray = append(emptyOperationsRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{}, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-empty-operations" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyOperationsRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyOperationsRulesArray) - }) - }) - - When("the operations field is null", func() { - nullOperationsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - nullOperationsRulesArray = append(nullOperationsRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: nil, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-null-operations-array" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, nullOperationsRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, nullOperationsRulesArray) - }) - }) - - When("the operations field contains the empty string", func() { - emptyOperationsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyOperationsRulesArray = append(emptyOperationsRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{""}, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-empty-string-operations" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyOperationsRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyOperationsRulesArray) - }) - }) - - When("the resources array has values but the apiVersions array does not", func() { - emptyStringResourceRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyStringResourceRulesArray = append(emptyStringResourceRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-resource-array" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - }) - - When("the apiVersions array has values but the resources array does not", func() { - emptyStringResourceRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyStringResourceRulesArray = append(emptyStringResourceRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{}, - }, - }) - - policyName := "policy-test-api-versions-array" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - }) - - When("one of the values in the resources field is the empty string", func() { - emptyStringResourceRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyStringResourceRulesArray = append(emptyStringResourceRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"", "pods"}, - }, - }) - - policyName := "policy-test-empty-resource" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringResourceRulesArray) - }) - }) - - When("one of the values in the apiVersions field is the empty string", func() { - emptyStringAPIVersionRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyStringAPIVersionRulesArray = append(emptyStringAPIVersionRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"", "v1"}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-empty-api-versions" - - It("should fail to create a ClusterAdmissionPolicy", func() { - checkCreationUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringAPIVersionRulesArray) - }) - - It("should fail to update to a ClusterAdmissionPolicy", func() { - checkUpdateUnsuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringAPIVersionRulesArray) - }) - }) - - When("a rules array with valid objects and an empty API group is specified", func() { - emptyStringAPIGroupsRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - emptyStringAPIGroupsRulesArray = append(emptyStringAPIGroupsRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{""}, - APIVersions: []string{"v1"}, - Resources: []string{"pods"}, - }, - }) - - policyName := "policy-test-empty-api-groups" - - It("should succeed creating a ClusterAdmissionPolicy", func() { - checkCreationSuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringAPIGroupsRulesArray) - }) - - It("should succeed updating a ClusterAdmissionPolicy", func() { - checkUpdateSuccessfulWithRules(policyName, namespace, policyServerFooName, emptyStringAPIGroupsRulesArray) - }) - }) - - When("a rules array with valid objects and a non-empty API group is specified", func() { - nonEmptyAPIGroupRulesArray := make([]admissionregistrationv1.RuleWithOperations, 0) - nonEmptyAPIGroupRulesArray = append(nonEmptyAPIGroupRulesArray, admissionregistrationv1.RuleWithOperations{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Update, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"apps"}, - APIVersions: []string{"v1"}, - Resources: []string{"deployments"}, - }, - }) - - policyName := "policy-test-non-empty-api-groups" - - It("should succeed creating a ClusterAdmissionPolicy", func() { - checkCreationSuccessfulWithRules(policyName, namespace, policyServerFooName, nonEmptyAPIGroupRulesArray) - }) - - It("should succeed updating a ClusterAdmissionPolicy", func() { - checkUpdateSuccessfulWithRules(policyName, namespace, policyServerFooName, nonEmptyAPIGroupRulesArray) - }) - }) - }) - }) -}) - -var _ = Describe("validate PolicyServer webhook with ", func() { - namespace := "kubewarden" - - It("should add kubewarden finalizer when creating a PolicyServer", func() { - pol := makePolicyServerTemplate("policyserver-test", namespace) - Expect(k8sClient.Create(ctx, pol)).To(Succeed()) - err := k8sClient.Get(ctx, client.ObjectKeyFromObject(pol), pol) - if err != nil { - fmt.Fprint(GinkgoWriter, err) - } - Expect(err).NotTo(HaveOccurred()) - - By("checking default values") - Expect(pol.ObjectMeta.Finalizers).To(HaveLen(1)) - Expect(pol.ObjectMeta.Finalizers[0]).To(Equal(constants.KubewardenFinalizer)) - - By("deleting the created PolicyServer") - deletePolicyServer(ctx, "policyserver-test", namespace) - }) - - It("should deny creating a PolicyServer with an invalid name", func() { - name := make([]byte, 64) - for i := range name { - name[i] = 'a' - } - - pol := makePolicyServerTemplate(string(name), namespace) - Expect(k8sClient.Create(ctx, pol)).ToNot(Succeed()) - }) -})