Thanks for your interest in contributing! This document covers everything you need to get started.
- Go 1.26+
- Docker
- kubectl
- golangci-lint
- hadolint for Dockerfile linting
- Helm for chart linting
- hurl for E2E test assertions
- A Kubernetes cluster for E2E testing (the CI uses kind)
- curl for HTTP assertions in E2E tests
- jq for JSON assertions in E2E tests
- oha (optional, only needed for the zero-downtime rollout test)
Clone the repository and build:
git clone https://github.com/HBTGmbH/k8s-httpcache.git
cd k8s-httpcache
go build .Build a static Linux binary (matching the CI):
CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags '-s -w -extldflags "-static" -buildid=' -o k8s-httpcache .go test -race ./...The CI uses gotestsum for nicer output and JUnit reports:
gotestsum --format testdox -- -race ./...Run all linting checks (aborts on first failure):
.github/test/lint-all.shThe project uses golangci-lint with an extensive rule set (see .golangci.yml). Run it locally:
golangci-lint runYAML files are linted with yamllint (config in .yamllint.yml), shell scripts with ShellCheck, and Markdown files with markdownlint-cli2 (config in .markdownlint-cli2.yaml):
yamllint --strict .
shellcheck .github/test/*.sh
npx --yes markdownlint-cli2 "**/*.md"Dockerfiles are linted with hadolint and the Helm chart with helm lint:
hadolint .github/build/Dockerfile .github/test/*/Dockerfile
helm lint --strict charts/k8s-httpcacheThe CI also runs govulncheck and deadcode detection:
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
go install golang.org/x/tools/cmd/deadcode@latest
deadcode -test ./...When modifying GitHub Actions workflows, run actionlint locally before pushing:
actionlintThe CI runs E2E tests against a kind cluster. To run them locally:
-
Create a kind cluster:
kind create cluster --name test --config .github/test/kind-config.yaml -
Build and load the test image:
CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags '-s -w -extldflags "-static" -buildid=' -o k8s-httpcache . mkdir -p .docker-context cp k8s-httpcache .docker-context/ docker build -f .github/test/varnish8/Dockerfile -t localhost/k8s-httpcache:test .docker-context kind load docker-image localhost/k8s-httpcache:test --name test
-
Install ingress-nginx and deploy:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml kubectl patch configmap -n ingress-nginx ingress-nginx-controller --type merge -p '{"data":{"upstream-keepalive-timeout":"5"}}' kubectl wait -n ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s kubectl apply -f .github/test/manifest.yaml kubectl rollout status deployment/k8s-httpcache --timeout=120s
-
Run all E2E tests (aborts on first failure):
.github/test/test-all.sh
Or run individual tests:
.github/test/smoke-test.sh # HTTP proxying, shard consistency .github/test/metrics-test.sh # Prometheus metrics, broadcast fan-out .github/test/debounce-test.sh # debounce coalescing and debounce-max .github/test/shard-test.sh # shard distribution across pods .github/test/values-update-test.sh # ConfigMap values live update .github/test/values-dir-update-test.sh # values-dir (mounted volume) live update .github/test/vcl-update-test.sh # VCL template live reload, retry & rollback .github/test/file-watch-disable-test.sh # file-watch disable verification .github/test/drain-sessions-test.sh # drain timing verification .github/test/drain-test.sh # connection draining .github/test/topology-test.sh # topology-aware routing .github/test/rollout-test.sh # zero-downtime rollout (requires oha)
metrics-test.shautomatically sets upkubectl port-forwardfor the metrics (:9101) and broadcast (:8088) ports if they are not already reachable, and cleans them up on exit. -
Quick rebuild cycle (no cluster recreation needed):
mkdir -p .docker-context \ && CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags '-s -w -extldflags "-static" -buildid=' -o k8s-httpcache . \ && cp k8s-httpcache .docker-context/ \ && docker build -t localhost/k8s-httpcache:test .docker-context -f .github/test/varnish8/Dockerfile \ && kind load docker-image localhost/k8s-httpcache:test --name test \ && kubectl rollout restart deployment/k8s-httpcache \ && kubectl rollout status deployment/k8s-httpcache --timeout=120s
-
Clean up:
kind delete cluster --name test
-
Fork and branch — Create a feature branch from
main. Use a descriptive name (e.g.fix-backend-port-resolution,add-health-check-endpoint). -
Make your changes — Keep commits focused. Each commit should compile and pass tests.
-
Run checks locally before pushing:
go test -race ./... golangci-lint run -
Open a pull request against
main. The PR description should explain what changed and why. Link any related issues. -
CI must pass — The Test and Build workflow runs unit tests, linting, govulncheck, deadcode analysis, a full build, and E2E tests. All checks must be green before merging.
-
Review — A maintainer will review your PR. Please address feedback and keep the PR up to date with
main.
- Follow standard Go conventions (Effective Go, Go Code Review Comments).
- The
.golangci.ymlenforces the project's style rules — if the linter is happy, the style is fine. - Use
gofmtfor formatting (enforced by CI). - VCL templates use
<</>>delimiters by default (configurable via--template-delims).
Releases are automated. When a tag matching v* is pushed to main, the CI builds multi-arch binaries and container images, creates checksums, and publishes a GitHub release with auto-generated release notes.