Skip to content

phase 3: helm test, CI matrix, and OCI release workflow#3

Merged
NoobCoder1209 merged 3 commits into
mainfrom
phase/3-tests
Jun 7, 2026
Merged

phase 3: helm test, CI matrix, and OCI release workflow#3
NoobCoder1209 merged 3 commits into
mainfrom
phase/3-tests

Conversation

@NoobCoder1209

Copy link
Copy Markdown
Owner

Summary

  • templates/tests/test-connection.yamlhelm.sh/hook: test pod with non-overlapping labels (so the chart's NetworkPolicy doesn't block its egress to the Service).
  • ci/{minimal,full,externalsecrets}-values.yaml — exercises default install, full optional-resource install, and ESO render (lint-only).
  • 3 GitHub workflows: lint.yml (helm lint + kubeconform + kube-linter), smoke.yml (kind 1.28 matrix, install + helm test + curl probe), release.yml (OCI publish to GHCR on v* tag).
  • Two values updates from kube-linter gates: podDisruptionBudget.unhealthyPodEvictionPolicy: AlwaysAllow default; default soft pod-anti-affinity by hostname.

Test plan

  • helm lint . clean against every ci/*-values.yaml.
  • helm template . | kubeconform -strict -ignore-missing-schemas -skip ExternalSecret,SecretStore,ClusterSecretStore -kubernetes-version 1.28.0 clean for every matrix file.
  • helm template . | kube-linter lint - clean for every matrix file.
  • Local kind 1.28.13 install + curl probe + helm test against minimal-values.yaml and full-values.yaml. helm test succeeds in ~6s.
  • CI runs both workflows on push.
  • First vX.Y.Z tag exercises release.yml.

- templates/tests/test-connection.yaml: helm.sh/hook test pod that
  curls the Service via wget and matches HTTP/.. 200. Uses distinct
  labels (component=test) so it does NOT match the chart's
  NetworkPolicy podSelector — without this the policy blocks the
  test pod's egress to the Service.
- ci/{minimal,full,externalsecrets}-values.yaml: matrix files that
  exercise default install, full optional-resource install, and
  ESO-rendered (lint-only) install.
- .github/workflows/lint.yml: helm lint + helm template | kubeconform
  + kube-linter against every ci/*-values.yaml. Pinned tool versions.
- .github/workflows/smoke.yml: kind 1.28.13 matrix. Install path runs
  helm install --wait, kubectl wait, in-cluster curl probe, and
  helm test. Lint-only path runs helm lint + kubeconform with ESO
  CRDs skipped. Diagnostics step on failure.
- .github/workflows/release.yml: triggers on tags v*. Verifies tag
  matches Chart.yaml version, then helm package + helm push to
  oci://ghcr.io/<owner>/charts. Owner lower-cased for OCI compliance.
- pdb.yaml: set unhealthyPodEvictionPolicy=AlwaysAllow (kube-linter
  gate; safe default for stateless services).
- values.yaml: default soft pod-anti-affinity by hostname (replicas
  spread across nodes when possible) — also satisfies kube-linter's
  no-anti-affinity gate.
- test-connection pod: cpu/memory requests+limits (kube-linter gate).

Verified end-to-end on a local kind 1.28 cluster:
- helm install minimal-values, full-values: clean
- in-cluster curl returns "hello from helm-chart-template"
- helm test (default + full-values): Succeeded in 6s
- kube-linter clean across all 3 matrix files
- kubeconform clean across all 3 matrix files (ESO Skip on the
  externalsecrets file as expected)
Major:
- values.yaml + _helpers.tpl + deployment.yaml: default
  pod-anti-affinity now uses the chart's selectorLabels (via a new
  http-echo.defaultAffinity helper) instead of a hardcoded
  app.kubernetes.io/name. nameOverride now works correctly. values
  default reverts to affinity: {} so an empty value triggers the
  synthesized rule; a populated value replaces entirely.
- smoke.yml: dropped redundant in-cluster curl probe + redundant
  kubectl wait. helm test already exercises connectivity from
  inside the cluster and is the source of truth.

Minor:
- test-connection.yaml: wget gets -T 5 -t 2 so failures surface in
  ~15s instead of busybox's default ~2 min retry loop.
- pdb.yaml: wrap unhealthyPodEvictionPolicy in `with` so users who
  set it to null don't render an empty value.
- smoke.yml: dropped the lint-only externalsecrets matrix entry
  (lint.yml already covers it on every PR; removing the duplicate
  saves CI minutes).

Nits:
- release.yml: dropped fetch-depth: 0 (helm package doesn't need
  history); added set -euo pipefail to the GHCR login step for
  consistency.
- ci/full-values.yaml + ci/externalsecrets-values.yaml: renamed the
  CI test-fixture key from API_TOKEN to DEMO_VALUE so it doesn't
  trip credential scanners; added a clarifying comment.

Verified locally:
- helm lint . clean across all 3 matrix files
- kubeconform clean across all 3
- kube-linter clean across all 3
- Default affinity renders synthesized rule with correct
  selectorLabels
- nameOverride=foo: synthesized rule's selectorLabels follow (name: foo)
- User-supplied affinity replaces synthesized rule entirely
The previous hook-delete-policy was `before-hook-creation,hook-succeeded`,
which deleted the test pod the moment it succeeded. `helm test --logs`
then failed to read logs of a pod that no longer existed:

  Error: unable to get pod logs for http-echo-test-connection:
  pods "http-echo-test-connection" not found

Drop `hook-succeeded` so the pod stays around after success. The next
`helm test` invocation will still clean it up via `before-hook-creation`.
@NoobCoder1209 NoobCoder1209 merged commit 19a6027 into main Jun 7, 2026
3 checks passed
@NoobCoder1209 NoobCoder1209 deleted the phase/3-tests branch June 7, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant