242 lines
8.8 KiB
Markdown
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.
|