Skip to content

Commit 6401597

Browse files
committed
Move to using providers instead of inline time based schdules, this will solidify the provider contract as a first class API
Signed-off-by: Erick Bourgeois <erick@jeb.ca>
1 parent 2add878 commit 6401597

68 files changed

Lines changed: 2648 additions & 2227 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CHANGELOG.md

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

1010
---
1111

12+
## [2026-06-16 13:00] - ADR 0009: examples, time-based provider deploy manifests, and prose docs
13+
14+
**Author:** Erick Bourgeois
15+
16+
### Changed
17+
- `src/crd.rs`: `ScheduledMachineSpec.schedule` is now a **required**
18+
`SpotScheduleRef` (was `Option<ScheduleSpec>`); removed `spec.spotSchedule`;
19+
added top-level `spec.enabled` master switch; removed `ScheduleSpec` and the
20+
`v1alpha1` `ScheduledMachine` module; added the `TimeBasedSpotSchedule` CRD
21+
(spec + status) in `spotschedules.5spot.finos.org`. `is_enabled()` now reads
22+
`spec.enabled`.
23+
- `src/providers/time_based.rs` (+tests) and `src/bin/spot_schedule_time_based.rs`:
24+
NEW core/default provider controller — `is_active_at`/`next_transition`/
25+
`reconcile`, mirroring `capital_markets`; publishes `status.active`.
26+
- `src/reconcilers/{scheduled_machine,helpers,spot_schedule_watch}.rs`: drive
27+
activation solely from the single `spec.schedule` provider verdict; removed
28+
`evaluate_schedule`; simplified `compose_should_be_active` (verdict + hold-last
29+
-state); replaced `validate_activation_source` with `validate_schedule_ref`;
30+
emergency-reclaim loop-breaker now patches `spec.enabled=false` (never the
31+
shared provider object); watch reverse-index keys off `spec.schedule`.
32+
- `src/metrics.rs` (+tests): added `fivespot_time_based_active` /
33+
`fivespot_time_based_transitions_total` + record fns.
34+
- `src/bin/{crdgen,crddoc}.rs`: emit the `TimeBasedSpotSchedule` CRD; single
35+
`v1beta1` `ScheduledMachine` (dropped the multi-version merge + schedule/
36+
spotSchedule XOR CEL); api.md regenerated.
37+
- `deploy/admission/validatingadmissionpolicy.yaml`: removed the inline-schedule
38+
rules (cron/day/hour) and the "at least one" rule; the remaining schedule rule
39+
pins `spec.schedule.apiVersion` to the provider group; `v1beta1`-only.
40+
- `docs/adr/0009-*.md` (+ index; amended 0006 §1/§3 and 0007);
41+
`docs/architecture/calm/architecture.json` (provider model + flows; validated,
42+
diagrams regenerated).
43+
- `examples/scheduledmachine-*.yaml`: migrated all to `5spot.finos.org/v1beta1`,
44+
added `spec.enabled: true`, and replaced every inline `schedule:` / removed
45+
`spotSchedule:` block with a single `spec.schedule:` provider reference. Basic
46+
/ weekend / tainted / bad-taint / kata / child-cluster now reference a
47+
`TimeBasedSpotSchedule`; `scheduledmachine-spot-schedule.yaml` references
48+
`CapitalMarketsSchedule` (`nyse-equities`) alone (no more AND-composition).
49+
- `examples/timebasedspotschedule.yaml`: NEW companion file defining the
50+
`business-hours` / `weekend-only` (ns `default`) and `business-hours-toronto`
51+
(ns `hosted-cluster-alpha`) provider objects the SM examples reference.
52+
- `examples/capitalmarketsschedule.yaml`, `examples/workshop/*`: updated
53+
comments / shape to `spec.schedule` reference + `spec.enabled`; workshop now
54+
installs the time-based provider and toggles `spec.enabled` / `tbss`.
55+
- `deploy/spot-schedule-providers/time-based/`: NEW provider deploy bundle
56+
(serviceaccount / clusterrole / clusterrolebinding / deployment /
57+
kustomization), mirroring the capital-markets bundle — least-privilege RBAC
58+
(get/list/watch `timebasedspotschedules`, update/patch its `/status`),
59+
hardened `Deployment` running `spot-schedule-time-based`.
60+
- `docs/src/**` and `docs/mkdocs.yml`: rewrote all spot-schedule prose to the
61+
ADR 0009 shape (required `spec.schedule` provider ref; `TimeBasedSpotSchedule`
62+
as the default first-party provider; `spec.enabled` master switch; emergency
63+
reclaim sets `spec.enabled`; VAP single group-pin rule 4 / `v1beta1`-only
64+
matchConstraints). Added a new `docs/src/guides/time-based-schedule.md` guide
65+
and listed it in the nav.
66+
67+
### Why
68+
ADR 0009 reified the former inline `ScheduledMachine.spec.schedule` window into
69+
the first-party `TimeBasedSpotSchedule` provider, made `spec.schedule` a required
70+
provider reference, dropped the SM `v1alpha1` version and the `spotSchedule`
71+
field, and introduced `spec.enabled`. Examples, deploy manifests, and docs had
72+
to match the new contract.
73+
74+
### Impact
75+
- [x] Breaking change (pre-release CRD contract: `spec.schedule` is now a
76+
required provider reference, `spec.spotSchedule` removed, SM `v1alpha1` dropped)
77+
- [x] Requires cluster rollout (new CRD + provider controller + RBAC; re-apply
78+
`deploy/crds/` and the time-based provider bundle)
79+
- [ ] Config change only
80+
- [ ] Documentation only
81+
82+
---
83+
84+
**Author:** Erick Bourgeois
85+
86+
### Changed
87+
- `docs/src/guides/create-your-own-provider.md`: NEW standalone guide — a
88+
complete, copy-pasteable walkthrough building a minimal `ManualSchedule`
89+
provider end to end: CRD (namespaced, `status` subresource), controller
90+
(status-patch reconcile, `Ready` semantics, event-driven not polling),
91+
least-privilege RBAC, deployment, referencing from a `ScheduledMachine`,
92+
verification, and additive versioning. Language-agnostic.
93+
- `docs/mkdocs.yml`: grouped both provider guides under a **Spot Schedule
94+
Providers** sub-section in Guides (CapitalMarketsSchedule + Create Your Own
95+
Provider) — makes the existing capital-markets guide more discoverable.
96+
- `docs/src/reference/spot-schedule-contract.md`: replaced the inline
97+
"implement your own" walkthrough with a pointer to the new guide (reference =
98+
spec, guide = how-to); added both guides to See-also.
99+
- `docs/src/guides/capital-markets-schedule.md`: added a See-also section
100+
cross-linking the concept, contract, and the new provider guide.
101+
102+
### Why
103+
The "create your own provider" how-to was buried inside the contract *reference*
104+
page; operators asked for it (and the CapitalMarketsSchedule guide) as
105+
first-class entries under **Guides**. Splitting how-to from spec also keeps the
106+
reference page focused.
107+
108+
### Impact
109+
- [ ] Breaking change
110+
- [ ] Requires cluster rollout
111+
- [ ] Config change only
112+
- [x] Documentation only
113+
114+
---
115+
12116
## [2026-06-15 10:30] - Restore dropped spot-schedule metric functions (build fix)
13117

14118
**Author:** Erick Bourgeois

deploy/admission/validatingadmissionpolicy.yaml

Lines changed: 17 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,11 @@ spec:
2727

2828
matchConstraints:
2929
resourceRules:
30-
# Both served versions must be validated (ADR 0007). v1beta1 is the
31-
# storage/current version (adds spec.spotSchedule and an optional
32-
# spec.schedule); v1alpha1 is the frozen, deprecated legacy version. A
33-
# version omitted here would BYPASS every validation below.
30+
# Single served/storage version since ADR 0009 (the pre-release v1alpha1
31+
# ScheduledMachine was dropped). spec.schedule is a required provider
32+
# reference. A version omitted here would BYPASS every validation below.
3433
- apiGroups: ["5spot.finos.org"]
35-
apiVersions: ["v1alpha1", "v1beta1"]
34+
apiVersions: ["v1beta1"]
3635
resources: ["scheduledmachines"]
3736
operations: ["CREATE", "UPDATE"]
3837

@@ -121,84 +120,23 @@ spec:
121120
(format: <positive integer> followed by s, m, or h)
122121
reason: Invalid
123122

124-
# NOTE on the schedule rules below: `spec.schedule` is OPTIONAL since
125-
# v1beta1 (ADR 0006) — a machine may instead delegate to a spotSchedule
126-
# provider. Each schedule rule is therefore guarded with
127-
# `!has(object.spec.schedule) || …` so a valid spotSchedule-only machine is
128-
# not rejected. The "at least one of schedule/spotSchedule" invariant is
129-
# enforced by rule 7a below (and by the CRD's own x-kubernetes-validations).
123+
# NOTE on the schedule rule below: since ADR 0009, `spec.schedule` is a
124+
# REQUIRED reference to a spot-schedule provider object (apiVersion/kind/
125+
# name in the spotschedules.5spot.finos.org group) — the former inline time
126+
# window moved to the TimeBasedSpotSchedule provider CRD, which carries its
127+
# own day/hour/timezone schema validation. The only ScheduledMachine-side
128+
# invariant left for this VAP is the provider group pin (rule 4). The
129+
# "required" and structural shape are enforced by the CRD schema itself.
130130

131-
# ── 4. Cron XOR explicit day/hour windows ────────────────────────────────
132-
# Using cron together with daysOfWeek or hoursOfDay is ambiguous.
133-
# The controller silently ignores daysOfWeek/hoursOfDay when cron is set;
134-
# we reject the spec at admission to prevent silent data loss.
135-
- expression: |
136-
!has(object.spec.schedule) ||
137-
!has(object.spec.schedule.cron) ||
138-
(object.spec.schedule.daysOfWeek.size() == 0 &&
139-
object.spec.schedule.hoursOfDay.size() == 0)
140-
message: >-
141-
spec.schedule: cron is mutually exclusive with daysOfWeek and hoursOfDay —
142-
set one or the other, not both
143-
reason: Invalid
144-
145-
# ── 5. Without cron, both day and hour windows must be provided ───────────
146-
- expression: |
147-
!has(object.spec.schedule) ||
148-
has(object.spec.schedule.cron) ||
149-
(object.spec.schedule.daysOfWeek.size() > 0 &&
150-
object.spec.schedule.hoursOfDay.size() > 0)
151-
message: >-
152-
spec.schedule: when cron is not set, both daysOfWeek and hoursOfDay must be
153-
non-empty to define an active window
154-
reason: Invalid
155-
156-
# ── 6. daysOfWeek item format ─────────────────────────────────────────────
157-
# Each element must be a day name (mon…sun), an inclusive range (mon-fri),
158-
# or a comma-separated combination (mon-wed,fri-sun).
159-
# Trivially true for empty arrays (cron path).
160-
- expression: |
161-
!has(object.spec.schedule) ||
162-
object.spec.schedule.daysOfWeek.all(d,
163-
d.matches('^(mon|tue|wed|thu|fri|sat|sun)(-(mon|tue|wed|thu|fri|sat|sun))?(,(mon|tue|wed|thu|fri|sat|sun)(-(mon|tue|wed|thu|fri|sat|sun))?)*$'))
164-
message: >-
165-
spec.schedule.daysOfWeek items must be day names or ranges
166-
(e.g. 'mon', 'mon-fri', 'mon-wed,fri-sun')
167-
reason: Invalid
168-
169-
# ── 7. hoursOfDay item format ─────────────────────────────────────────────
170-
# Each element must be a 1-2 digit hour, a range (9-17), or a comma-
171-
# separated combination (0-9,18-23). Numeric bounds (0-23) are enforced
172-
# at runtime by the schedule evaluator.
173-
# Trivially true for empty arrays (cron path).
174-
- expression: |
175-
!has(object.spec.schedule) ||
176-
object.spec.schedule.hoursOfDay.all(h,
177-
h.matches('^[0-9]{1,2}(-[0-9]{1,2})?(,[0-9]{1,2}(-[0-9]{1,2})?)*$'))
178-
message: >-
179-
spec.schedule.hoursOfDay items must be hours or ranges
180-
(e.g. '9', '9-17', '0-9,18-23')
181-
reason: Invalid
182-
183-
# ── 7a. At least one activation source ────────────────────────────────────
184-
# A machine must declare an inline schedule, a spotSchedule provider, or
185-
# both (ADR 0006). Mirrors the CRD x-kubernetes-validations and the runtime
186-
# validate_activation_source() in src/reconcilers/helpers.rs.
187-
- expression: "has(object.spec.schedule) || has(object.spec.spotSchedule)"
188-
message: >-
189-
at least one of spec.schedule or spec.spotSchedule must be set
190-
reason: Invalid
191-
192-
# ── 7b. spotSchedule.apiVersion group pin ─────────────────────────────────
131+
# ── 4. schedule.apiVersion group pin ──────────────────────────────────────
193132
# The provider reference group MUST be spotschedules.5spot.finos.org
194-
# (ADR 0006 §2 / §6) — providers are untrusted inputs and the controller's
195-
# read-only RBAC is scoped to exactly this group. Mirrors the CRD field CEL
196-
# and the runtime group check.
133+
# (ADR 0006 §2 / §6, ADR 0009) — providers are untrusted inputs and the
134+
# controller's read-only RBAC is scoped to exactly this group. Mirrors the
135+
# CRD field CEL and the runtime validate_schedule_ref() check.
197136
- expression: |
198-
!has(object.spec.spotSchedule) ||
199-
object.spec.spotSchedule.apiVersion.startsWith('spotschedules.5spot.finos.org/')
137+
object.spec.schedule.apiVersion.startsWith('spotschedules.5spot.finos.org/')
200138
message: >-
201-
spec.spotSchedule.apiVersion group must be spotschedules.5spot.finos.org
139+
spec.schedule.apiVersion group must be spotschedules.5spot.finos.org
202140
reason: Invalid
203141
204142
# ── 8. bootstrapSpec.apiVersion must use a namespaced API group ───────────

0 commit comments

Comments
 (0)