Files
tech-log-backend/docs/superpowers/plans/2026-07-30-redis-lab-strict-kubeconfig-renderer.md
T

242 lines
8.8 KiB
Markdown

# Redis Lab Strict Kubeconfig Renderer Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use
> `superpowers:subagent-driven-development` to implement this plan task-by-task. Steps use checkbox
> (`- [ ]`) syntax for tracking.
**Goal:** Complete parent Task 11.1A by replacing mutation-by-mutation kubeconfig filtering with a
pinned-K3s, strict block-grammar validator/renderer and passing an independent safety review.
**Architecture:** Freeze the already-reviewed lifecycle/ownership state machine as Task 11.1A-1.
Move kubeconfig validation/rendering into one tracked AWK program, Task 11.1A-2. The program accepts
only the exact single-cluster/context/user block grammar emitted by the pinned K3s slice, transforms
only lab identity fields, and rejects every non-allowlisted structure before any lab `kubectl`
command.
**Tech Stack:** Bash 5 strict mode, POSIX-compatible AWK features already used by the repository,
the fake-command shell contract, Gradle 9, Java 21.
## Global Constraints
- Do not create a VM, run real Multipass/k3s/kubectl, inspect host inventory, or access the network.
- Do not modify Task 11.1A-1 ownership, state, signal, lock, cleanup or fingerprint behavior.
- Do not add `yq`, PyYAML, Ruby, Java YAML runtime, or another downloadable parser dependency.
- The only accepted source grammar is the pinned K3s admin kubeconfig block-style shape defined in
deep design §37.13.4.1.
- `preferences: {}` is the only permitted flow collection.
- Validation failure removes the destination, emits only `redis-lab: lab kubeconfig invalid`, and
occurs before lab `kubectl`.
- Preserve prior `CREATED|RECONCILE` state and delete only exact marker-proven current-run VMs.
- Tests must show RED against the current implementation before production changes.
- Human-only Git policy applies: do not stage, commit, amend or push.
---
### Task 1: Extract a strict generated-kubeconfig renderer
**Files:**
- Create: `infra/redis-lab/lib/render-kubeconfig.awk`
- Modify: `infra/redis-lab/bin/redis-lab`
- Modify: `infra/redis-lab/test/redis-lab-contract.sh`
**Interfaces:**
- Consumes: `awk -v address=<validated IPv4> -v target=ca-redis-lab -f <renderer> <source>`.
- Produces: rendered kubeconfig on stdout and exit `0`, or no accepted output and non-zero exit.
- Integration: `render_lab_kubeconfig <source> <destination> <server-address>` performs atomic
temporary render, mode `0600`, destination replacement only after renderer success.
- [x] **Step 1: Add realistic positive and sibling-flow RED fixtures**
Change the fake `valid` kubeconfig to this complete credential-data shape, using canary values
rather than real certificate material:
```yaml
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: preserve-default-ca-canary
server: https://127.0.0.1:6443
name: default
contexts:
- context:
cluster: default
namespace: team-default
user: default
name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
user:
client-certificate-data: preserve-default-client-cert-canary
client-key-data: preserve-default-client-key-canary
```
Add separate public `up` variants containing, after their canonical item:
```yaml
cluster : {server: https://foreign.invalid:6443}
```
and:
```yaml
context : {cluster: foreign, user: foreign}
```
Each variant must assert failure, zero lab `kubectl`, three exact marker-proven deletes, removed
rendered kubeconfig, and no forbidden fake invocation.
- [x] **Step 2: Run the direct contract and verify RED**
Run:
```bash
bash -n infra/redis-lab/bin/redis-lab infra/redis-lab/test/redis-lab-contract.sh
bash infra/redis-lab/test/redis-lab-contract.sh
```
Expected: syntax succeeds and the first new sibling-flow case fails because the current renderer
unexpectedly accepts it.
- [x] **Step 3: Implement the strict AWK state machine**
`render-kubeconfig.awk` must use an explicit `state` transition for every accepted line. It must
not print from a catch-all rule. The accepted transition sequence is:
```text
apiVersion -> clusters -> cluster-item -> ca-data -> server -> cluster-name
-> contexts -> context-item -> context-cluster -> optional-namespace -> context-user
-> context-name -> current-context -> kind -> preferences -> users -> user-name
-> user-body -> client-cert -> client-key -> EOF
```
Exact identity transitions print these replacements:
```awk
print " server: https://" address ":6443"
print " name: " target
print " cluster: " target
print " user: " target
print "current-context: " target
print "- name: " target
```
CA/client credential and namespace transitions print `$0` unchanged. Any unmatched line sets
`invalid=1`; `END` exits non-zero unless the final state is `client-key`, every required
transition occurred once, the input had no tab/CR/YAML marker, and no trailing line exists.
- [x] **Step 4: Integrate the renderer fail-closed**
Add:
```bash
KUBECONFIG_RENDERER="${REPOSITORY_ROOT}/infra/redis-lab/lib/render-kubeconfig.awk"
```
`validate_static_contract` must require a readable regular non-symlink renderer at that exact
canonical path. Replace the inline AWK body with:
```bash
local render_next="${destination_file}.next"
rm -f -- "${render_next}"
if ! awk -v address="${server_address}" -v target="${CONTEXT_NAME}" \
-f "${KUBECONFIG_RENDERER}" "${source_file}" >"${render_next}"; then
rm -f -- "${render_next}" "${destination_file}"
fail 'lab kubeconfig invalid'
return 1
fi
chmod 0600 -- "${render_next}"
mv -f -- "${render_next}" "${destination_file}"
```
Add the `.next` destination to symlink-child validation. Propagate `rm`, `chmod` and `mv`
failures with the same sanitized error and without retaining a partially accepted destination.
- [x] **Step 5: Run focused GREEN**
Run the direct contract again. Expected: `redis-lab-contract: PASS`, exit `0`.
### Task 2: Complete the mutation matrix and parent acceptance
**Files:**
- Modify: `infra/redis-lab/test/redis-lab-contract.sh`
- Modify: `infra/redis-lab/README.md`
- Modify: `docs/superpowers/plans/2026-07-29-redis-production-capability-completion.md`
- Modify:
`.superpowers/sdd/2026-07-29-redis-production-capability-completion/progress.md`
- Create:
`.superpowers/sdd/2026-07-29-redis-production-capability-completion/task-11-1a-2-brief.md`
- Create:
`.superpowers/sdd/2026-07-29-redis-production-capability-completion/task-11-1a-2-report.md`
**Interfaces:**
- Consumes: Task 1 strict renderer and existing lifecycle fake runtime.
- Produces: parent Task 11.1A review package with no open Critical/Important finding.
- [x] **Step 1: Add one mutation per grammar boundary**
Add table-driven fixture variants for missing, duplicate, reordered and unknown keys; whitespace
before colon; quoted/tagged/explicit keys; anchor/alias/merge; unexpected `{}`/`[]`; tab, CRLF,
`---`/`...`, and trailing content. Every case must assert failure before lab `kubectl`, exact
current-run cleanup and removed render output.
- [x] **Step 2: Prove scalar preservation and exact transformation**
The positive case must assert:
```text
server: https://192.0.2.10:6443
name/current-context: ca-redis-lab
namespace: team-default
preserve-default-ca-canary
preserve-default-client-cert-canary
preserve-default-client-key-canary
```
It must also assert that no `name: default`, `cluster: default`, `user: default`,
`current-context: default` or loopback server remains.
- [x] **Step 3: Re-run the full fake-only verification**
Run:
```bash
bash -n infra/redis-lab/bin/redis-lab infra/redis-lab/test/redis-lab-contract.sh
bash infra/redis-lab/test/redis-lab-contract.sh
cd src
./gradlew :adapter:outbound:cache-redis:redisLabContractTest --console=plain
./gradlew :adapter:outbound:cache-redis:test --console=plain
./gradlew :adapter:outbound:cache-redis:check --dry-run --console=plain
```
Expected: direct `PASS`; both Gradle executions `BUILD SUCCESSFUL`; dry-run includes
`redisLabContractTest`.
- [x] **Step 4: Run an independent scoped review**
Reviewer acceptance:
- strict renderer has no catch-all pass-through;
- the valid pinned fixture reaches EOF exactly once;
- every non-allowlisted structural line fails;
- destination publication is atomic/fail-closed;
- Task 11.1A-1 lifecycle code is unchanged except the renderer call and static path checks;
- Critical `0`, Important `0`, both spec and quality PASS.
- [x] **Step 5: Close the parent task**
Only after Step 4 passes, replace the ledger `BLOCKED` state with an additive resolution line:
```text
Task 11.1A-2: complete (human-only commit policy; strict renderer review clean)
Task 11.1A: complete (11.1A-1 lifecycle + 11.1A-2 renderer; fake-only evidence)
```
Do not claim live readiness, R2 or VM/k3s qualification.