Skip to content

Commit d68ad14

Browse files
committed
New error_chain() helper flattens the error source() chain. Idempotency sweep across the code base to make sure 409 tend to OK.
Signed-off-by: Erick Bourgeois <erick@jeb.ca>
1 parent a34abfa commit d68ad14

8 files changed

Lines changed: 459 additions & 219 deletions

File tree

.cargo/audit.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ ignore = [
1212
# 2. Calls rand::rng() from within that logger
1313
# Neither condition applies to 5-spot.
1414
"RUSTSEC-2026-0097",
15+
16+
# RUSTSEC-2026-0173: proc-macro-error2 is UNMAINTAINED (a maintenance-status
17+
# advisory, not a vulnerability — no known exploit). It is a build-time proc-macro
18+
# pulled transitively via jiff 0.2.29 -> defmt -> defmt-macros, and defmt is jiff's
19+
# OPTIONAL embedded (no_std) feature: `cargo tree -e features -i defmt` reports
20+
# "nothing to print" for the host target, so proc-macro-error2 is never compiled
21+
# into the 5-Spot binary — it exists only as a Cargo.lock entry. Removal awaits
22+
# upstream (kube bumping jiff, or defmt-macros dropping proc-macro-error2).
23+
"RUSTSEC-2026-0173",
1524
]

.claude/CHANGELOG.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,126 @@ The format is based on the regulated environment requirements:
99

1010
---
1111

