feat(valkey): one Valkey instance per application (1.0.0+up9.1.2.alpine)
Validate Helm Charts / Detect changed charts (pull_request) Successful in 3s
Validate Helm Charts / Resource quantities (pull_request) Successful in 8s
Validate Helm Charts / Build ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} (pull_request) Successful in 4s
Validate Helm Charts / Trivy config ${{ fromJSON(needs.detect-changed-charts.outputs.scan-charts) }} (pull_request) Successful in 11s
Validate Helm Charts / Install-test ${{ fromJSON(needs.detect-changed-charts.outputs.install-charts) }} (pull_request) Successful in 1m22s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 7s
Publish Valkey Helm Chart / Publish Valkey Helm Chart (push) Successful in 7s

A new chart for a single Valkey instance per application, replacing the
shared cluster-redis whose separation rests on one ACL file in one
1Password field - a construction in which every change touches six
applications and which cost a month of Paperless standstill.

Shape: StatefulSet with one replica, ClusterIP Service, no cluster mode,
no Ingress and no value to add one.

The password arrives as a finished configuration line ("requirepass
<secret>") in the 1Password field auth-conf, is mounted as a file and
included by the main configuration - never in argv, never in the
environment. The include is rendered last, so nothing in extraConfig can
override it. Because a misspelled or empty field would leave an open
server that looks perfectly healthy, an init container greps the mounted
file for the line and refuses to start the pod without it; it never
prints the file.

maxmemory is derived from the computed memory limit (maxmemoryFactor,
default 0.75) and a configured value above that limit aborts the render:
otherwise the kubelet's OOM kill decides what the eviction policy was
configured to decide. Persistence and eviction are documented as pairs -
queue (PVC + noeviction, the default) and cache (emptyDir +
allkeys-lru) - and the combination "no persistence + noeviction" carries
a warning in values.yaml and in NOTES.txt.

Probes: valkey-cli ping as an exec probe, authenticated through
VALKEYCLI_AUTH read out of the mounted file, with "-e" and an assertion
on the answer PONG. Without -e valkey-cli exits 0 on an error reply
(valkey 9.1.2, src/valkey-cli.c: the config.set_errcode branch in
cliReadReply), which would make the probe unable to fail.

Hardening: uid 999 / gid 1000 - the image's own valkey user - with
fsGroup matching, runAsNonRoot, readOnlyRootFilesystem, all capabilities
dropped and seccompProfile RuntimeDefault in the pod context so the init
container is covered too. /data is the only writable path.

NetworkPolicy on by default (first chart in this repo with one), with a
switch and a list of additional namespaces; values.yaml names that it is
only a boundary where the CNI enforces policies.

Wiring: publish-valkey.yml, the chart-name choice list, ci/valkey
fixtures for the persistent variant, a plain docker-versioning Renovate
block, README chart row.
This commit was merged in pull request #53.
This commit is contained in:
jklein committed 2026-09-27 11:24:55 +00:00
1 parent e0b552d93e
commit 813c796a7b
19 files changed
+1687

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
# Fixtures for the valkey install-test: the one Secret the 1Password operator
# would otherwise materialize from the OnePasswordItem the chart renders
# (release name: ci, stub CRD in ci/common/).
#
# The field name matters as much as the value. The chart mounts exactly the key
# "auth-conf" and its content has to be a COMPLETE Valkey configuration line,
# because the configuration "include"s the file - so this fixture is also what
# keeps the init container's check honest: give it anything that is not a
# "requirepass <password>" line and the pod refuses to start, which is the
# failure this chart exists to make loud.
#
# No throwaway infrastructure here: Valkey depends on nothing. What this
# install-test does exercise, and why it is the persistent variant, is the risky
# path - a provisioned PVC, the pod's fsGroup against it, and an append-only
# file being created and replayed on a volume the container did not create.
apiVersion: v1
kind: Secret
metadata:
name: ci-valkey-secret
stringData:
# A throwaway password. Nothing here survives the run - but it is a real
# password rather than an empty string on purpose: an empty one would produce
# exactly the open server the chart refuses to start, and the run would fail
# for the right reason at the wrong place.
auth-conf: requirepass ci-valkey-throwaway-password
+37
View File
@@ -0,0 +1,37 @@
# CI values for the valkey install-test. The PERSISTENT variant on purpose: it is
# the chart's default and the only risky path there is (a provisioned PVC, the
# pod's fsGroup against it, an append-only file created on a volume the container
# did not create). The machinery can run one variant per chart, so the
# non-persistent path is covered by a local render only - named as a known gap in
# the PR that introduced this chart.
secrets:
authSecretRef: op://ci/valkey
persistence:
enabled: true
appendonly: true
# k3d's dynamic provisioner. The chart's default, longhorn-encrypted-ns, does
# not exist on the runner, and a pre-bound hostPath PV would be worse than this
# one: the kubelet does not apply fsGroup to a hostPath volume at all, so the
# test would silently stop exercising the ownership path it is here for.
storageClass: local-path
size: 128Mi
valkey:
# Small, so the whole thing stays inside the runner's budget. It still
# exercises the derivation: 64Mi x limitFactor 3 = 192Mi limit, maxmemory =
# 0.75 x that = 144Mi - and the render aborts if a configured maxmemory ever
# ends up above the limit.
resources:
requests:
cpu: 50m
memory: 64Mi
ephemeral-storage: 32Mi
# Left ON, although nothing in the test connects from another namespace: the
# point here is that the object is accepted and does not keep the pod's own
# traffic (the exec probes are local to the pod and unaffected) from working.
# Whether the policy actually FILTERS is a property of the cluster's CNI and is
# not claimed by this test.
networkPolicy:
enabled: true