From 8e019fa46aad086805db9619b37ca4b920c0df56 Mon Sep 17 00:00:00 2001 From: JasonPowr Date: Fri, 11 Jul 2025 15:02:38 +0100 Subject: [PATCH] ci: add byok install test --- README.md | 11 +- go.mod | 51 ++-- go.sum | 116 ++++++--- test/e2e/byok_install_test.go | 220 ++++++++++++++++++ test/e2e/common_install_test.go | 125 +++++----- .../byok/cluster_image_policy.yaml.tpl | 40 ++++ .../byok/policy_controller.yaml.tpl | 24 ++ .../custom_resources/byok/trust_root.yaml.tpl | 30 +++ test/e2e/e2e_suite_test.go | 15 +- test/e2e/utils/common.go | 43 ++-- test/e2e/utils/cosign.go | 4 +- test/e2e/utils/image.go | 43 ++++ test/e2e/utils/kubernetes.go | 51 ++-- test/e2e/utils/rhtas.go | 199 ++++++++++++++++ test/e2e/utils/tuf.go | 42 ---- 15 files changed, 793 insertions(+), 221 deletions(-) create mode 100644 test/e2e/byok_install_test.go create mode 100644 test/e2e/custom_resources/byok/cluster_image_policy.yaml.tpl create mode 100644 test/e2e/custom_resources/byok/policy_controller.yaml.tpl create mode 100644 test/e2e/custom_resources/byok/trust_root.yaml.tpl create mode 100644 test/e2e/utils/image.go create mode 100644 test/e2e/utils/rhtas.go delete mode 100644 test/e2e/utils/tuf.go diff --git a/README.md b/README.md index c0df4979..0b318a92 100644 --- a/README.md +++ b/README.md @@ -104,14 +104,7 @@ The E2E test suite validates the Policy Controller operator against a OpenShift/ ### 1. Build and deploy Build and deploy the policy controller operator to your cluster. -### 2. Build a test image -Build a test image and define an env var for it. -```sh -export TEST_IMAGE=quay.io//policy-controller-test:latest -echo 'FROM scratch' | podman build -f - -t $TEST_IMAGE . && podman push $TEST_IMAGE -``` - -### 3. Define RHTAS ENV var +### 2. Define RHTAS ENV var ```sh export RHTAS_INSTALL_NAMESPACE=openshift-rhtas-operator source ./test/tas-env-variables.sh @@ -132,7 +125,5 @@ export INJECT_CA=true make e2e-test ``` -NOTE: On subsequent runs of the end-to-end tests, you’ll need to rebuild and push your image so that the policy controller will not trust it. - # Documentation For more information on the Policy controller please visit the upstream documentation: https://docs.sigstore.dev/policy-controller/overview/ diff --git a/go.mod b/go.mod index 302b76ab..d5757b57 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/securesign/policy-controller-operator go 1.23.6 require ( + github.com/google/go-containerregistry v0.20.3 github.com/onsi/ginkgo/v2 v2.23.4 github.com/onsi/gomega v1.37.0 github.com/stretchr/testify v1.10.0 @@ -13,19 +14,42 @@ require ( sigs.k8s.io/e2e-framework v0.6.0 ) +require ( + github.com/cenkalti/backoff/v5 v5.0.2 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect + github.com/docker/cli v27.5.0+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker-credential-helpers v0.8.2 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/sigstore/protobuf-specs v0.4.1 // indirect + github.com/sigstore/sigstore v1.9.4 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/theupdateframework/go-tuf/v2 v2.1.1 // indirect + github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect + github.com/vbatts/tar-split v0.11.6 // indirect + golang.org/x/crypto v0.38.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect +) + require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect @@ -34,10 +58,10 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect - github.com/google/uuid v1.6.0 // indirect + github.com/google/uuid v1.6.0 github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect @@ -47,21 +71,22 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/sigstore/sigstore-go v1.0.0 + github.com/spf13/pflag v1.0.6 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/net v0.40.0 // indirect + golang.org/x/oauth2 v0.29.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/time v0.9.0 // indirect golang.org/x/tools v0.31.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect + google.golang.org/protobuf v1.36.6 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 413264be..d6a515c4 100644 --- a/go.sum +++ b/go.sum @@ -1,36 +1,49 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8= +github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= +github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= +github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= +github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -42,6 +55,8 @@ github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcb github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= +github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -49,6 +64,8 @@ github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= 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/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= +github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -57,17 +74,18 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ= +github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -79,6 +97,10 @@ github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -96,17 +118,29 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= +github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= +github.com/sigstore/sigstore v1.9.4 h1:64+OGed80+A4mRlNzRd055vFcgBeDghjZw24rPLZgDU= +github.com/sigstore/sigstore v1.9.4/go.mod h1:Q7tGTC3gbtK7c3jcxEmGc2MmK4rRpIRzi3bxRFWKvEY= +github.com/sigstore/sigstore-go v1.0.0 h1:4N07S2zLxf09nTRwaPKyAxbKzpM8WJYUS8lWWaYxneU= +github.com/sigstore/sigstore-go v1.0.0/go.mod h1:UYsZ/XHE4eltv1o1Lu+n6poW1Z5to3f0+emvfXNxIN8= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/theupdateframework/go-tuf/v2 v2.1.1 h1:OWcoHItwsGO+7m0wLa7FDWPR4oB1cj0zOr1kosE4G+I= +github.com/theupdateframework/go-tuf/v2 v2.1.1/go.mod h1:V675cQGhZONR0OGQ8r1feO0uwtsTBYPDWHzAAPn5rjE= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= +github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= +github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -122,32 +156,35 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= +golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -162,8 +199,15 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e h1:UdXH7Kzbj+Vzastr5nVfccbmFsmYNygVLSPk1pEfDoY= +google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e/go.mod h1:085qFyf2+XaZlRdCgKNCIZ3afY2p4HHZdoIRpId8F4A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e h1:ztQaXfzEXTmCBvbtWYRhJxW+0iJcz2qXfd38/e9l7bA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= +google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= @@ -174,6 +218,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc= k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k= k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0= diff --git a/test/e2e/byok_install_test.go b/test/e2e/byok_install_test.go new file mode 100644 index 00000000..c6263955 --- /dev/null +++ b/test/e2e/byok_install_test.go @@ -0,0 +1,220 @@ +package e2e + +import ( + "fmt" + "path/filepath" + "strconv" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + e2e_utils "github.com/securesign/policy-controller-operator/test/e2e/utils" + admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const ( + policyControllerBYOKCrPath = "custom_resources/byok/policy_controller.yaml.tpl" + trustRootBYOKCrPath = "custom_resources/byok/trust_root.yaml.tpl" + clusterimagepolicyBYOKCrPath = "custom_resources/byok/cluster_image_policy.yaml.tpl" + byokTestNS = "pco-e2e-byok" + byokTestImageEnv = "BYOK_TEST_IMAGE" +) + +var ( + policyControllerBYOKCrABSPath = "" + trustRootBYOKCrABSPath = "" + clusterImagePolicyBYOKCrABSPath = "" + byokRenderedTrustRoot []byte + byokRenderedClusteImagePolicy []byte +) + +var _ = Describe("policy-controller-operator byok", Ordered, func() { + var err error + + BeforeAll(func() { + policyControllerBYOKCrABSPath, err = filepath.Abs(policyControllerBYOKCrPath) + Expect(err).ToNot(HaveOccurred()) + + trustRootBYOKCrABSPath, err = filepath.Abs(trustRootBYOKCrPath) + Expect(err).ToNot(HaveOccurred()) + + clusterImagePolicyBYOKCrABSPath, err = filepath.Abs(clusterimagepolicyBYOKCrPath) + Expect(err).ToNot(HaveOccurred()) + + By("ensuring the policy-controller-operator namespace exists") + Expect(k8sClient.Create(ctx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{Name: e2e_utils.InstallNamespace}, + })).To(SatisfyAny(Succeed(), MatchError(ContainSubstring("already exists")))) + + By("applying the operator bundle: " + policyControllerBYOKCrABSPath) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, nil, policyControllerBYOKCrABSPath)).To(Succeed()) + + DeferCleanup(func() { + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, "test-pod", byokTestNS)).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, byokTestNS, "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "policy.sigstore.dev", Version: "v1alpha1", Kind: "TrustRoot"}, "byok-install-trust-root", "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "policy.sigstore.dev", Version: "v1beta1", Kind: "ClusterImagePolicy"}, "byok-install-cluster-image-policy", "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "rhtas.charts.redhat.com", Version: "v1alpha1", Kind: "PolicyController"}, "policycontroller-sample", e2e_utils.InstallNamespace)).To(Succeed()) + }) + }) + + It("creates all required resources", func() { + type resource struct { + name string + obj client.Object + } + tests := []resource{ + {e2e_utils.DeploymentName, &appsv1.Deployment{}}, + {e2e_utils.ValidatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, + {e2e_utils.MutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, + {e2e_utils.CipValidatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, + {e2e_utils.CipMutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, + {e2e_utils.WebhookSvc, &corev1.Service{}}, + {e2e_utils.MetricsSvc, &corev1.Service{}}, + {e2e_utils.SecretName, &corev1.Secret{}}, + {"config-policy-controller", &corev1.ConfigMap{}}, + {"config-image-policies", &corev1.ConfigMap{}}, + {"config-sigstore-keys", &corev1.ConfigMap{}}, + {"policycontroller-sample-policy-controller-webhook-logging", &corev1.ConfigMap{}}, + } + for _, tt := range tests { + By("checking " + tt.name) + e2e_utils.ExpectExists(tt.name, e2e_utils.InstallNamespace, tt.obj, k8sClient, ctx) + } + }) + + It("eventually has the deployment ready", func() { + dep := &appsv1.Deployment{} + e2e_utils.ExpectExists(e2e_utils.DeploymentName, e2e_utils.InstallNamespace, dep, k8sClient, ctx) + + desired := *dep.Spec.Replicas + Eventually(func() int32 { + k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep) + Expect(err).ToNot(HaveOccurred()) + return dep.Status.ReadyReplicas + }).Should(Equal(desired), "timed out waiting for %d pods to be Ready in Deployment %q", desired, e2e_utils.DeploymentName) + }) + + It("injects the CA bundle and the Deployment rolls out", func() { + inject, err := strconv.ParseBool(strings.TrimSpace(e2e_utils.InjectCA())) + if err != nil { + panic(fmt.Errorf("invalid value for INJECT_CA: %w", err)) + } + if !inject { + Skip("CA-injection tests are disabled for this run") + } + + Expect(e2e_utils.InjectCAIntoDeployment(ctx, k8sClient, e2e_utils.DeploymentName, e2e_utils.InstallNamespace)).To(Succeed()) + Eventually(func() (bool, error) { + cm := &corev1.ConfigMap{} + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "trusted-ca-bundle"}, cm) + if err != nil { + return false, err + } + bundle, ok := cm.Data["ca-bundle.crt"] + return ok && len(bundle) > 0, nil + }).Should(BeTrue(), "trusted-ca-bundle never got its ca-bundle.crt") + + dep := &appsv1.Deployment{} + Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep)).To(Succeed(), "failed to read Deployment after CA injection") + + desired := *dep.Spec.Replicas + Eventually(func() (int32, error) { + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep); err != nil { + return 0, err + } + return dep.Status.ReadyReplicas, nil + }).Should(Equal(desired), "timed out waiting for %d Ready replicas in Deployment %q", desired, e2e_utils.DeploymentName) + }) + + It("creates a TrustRoot and adds it to the sigstore-keys ConfigMap", func() { + trustedrootValues, err := e2e_utils.ParseTufRoot(ctx) + Expect(err).NotTo(HaveOccurred()) + byokRenderedTrustRoot, err = e2e_utils.RenderTemplate(trustRootBYOKCrABSPath, map[string]string{ + "FULCIO_ORG_NAME": trustedrootValues.FulcioOrgName, + "FULCIO_COMMON_NAME": trustedrootValues.FulcioCommonName, + "FULCIO_URL": e2e_utils.FulcioUrl(), + "FULCIO_CERT_CHAIN": trustedrootValues.FulcioCertChain, + "CTLOG_URL": fmt.Sprintf("http://ctlog.%s.svc.cluster.local", e2e_utils.RhtasInstallNamespace()), + "CTLOG_HASH_ALGORITHM": trustedrootValues.CtLogHashAlgo, + "CTFE_PUBLIC_KEY": trustedrootValues.CtfePublicKey, + "REKOR_URL": e2e_utils.RekorUrl(), + "REKOR_HASH_ALGORITHM": trustedrootValues.RekorHashAlgo, + "REKOR_PUBLIC_KEY": trustedrootValues.RekorPublicKey, + "TSA_ORG_NAME": trustedrootValues.TsaOrgName, + "TSA_COMMON_NAME": trustedrootValues.TsaCommonName, + "TSA_URL": e2e_utils.TsaUrl(), + "TSA_CERT_CHAIN": trustedrootValues.TsaCertChain, + }) + Expect(err).NotTo(HaveOccurred()) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, byokRenderedTrustRoot, "")).To(Succeed()) + + Eventually(func() (string, error) { + cm := &corev1.ConfigMap{} + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "config-sigstore-keys"}, cm); err != nil { + return "", err + } + val, ok := cm.Data["byok-install-trust-root"] + if !ok { + return "", fmt.Errorf("key not present yet") + } + return val, nil + }).ShouldNot(BeEmpty(), "timed out waiting for ConfigMap 'config-sigstore-keys' to have the byok-install-trust-root key") + }) + + It("creates a Cluster image policy and adds it to the config-image-policies ConfigMap", func() { + + byokRenderedClusteImagePolicy, err = e2e_utils.RenderTemplate(clusterImagePolicyBYOKCrABSPath, map[string]string{ + "FULCIO_URL": e2e_utils.FulcioUrl(), + "REKOR_URL": e2e_utils.RekorUrl(), + "OIDC_ISSUER_URL": e2e_utils.OidcIssuerUrl(), + "OIDC_ISSUER_SUBJECT": e2e_utils.OidcIssuerSubject(), + "TEST_IMAGE": e2e_utils.PrepareImage(ctx, byokTestImageEnv), + }) + Expect(err).NotTo(HaveOccurred()) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, byokRenderedClusteImagePolicy, "")).To(Succeed()) + + Eventually(func() (string, error) { + cm := &corev1.ConfigMap{} + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "config-image-policies"}, cm); err != nil { + return "", err + } + val, ok := cm.Data["byok-install-cluster-image-policy"] + if !ok { + return "", fmt.Errorf("key not present yet") + } + return val, nil + }).ShouldNot(BeEmpty(), "timed out waiting for ConfigMap 'config-image-policies' to have the byok-install-cluster-image-policy key") + }) + + It("should create a test namespace", func() { + Expect(e2e_utils.CreateTestNamespace(ctx, k8sClient, byokTestNS)).NotTo(HaveOccurred()) + }) + + It("should reject pod creation in a watched namespace and sign the image", func() { + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, byokTestNS, e2e_utils.PrepareImage(ctx, byokTestImageEnv))). + To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) + e2e_utils.VerifyByCosign(ctx, e2e_utils.PrepareImage(ctx, byokTestImageEnv)) + }) + + It("should reject pod creation in a watched namespace and attach a provenance", func() { + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, byokTestNS, e2e_utils.PrepareImage(ctx, byokTestImageEnv))). + To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) + e2e_utils.AttachProvenance(ctx, e2e_utils.PrepareImage(ctx, byokTestImageEnv)) + }) + + It("should reject pod creation in a watched namespace and attach an SBOM", func() { + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, byokTestNS, e2e_utils.PrepareImage(ctx, byokTestImageEnv))). + To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) + e2e_utils.AttachSBOM(ctx, e2e_utils.PrepareImage(ctx, byokTestImageEnv)) + }) + + It("should accept the pod", func() { + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, byokTestNS, e2e_utils.PrepareImage(ctx, byokTestImageEnv))).NotTo(HaveOccurred()) + }) +}) diff --git a/test/e2e/common_install_test.go b/test/e2e/common_install_test.go index 086a33d2..4b1d12e9 100644 --- a/test/e2e/common_install_test.go +++ b/test/e2e/common_install_test.go @@ -18,59 +18,48 @@ import ( ) const ( - installNamespace = "policy-controller-operator" - testNamespace = "pco-e2e" - - policyControllerCrPath = "custom_resources/common_install/policy_controller.yaml.tpl" - trustRootCrPath = "custom_resources/common_install/trust_root.yaml.tpl" - clusterimagepolicyCrPath = "custom_resources/common_install/cluster_image_policy.yaml.tpl" - - deploymentName = "policycontroller-sample-policy-controller-webhook" - validatingWebhookName = "policy.rhtas.com" - mutatingWebhookName = "policy.rhtas.com" - cipValidatingWebhookName = "validating.clusterimagepolicy.rhtas.com" - cipMutatingWebhookName = "defaulting.clusterimagepolicy.rhtas.com" - webhookSvc = "webhook" - metricsSvc = "policycontroller-sample-policy-controller-webhook-metrics" - secretName = "webhook-certs" + policyControllerCommonCrPath = "custom_resources/common_install/policy_controller.yaml.tpl" + trustRootCommonCrPath = "custom_resources/common_install/trust_root.yaml.tpl" + clusterimagepolicyCommonCrPath = "custom_resources/common_install/cluster_image_policy.yaml.tpl" + commonTestNS = "pco-e2e" + commonTestImageEnv = "COMMON_TEST_IMAGE" ) var ( - policyControllerCrABSPath = "" - trustRootCrABSPath = "" - clusterImagePolicyCrABSPath = "" - renderedTrustRoot []byte - renderedClusteImagePolicy []byte - - err error + policyControllerCommonCrABSPath = "" + trustRootCommonCrABSPath = "" + clusterImagePolicyCommonCrABSPath = "" + commonRenderedTrustRoot []byte + commonRenderedClusteImagePolicy []byte ) -var _ = Describe("policy-controller-operator installation", Ordered, func() { +var _ = Describe("policy-controller-operator common installation", Ordered, func() { + var err error BeforeAll(func() { - policyControllerCrABSPath, err = filepath.Abs(policyControllerCrPath) + policyControllerCommonCrABSPath, err = filepath.Abs(policyControllerCommonCrPath) Expect(err).ToNot(HaveOccurred()) - trustRootCrABSPath, err = filepath.Abs(trustRootCrPath) + trustRootCommonCrABSPath, err = filepath.Abs(trustRootCommonCrPath) Expect(err).ToNot(HaveOccurred()) - clusterImagePolicyCrABSPath, err = filepath.Abs(clusterimagepolicyCrPath) + clusterImagePolicyCommonCrABSPath, err = filepath.Abs(clusterimagepolicyCommonCrPath) Expect(err).ToNot(HaveOccurred()) By("ensuring the policy-controller-operator namespace exists") Expect(k8sClient.Create(ctx, &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: installNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: e2e_utils.InstallNamespace}, })).To(SatisfyAny(Succeed(), MatchError(ContainSubstring("already exists")))) - By("applying the operator bundle: " + policyControllerCrABSPath) - Expect(e2e_utils.ApplyManifest(ctx, k8sClient, nil, policyControllerCrABSPath)).To(Succeed()) + By("applying the operator bundle: " + policyControllerCommonCrABSPath) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, nil, policyControllerCommonCrABSPath)).To(Succeed()) DeferCleanup(func() { - Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, "test-pod", testNamespace)).To(Succeed()) - Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, testNamespace, "")).To(Succeed()) - Expect(e2e_utils.DeleteManifest(ctx, k8sClient, renderedTrustRoot, "")).To(Succeed()) - Expect(e2e_utils.DeleteManifest(ctx, k8sClient, renderedClusteImagePolicy, "")).To(Succeed()) - Expect(e2e_utils.DeleteManifest(ctx, k8sClient, nil, policyControllerCrABSPath)).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}, "test-pod", commonTestNS)).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}, commonTestNS, "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "policy.sigstore.dev", Version: "v1alpha1", Kind: "TrustRoot"}, "common-install-trust-root", "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "policy.sigstore.dev", Version: "v1beta1", Kind: "ClusterImagePolicy"}, "common-install-cluster-image-policy", "")).To(Succeed()) + Expect(e2e_utils.DeleteResource(ctx, k8sClient, schema.GroupVersionKind{Group: "rhtas.charts.redhat.com", Version: "v1alpha1", Kind: "PolicyController"}, "policycontroller-sample", e2e_utils.InstallNamespace)).To(Succeed()) }) }) @@ -80,14 +69,14 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { obj client.Object } tests := []resource{ - {deploymentName, &appsv1.Deployment{}}, - {validatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, - {mutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, - {cipValidatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, - {cipMutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, - {webhookSvc, &corev1.Service{}}, - {metricsSvc, &corev1.Service{}}, - {secretName, &corev1.Secret{}}, + {e2e_utils.DeploymentName, &appsv1.Deployment{}}, + {e2e_utils.ValidatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, + {e2e_utils.MutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, + {e2e_utils.CipValidatingWebhookName, &admissionregistrationv1.ValidatingWebhookConfiguration{}}, + {e2e_utils.CipMutatingWebhookName, &admissionregistrationv1.MutatingWebhookConfiguration{}}, + {e2e_utils.WebhookSvc, &corev1.Service{}}, + {e2e_utils.MetricsSvc, &corev1.Service{}}, + {e2e_utils.SecretName, &corev1.Secret{}}, {"config-policy-controller", &corev1.ConfigMap{}}, {"config-image-policies", &corev1.ConfigMap{}}, {"config-sigstore-keys", &corev1.ConfigMap{}}, @@ -95,20 +84,20 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { } for _, tt := range tests { By("checking " + tt.name) - e2e_utils.ExpectExists(tt.name, installNamespace, tt.obj, k8sClient, ctx) + e2e_utils.ExpectExists(tt.name, e2e_utils.InstallNamespace, tt.obj, k8sClient, ctx) } }) It("eventually has the deployment ready", func() { dep := &appsv1.Deployment{} - e2e_utils.ExpectExists(deploymentName, installNamespace, dep, k8sClient, ctx) + e2e_utils.ExpectExists(e2e_utils.DeploymentName, e2e_utils.InstallNamespace, dep, k8sClient, ctx) desired := *dep.Spec.Replicas Eventually(func() int32 { - k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: deploymentName}, dep) + k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep) Expect(err).ToNot(HaveOccurred()) return dep.Status.ReadyReplicas - }).Should(Equal(desired), "timed out waiting for %d pods to be Ready in Deployment %q", desired, deploymentName) + }).Should(Equal(desired), "timed out waiting for %d pods to be Ready in Deployment %q", desired, e2e_utils.DeploymentName) }) It("injects the CA bundle and the Deployment rolls out", func() { @@ -120,10 +109,10 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { Skip("CA-injection tests are disabled for this run") } - Expect(e2e_utils.InjectCAIntoDeployment(ctx, k8sClient, deploymentName, installNamespace)).To(Succeed()) + Expect(e2e_utils.InjectCAIntoDeployment(ctx, k8sClient, e2e_utils.DeploymentName, e2e_utils.InstallNamespace)).To(Succeed()) Eventually(func() (bool, error) { cm := &corev1.ConfigMap{} - err := k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: "trusted-ca-bundle"}, cm) + err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "trusted-ca-bundle"}, cm) if err != nil { return false, err } @@ -132,31 +121,31 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { }).Should(BeTrue(), "trusted-ca-bundle never got its ca-bundle.crt") dep := &appsv1.Deployment{} - Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: deploymentName}, dep)).To(Succeed(), "failed to read Deployment after CA injection") + Expect(k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep)).To(Succeed(), "failed to read Deployment after CA injection") desired := *dep.Spec.Replicas Eventually(func() (int32, error) { - if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: deploymentName}, dep); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: e2e_utils.DeploymentName}, dep); err != nil { return 0, err } return dep.Status.ReadyReplicas, nil - }).Should(Equal(desired), "timed out waiting for %d Ready replicas in Deployment %q", desired, deploymentName) + }).Should(Equal(desired), "timed out waiting for %d Ready replicas in Deployment %q", desired, e2e_utils.DeploymentName) }) It("creates a TrustRoot and adds it to the sigstore-keys ConfigMap", func() { - encodedRoot, err := e2e_utils.ResolveBase64TufRoot(ctx) + tufroot, err := e2e_utils.ResolveTufRoot(ctx) Expect(err).NotTo(HaveOccurred()) - renderedTrustRoot, err = e2e_utils.RenderTemplate(trustRootCrABSPath, map[string]string{ + commonRenderedTrustRoot, err = e2e_utils.RenderTemplate(trustRootCommonCrABSPath, map[string]string{ "TUFMirror": e2e_utils.TufUrl(), - "TUFRoot": encodedRoot, + "TUFRoot": e2e_utils.Base64EncodeString(tufroot), }) Expect(err).NotTo(HaveOccurred()) - Expect(e2e_utils.ApplyManifest(ctx, k8sClient, renderedTrustRoot, "")).To(Succeed()) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, commonRenderedTrustRoot, "")).To(Succeed()) Eventually(func() (string, error) { cm := &corev1.ConfigMap{} - if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: "config-sigstore-keys"}, cm); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "config-sigstore-keys"}, cm); err != nil { return "", err } val, ok := cm.Data["common-install-trust-root"] @@ -169,19 +158,19 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { It("creates a Cluster image policy and adds it to the config-image-policies ConfigMap", func() { - renderedClusteImagePolicy, err = e2e_utils.RenderTemplate(clusterImagePolicyCrABSPath, map[string]string{ + commonRenderedClusteImagePolicy, err = e2e_utils.RenderTemplate(clusterImagePolicyCommonCrABSPath, map[string]string{ "FULCIO_URL": e2e_utils.FulcioUrl(), "REKOR_URL": e2e_utils.RekorUrl(), "OIDC_ISSUER_URL": e2e_utils.OidcIssuerUrl(), "OIDC_ISSUER_SUBJECT": e2e_utils.OidcIssuerSubject(), - "TEST_IMAGE": e2e_utils.TestImage(), + "TEST_IMAGE": e2e_utils.PrepareImage(ctx, commonTestImageEnv), }) Expect(err).NotTo(HaveOccurred()) - Expect(e2e_utils.ApplyManifest(ctx, k8sClient, renderedClusteImagePolicy, "")).To(Succeed()) + Expect(e2e_utils.ApplyManifest(ctx, k8sClient, commonRenderedClusteImagePolicy, "")).To(Succeed()) Eventually(func() (string, error) { cm := &corev1.ConfigMap{} - if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: installNamespace, Name: "config-image-policies"}, cm); err != nil { + if err := k8sClient.Get(ctx, client.ObjectKey{Namespace: e2e_utils.InstallNamespace, Name: "config-image-policies"}, cm); err != nil { return "", err } val, ok := cm.Data["common-install-cluster-image-policy"] @@ -193,28 +182,28 @@ var _ = Describe("policy-controller-operator installation", Ordered, func() { }) It("should create a test namespace", func() { - Expect(e2e_utils.CreateTestNamespace(ctx, k8sClient, testNamespace)).NotTo(HaveOccurred()) + Expect(e2e_utils.CreateTestNamespace(ctx, k8sClient, commonTestNS)).NotTo(HaveOccurred()) }) It("should reject pod creation in a watched namespace and sign the image", func() { - Expect(e2e_utils.CreateTestPod(ctx, k8sClient, testNamespace)). + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, commonTestNS, e2e_utils.PrepareImage(ctx, commonTestImageEnv))). To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) - e2e_utils.VerifyByCosign(ctx, e2e_utils.TestImage()) + e2e_utils.VerifyByCosign(ctx, e2e_utils.PrepareImage(ctx, commonTestImageEnv)) }) It("should reject pod creation in a watched namespace and attach a provenance", func() { - Expect(e2e_utils.CreateTestPod(ctx, k8sClient, testNamespace)). + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, commonTestNS, e2e_utils.PrepareImage(ctx, commonTestImageEnv))). To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) - e2e_utils.AttachProvenance(ctx, e2e_utils.TestImage()) + e2e_utils.AttachProvenance(ctx, e2e_utils.PrepareImage(ctx, commonTestImageEnv)) }) It("should reject pod creation in a watched namespace and attach an SBOM", func() { - Expect(e2e_utils.CreateTestPod(ctx, k8sClient, testNamespace)). + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, commonTestNS, e2e_utils.PrepareImage(ctx, commonTestImageEnv))). To(MatchError(ContainSubstring(`admission webhook "policy.rhtas.com" denied the request`))) - e2e_utils.AttachSBOM(ctx, e2e_utils.TestImage()) + e2e_utils.AttachSBOM(ctx, e2e_utils.PrepareImage(ctx, commonTestImageEnv)) }) It("should accept the pod", func() { - Expect(e2e_utils.CreateTestPod(ctx, k8sClient, testNamespace)).NotTo(HaveOccurred()) + Expect(e2e_utils.CreateTestPod(ctx, k8sClient, commonTestNS, e2e_utils.PrepareImage(ctx, commonTestImageEnv))).NotTo(HaveOccurred()) }) }) diff --git a/test/e2e/custom_resources/byok/cluster_image_policy.yaml.tpl b/test/e2e/custom_resources/byok/cluster_image_policy.yaml.tpl new file mode 100644 index 00000000..acbde8bc --- /dev/null +++ b/test/e2e/custom_resources/byok/cluster_image_policy.yaml.tpl @@ -0,0 +1,40 @@ +apiVersion: policy.sigstore.dev/v1beta1 +kind: ClusterImagePolicy +metadata: + name: byok-install-cluster-image-policy +spec: + images: + - glob: "**" + authorities: + - keyless: + url: {{ .FULCIO_URL }} + trustRootRef: byok-install-trust-root + identities: + - issuer: {{ .OIDC_ISSUER_URL }} + subject: {{ .OIDC_ISSUER_SUBJECT }} + ctlog: + url: {{ .REKOR_URL }} + trustRootRef: byok-install-trust-root + attestations: + - name: match-sbom + predicateType: https://cyclonedx.org/bom + policy: + type: cue + data: | + predicate: { + metadata: { + component: { + name: "{{ .TEST_IMAGE }}" + } + } + } + - name: provenance-check + predicateType: https://slsa.dev/provenance/v0.2 + policy: + type: cue + data: | + predicate: { + builder: { + id: "e2e-test" + } + } diff --git a/test/e2e/custom_resources/byok/policy_controller.yaml.tpl b/test/e2e/custom_resources/byok/policy_controller.yaml.tpl new file mode 100644 index 00000000..f5292845 --- /dev/null +++ b/test/e2e/custom_resources/byok/policy_controller.yaml.tpl @@ -0,0 +1,24 @@ +apiVersion: rhtas.charts.redhat.com/v1alpha1 +kind: PolicyController +metadata: + name: policycontroller-sample + namespace: policy-controller-operator +spec: + policy-controller: + cosign: + webhookName: "policy.rhtas.com" + webhook: + name: webhook + extraArgs: + webhook-name: policy.rhtas.com + mutating-webhook-name: defaulting.clusterimagepolicy.rhtas.com + validating-webhook-name: validating.clusterimagepolicy.rhtas.com + failurePolicy: Fail + namespaceSelector: + matchExpressions: + - key: policy.rhtas.com/include + operator: In + values: ["true"] + webhookNames: + defaulting: "defaulting.clusterimagepolicy.rhtas.com" + validating: "validating.clusterimagepolicy.rhtas.com" diff --git a/test/e2e/custom_resources/byok/trust_root.yaml.tpl b/test/e2e/custom_resources/byok/trust_root.yaml.tpl new file mode 100644 index 00000000..136fba05 --- /dev/null +++ b/test/e2e/custom_resources/byok/trust_root.yaml.tpl @@ -0,0 +1,30 @@ +apiVersion: policy.sigstore.dev/v1alpha1 +kind: TrustRoot +metadata: + name: byok-install-trust-root +spec: + sigstoreKeys: + certificateAuthorities: + - subject: + organization: {{ .FULCIO_ORG_NAME }} + commonName: {{ .FULCIO_COMMON_NAME }} + uri: {{ .FULCIO_URL }} + certChain: |- +{{ nindent 8 .FULCIO_CERT_CHAIN }} + ctLogs: + - baseURL: {{ .CTLOG_URL }} + hashAlgorithm: {{ .CTLOG_HASH_ALGORITHM }} + publicKey: |- +{{ nindent 8 .CTFE_PUBLIC_KEY }} + tLogs: + - baseURL: {{ .REKOR_URL }} + hashAlgorithm: {{ .REKOR_HASH_ALGORITHM }} + publicKey: |- +{{ nindent 8 .REKOR_PUBLIC_KEY }} + timestampAuthorities: + - subject: + organization: {{ .TSA_ORG_NAME }} + commonName: {{ .TSA_COMMON_NAME }} + uri: {{ .TSA_URL }} + certChain: |- +{{ nindent 8 .TSA_CERT_CHAIN }} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 3cdc2304..45056005 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -54,11 +54,12 @@ var _ = SynchronizedBeforeSuite(func() []byte { ctx = context.Background() fmt.Println(">>> Running tests with the following parameters:") - fmt.Printf(" %-22s %s\n", "TUF URL:", e2e_utils.TufUrl()) - fmt.Printf(" %-22s %s\n", "Rekor URL:", e2e_utils.RekorUrl()) - fmt.Printf(" %-22s %s\n", "Fulcio URL:", e2e_utils.FulcioUrl()) - fmt.Printf(" %-22s %s\n", "OIDC Issuer URL:", e2e_utils.OidcIssuerUrl()) - fmt.Printf(" %-22s %s\n", "OIDC Issuer Subject:", e2e_utils.OidcIssuerSubject()) - fmt.Printf(" %-22s %s\n", "Inject CA:", e2e_utils.InjectCA()) - fmt.Printf(" %-22s %s\n", "Test Image:", e2e_utils.TestImage()) + fmt.Printf(" %-22s %s\n", "RHTAS Install Namespace:", e2e_utils.RhtasInstallNamespace()) + fmt.Printf(" %-24s %s\n", "TUF URL:", e2e_utils.TufUrl()) + fmt.Printf(" %-24s %s\n", "TSA URL:", e2e_utils.TsaUrl()) + fmt.Printf(" %-24s %s\n", "Rekor URL:", e2e_utils.RekorUrl()) + fmt.Printf(" %-24s %s\n", "Fulcio URL:", e2e_utils.FulcioUrl()) + fmt.Printf(" %-24s %s\n", "OIDC Issuer URL:", e2e_utils.OidcIssuerUrl()) + fmt.Printf(" %-24s %s\n", "OIDC Issuer Subject:", e2e_utils.OidcIssuerSubject()) + fmt.Printf(" %-24s %s\n", "Inject CA:", e2e_utils.InjectCA()) }) diff --git a/test/e2e/utils/common.go b/test/e2e/utils/common.go index 8317862c..1c13934e 100644 --- a/test/e2e/utils/common.go +++ b/test/e2e/utils/common.go @@ -3,8 +3,11 @@ package e2e_utils import ( "bytes" "context" + "encoding/base64" "fmt" "os" + "path/filepath" + "strings" "text/template" . "github.com/onsi/gomega" @@ -12,13 +15,15 @@ import ( ) const ( - defaultFulcioUrl = "http://fulcio-server.local" - fulcioEnv = "COSIGN_FULCIO_URL" - - defaultRekorUrl = "http://rekor-server.local" - rekorEnv = "COSIGN_REKOR_URL" - - testImageEnv = "TEST_IMAGE" + InstallNamespace = "policy-controller-operator" + DeploymentName = "policycontroller-sample-policy-controller-webhook" + ValidatingWebhookName = "policy.rhtas.com" + MutatingWebhookName = "policy.rhtas.com" + CipValidatingWebhookName = "validating.clusterimagepolicy.rhtas.com" + CipMutatingWebhookName = "defaulting.clusterimagepolicy.rhtas.com" + WebhookSvc = "webhook" + MetricsSvc = "policycontroller-sample-policy-controller-webhook-metrics" + SecretName = "webhook-certs" ) func EnvOrDefault(env string, defualt string) string { @@ -29,18 +34,6 @@ func EnvOrDefault(env string, defualt string) string { return defualt } -func FulcioUrl() string { - return EnvOrDefault(fulcioEnv, defaultFulcioUrl) -} - -func RekorUrl() string { - return EnvOrDefault(rekorEnv, defaultRekorUrl) -} - -func TestImage() string { - return EnvOrDefault(testImageEnv, "") -} - func ExpectExists(name, namespace string, obj client.Object, k8sClient client.Client, ctx context.Context) { Eventually(func() error { return k8sClient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: name}, obj) @@ -48,7 +41,8 @@ func ExpectExists(name, namespace string, obj client.Object, k8sClient client.Cl } func RenderTemplate(path string, data interface{}) ([]byte, error) { - tpl, err := template.ParseFiles(path) + funcMap := template.FuncMap{"nindent": nindent} + tpl, err := template.New(filepath.Base(path)).Funcs(funcMap).ParseFiles(path) if err != nil { return nil, fmt.Errorf("failed to parse template %s: %w", path, err) } @@ -60,3 +54,12 @@ func RenderTemplate(path string, data interface{}) ([]byte, error) { return buf.Bytes(), nil } + +func nindent(n int, s string) string { + pad := strings.Repeat(" ", n) + return pad + strings.ReplaceAll(strings.TrimRight(s, "\n"), "\n", "\n"+pad) +} + +func Base64EncodeString(src []byte) string { + return base64.StdEncoding.EncodeToString(src) +} diff --git a/test/e2e/utils/cosign.go b/test/e2e/utils/cosign.go index 4c4392af..6e3e30f9 100644 --- a/test/e2e/utils/cosign.go +++ b/test/e2e/utils/cosign.go @@ -13,7 +13,7 @@ func VerifyByCosign(ctx context.Context, targetImageName string) { Expect(oidcToken).ToNot(BeEmpty()) Expect(Execute("cosign", "initialize", "--mirror="+TufUrl(), "--root="+TufUrl()+"/root.json")).To(Succeed()) - Expect(Execute("cosign", "sign", "-y", "--fulcio-url="+FulcioUrl(), "--rekor-url="+RekorUrl(), "--oidc-issuer="+OidcIssuerUrl(), "--oidc-client-id="+OidcClientID(), "--identity-token="+oidcToken, targetImageName)).To(Succeed()) + Expect(Execute("cosign", "sign", "-y", "--timestamp-server-url="+TsaUrl(), "--fulcio-url="+FulcioUrl(), "--rekor-url="+RekorUrl(), "--oidc-issuer="+OidcIssuerUrl(), "--oidc-client-id="+OidcClientID(), "--identity-token="+oidcToken, targetImageName)).To(Succeed()) Expect(Execute("cosign", "verify", "--rekor-url="+RekorUrl(), "--certificate-identity-regexp", ".*@redhat", "--certificate-oidc-issuer-regexp", ".*keycloak.*", targetImageName)).To(Succeed()) } @@ -35,6 +35,7 @@ func AttachProvenance(ctx context.Context, targetImageName string) { "--type", "slsaprovenance", "--fulcio-url="+FulcioUrl(), "--rekor-url="+RekorUrl(), + "--timestamp-server-url="+TsaUrl(), "--oidc-issuer="+OidcIssuerUrl(), "--oidc-client-id="+OidcClientID(), "--identity-token="+oidcToken, @@ -67,6 +68,7 @@ func AttachSBOM(ctx context.Context, targetImageName string) { "--yes", "--predicate", "-", "--type", "cyclonedx", + "--timestamp-server-url="+TsaUrl(), "--fulcio-url="+FulcioUrl(), "--rekor-url="+RekorUrl(), "--oidc-issuer="+OidcIssuerUrl(), diff --git a/test/e2e/utils/image.go b/test/e2e/utils/image.go new file mode 100644 index 00000000..48aeb31c --- /dev/null +++ b/test/e2e/utils/image.go @@ -0,0 +1,43 @@ +package e2e_utils + +import ( + "context" + "fmt" + "os" + + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/random" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/uuid" +) + +func PrepareImage(ctx context.Context, imageENV string) string { + if v, ok := os.LookupEnv(imageENV); ok { + return v + } + + image, err := random.Image(1024, 8) + if err != nil { + panic(err.Error()) + } + + targetImageName := fmt.Sprintf("ttl.sh/%s:15m", uuid.New().String()) + ref, err := name.ParseReference(targetImageName) + if err != nil { + panic(err.Error()) + } + + pusher, err := remote.NewPusher() + if err != nil { + panic(err.Error()) + } + + err = pusher.Push(ctx, ref, image) + if err != nil { + panic(err.Error()) + } + if err = os.Setenv(imageENV, targetImageName); err != nil { + panic(err.Error()) + } + return targetImageName +} diff --git a/test/e2e/utils/kubernetes.go b/test/e2e/utils/kubernetes.go index 2ef97a24..b3c48aa3 100644 --- a/test/e2e/utils/kubernetes.go +++ b/test/e2e/utils/kubernetes.go @@ -10,6 +10,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer/yaml" + "k8s.io/apimachinery/pkg/util/wait" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" @@ -27,7 +28,7 @@ func InjectCA() string { return EnvOrDefault(injectCA, defaultInjectCA) } -func CreateTestPod(ctx context.Context, k8sClient client.Client, ns string) error { +func CreateTestPod(ctx context.Context, k8sClient client.Client, ns, testImage string) error { pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: "test-pod", @@ -37,7 +38,7 @@ func CreateTestPod(ctx context.Context, k8sClient client.Client, ns string) erro Containers: []corev1.Container{ { Name: "test-image", - Image: TestImage(), + Image: testImage, }, }, }, @@ -79,36 +80,36 @@ func ApplyManifest(ctx context.Context, k8sClient client.Client, data []byte, fi return k8sClient.Patch(ctx, obj, client.Apply, client.FieldOwner("policy-controller-operator")) } -func DeleteManifest(ctx context.Context, k8sClient client.Client, data []byte, filepath string) error { - var err error - - if data == nil { - data, err = os.ReadFile(filepath) - if err != nil { - return err - } - } - - dec := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme) - obj := &unstructured.Unstructured{} - _, _, err = dec.Decode(data, nil, obj) - if err != nil { - return fmt.Errorf("error decoding %s into Unstructured: %w", filepath, err) - } - return k8sClient.Delete(ctx, obj) -} - func DeleteResource(ctx context.Context, k8sClient client.Client, gvk schema.GroupVersionKind, name, namespace string) error { obj := &unstructured.Unstructured{} obj.SetGroupVersionKind(gvk) obj.SetName(name) - obj.SetNamespace(namespace) + if namespace != "" { + obj.SetNamespace(namespace) + } if err := k8sClient.Delete(ctx, obj); err != nil && !errors.IsNotFound(err) { - return fmt.Errorf("failed to delete %s %q in namespace %q: %w", - gvk.Kind, name, namespace, err) + return fmt.Errorf("failed to delete %s %q: %w", gvk.Kind, name, err) + } + + key := client.ObjectKey{Name: name, Namespace: namespace} + backoff := wait.Backoff{ + Duration: 1 * time.Second, + Factor: 1.5, + Steps: 10, + Jitter: 0.1, } - return nil + return wait.ExponentialBackoffWithContext(ctx, backoff, func(ctx context.Context) (bool, error) { + getErr := k8sClient.Get(ctx, key, obj) + switch { + case errors.IsNotFound(getErr): + return true, nil + case getErr != nil: + return false, getErr + default: + return false, nil + } + }) } func InjectCAIntoDeployment(ctx context.Context, k8sClient client.Client, deploymentName, namespace string) error { diff --git a/test/e2e/utils/rhtas.go b/test/e2e/utils/rhtas.go new file mode 100644 index 00000000..e76cdb18 --- /dev/null +++ b/test/e2e/utils/rhtas.go @@ -0,0 +1,199 @@ +package e2e_utils + +import ( + "context" + "crypto/ecdsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" + "io" + "net/http" + + "github.com/sigstore/sigstore-go/pkg/tuf" +) + +const ( + defaultFulcioUrl = "http://fulcio-server.local" + fulcioEnv = "COSIGN_FULCIO_URL" + + defaultRekorUrl = "http://rekor-server.local" + rekorEnv = "COSIGN_REKOR_URL" + + defaultTsaUrl = "http://tsa-server.local" + tsaEnv = "TSA_URL" + + defaultTufMirror = "http://tuf.local" + tufMirrorEnv = "TUF_URL" + + defaultRhtasInstallNamespace = "openshift-rhtas-operator" + rhtasInstallNamespaceEnv = "RHTAS_INSTALL_NAMESPACE" + + fulcioTufTarget = "fulcio_v1.crt.pem" + tsaTufTarget = "tsa.certchain.pem" + ctlogTufTarget = "ctfe.pub" + rekorTufTarget = "rekor.pub" +) + +type TrustRootValues struct { + FulcioOrgName string + FulcioCommonName string + FulcioCertChain string + CtfePublicKey string + CtLogHashAlgo string + RekorPublicKey string + RekorHashAlgo string + TsaOrgName string + TsaCommonName string + TsaCertChain string +} + +func FulcioUrl() string { + return EnvOrDefault(fulcioEnv, defaultFulcioUrl) +} + +func RekorUrl() string { + return EnvOrDefault(rekorEnv, defaultRekorUrl) +} + +func TsaUrl() string { + return EnvOrDefault(tsaEnv, defaultTsaUrl) +} + +func TufUrl() string { + return EnvOrDefault(tufMirrorEnv, defaultTufMirror) +} + +func RhtasInstallNamespace() string { + return EnvOrDefault(rhtasInstallNamespaceEnv, defaultRhtasInstallNamespace) +} + +func ParseTufRoot(ctx context.Context) (*TrustRootValues, error) { + tufRoot, err := ResolveTufRoot(ctx) + if err != nil { + return nil, err + } + opts := tuf.Options{ + Root: tufRoot, + RepositoryBaseURL: TufUrl(), + DisableLocalCache: true, + } + client, err := tuf.New(&opts) + if err != nil { + return nil, fmt.Errorf("init TUF client: %w", err) + } + + raw := make(map[string][]byte, 4) + for _, name := range []string{fulcioTufTarget, tsaTufTarget, ctlogTufTarget, rekorTufTarget} { + b, err := client.GetTarget(name) + if err != nil { + return nil, err + } + raw[name] = b + } + + fCN, fOrg, fChain, err := parsePEMBundle(raw[fulcioTufTarget]) + if err != nil { + return nil, fmt.Errorf("parse fulcio cert: %w", err) + } + + tCN, tOrg, tChain, err := parsePEMBundle(raw[tsaTufTarget]) + if err != nil { + return nil, fmt.Errorf("parse tsa cert: %w", err) + } + + ctKey, ctAlgo, err := parsePubKey(raw[ctlogTufTarget]) + if err != nil { + return nil, fmt.Errorf("parse ctfe key: %w", err) + } + + rKey, rAlgo, err := parsePubKey(raw[rekorTufTarget]) + if err != nil { + return nil, fmt.Errorf("parse rekor key: %w", err) + } + + return &TrustRootValues{ + FulcioOrgName: fOrg, + FulcioCommonName: fCN, + FulcioCertChain: Base64EncodeString([]byte(fChain)), + CtfePublicKey: ctKey, + CtLogHashAlgo: ctAlgo, + RekorPublicKey: rKey, + RekorHashAlgo: rAlgo, + TsaOrgName: tOrg, + TsaCommonName: tCN, + TsaCertChain: Base64EncodeString([]byte(tChain)), + }, nil +} + +func ResolveTufRoot(ctx context.Context) ([]byte, error) { + url := fmt.Sprintf("%s/root.json", TufUrl()) + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return []byte{}, err + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return []byte{}, err + } + defer resp.Body.Close() + + rootBytes, err := io.ReadAll(resp.Body) + if err != nil { + return []byte{}, err + } + + return rootBytes, nil +} + +func parsePEMBundle(pemBytes []byte) (cn, org, bundle string, err error) { + var block *pem.Block + rest := pemBytes + for { + block, rest = pem.Decode(rest) + if block == nil { + return "", "", "", errors.New("no cert in bundle") + } + if block.Type == "CERTIFICATE" { + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return "", "", "", err + } + cn = cert.Subject.CommonName + if len(cert.Subject.Organization) > 0 { + org = cert.Subject.Organization[0] + } + break + } + } + return cn, org, string(pemBytes), nil +} + +func parsePubKey(pemBytes []byte) (keyB64, algo string, err error) { + block, _ := pem.Decode(pemBytes) + if block == nil { + return "", "", errors.New("no key found") + } + + keyB64 = Base64EncodeString(pemBytes) + switch block.Type { + case "PUBLIC KEY": + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return "", "", err + } + switch pub.(type) { + case *ecdsa.PublicKey: + algo = "sha256" + default: + algo = "unknown" + } + default: + algo = "unknown" + } + + return keyB64, algo, nil +} diff --git a/test/e2e/utils/tuf.go b/test/e2e/utils/tuf.go deleted file mode 100644 index 6f0f707b..00000000 --- a/test/e2e/utils/tuf.go +++ /dev/null @@ -1,42 +0,0 @@ -package e2e_utils - -import ( - "context" - "encoding/base64" - "fmt" - "io" - "net/http" -) - -const ( - defaultTufMirror = "http://tuf.local" - tufMirrorEnv = "TUF_URL" -) - -func TufUrl() string { - return EnvOrDefault(tufMirrorEnv, defaultTufMirror) -} - -func ResolveBase64TufRoot(ctx context.Context) (string, error) { - url := fmt.Sprintf("%s/root.json", TufUrl()) - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) - if err != nil { - return "", err - } - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - rootBytes, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } - - encodedRoot := base64.StdEncoding.EncodeToString(rootBytes) - return encodedRoot, nil -}