12+
## [2026-06-24 11:00] - Silence RUSTSEC-2026-0173 (proc-macro-error2 unmaintained) with justification
13+
14+
**Author:** Erick Bourgeois
15+
16+
### Changed
17+
- `.cargo/audit.toml` and `deny.toml`: ignore `RUSTSEC-2026-0173` with a documented
18+
justification (kept in sync across both tools).
19+
20+
### Why
21+
CI `cargo audit` started failing on `proc-macro-error2` being **unmaintained** (a
22+
maintenance-status advisory, not a vulnerability). It is reached only transitively via
23+
`jiff 0.2.29 -> defmt -> defmt-macros`, and `defmt` is jiff's optional embedded/no_std
24+
feature — `cargo tree -e features -i defmt` prints "nothing to print" for the host
25+
target, so the crate is never compiled into the 5-Spot binary; it exists only as a
26+
`Cargo.lock` entry. No runtime or build exposure. Removal awaits upstream (kube bumping
27+
jiff, or defmt-macros dropping proc-macro-error2). Verified `cargo audit` passes.
28+
29+
### Impact
30+
- [ ] Breaking change
31+
- [ ] Requires cluster rollout
32+
- [x] Config change only
33+
- [ ] Documentation only
34+
35+
## [2026-06-24 10:30] - Idempotency sweep: 409 AlreadyExists / duplicate finalizers no longer Error the ScheduledMachine
36+
37+
**Author:** Erick Bourgeois
38+
39+
### Changed
40+
- `src/reconcilers/helpers.rs` (`create_dynamic_resource`): treat a `409 AlreadyExists`
41+
from `api.create(...)` as success instead of propagating it. The bootstrap config,
42+
infrastructure object, and CAPI Machine are all created through this helper; a prior
43+
reconcile having created them IS the desired state.
44+
- `src/reconcilers/helpers.rs` (`add_finalizer`): only append our finalizer if it isn't
45+
already present — a double-call can no longer write a duplicate (which would wedge GC).
46+
- `src/reconcilers/helpers_tests.rs`: +3 unit tests (create 409 → Ok; create 500 still
47+
propagates; add_finalizer writes exactly one copy).
48+
- Audited the rest of the codebase for idempotency and confirmed it already holds: the
49+
only other `.create()` is a `SelfSubjectAccessReview` (a virtual auth check, never
50+
409s); all `.delete()` sites swallow 404; the kata ConfigMap uses `Patch::Apply` (SSA);
51+
Events go through the kube `recorder`; status/spec writes use `Patch::Merge`/`Apply`.
52+
53+
### Why
54+
On every reconcile after the first, `add_machine_to_cluster` re-issues the create and
55+
got `409 AlreadyExists` (e.g. `k0sworkerconfigs "…" already exists`). That bubbled up as
56+
`MachineCreationFailed`, flipping the ScheduledMachine into the **Error** phase — which
57+
skips ALL Active-phase work, including node-taint reconciliation. On the k0smotron
58+
workshop tier this presented as **Flag 1 (worker joins) passing but Flag 2 (the Node
59+
carries the spot taint) never happening**: the SM was wedged in Error, so it never ran
60+
`handle_active_phase`. Idempotent create is standard controller behavior and fixes it.
61+
62+
### Impact
63+
- [ ] Breaking change
64+
- [x] Requires cluster rollout
65+
- [ ] Config change only
66+
- [ ] Documentation only
67+
68+
## [2026-06-24 03:10] - Fix Mermaid flow diagrams broken by special characters
69+
70+
**Author:** Erick Bourgeois
71+
72+
### Changed
73+
- `docs/architecture/calm/architecture.json`: escaped Mermaid-hostile characters in
74+
three flow transition descriptions using Mermaid entity codes (which render as the
75+
literal characters):
76+
- `flow-emergency-reclaim` t1: `reclaim-requested="true"``reclaim-requested=#quot;true#quot;`.
77+
The embedded double quotes terminated the Mermaid label string early, causing a parse
78+
error that left the **Emergency Reclaim** diagram blank on the docs site.
79+
- `flow-kata-config-delivery` t2: `/etc/5spot/kata-config/<key>``…/#lt;key#gt;`.
80+
- `flow-kata-config-delivery` t5: `restart <restartService>``restart #lt;restartService#gt;`.
81+
Raw `<…>` placeholders were parsed as HTML tags and silently dropped from the rendered label.
82+
- `docs/src/architecture/flows.md`: regenerated via `make calm-diagrams` (auto-generated;
83+
not hand-edited).
84+
- `docs/src/concepts/kata-config-delivery.md`: hand-written `sequenceDiagram` — replaced a
85+
`;` with `,` inside a `Note over Agent,Host: …` label. Mermaid treats `;` as a statement
86+
separator even inside note text, so it split the note mid-string and failed the whole
87+
diagram to parse.
88+
89+
### Why
90+
The `flows.md.hbs` template emits transition descriptions verbatim into `flowchart` node
91+
labels (`t1["…"]`). Mermaid cannot contain an unescaped `"` inside a quoted label, and
92+
treats `<…>` as HTML — so these characters either broke parsing (Emergency Reclaim) or
93+
ate label text (Kata Config Delivery). Fixing at the source-of-truth `architecture.json`
94+
keeps the diagrams correct after every regeneration. The `phase -> …` arrows in other
95+
flows contain only `>` and render fine, so they were left unchanged.
96+
97+
### Impact
98+
- [ ] Breaking change
99+
- [ ] Requires cluster rollout
100+
- [ ] Config change only
101+
- [x] Documentation only
102+
103+
---
104+
105+
## [2026-06-24 02:30] - Surface the real cause of child-cluster connection failures (error_chain)
106+
107+
**Author:** Erick Bourgeois
108+
109+
### Changed
110+
- `src/reconcilers/helpers.rs`: new `error_chain(&dyn Error)` helper that flattens an
111+
error's `source()` chain into one string, and wired it into all six child-cluster
112+
failure logs (taint GET/PATCH/reconcile, kata clear/stamp). These previously logged
113+
only the terse top-level `Display` ("client error (Connect)"), hiding the real cause.
114+
- `src/reconcilers/helpers_tests.rs`: +2 unit tests for `error_chain` (multi-level
115+
source chain; single error with no source).
116+
117+
### Why
118+
A k0smotron-hosted control plane (workshop k0smotron tier) exposes a self-signed API
119+
server cert that **rustls** (5-Spot's TLS backend, kube 3.1 default) rejects where
120+
OpenSSL/curl `-k` accept it — so 5-Spot's child client fails to taint/drain the worker
121+
Node with `client error (Connect)`, with no visible cause. Logging the full chain makes
122+
the exact rustls reason (unknown issuer / SAN mismatch / unsupported signature) visible
123+
so the fix can target it (a proper cert, `tls_server_name`, or `insecure-skip-tls-verify`
124+
on the child kubeconfig — kube already honors the latter via `Config::accept_invalid_certs`).
125+
126+
### Impact
127+
- [ ] Breaking change
128+
- [ ] Requires cluster rollout
129+
- [ ] Config change only
130+
- [x] Diagnostics only (no behavior change)
131+
12132
## [2026-06-23 11:45] - Ship the spot-schedule provider binaries in the image (ADR 0009 providers were unrunnable)
13133

14134
**Author:** Erick Bourgeois

0 commit comments

Comments
 (0)