Allow stopped single-writer outbox maintenance through Helm
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
This commit is contained in:
parent
84c78e072c
commit
dc042022f7
4 changed files with 96 additions and 4 deletions
|
|
@ -29,8 +29,10 @@ metadata:
|
|||
{{- include "access-engine.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.outbox.enabled }}
|
||||
{{- if ne (int .Values.replicaCount) 1 }}
|
||||
{{- fail "outbox requires replicaCount=1: one writer per source and PVC" }}
|
||||
{{- $replicas := .Values.replicaCount }}
|
||||
{{- $numericKind := has (kindOf $replicas) (list "int" "int64" "int32" "uint" "uint64" "float64" "float32") }}
|
||||
{{- if or (not $numericKind) (not (or (eq (toString $replicas) "0") (eq (toString $replicas) "1"))) }}
|
||||
{{- fail "outbox requires numeric integer replicaCount 0 (stopped) or 1 (single writer)" }}
|
||||
{{- end }}
|
||||
strategy:
|
||||
type: Recreate
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ metadata:
|
|||
{{- include "flex-auth.labels" . | nindent 4 }}
|
||||
spec:
|
||||
{{- if .Values.outbox.enabled }}
|
||||
{{- if ne (int .Values.replicaCount) 1 }}
|
||||
{{- fail "outbox requires replicaCount=1: one writer per source and PVC" }}
|
||||
{{- $replicas := .Values.replicaCount }}
|
||||
{{- $numericKind := has (kindOf $replicas) (list "int" "int64" "int32" "uint" "uint64" "float64" "float32") }}
|
||||
{{- if or (not $numericKind) (not (or (eq (toString $replicas) "0") (eq (toString $replicas) "1"))) }}
|
||||
{{- fail "outbox requires numeric integer replicaCount 0 (stopped) or 1 (single writer)" }}
|
||||
{{- end }}
|
||||
strategy:
|
||||
type: Recreate
|
||||
|
|
|
|||
65
tests/test_outbox_replicas_chart.py
Normal file
65
tests/test_outbox_replicas_chart.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Actual Helm stop/start renders and unsafe replica value rejection; offline only."""
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
import yaml
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
def render(chart, value, encoding="yaml", success=True):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
command = ["helm", "template", "flex-auth-secrets-engine", str(ROOT / "charts" / chart),
|
||||
"--namespace", "flex-auth", "-f", str(ROOT / "values/secrets-engine.yaml"),
|
||||
"--set", "outbox.enabled=true", "--set", "outbox.source=flex-auth.secrets-engine",
|
||||
"--set", "envelopeSigning.enabled=true", "--set", "envelopeSigning.kid=test-kid",
|
||||
"--set", "envelopeSigning.secretName=proposed-seed", "--set", "envelopeSigning.secretKey=seed"]
|
||||
if encoding == "yaml":
|
||||
overlay = Path(directory) / "values.yaml"
|
||||
overlay.write_text(yaml.safe_dump({"replicaCount": value}))
|
||||
command += ["-f", str(overlay)]
|
||||
elif encoding == "json":
|
||||
command += ["--set-json", "replicaCount=" + json.dumps(value)]
|
||||
elif encoding == "string":
|
||||
command += ["--set-string", "replicaCount=" + str(value)]
|
||||
else:
|
||||
command += ["--set", "replicaCount=" + str(value)]
|
||||
result = subprocess.run(command, capture_output=True, text=True)
|
||||
if not success:
|
||||
assert result.returncode != 0, (chart, encoding, value)
|
||||
assert "replicaCount" in result.stderr, result.stderr
|
||||
return
|
||||
assert result.returncode == 0, result.stderr
|
||||
documents = [d for d in yaml.safe_load_all(result.stdout) if d]
|
||||
deployment = next(d for d in documents if d["kind"] == "Deployment")
|
||||
assert deployment["spec"]["strategy"] == {"type": "Recreate"}
|
||||
assert type(deployment["spec"]["replicas"]) is int
|
||||
assert deployment["spec"]["replicas"] == value
|
||||
return documents
|
||||
|
||||
count = 0
|
||||
for chart in ("flex-auth", "access-engine"):
|
||||
for encoding in ("yaml", "json", "cli"):
|
||||
stopped = render(chart, 0, encoding)
|
||||
ready = render(chart, 1, encoding)
|
||||
normalized = copy.deepcopy(ready)
|
||||
next(d for d in normalized if d["kind"] == "Deployment")["spec"]["replicas"] = 0
|
||||
assert stopped == normalized, "stop must change replicas only, including identical pod projection/PVC"
|
||||
count += 2
|
||||
for encoding in ("yaml", "json"):
|
||||
for value in (-1, 2, 1.5, 0.5, True, False, None, "0", "1", [], {}):
|
||||
render(chart, value, encoding, success=False)
|
||||
count += 1
|
||||
# Helm loses lexical 0 vs0.0 and1 vs1.0 before template evaluation.
|
||||
for value in (0.0, 1.0):
|
||||
render(chart, value, encoding)
|
||||
count += 1
|
||||
for value in ("-1", "2", "0.5", "1.5", "true", "false", "null", "0.0", "1.0"):
|
||||
render(chart, value, "cli", success=False)
|
||||
count += 1
|
||||
for value in ("0", "1"):
|
||||
render(chart, value, "string", success=False)
|
||||
count += 1
|
||||
print(f"stopped/single-writer chart checks passed: {count} actual Helm renders")
|
||||
|
|
@ -302,3 +302,26 @@ unchanged. Exact local source resolution is no central/private read-placement,
|
|||
reef binding, custody, native rollout or consumer trust admission. Future signer
|
||||
image/config adoption remains separate. T06 remains `progress`; no second work
|
||||
item duplicates this bounded dependency.
|
||||
|
||||
|
||||
2026-10-16 bounded stopped-chart source preparation (Custodian
|
||||
`access-native-admission/stopped-chart-r2`): both outbox charts admit numeric
|
||||
replicaCount exactly 0 (maintenance stop) or 1 (single writer), with Recreate,
|
||||
process flock and bounded persistence unchanged. Fractions, strings, booleans,
|
||||
negative values and 2+ refuse; Helm decodes YAML/JSON integral 0.0/1.0 as the same
|
||||
numeric values as 0/1, so lexical float spelling is not distinguished. Actual
|
||||
Helm stopped/ready renders differ only in replicas and retain the same Platform
|
||||
source-rendered deployment projection. Owner preparation and independent
|
||||
coordinator checks approve this bounded source change only; 86 Helm renders and
|
||||
the existing outbox/delivery/receiver-port suite pass. No native stop, bootstrap,
|
||||
rollout, custody, public trust, resource or protected-effect approval follows.
|
||||
The proposed immutable image remains source revision
|
||||
`84c78e072c8f3daf2bc3b90f8efc8fbeb16c3cf0`, ordinary CI402 verified digest
|
||||
`sha256:09a83b273740133518cc7e7915fb211381e06545d347d2dd1974042525ac981f`.
|
||||
This chart-only successor requires its own exact source/chart pin and reviewed
|
||||
runtime scope; it can explicitly select the unchanged verified 84 image without
|
||||
rebuilding it or claiming that its revision label names the new chart commit.
|
||||
These chart/test/workplan-only paths do not trigger the existing image workflow.
|
||||
Frozen rollout R1, Platform P2 and the package extraction2b/current05a remain
|
||||
unchanged. T06 stays `progress`; actual stopped-fence verification and all native
|
||||
admission gates remain open under the same existing task.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue