Skip to content

Commit f1b9fdc

Browse files
kvapsclaude
andcommitted
docs(plan): add 10.3 design summary with concrete YAML examples
Adds a "10.3 design summary" subsection with: - Go pseudocode for DRBDOptions / DRBDNetOptions / DRBDResourceOptions / EncryptionConfig structs, including the *int32 / *bool override-semantics convention (nil = not overridden, non-nil = explicitly set, even if zero) - ResourceDefinition before/after YAML showing how Spec.Props["DrbdOptions/Net/protocol"] becomes Spec.DRBDOptions.Net.Protocol (with admission-validated enum) and how plaintext passphrase in spec becomes a Secret reference - Node before/after YAML showing topology moving from Spec.Props["Aux/zone"] to native metadata.labels and observed state (BlockstorVersion) moving from Spec to Status - REST shim transcoder shape — golinstor wire format stays identical via bidirectional translation, with ExtraProps as forward-compat shim for unknown keys (goal: empty in steady-state production) - Validation strategy (kubebuilder enums + defaults + CEL for cross-field invariants) - Resolver and .res renderer impact: per-section field-by-field merge instead of map[string]string overlay; same line count, no string parsing Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 3bc27f6 commit f1b9fdc

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

PLAN.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,141 @@ LINSTOR boundary.
555555
- [ ] REST shim transcoder: `/v1/resource-definitions` and `/v1/resource-groups` POST/PUT accept upstream-shaped `{props: {...}}` payloads from golinstor, decode into the typed structs, write `ExtraProps` only for keys we don't recognise. GET responses re-emit the typed fields as `props` on the wire so golinstor stays happy.
556556
- [ ] Validation via kubebuilder enums (`+kubebuilder:validation:Enum=majority;off`) — apiserver rejects garbage values at admission, satellite stops parsing strings at runtime.
557557

