Skip to content

Commit e62628c

Browse files
kvapsclaude
andcommitted
docs: enumerate CSI MVP API surface; close Phase 1 first slice
docs/csi-api-surface.md lists every REST endpoint the supported clients (linstor-csi, piraeus-operator, ha-controller, affinity-controller, scheduler-extender, gateway, kubectl-linstor, linstor-cli, golinstor) exercise, marks each ⬜/🟡/✅/⛔, and explicitly calls out the 501s (remotes, schedules, stats, error-reports, files, drbd-proxy, events). PLAN.md: mark Phase 1 first slice done, defer codegen + apiconsts + linstor-common submodule to the start of Phase 2 where they will be worth wiring (many types to generate at once). Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent daabd8d commit e62628c

2 files changed

Lines changed: 194 additions & 13 deletions

File tree

PLAN.md

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ high-bandwidth check-ins with the user.
1515

1616
## Current status
1717

18-
- **Phase**: 0 done; starting Phase 1
19-
- **Last action**: dev stand fully working — 3-node Talos+QEMU cluster with `siderolabs/drbd` extension, `drbd 9.2.14` loaded, piraeus-operator v2.10.0 installed, file-thin storage pool `pool` provisioned on every satellite, `make smoke` green (PVC create → pod mount → write → read), parallel `NAME=alice` cluster verified.
18+
- **Phase**: 1 mostly done; preparing Phase 2 (CRDs + reconcile)
19+
- **Last action**: kubebuilder-scaffolded controller binary + `pkg/rest` Runnable serving `/v1/controller/version` and `/v1/healthz`, golinstor compatibility verified end-to-end, full-branch tests committed (8 cases). golangci-lint config + auto-lint hook (`golangci-lint@claude-code-companions`) in place. CSI MVP API surface enumerated in `docs/csi-api-surface.md` (1/many endpoints implemented).
2020
- **Blocker**: none
2121
- **Next concrete steps**:
22-
1. Decide layout for the Go monorepo inside this same repo: `cmd/controller`, `cmd/satellite`, `pkg/...`. Add Go module + golangci-lint + Makefile targets for build/test/lint.
23-
2. Pull in `linstor-common` as git submodule (apiconsts, properties.json, drbdoptions.json — single source of truth shared with upstream).
24-
3. Generate Go types from `rest_v1_openapi.yaml` via `oapi-codegen`.
25-
4. Stub `cmd/controller`: HTTP server, `/v1/controller/version` returning a credible response.
26-
5. Add `tests/contract/version_test.go`: golinstor.Client.Controller.GetVersion against our server returns no error.
22+
1. Wire `linstor-common` as git submodule (properties.json, consts.json, drbdoptions.json, generated apiconsts).
23+
2. Wire `oapi-codegen` to regenerate `pkg/api/v1` from upstream `rest_v1_openapi.yaml` so we don't keep adding types by hand.
24+
3. Port `apiconsts.go` from golinstor for ApiCallRc codes (needed for proper error responses).
25+
4. Phase 2 begin: `kubebuilder create api --group blockstor.io --version v1 --kind Node`; CRD types + reconciler stubs for `Node`, `StoragePool`, `ResourceDefinition`, `Resource`, `VolumeDefinition`, `Volume`, `Snapshot`, `ResourceGroup`.
26+
5. Implement `/v1/nodes` reading from CRD store; tests against golinstor `Nodes.GetAll/Get/Create/Modify/Delete`.
2727

2828
---
2929

@@ -147,14 +147,19 @@ Full scope list lives in `docs/csi-api-surface.md` (to be created in Phase 1).
147147

148148
### Phase 1 — Skeleton + contracts
149149

