Skip to content

Commit 8b54b3e

Browse files
committed
[DND-1417] Add CI: lint, render-diff, and kind functional tests
Adds chart testing to a repo that previously had none (only version-bump, release, and readme-preview workflows). All checks run on GitHub-hosted runners with public actions — this is a public repo, so no self-hosted runners, no private gha-tools action, and no secrets. - lint-render.yaml (hard gate): helm lint + helm template + kubeconform strict across one value set per backend in test-values/. - helm-diff.yaml (informational): renders each scenario on base vs head and posts the manifest diff as a sticky PR comment. - functional-test.yaml: ephemeral kind cluster + lightweight mock backends (MinIO, Azurite) + a real S3 round-trip smoke test. s3->MinIO is the hard gate; filesystem/transient/azureblob run non-blocking until their bugs land. - ci/functional/: mock manifests, functional values, smoke-test.sh. - CONTRIBUTING.md: fork-PR handling + CI overview. Actions are SHA-pinned. test-values/ live at repo root so chart-testing changes do not trip verify-chart-version/release or require a Chart.yaml bump. The new CI surfaced pre-existing latent chart bugs (functional legs / autoscaling scenario are gated accordingly): - DND-1442: filesystem/transient omit jclouds.identity/credential -> s3proxy won't start. - DND-1443: hpa.yaml uses the removed autoscaling/v2beta1 API. - DND-1416: azureblob endpoint typo/guard.
1 parent 219579e commit 8b54b3e

21 files changed

