From 08fd23f2b2c310dc0dbe1c27d438b965854b9484 Mon Sep 17 00:00:00 2001 From: Rafael Benevides Date: Mon, 6 Jul 2026 15:56:44 -0300 Subject: [PATCH] HYPERFLEET-1321 - test: add entity render scenarios and refactor test-helm script --- .bingo/Variables.mk | 7 + .bingo/variables.env | 3 + .bingo/yq.mod | 5 + .bingo/yq.sum | 73 ++++++ Makefile | 250 +------------------- charts/ci/entities-all-fields-values.yaml | 34 +++ scripts/test-helm.sh | 270 ++++++++++++++++++++++ 7 files changed, 394 insertions(+), 248 deletions(-) create mode 100644 .bingo/yq.mod create mode 100644 .bingo/yq.sum create mode 100644 charts/ci/entities-all-fields-values.yaml create mode 100755 scripts/test-helm.sh diff --git a/.bingo/Variables.mk b/.bingo/Variables.mk index 0b38b9b7..ee630acd 100644 --- a/.bingo/Variables.mk +++ b/.bingo/Variables.mk @@ -58,3 +58,10 @@ $(OAPI_CODEGEN): $(BINGO_DIR)/oapi-codegen.mod @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. @echo "(re)installing $(GOBIN)/oapi-codegen-v2.6.0" @cd $(BINGO_DIR) && GOWORK=off GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) GOARM=$(GOHOSTARM) $(GO) build -mod=mod -modfile=oapi-codegen.mod -o=$(GOBIN)/oapi-codegen-v2.6.0 "github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen" + +YQ := $(GOBIN)/yq-v4.53.3 +$(YQ): $(BINGO_DIR)/yq.mod + @# Install binary/ries using Go 1.14+ build command. This is using bwplotka/bingo-controlled, separate go module with pinned dependencies. + @echo "(re)installing $(GOBIN)/yq-v4.53.3" + @cd $(BINGO_DIR) && GOWORK=off GOOS=$(GOHOSTOS) GOARCH=$(GOHOSTARCH) GOARM=$(GOHOSTARM) $(GO) build -mod=mod -modfile=yq.mod -o=$(GOBIN)/yq-v4.53.3 "github.com/mikefarah/yq/v4" + diff --git a/.bingo/variables.env b/.bingo/variables.env index a81a4aea..af29e307 100644 --- a/.bingo/variables.env +++ b/.bingo/variables.env @@ -19,3 +19,6 @@ KUBECONFORM="${GOBIN}/kubeconform-v0.7.0" MOCKGEN="${GOBIN}/mockgen-v0.6.0" OAPI_CODEGEN="${GOBIN}/oapi-codegen-v2.6.0" + +YQ="${GOBIN}/yq-v4.53.3" + diff --git a/.bingo/yq.mod b/.bingo/yq.mod new file mode 100644 index 00000000..b784d8d8 --- /dev/null +++ b/.bingo/yq.mod @@ -0,0 +1,5 @@ +module _ // Auto generated by https://github.com/bwplotka/bingo. DO NOT EDIT + +go 1.25.7 + +require github.com/mikefarah/yq/v4 v4.53.3 diff --git a/.bingo/yq.sum b/.bingo/yq.sum new file mode 100644 index 00000000..8c598217 --- /dev/null +++ b/.bingo/yq.sum @@ -0,0 +1,73 @@ +github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY= +github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU= +github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8= +github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= +github.com/alecthomas/participle/v2 v2.1.4 h1:W/H79S8Sat/krZ3el6sQMvMaahJ+XcM9WSI2naI7w2U= +github.com/alecthomas/participle/v2 v2.1.4/go.mod h1:8tqVbpTX20Ru4NfYQgZf4mP18eXPTBViyMWiArNEgGI= +github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= +github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= +github.com/elliotchance/orderedmap v1.8.0 h1:TrOREecvh3JbS+NCgwposXG5ZTFHtEsQiCGOhPElnMw= +github.com/elliotchance/orderedmap v1.8.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= +github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= +github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= +github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= +github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mikefarah/yq/v4 v4.53.3 h1:S0kwPkINdOjgPskW5CfYTGo/Vt7c3gUnUkKSp7CNqD0= +github.com/mikefarah/yq/v4 v4.53.3/go.mod h1:4mvgujMtSAB3lA27+lwg8eIud/4QUQw2/1rLbxgiauw= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/gopher-lua v1.1.2 h1:yF/FjE3hD65tBbt0VXLE13HWS9h34fdzJmrWRXwobGA= +github.com/yuin/gopher-lua v1.1.2/go.mod h1:7aRmXIWl37SqRf0koeyylBEzJ+aPt8A+mmkQ4f1ntR8= +github.com/zclconf/go-cty v1.18.1 h1:yEGE8M4iIZlyKQURZNb2SnEyZlZHUcBCnx6KF81KuwM= +github.com/zclconf/go-cty v1.18.1/go.mod h1:qpnV6EDNgC1sns/AleL1fvatHw72j+S+nS+MJ+T2CSg= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U= +go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= +golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= +golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/Makefile b/Makefile index 7ddd87ae..f3e06803 100755 --- a/Makefile +++ b/Makefile @@ -322,255 +322,9 @@ verify-helm-docs: $(HELM_DOCS) ## Verify chart README is up to date @git diff --exit-code charts/README.md > /dev/null 2>&1 || \ (echo "ERROR: charts/README.md is out of date. Run 'make helm-docs' and commit the result." && exit 1) -# kubeconform flags for validating rendered Helm templates against Kubernetes -# and CRD schemas. Uses the datreeio/CRDs-catalog for ServiceMonitor and -# PrometheusRule schemas. -KUBECONFORM_FLAGS := \ - -strict \ - -kubernetes-version 1.30.0 \ - -schema-location default \ - -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' - .PHONY: test-helm -test-helm: $(KUBECONFORM) verify-helm-docs ## Test Helm charts (lint, template, validate, kubeconform) - @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - @echo "Testing Helm charts..." - @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - @if ! command -v helm > /dev/null; then \ - echo "Error: helm not found. Please install Helm:"; \ - echo " brew install helm # macOS"; \ - echo " curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # Linux"; \ - exit 1; \ - fi - @echo "Linting Helm chart..." - helm lint charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' - @echo "" - @echo "Testing template rendering with default values..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Default values template OK" - @echo "" - @echo "Testing template with external database..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set database.postgresql.enabled=false \ - --set database.external.enabled=true \ - --set database.external.secretName=my-db-secret | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "External database config template OK" - @echo "" - @echo "Testing template with autoscaling..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set autoscaling.enabled=true \ - --set autoscaling.minReplicas=2 \ - --set autoscaling.maxReplicas=5 | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Autoscaling config template OK" - @echo "" - @echo "Testing template with PDB enabled..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set podDisruptionBudget.enabled=true \ - --set podDisruptionBudget.minAvailable=1 | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "PDB config template OK" - @echo "" - @echo "Testing template with ServiceMonitor enabled..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set serviceMonitor.enabled=true \ - --set serviceMonitor.interval=15s | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "ServiceMonitor config template OK" - @echo "" - @echo "Testing template with PodMonitoring enabled..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set monitoring.podMonitoring.enabled=true \ - --set monitoring.podMonitoring.interval=15s | $(KUBECONFORM) $(KUBECONFORM_FLAGS) -ignore-missing-schemas - @echo "PodMonitoring config template OK" - @echo "" - @echo "Testing template with PodMonitoring and TLS enabled..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set monitoring.podMonitoring.enabled=true \ - --set config.metrics.tls.enabled=true \ - --set monitoring.podMonitoring.tlsConfig.insecureSkipVerify=true | $(KUBECONFORM) $(KUBECONFORM_FLAGS) -ignore-missing-schemas - @echo "PodMonitoring with TLS config template OK" - @echo "" - @echo "Testing template with auth disabled..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set config.server.jwt.enabled=false | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Auth disabled config template OK" - @echo "" - @echo "Testing template with custom image..." - helm template test-release charts/ \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set image.registry=quay.io \ - --set image.repository=myorg/hyperfleet-api \ - --set image.tag=v1.0.0 | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Custom image config template OK" - @echo "" - @echo "Testing template with sidecar injection..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set-json 'sidecars=[{"name":"test-sidecar","image":"busybox:1.36","command":["sleep","infinity"]}]' | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Sidecar injection config template OK" - @echo "" - @echo "Testing template with native sidecar injection..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set-json 'nativeSidecars=[{"name":"cloud-sql-proxy","restartPolicy":"Always","image":"gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.3","args":["--structured-logs","--port=5432","project:region:instance"]}]'); \ - echo "$$OUTPUT" | grep -q 'name: cloud-sql-proxy' || { echo "FAIL: cloud-sql-proxy not found in rendered output"; exit 1; }; \ - PROXY_LINE=$$(echo "$$OUTPUT" | grep -n 'name: cloud-sql-proxy' | head -1 | cut -d: -f1); \ - MIGRATE_LINE=$$(echo "$$OUTPUT" | grep -n 'name: db-migrate' | head -1 | cut -d: -f1); \ - if [ "$$PROXY_LINE" -ge "$$MIGRATE_LINE" ]; then echo "FAIL: cloud-sql-proxy must appear before db-migrate"; exit 1; fi; \ - echo "$$OUTPUT" | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Native sidecar injection config template OK" - @echo "" - @echo "Testing template with native sidecars and no database..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set database.postgresql.enabled=false \ - --set-json 'nativeSidecars=[{"name":"test-proxy","restartPolicy":"Always","image":"busybox:1.36","command":["sleep","infinity"]}]'); \ - echo "$$OUTPUT" | grep -q 'name: test-proxy' || { echo "FAIL: test-proxy not found in rendered output"; exit 1; }; \ - if echo "$$OUTPUT" | grep -q 'name: wait-for-db'; then echo "FAIL: wait-for-db should not appear when no database is configured"; exit 1; fi; \ - if echo "$$OUTPUT" | grep -q 'name: db-migrate'; then echo "FAIL: db-migrate should not appear when no database is configured"; exit 1; fi; \ - echo "$$OUTPUT" | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Native sidecar without database config template OK" - @echo "" - @echo "Testing template with full adapter config..." - helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set-json 'adapters.cluster=["validation","dns","pullsecret","hypershift"]' \ - --set-json 'adapters.nodepool=["validation","hypershift"]' | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Full adapter config template OK" - @echo "" - @echo "Testing template with validation schema enabled..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set validationSchema.enabled=true \ - --set-string 'validationSchema.content=openapi: 3.0.0'); \ - echo "$$OUTPUT" | grep -q 'app.kubernetes.io/component: validation-schema' || { echo "FAIL: validation-schema ConfigMap not found"; exit 1; }; \ - echo "$$OUTPUT" | grep -q '/etc/hyperfleet/validation-schema' || { echo "FAIL: validation schema volume mount not found"; exit 1; }; \ - echo "$$OUTPUT" | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Validation schema enabled config template OK" - @echo "" - @echo "Testing template with validation schema disabled (default)..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]'); \ - if echo "$$OUTPUT" | grep -q 'validation-schema'; then echo "FAIL: validation-schema should not appear when disabled"; exit 1; fi; \ - echo "$$OUTPUT" | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Validation schema disabled config template OK" - @echo "" - @echo "Testing template with validation schema existingConfigMap..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set validationSchema.enabled=true \ - --set validationSchema.existingConfigMap=my-validation-schema); \ - echo "$$OUTPUT" | grep -q 'my-validation-schema' || { echo "FAIL: existingConfigMap name not found"; exit 1; }; \ - if echo "$$OUTPUT" | grep -q 'app.kubernetes.io/component: validation-schema'; then echo "FAIL: generated ConfigMap should not appear with existingConfigMap"; exit 1; fi; \ - echo "$$OUTPUT" | $(KUBECONFORM) $(KUBECONFORM_FLAGS) - @echo "Validation schema existingConfigMap config template OK" - @echo "" - @echo "Testing validation schema fails without content or existingConfigMap..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set validationSchema.enabled=true 2>&1); \ - if [ $$? -eq 0 ]; then \ - echo "FAIL: should fail when validationSchema.enabled=true without content or existingConfigMap"; exit 1; \ - fi; \ - echo "$$OUTPUT" | grep -q 'validationSchema.content is required' || { \ - echo "FAIL: expected validationSchema validation error message"; echo "$$OUTPUT"; exit 1; \ - } - @echo "Validation schema validation (no content) OK" - @echo "" - @echo "Testing validation schema fails with whitespace-only content..." - @OUTPUT=$$(helm template test-release charts/ \ - --set image.registry=quay.io \ - --set image.repository=openshift-hyperfleet/hyperfleet-api \ - --set image.tag=test \ - --set 'adapters.cluster=["validation"]' \ - --set 'adapters.nodepool=["validation"]' \ - --set validationSchema.enabled=true \ - --set-string 'validationSchema.content= ' 2>&1); \ - if [ $$? -eq 0 ]; then \ - echo "FAIL: should fail when validationSchema.content is whitespace-only"; exit 1; \ - fi; \ - echo "$$OUTPUT" | grep -q 'validationSchema.content is required' || { \ - echo "FAIL: expected validationSchema validation error message"; echo "$$OUTPUT"; exit 1; \ - } - @echo "Validation schema validation (whitespace-only content) OK" - @echo "" - @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" - @echo "All Helm chart tests passed!" - @echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +test-helm: $(KUBECONFORM) $(YQ) verify-helm-docs ## Test Helm charts (lint, template, validate, kubeconform) + @KUBECONFORM=$(KUBECONFORM) YQ=$(YQ) ./scripts/test-helm.sh ##@ Container Images diff --git a/charts/ci/entities-all-fields-values.yaml b/charts/ci/entities-all-fields-values.yaml new file mode 100644 index 00000000..be4715c9 --- /dev/null +++ b/charts/ci/entities-all-fields-values.yaml @@ -0,0 +1,34 @@ +# Test values: entities with all optional fields populated. +# Used by make test-helm to validate configmap template rendering. +image: + registry: quay.io + repository: openshift-hyperfleet/hyperfleet-api + tag: test + +adapters: + cluster: ["validation"] + nodepool: ["validation"] + +config: + entities: + - kind: Channel + plural: channels + spec_schema_name: ChannelSpec + search_disallowed_fields: [spec] + - kind: Version + plural: versions + parent_kind: Channel + on_parent_delete: restrict + spec_schema_name: VersionSpec + search_disallowed_fields: [spec] + required_adapters: + - validation + references: + - ref_type: channel + target_kind: Channel + min: 1 + max: 1 + - kind: WifConfig + plural: wifconfigs + spec_schema_name: WifConfigSpec + search_disallowed_fields: [spec] diff --git a/scripts/test-helm.sh b/scripts/test-helm.sh new file mode 100755 index 00000000..b968848c --- /dev/null +++ b/scripts/test-helm.sh @@ -0,0 +1,270 @@ +#!/usr/bin/env bash +set -euo pipefail + +CHART_DIR="charts" +RELEASE_NAME="test-release" + +KUBECONFORM="${KUBECONFORM:-kubeconform}" +YQ="${YQ:-yq}" +KUBECONFORM_FLAGS=( + -strict + -kubernetes-version 1.30.0 + -schema-location default + -schema-location 'https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/{{.Group}}/{{.ResourceKind}}_{{.ResourceAPIVersion}}.json' +) + +PASSED=0 +FAILED=0 +SCENARIO_FAILED=0 + +DEFAULT_SETS=( + --set image.registry=quay.io + --set image.repository=openshift-hyperfleet/hyperfleet-api + --set image.tag=test + --set 'adapters.cluster=["validation"]' + --set 'adapters.nodepool=["validation"]' +) + +render() { + helm template "$RELEASE_NAME" "$CHART_DIR" "${DEFAULT_SETS[@]}" "$@" +} + +kubeconform_validate() { + "$KUBECONFORM" "${KUBECONFORM_FLAGS[@]}" "$@" +} + +extract_config_yaml() { + sed -n '/config.yaml: |/,/^[^ ]/{ /config.yaml: |/d; /^[^ ]/d; s/^ //; p; }' +} + +validate_yaml() { + "$YQ" eval '.' - > /dev/null +} + +pass() { + if [ "$SCENARIO_FAILED" -eq 0 ]; then + echo " ✓ $1" + PASSED=$((PASSED + 1)) + fi +} + +fail() { + echo " ✗ FAIL: $1" + FAILED=$((FAILED + 1)) + SCENARIO_FAILED=1 +} + +run_test() { + SCENARIO_FAILED=0 + echo "" + echo "Testing $1..." +} + +assert_contains() { + local input="$1" pattern="$2" msg="$3" + if ! echo "$input" | grep -q "$pattern"; then + fail "$msg" + fi +} + +assert_not_contains() { + local input="$1" pattern="$2" msg="$3" + if echo "$input" | grep -q "$pattern"; then + fail "$msg" + fi +} + +# ─── Pre-flight checks ─────────────────────────────────────────────── + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Testing Helm charts..." +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +if ! command -v helm > /dev/null; then + echo "Error: helm not found. Please install Helm:" + echo " brew install helm # macOS" + echo " curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # Linux" + exit 1 +fi + +# ─── Lint ───────────────────────────────────────────────────────────── + +echo "" +echo "Linting Helm chart..." +helm lint "$CHART_DIR" "${DEFAULT_SETS[@]}" + +# ─── Template rendering tests ──────────────────────────────────────── + +run_test "template rendering with default values" +render | kubeconform_validate +pass "Default values template" + +run_test "template with external database" +render \ + --set database.postgresql.enabled=false \ + --set database.external.enabled=true \ + --set database.external.secretName=my-db-secret | kubeconform_validate +pass "External database config template" + +run_test "template with autoscaling" +render \ + --set autoscaling.enabled=true \ + --set autoscaling.minReplicas=2 \ + --set autoscaling.maxReplicas=5 | kubeconform_validate +pass "Autoscaling config template" + +run_test "template with PDB enabled" +render \ + --set podDisruptionBudget.enabled=true \ + --set podDisruptionBudget.minAvailable=1 | kubeconform_validate +pass "PDB config template" + +run_test "template with ServiceMonitor enabled" +render \ + --set serviceMonitor.enabled=true \ + --set serviceMonitor.interval=15s | kubeconform_validate +pass "ServiceMonitor config template" + +run_test "template with PodMonitoring enabled" +render \ + --set monitoring.podMonitoring.enabled=true \ + --set monitoring.podMonitoring.interval=15s | kubeconform_validate -ignore-missing-schemas +pass "PodMonitoring config template" + +run_test "template with PodMonitoring and TLS enabled" +render \ + --set monitoring.podMonitoring.enabled=true \ + --set config.metrics.tls.enabled=true \ + --set monitoring.podMonitoring.tlsConfig.insecureSkipVerify=true | kubeconform_validate -ignore-missing-schemas +pass "PodMonitoring with TLS config template" + +run_test "template with auth disabled" +render --set config.server.jwt.enabled=false | kubeconform_validate +pass "Auth disabled config template" + +run_test "template with custom image" +helm template "$RELEASE_NAME" "$CHART_DIR" \ + --set 'adapters.cluster=["validation"]' \ + --set 'adapters.nodepool=["validation"]' \ + --set image.registry=quay.io \ + --set image.repository=myorg/hyperfleet-api \ + --set image.tag=v1.0.0 | kubeconform_validate +pass "Custom image config template" + +run_test "template with sidecar injection" +render \ + --set-json 'sidecars=[{"name":"test-sidecar","image":"busybox:1.36","command":["sleep","infinity"]}]' | kubeconform_validate +pass "Sidecar injection config template" + +run_test "template with native sidecar injection" +OUTPUT=$(render \ + --set-json 'nativeSidecars=[{"name":"cloud-sql-proxy","restartPolicy":"Always","image":"gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.3","args":["--structured-logs","--port=5432","project:region:instance"]}]') +assert_contains "$OUTPUT" 'name: cloud-sql-proxy' "cloud-sql-proxy not found in rendered output" +PROXY_LINE=$(echo "$OUTPUT" | grep -n 'name: cloud-sql-proxy' | head -1 | cut -d: -f1) +MIGRATE_LINE=$(echo "$OUTPUT" | grep -n 'name: db-migrate' | head -1 | cut -d: -f1) +if [ "$PROXY_LINE" -ge "$MIGRATE_LINE" ]; then + fail "cloud-sql-proxy must appear before db-migrate" +else + echo "$OUTPUT" | kubeconform_validate + pass "Native sidecar injection config template" +fi + +run_test "template with native sidecars and no database" +OUTPUT=$(render \ + --set database.postgresql.enabled=false \ + --set-json 'nativeSidecars=[{"name":"test-proxy","restartPolicy":"Always","image":"busybox:1.36","command":["sleep","infinity"]}]') +assert_contains "$OUTPUT" 'name: test-proxy' "test-proxy not found in rendered output" +assert_not_contains "$OUTPUT" 'name: wait-for-db' "wait-for-db should not appear when no database is configured" +assert_not_contains "$OUTPUT" 'name: db-migrate' "db-migrate should not appear when no database is configured" +echo "$OUTPUT" | kubeconform_validate +pass "Native sidecar without database config template" + +run_test "template with full adapter config" +helm template "$RELEASE_NAME" "$CHART_DIR" \ + --set image.registry=quay.io \ + --set image.repository=openshift-hyperfleet/hyperfleet-api \ + --set image.tag=test \ + --set-json 'adapters.cluster=["validation","dns","pullsecret","hypershift"]' \ + --set-json 'adapters.nodepool=["validation","hypershift"]' | kubeconform_validate +pass "Full adapter config template" + +# ─── Validation schema tests ───────────────────────────────────────── + +run_test "template with validation schema enabled" +OUTPUT=$(render \ + --set validationSchema.enabled=true \ + --set-string 'validationSchema.content=openapi: 3.0.0') +assert_contains "$OUTPUT" 'app.kubernetes.io/component: validation-schema' "validation-schema ConfigMap not found" +assert_contains "$OUTPUT" '/etc/hyperfleet/validation-schema' "validation schema volume mount not found" +echo "$OUTPUT" | kubeconform_validate +pass "Validation schema enabled config template" + +run_test "template with validation schema disabled (default)" +OUTPUT=$(render) +assert_not_contains "$OUTPUT" 'validation-schema' "validation-schema should not appear when disabled" +echo "$OUTPUT" | kubeconform_validate +pass "Validation schema disabled config template" + +run_test "template with validation schema existingConfigMap" +OUTPUT=$(render \ + --set validationSchema.enabled=true \ + --set validationSchema.existingConfigMap=my-validation-schema) +assert_contains "$OUTPUT" 'my-validation-schema' "existingConfigMap name not found" +assert_not_contains "$OUTPUT" 'app.kubernetes.io/component: validation-schema' "generated ConfigMap should not appear with existingConfigMap" +echo "$OUTPUT" | kubeconform_validate +pass "Validation schema existingConfigMap config template" + +run_test "validation schema fails without content or existingConfigMap" +OUTPUT=$(render --set validationSchema.enabled=true 2>&1 || true) +assert_contains "$OUTPUT" 'validationSchema.content is required' "expected validationSchema validation error message" +pass "Validation schema validation (no content)" + +run_test "validation schema fails with whitespace-only content" +OUTPUT=$(render --set validationSchema.enabled=true --set-string 'validationSchema.content= ' 2>&1 || true) +assert_contains "$OUTPUT" 'validationSchema.content is required' "expected validationSchema validation error message" +pass "Validation schema validation (whitespace-only content)" + +# ─── Entity rendering tests ────────────────────────────────────────── + +run_test "template with default entities (Channel, Version, WifConfig)" +OUTPUT=$(render) +CONFIG_YAML=$(echo "$OUTPUT" | extract_config_yaml) +echo "$CONFIG_YAML" | validate_yaml || fail "rendered config.yaml is not valid YAML" +assert_contains "$CONFIG_YAML" 'kind: Channel' "Channel entity not found in config.yaml" +assert_contains "$CONFIG_YAML" 'kind: Version' "Version entity not found in config.yaml" +assert_contains "$CONFIG_YAML" 'kind: WifConfig' "WifConfig entity not found in config.yaml" +echo "$OUTPUT" | kubeconform_validate +pass "Default entities config template" + +run_test "template with entities using all optional fields" +OUTPUT=$(helm template "$RELEASE_NAME" "$CHART_DIR" -f charts/ci/entities-all-fields-values.yaml) +CONFIG_YAML=$(echo "$OUTPUT" | extract_config_yaml) +echo "$CONFIG_YAML" | validate_yaml || fail "rendered config.yaml is not valid YAML with all entity fields" +assert_contains "$CONFIG_YAML" 'parent_kind: Channel' "parent_kind not found in config.yaml" +assert_contains "$CONFIG_YAML" 'on_parent_delete: restrict' "on_parent_delete not found in config.yaml" +assert_contains "$CONFIG_YAML" 'ref_type: channel' "references not found in config.yaml" +assert_contains "$CONFIG_YAML" 'required_adapters:' "required_adapters not found in config.yaml" +echo "$OUTPUT" | kubeconform_validate +pass "Entities with all optional fields config template" + +run_test "template with empty entities list" +OUTPUT=$(render --set-json 'config.entities=[]') +CONFIG_YAML=$(echo "$OUTPUT" | extract_config_yaml) +echo "$CONFIG_YAML" | validate_yaml || fail "rendered config.yaml is not valid YAML with empty entities" +assert_contains "$CONFIG_YAML" 'entities: \[\]' "expected entities: [] in config.yaml" +echo "$OUTPUT" | kubeconform_validate +pass "Empty entities list config template" + +# ─── Summary ───────────────────────────────────────────────────────── + +echo "" +if [ "$FAILED" -gt 0 ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "FAILED: $FAILED test(s) failed, $PASSED passed" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + exit 1 +fi + +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "All Helm chart tests passed! ($PASSED tests)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"