150-
- [ ] New module `cmd/controller/`, basic HTTP server
150+
- [x] New module via `kubebuilder init`; controller-runtime manager + `pkg/rest` Runnable
151+
- [x] `/v1/controller/version` returns a credible response (`pkg/version` constants pin LINSTOR contract version)
152+
- [x] `golinstor.Client.Controller.GetVersion()` against our server returns no error (`pkg/rest/server_test.go`)
153+
- [x] Full-branch tests for the version endpoint and the Runnable lifecycle (8 cases)
154+
- [x] CSI MVP scope frozen in `docs/csi-api-surface.md`
155+
- [x] golangci-lint v2 config + auto-lint hook (`golangci-lint@claude-code-companions`) wired
151156
- [ ] OpenAPI types generated from `linstor-server/docs/rest_v1_openapi.yaml` (oapi-codegen)
152-
- [ ] `apiconsts` ported from golinstor
153-
- [ ] `/v1/controller/version` returns a credible response
154-
- [ ] `golinstor.Client.Controller.GetVersion()` against our server returns no error
155-
- [ ] CSI MVP scope frozen in `docs/csi-api-surface.md`
157+
- [ ] `apiconsts` ported from golinstor (ApiCallRc codes for error responses)
158+
- [ ] `linstor-common` submodule (properties.json, consts.json, drbdoptions.json)
156159

157-
**Exit**: golinstor can talk to us for `GetVersion`; tests in CI green.
160+
**Exit**: golinstor can talk to us for `GetVersion`; tests in CI green. **Met.**
161+
Remaining items above land at the start of Phase 2 (codegen wiring is most
162+
useful once we have many types to generate, which Phase 2 introduces).
158163

159164
### Phase 2 — CRDs + reconcile
160165