558+
#### 10.3 design summary
559+
560+
Typed structs (Go pseudocode):
561+
562+
```go
563+
// api/v1alpha1/drbd_options.go
564+
type DRBDOptions struct {
565+
Net *DRBDNetOptions `json:"net,omitempty"`
566+
Disk *DRBDDiskOptions `json:"disk,omitempty"`
567+
PeerDevice *DRBDPeerDeviceOptions `json:"peerDevice,omitempty"`
568+
Resource *DRBDResourceOptions `json:"resource,omitempty"`
569+
Handlers map[string]string `json:"handlers,omitempty"`
570+
}
571+
572+
type DRBDNetOptions struct {
573+
// +kubebuilder:validation:Enum=A;B;C
574+
Protocol string `json:"protocol,omitempty"`
575+
SharedSecretRef *corev1.LocalObjectReference `json:"sharedSecretRef,omitempty"`
576+
AllowTwoPrimaries *bool `json:"allowTwoPrimaries,omitempty"`
577+
MaxBuffers *int32 `json:"maxBuffers,omitempty"`
578+
// +kubebuilder:validation:Enum=disconnect;discard-younger-primary;discard-older-primary;discard-zero-changes;discard-least-changes;discard-local;discard-remote
579+
AfterSb0Pri string `json:"afterSb0Pri,omitempty"`
580+
// ...
581+
}
582+
583+
type DRBDResourceOptions struct {
584+
AutoPromote *bool `json:"autoPromote,omitempty"`
585+
// +kubebuilder:validation:Enum=off;majority;all
586+
Quorum string `json:"quorum,omitempty"`
587+
// +kubebuilder:validation:Enum=io-error;suspend-io;freeze-io
588+
OnNoQuorum string `json:"onNoQuorum,omitempty"`
589+
}
590+
591+
// api/v1alpha1/encryption.go
592+
type EncryptionConfig struct {
593+
PassphraseSecretRef *corev1.LocalObjectReference `json:"passphraseSecretRef,omitempty"`
594+
}
595+
596+
// Embedded on RG / RD / Resource:
597+
// Spec.DRBDOptions *DRBDOptions
598+
// Spec.Encryption *EncryptionConfig (RD only)
599+
// Spec.ExtraProps map[string]string // legacy compat shim
600+
```
601+
602+
`*int32` / `*bool` so `nil` means "not overridden" and any non-nil
603+
value (including zero) means "explicitly set". Override semantics
604+
flow Cluster → RG → RD → Resource, lowest scope wins per-field.
605+
606+
ResourceDefinition before/after:
607+
608+
```yaml
609+
# Before (current)
610+
kind: ResourceDefinition
611+
spec:
612+
resourceGroupName: rg-fast
613+
props:
614+
"DrbdOptions/Net/protocol": "B"
615+
"DrbdOptions/Encryption/passphrase": "topsecret" # plaintext in spec
616+
"StorPoolName": "nvme"
617+
volumeDefinitions:
618+
- volumeNumber: 0
619+
sizeKib: 1048576
620+
props:
621+
"DrbdCurrentGi": "78A0DDD..." # observed in spec (bug)
622+
623+
# After (Phase 10.3)
624+
kind: ResourceDefinition
625+
spec:
626+
resourceGroupName: rg-fast
627+
drbdOptions:
628+
net:
629+
protocol: B # admission-validated enum
630+
encryption:
631+
passphraseSecretRef:
632+
name: pvc-41cc4aa3-luks # ref instead of plaintext
633+
volumeDefinitions:
634+
- volumeNumber: 0
635+
sizeKib: 1048576
636+
status:
637+
volumes:
638+
- volumeNumber: 0
639+
currentGi: "78A0DDD..." # observed in Status (10.2)
640+
```
641+
642+
Node before/after — topology moves to native labels:
643+
644+
```yaml
645+
# Before
646+
kind: Node
647+
metadata:
648+
name: n1
649+
spec:
650+
type: SATELLITE
651+
props:
652+
"Aux/zone": "us-east-1a" # string-keyed topology
653+
"DrbdOptions/TcpPortRange": "7000-7999"
654+
"BlockstorVersion": "0.0.0-test" # observed in spec
655+
656+
# After
657+
kind: Node
658+
metadata:
659+
name: n1
660+
labels:
661+
topology.blockstor.io/zone: us-east-1a # native labels
662+
spec:
663+
type: SATELLITE
664+
drbdPortRange: { min: 7000, max: 7999 } # typed range
665+
autoTieBreaker: true
666+
status:
667+
blockstorVersion: "0.0.0-test" # observed → Status
668+
```
669+
670+
REST shim (`pkg/rest/transcode.go`) does bidirectional translation
671+
so golinstor sees the upstream-shaped `{props: {...}}` wire format
672+
unchanged. Unknown keys land in `Spec.ExtraProps`; on the next
673+
release we either type them into `DRBDOptions` (transcoder routes
674+
to typed field, ExtraProps stops carrying them) or leave them in
675+
ExtraProps if upstream's semantic is too unstable to commit a typed
676+
schema. Goal: `ExtraProps` is empty on a steady-state production
677+
cluster.
678+
679+
Validation:
680+
- kubebuilder `+kubebuilder:validation:Enum=...` for string enums →
681+
apiserver rejects garbage at admission
682+
- `+kubebuilder:default:=...` for sane defaults
683+
- CEL validation for cross-field invariants (e.g. "AllowTwoPrimaries
684+
requires Protocol=C" — DRBD-9 doesn't allow async dual-primary)
685+
686+
The `pkg/drbd/options.go` resolver becomes a per-section field-by-
687+
field merge instead of `map[string]string` overlay. The `.res` file
688+
renderer in `pkg/satellite/reconciler.buildResFile` reads typed
689+
fields directly and emits each non-zero one into the right
690+
`drbdadm` section block — same line count as today's
691+
`splitDRBDOptions` + section-detection but without string parsing.
692+
558693
### 10.4 — Eliminate `KVEntry` CRD
559694

560695
- [ ] **`ControllerProps` instance → typed singleton CRD**. New `ControllerConfig` cluster-scoped CRD, name=`default`, with structured fields (`Spec.DRBDOptions`, `Spec.AutoQuorum`, `Spec.AutoAddQuorumTiebreaker`, `Spec.PassphraseSecretRef`). Migration job seeds one from existing `KVEntry/ControllerProps` items.

0 commit comments

Comments
 (0)