Lines changed: 889 additions & 0 deletions
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
name: Functional Test (kind)
2+
3+
# Installs the chart into an ephemeral kind cluster (thrown away with the runner)
4+
# alongside lightweight mock backends, then performs a real S3 round-trip through
5+
# s3proxy. Public repo → GitHub-hosted runner + kind, no cluster credentials, so
6+
# it is safe to run on every PR (including forks).
7+
#
8+
# Backend coverage:
9+
# s3 -> MinIO mock, HARD GATE (full S3 round-trip).
10+
# filesystem / transient -> NON-BLOCKING (soft_fail): the chart does not emit
11+
# jclouds.identity/jclouds.credential for local
12+
# backends, so s3proxy refuses to start
13+
# ("Properties file must contain: jclouds.identity
14+
# and jclouds.credential"). Tracked in DND-1442.
15+
# azureblob -> Azurite mock, NON-BLOCKING (soft_fail) until DND-1416
16+
# fixes the endpoint property (jclouds.azureblob.endpoint
17+
# -> jclouds.endpoint); the failing round-trip is the
18+
# functional test demonstrably catching that regression.
19+
# Each soft_fail leg auto-goes-green once its bug is fixed.
20+
# gcs/b2/openstack-swift/rackspace are render-only (covered by lint-render + helm-diff):
21+
# gcs has no jclouds.endpoint override in the chart, and the others have no
22+
# lightweight in-cluster emulator.
23+
24+
on:
25+
pull_request:
26+
branches:
27+
- main
28+
paths:
29+
- charts/s3proxy/**
30+
- ci/functional/**
31+
- test-values/**
32+
- .github/workflows/functional-test.yaml
33+
workflow_dispatch:
34+
35+
permissions:
36+
contents: read
37+
38+
jobs:
39+
functional:
40+
name: functional (${{ matrix.backend }})
41+
runs-on: ubuntu-latest
42+
continue-on-error: ${{ matrix.soft_fail == true }}
43+
strategy:
44+
fail-fast: false
45+
matrix:
46+
include:
47+
- backend: s3
48+
values: ci/functional/values/s3.yaml
49+
mock: ci/functional/mocks/minio.yaml
50+
- backend: filesystem
51+
values: ci/functional/values/filesystem.yaml
52+
soft_fail: true
53+
- backend: transient
54+
values: ci/functional/values/transient.yaml
55+
soft_fail: true
56+
- backend: azureblob
57+
values: ci/functional/values/azureblob.yaml
58+
mock: ci/functional/mocks/azurite.yaml
59+
soft_fail: true
60+
env:
61+
NAMESPACE: s3proxy-test
62+
RELEASE: s3proxy
63+
steps:
64+
- name: Checkout
65+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
66+
67+
- name: Set up Helm
68+
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
69+
with:
70+
version: v3.19.2
71+
72+
- name: Create kind cluster
73+
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
74+
75+
- name: Create namespace
76+
run: kubectl create namespace "${NAMESPACE}"
77+
78+
- name: Deploy mock backend (${{ matrix.backend }})
79+
if: matrix.mock
80+
run: |
81+
kubectl apply -n "${NAMESPACE}" -f "${{ matrix.mock }}"
82+
kubectl -n "${NAMESPACE}" rollout status deploy --timeout=180s
83+
84+
- name: Install s3proxy
85+
run: |
86+
helm upgrade --install "${RELEASE}" charts/s3proxy \
87+
-n "${NAMESPACE}" \
88+
--values "${{ matrix.values }}" \
89+
--wait --timeout 5m
90+
91+
- name: Smoke test (S3 round-trip)
92+
run: |
93+
chmod +x ci/functional/smoke-test.sh
94+
ci/functional/smoke-test.sh "${RELEASE}" "${NAMESPACE}" 9000 test-access-key test-secret-key
95+
96+
- name: Diagnostics on failure
97+
if: failure()
98+
run: |
99+
kubectl -n "${NAMESPACE}" get all || true
100+
kubectl -n "${NAMESPACE}" describe pods || true
101+
kubectl -n "${NAMESPACE}" logs -l app.kubernetes.io/name=s3proxy --all-containers --tail=300 || true
102+
kubectl -n "${NAMESPACE}" get events --sort-by=.lastTimestamp || true

.github/workflows/helm-diff.yaml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
name: Helm Render Diff
2+
3+
# Informational: renders every test-values scenario on both the PR base and head,
4+
# diffs the resulting Kubernetes manifests, and posts a sticky PR comment so
5+
# reviewers can see exactly what a change does to the rendered output.
6+
# Runs on GitHub-hosted runners with only public actions (public repo, no secrets
7+
# beyond the automatic GITHUB_TOKEN). Never fails the PR.
8+
9+
on:
10+
pull_request:
11+
branches:
12+
- main
13+
14+
permissions:
15+
contents: read
16+
pull-requests: write
17+
18+
env:
19+
KUBE_VERSION: "1.29.0"
20+
21+
jobs:
22+
render-diff:
23+
runs-on: ubuntu-latest
24+
steps:
25+
- name: Checkout PR head
26+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
27+
with:
28+
ref: ${{ github.event.pull_request.head.sha }}
29+
path: head
30+
31+
- name: Checkout base
32+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
33+
with:
34+
ref: ${{ github.event.pull_request.base.sha }}
35+
path: base
36+
37+
- name: Set up Helm
38+
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
39+
with:
40+
version: v3.19.2
41+
42+
- name: Render and diff
43+
run: |
44+
set -uo pipefail
45+
render() {
46+
# render <repo-dir> <values-relpath> <out-file>
47+
local dir="$1" vals="$2" out="$3"
48+
if [ -f "${dir}/${vals}" ] && [ -d "${dir}/charts/s3proxy" ]
49+
then
50+
helm template s3proxy "${dir}/charts/s3proxy" \
51+
--values "${dir}/${vals}" \
52+
--kube-version "${KUBE_VERSION}" > "$out" 2>"${out}.err" || {
53+
echo "render error for ${dir}/${vals}:"
54+
cat "${out}.err"
55+
: > "$out"
56+
}
57+
else
58+
: > "$out"
59+
fi
60+
}
61+
62+
summary=diff-summary.md
63+
echo "## 📊 Helm render diff (base vs PR)" > "$summary"
64+
echo "" >> "$summary"
65+
echo "_Rendered with Kubernetes ${KUBE_VERSION}. Informational only — this check never fails the PR._" >> "$summary"
66+
67+
changed=0
68+
for f in head/test-values/*.yaml
69+
do
70+
name="$(basename "$f")"
71+
render head "test-values/${name}" "/tmp/head-${name}"
72+
render base "test-values/${name}" "/tmp/base-${name}"
73+
if ! diff -u "/tmp/base-${name}" "/tmp/head-${name}" > "/tmp/diff-${name}"
74+
then
75+
changed=1
76+
{
77+
echo ""
78+
echo "<details><summary>🔄 <code>${name}</code></summary>"
79+
echo ""
80+
echo '```diff'
81+
cat "/tmp/diff-${name}"
82+
echo '```'
83+
echo ""
84+
echo "</details>"
85+
} >> "$summary"
86+
fi
87+
done
88+
89+
if [ "$changed" -eq 0 ]
90+
then
91+
echo "" >> "$summary"
92+
echo "_No rendered manifest changes across the test-values scenarios._" >> "$summary"
93+
fi
94+
95+
# Always publish the full diff to the job summary.
96+
cat "$summary" >> "$GITHUB_STEP_SUMMARY"
97+
98+
# Cap the PR comment to stay well under GitHub's 65536-char limit.
99+
if [ "$(wc -c < "$summary")" -gt 60000 ]
100+
then
101+
head -c 60000 "$summary" > diff-comment.md
102+
{
103+
echo ""
104+
echo "…diff truncated — see the full render diff in the workflow job summary."
105+
} >> diff-comment.md
106+
else
107+
cp "$summary" diff-comment.md
108+
fi
109+
110+
- name: Post sticky comment
111+
continue-on-error: true
112+
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
113+
with:
114+
header: helm-render-diff
115+
path: diff-comment.md

.github/workflows/lint-render.yaml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Lint & Render
2+
3+
# Hard gate: helm lint + helm template + kubeconform schema validation across a
4+
# representative value set per backend. Runs on GitHub-hosted runners with only
5+
# public actions (this is a public repo — no self-hosted runners, no secrets).
6+
7+
on:
8+
pull_request:
9+
branches:
10+
- main
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
16+
env:
17+
KUBE_VERSION: "1.29.0"
18+
KUBECONFORM_VERSION: "v0.6.7"
19+
20+
jobs:
21+
lint-render:
22+
name: lint-render (${{ matrix.values }})
23+
runs-on: ubuntu-latest
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
values:
28+
- test-values/filesystem.yaml
29+
- test-values/transient.yaml
30+
- test-values/s3.yaml
31+
- test-values/azureblob.yaml
32+
- test-values/gcs.yaml
33+
- test-values/b2.yaml
34+
- test-values/openstack-swift.yaml
35+
- test-values/rackspace.yaml
36+
- test-values/multi-backend.yaml
37+
- test-values/ingress.yaml
38+
steps:
39+
- name: Checkout
40+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41+
42+
- name: Set up Helm
43+
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
44+
with:
45+
version: v3.19.2
46+
47+
- name: Helm lint
48+
run: helm lint charts/s3proxy --values "${{ matrix.values }}"
49+
50+
- name: Helm template
51+
run: |
52+
helm template s3proxy charts/s3proxy \
53+
--values "${{ matrix.values }}" \
54+
--kube-version "${KUBE_VERSION}" > rendered.yaml
55+
echo "----- rendered manifests -----"
56+
cat rendered.yaml
57+
58+
- name: Install kubeconform
59+
run: |
60+
curl -sSfL "https://github.com/yannh/kubeconform/releases/download/${KUBECONFORM_VERSION}/kubeconform-linux-amd64.tar.gz" \
61+
| tar -xz kubeconform
62+
sudo install kubeconform /usr/local/bin/kubeconform
63+
kubeconform -v
64+
65+
- name: Validate rendered manifests (kubeconform)
66+
run: |
67+
kubeconform -strict -summary \
68+
-kubernetes-version "${KUBE_VERSION}" \
69+
-schema-location default \
70+
rendered.yaml

CONTRIBUTING.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Contributing to s3proxy-chart
2+
3+
Thanks for contributing! This chart deploys [S3Proxy](https://github.com/gaul/s3proxy)
4+
to Kubernetes. Please read the notes below before opening a pull request.
5+
6+
## Chart layout
7+
8+
- The chart lives in [`charts/s3proxy/`](charts/s3proxy).
9+
- **`README.md` is auto-generated** by helm-docs from
10+
[`charts/s3proxy/README.md.gotmpl`](charts/s3proxy/README.md.gotmpl) and
11+
`values.yaml` doc comments. Never edit `README.md` directly — edit the template
12+
and/or the `values.yaml` comments.
13+
- Bump `version` in [`charts/s3proxy/Chart.yaml`](charts/s3proxy/Chart.yaml) for any
14+
change under `charts/s3proxy/**` — the `verify-chart-version` check enforces that
15+
the version is greater than the latest release.
16+
17+
## CI checks
18+
19+
Every PR runs three checks (all on GitHub-hosted runners — no self-hosted runners,
20+
no secrets, so they are safe to run for fork PRs):
21+
22+
| Workflow | What it does | Gate |
23+
|----------|--------------|------|
24+
| **Lint & Render** (`lint-render.yaml`) | `helm lint` + `helm template` + `kubeconform` schema validation across one value set per backend in [`test-values/`](test-values) | Blocking |
25+
| **Helm Render Diff** (`helm-diff.yaml`) | Renders each `test-values/` scenario on the PR base and head and posts a manifest diff as a sticky PR comment | Informational |
26+
| **Functional Test (kind)** (`functional-test.yaml`) | Spins up an ephemeral [kind](https://kind.sigs.k8s.io) cluster, deploys mock backends ([MinIO](https://min.io), [Azurite](https://github.com/Azure/Azurite)), installs the chart, and runs a real S3 round-trip through s3proxy | Blocking (azureblob leg non-blocking, see below) |
27+
28+
### Adding coverage for a change
29+
30+
- Touching a template or `values.yaml`? Make sure the affected backend has a
31+
representative file in [`test-values/`](test-values); add one if not.
32+
- Adding functional coverage for a backend needs a lightweight in-cluster mock
33+
(see [`ci/functional/mocks/`](ci/functional/mocks)) and a values file in
34+
[`ci/functional/values/`](ci/functional/values), then a matrix entry in
35+
`functional-test.yaml`.
36+
37+
### Known non-blocking legs
38+
39+
The functional test surfaced pre-existing chart bugs. These legs run with
40+
`continue-on-error` so they do not block PRs, and each will auto-go-green once its
41+
bug is fixed — the failing round-trip is the functional test doing its job:
42+
43+
- **filesystem** / **transient** — the chart does not emit
44+
`jclouds.identity`/`jclouds.credential` for local backends, so s3proxy refuses
45+
to start. Tracked in **DND-1442**.
46+
- **azureblob** — the chart emits `jclouds.azureblob.endpoint` instead of the
47+
provider-agnostic `jclouds.endpoint`, so a custom endpoint (including the Azurite
48+
mock) is ignored. Tracked in **DND-1416**.
49+
50+
The **s3 → MinIO** leg is the blocking functional gate.
51+
52+
`autoscaling` is deliberately not enabled in any `test-values/` scenario because
53+
`hpa.yaml` still emits the removed `autoscaling/v2beta1` API (kubeconform rejects
54+
it) — tracked in **DND-1443**. Add an autoscaling scenario once that is fixed.
55+
56+
## Fork pull requests
57+
58+
All CI checks are secret-free and run on GitHub-hosted runners, so they are safe
59+
to approve for external contributions. GitHub requires a maintainer to approve
60+
workflow runs for first-time / outside contributors — approving them is safe here
61+
because none of these workflows hold credentials or touch Comet infrastructure.
62+
63+
## Local validation
64+
65+
```bash
66+
# Lint + render + schema-validate a scenario
67+
helm lint charts/s3proxy -f test-values/s3.yaml
68+
helm template s3proxy charts/s3proxy -f test-values/s3.yaml --kube-version 1.29.0 \
69+
| kubeconform -strict -summary -kubernetes-version 1.29.0 -schema-location default -
70+
71+
# Functional round-trip against a local kind cluster
72+
kind create cluster
73+
kubectl create namespace s3proxy-test
74+
kubectl apply -n s3proxy-test -f ci/functional/mocks/minio.yaml
75+
kubectl -n s3proxy-test rollout status deploy --timeout=180s
76+
helm upgrade --install s3proxy charts/s3proxy -n s3proxy-test \
77+
-f ci/functional/values/s3.yaml --wait --timeout 5m
78+
ci/functional/smoke-test.sh s3proxy s3proxy-test 9000 test-access-key test-secret-key
79+
kind delete cluster
80+
```

0 commit comments

Comments
 (0)