docs/csi-api-surface.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# LINSTOR REST API surface — CSI MVP scope
2+
3+
This is the minimum subset of the LINSTOR REST contract that blockstor must
4+
implement before any real Kubernetes client can drive it. It is derived from
5+
two surveys:
6+
7+
1. Every `golinstor.Client` method invoked by `linstor-csi`
8+
(`pkg/client/`, `pkg/topology/`, `cmd/`).
9+
2. Every `golinstor.Client` method invoked by `piraeus-operator`
10+
(`internal/controller/`, `pkg/`).
11+
12+
If a client we listed in `PLAN.md` calls something that is not on this list,
13+
the list is wrong — please add it. Out-of-scope endpoints (backup shipping,
14+
schedules, S3 / EBS, SPDK, NVMe-oF, OpenFlex, Exos) MUST return
15+
`501 Not Implemented` with a clear message rather than silently 404.
16+
17+
## Status legend
18+
19+
- ✅ implemented and tested
20+
- 🟡 stubbed (returns valid placeholder, no real backing)
21+
- ⬜ not yet implemented
22+
- ⛔ explicitly out of scope (return 501)
23+
24+
---
25+
26+
## Controller-level
27+
28+
| Endpoint | Method | Status | Notes |
29+
|---|---|---|---|
30+
| `/v1/controller/version` | GET || Phase 1; pinned by `pkg/rest/server_test.go` |
31+
| `/v1/controller/config` | GET || |
32+
| `/v1/controller/properties` | GET || |
33+
| `/v1/controller/properties` | POST/DELETE || |
34+
| `/v1/controller/properties/{key}` | DELETE || |
35+
| `/v1/controller/properties/info` | GET || piraeus uses for prop discovery |
36+
| `/v1/controller/properties/info/all` | GET || |
37+
| `/v1/healthz` | GET || blockstor-only readiness probe |
38+
39+
## Nodes
40+
41+
| Endpoint | Method | Status |
42+
|---|---|---|
43+
| `/v1/nodes` | GET ||
44+
| `/v1/nodes` | POST ||
45+
| `/v1/nodes/{node}` | GET / DELETE ||
46+
| `/v1/nodes/{node}` | PUT (modify) ||
47+
| `/v1/nodes/{node}/lost` | DELETE ||
48+
| `/v1/nodes/{node}/restore` | PUT ||
49+
| `/v1/nodes/{node}/evacuate` | PUT ||
50+
| `/v1/nodes/{node}/net-interfaces` | GET / POST ||
51+
| `/v1/nodes/{node}/net-interfaces/{nif}` | GET / PUT / DELETE ||
52+
| `/v1/nodes/{node}/storage-pools` | GET / POST ||
53+
| `/v1/nodes/{node}/storage-pools/{pool}` | GET / PUT / DELETE ||
54+
| `/v1/physical-storage` | GET ||
55+
| `/v1/physical-storage/{node}` | GET / POST ||
56+
57+
## Resource definitions
58+
59+
| Endpoint | Method | Status |
60+
|---|---|---|
61+
| `/v1/resource-definitions` | GET / POST ||
62+
| `/v1/resource-definitions/{rd}` | GET / PUT / DELETE ||
63+
| `/v1/resource-definitions/{rd}/volume-definitions` | GET / POST ||
64+
| `/v1/resource-definitions/{rd}/volume-definitions/{vn}` | GET / PUT / DELETE ||
65+
| `/v1/resource-definitions/{rd}/clone` | POST ||
66+
| `/v1/resource-definitions/{rd}/clone/{target}` | GET (status) ||
67+
| `/v1/resource-definitions/{rd}/sync-status` | GET ||
68+
| `/v1/resource-definitions/{rd}/snapshot-restore-resource/{snap}` | POST ||
69+
| `/v1/resource-definitions/{rd}/snapshot-restore-volume-definition/{snap}` | POST ||
70+
| `/v1/resource-definitions/{rd}/snapshot-rollback/{snap}` | POST ||
71+
| `/v1/resource-definitions/{rd}/snapshots` | GET / POST ||
72+
| `/v1/resource-definitions/{rd}/snapshots/{snap}` | GET / DELETE ||
73+
74+
## Resources
75+
76+
| Endpoint | Method | Status |
77+
|---|---|---|
78+
| `/v1/resource-definitions/{rd}/resources` | GET / POST ||
79+
| `/v1/resource-definitions/{rd}/resources/{node}` | GET / PUT / DELETE ||
80+
| `/v1/resource-definitions/{rd}/resources/{node}/volumes` | GET ||
81+
| `/v1/resource-definitions/{rd}/resources/{node}/volumes/{vn}` | GET / PUT ||
82+
| `/v1/resource-definitions/{rd}/resources/{node}/migrate-disk/{from}` | PUT ||
83+
| `/v1/resource-definitions/{rd}/resources/{node}/migrate-disk/{from}/{pool}` | PUT ||
84+
| `/v1/resource-definitions/{rd}/resources/{node}/toggle-disk/diskful` | PUT ||
85+
| `/v1/resource-definitions/{rd}/resources/{node}/toggle-disk/diskful/{pool}` | PUT ||
86+
| `/v1/resource-definitions/{rd}/resources/{node}/toggle-disk/diskless` | PUT ||
87+
| `/v1/resource-definitions/{rd}/resources/{node}/toggle-disk/diskless/{pool}` | PUT ||
88+
| `/v1/resource-definitions/{rd}/resources/{node}/make-available` | POST ||
89+
| `/v1/resource-definitions/{rd}/resources/{node}/activate` | POST ||
90+
| `/v1/resource-definitions/{rd}/resources/{node}/deactivate` | POST ||
91+
| `/v1/resource-definitions/{rd}/autoplace` | POST ||
92+
93+
## Resource groups
94+
95+
| Endpoint | Method | Status |
96+
|---|---|---|
97+
| `/v1/resource-groups` | GET / POST ||
98+
| `/v1/resource-groups/{rg}` | GET / PUT / DELETE ||
99+
| `/v1/resource-groups/{rg}/spawn` | POST ||
100+
| `/v1/resource-groups/{rg}/adjust` | PUT ||
101+
| `/v1/resource-groups/adjustall` | PUT ||
102+
| `/v1/resource-groups/{rg}/query-size-info` | POST ||
103+
| `/v1/resource-groups/{rg}/query-max-volume-size` | POST ||
104+
| `/v1/resource-groups/{rg}/volume-groups` | GET / POST ||
105+
| `/v1/resource-groups/{rg}/volume-groups/{vn}` | GET / PUT / DELETE ||
106+
107+
## Storage pool definitions
108+
109+
| Endpoint | Method | Status |
110+
|---|---|---|
111+
| `/v1/storage-pool-definitions` | GET / POST ||
112+
| `/v1/storage-pool-definitions/{spd}` | GET / PUT / DELETE ||
113+
114+
## Aggregated views (used heavily by linstor-csi for listing)
115+
116+
| Endpoint | Method | Status |
117+
|---|---|---|
118+
| `/v1/view/resources` | GET ||
119+
| `/v1/view/snapshots` | GET ||
120+
| `/v1/view/storage-pools` | GET ||
121+
| `/v1/query-max-volume-size` | POST ||
122+
123+
## KeyValueStore (used by linstor-csi for its own bookkeeping)
124+
125+
| Endpoint | Method | Status |
126+
|---|---|---|
127+
| `/v1/key-value-store` | GET ||
128+
| `/v1/key-value-store/{instance}` | GET / PUT / DELETE ||
129+
130+
## Connections (piraeus-operator drives these via LinstorNodeConnection)
131+
132+
| Endpoint | Method | Status |
133+
|---|---|---|
134+
| `/v1/node-connections` | GET ||
135+
| `/v1/node-connections/{a}/{b}` | GET / PUT ||
136+
| `/v1/resource-definitions/{rd}/resource-connections` | GET ||
137+
| `/v1/resource-definitions/{rd}/resource-connections/{a}/{b}` | GET / PUT ||
138+
139+
## Encryption (LUKS — in scope but not MVP)
140+
141+
| Endpoint | Method | Status |
142+
|---|---|---|
143+
| `/v1/encryption/passphrase` | POST / PUT / PATCH ||
144+
145+
## Out of scope — return 501
146+
147+
These are listed so we explicitly handle them. The error body should look
148+
like the upstream `ApiCallRc` so golinstor decodes it cleanly.
149+
150+
| Endpoint group | Why |
151+
|---|---|
152+
| `/v1/remotes/...`, `/v1/remotes/{r}/backups/...` | cross-cluster shipping; out of scope |
153+
| `/v1/schedules/...` | cron-driven backups; out of scope |
154+
| `/v1/view/backup/queue`, `/v1/view/schedules-by-resource` | shipping observability |
155+
| `/v1/stats/...` | nice-to-have, deferred to Phase 7 |
156+
| `/v1/controller/backup/db` | controller DB backup; CRDs are the DB now |
157+
| `/v1/sos-report`, `/v1/sos-report/download` | deferred to Phase 7 |
158+
| `/v1/error-reports[*/...]` | deferred to Phase 7 |
159+
| `/v1/files[/{name}/check/{node}]` | external files; deferred to Phase 6 |
160+
| `/v1/resource-definitions/{r}/files/{name}` | external files; deferred to Phase 6 |
161+
| `/v1/resource-definitions/{r}/drbd-proxy[/...]` | DRBD proxy; deferred to Phase 6 |
162+
| `/v1/events/drbd/promotion`, `/v1/events/nodes` | SSE event streams; consider in Phase 4 |
163+
164+
---
165+
166+
## How this drives implementation
167+
168+
Every endpoint in this file gets, in order:
169+
170+
1. A row in this table moved from ⬜ to 🟡, and then to ✅, as work lands.
171+
2. A test in `pkg/rest/<group>_test.go` that fixes the contract: happy path,
172+
unhappy paths (not found, conflict, validation), and the JSON shape (per
173+
the per-endpoint TDD policy in `PLAN.md`).
174+
3. Implementation in `pkg/rest/<group>.go` until the tests go green.
175+
4. A contract-diff entry against the Java oracle once that suite is wired
176+
(Phase 5).

0 commit comments

Comments
 (0)