Skip to content

Commit 86b9fb1

Browse files
kvapsclaude
andcommitted
feat(api): typed Resource.Spec.StoragePool (Phase 10.3 step)
Replaces Spec.Props["StorPoolName"] with a typed Resource.Spec.StoragePool string field. Dispatcher's buildVolumes and the autoplacer dispatch path read typed first; legacy Props fallback covers pre-migration data and any reader still looking at the old key. Auto-diskful promotion in ResourceReconciler now stamps both the typed field (source of truth) and the legacy Props key (forward- compat). The k8s store's wireToCRDResourceSpec lifts the wire Props["StorPoolName"] into the typed slot on Create/Update; crdToWireResource re-emits it back into Props on GET so golinstor keeps seeing the unchanged shape. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent eac2206 commit 86b9fb1

5 files changed

Lines changed: 52 additions & 2 deletions

File tree

api/v1alpha1/resource_types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ type ResourceSpec struct {
5454
// +optional
5555
ExtraProps map[string]string `json:"extraProps,omitempty"`
5656

57+
// storagePool is the LINSTOR storage pool name this replica
58+
// allocates from. Replaces `Spec.Props["StorPoolName"]` (which
59+
// the dispatcher's buildVolumes already prefers when set).
60+
// Phase 10.3 step.
61+
// +optional
62+
StoragePool string `json:"storagePool,omitempty"`
63+
5764
// volumes carries per-volume seed configuration that the
5865
// satellite applies once on first activation of this replica.
5966
// Today the only field is SeedFromGi (Phase 8.1) — when set,

config/crd/bases/blockstor.io.blockstor.io_resources.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ spec:
229229
resourceDefinitionName:
230230
description: resourceDefinitionName is the parent ResourceDefinition.
231231
type: string
232+
storagePool:
233+
description: |-
234+
storagePool is the LINSTOR storage pool name this replica
235+
allocates from. Replaces `Spec.Props["StorPoolName"]` (which
236+
the dispatcher's buildVolumes already prefers when set).
237+
Phase 10.3 step.
238+
type: string
232239
volumes:
233240
description: |-
234241
volumes carries per-volume seed configuration that the

internal/controller/resource_controller.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ func (r *ResourceReconciler) maybeAutoDiskful(ctx context.Context, target *block
224224
target.Spec.Flags = slices.DeleteFunc(target.Spec.Flags,
225225
func(s string) bool { return s == apiv1.ResourceFlagDiskless })
226226

227+
// Phase 10.3 step: prefer typed Spec.StoragePool. Keep the
228+
// legacy Props key in sync for forward-compat with any reader
229+
// that hasn't migrated.
230+
target.Spec.StoragePool = pool
231+
227232
if target.Spec.Props == nil {
228233
target.Spec.Props = map[string]string{}
229234
}

pkg/dispatcher/dispatcher.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,18 @@ func buildVolumes(rd *blockstoriov1alpha1.ResourceDefinition, target *blockstori
419419
return nil
420420
}
421421

422-
pool := target.Spec.Props["StorPoolName"]
422+
// Phase 10.3 step: typed `Spec.StoragePool` wins over the legacy
423+
// `Spec.Props["StorPoolName"]` so we don't read stale data when
424+
// the controller has already migrated. RD-level fallback still
425+
// goes through Props since RD has no typed slot for the pool
426+
// name (it lives on a per-volume VG.Props in
427+
// ResourceGroupVolumeGroup; the RD-prop fallback is the legacy
428+
// shim).
429+
pool := target.Spec.StoragePool
430+
if pool == "" {
431+
pool = target.Spec.Props["StorPoolName"]
432+
}
433+
423434
if pool == "" {
424435
pool = rd.Spec.Props["StorPoolName"]
425436
}
@@ -471,7 +482,11 @@ func (d *Dispatcher) DeleteResource(ctx context.Context, target *blockstoriov1al
471482

472483
defer func() { _ = closer() }()
473484

474-
pool := target.Spec.Props["StorPoolName"]
485+
pool := target.Spec.StoragePool
486+
if pool == "" {
487+
pool = target.Spec.Props["StorPoolName"]
488+
}
489+
475490
if pool == "" && rd != nil {
476491
pool = rd.Spec.Props["StorPoolName"]
477492
}

pkg/store/k8s/resources.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ func mergeVolumeObservations(dst *[]crdv1alpha1.ResourceVolumeStatus, observatio
253253
func crdToWireResource(crd *crdv1alpha1.Resource) apiv1.Resource {
254254
props := mergeProps(crd.Spec.Props, typedToProps(crd.Spec.DRBDOptions, crd.Spec.ExtraProps))
255255

256+
// Phase 10.3 step: re-emit typed StoragePool back into Props
257+
// so golinstor + the dispatcher's legacy fallback see the
258+
// upstream-compatible shape on GET.
259+
if crd.Spec.StoragePool != "" {
260+
if props == nil {
261+
props = map[string]string{}
262+
}
263+
264+
props["StorPoolName"] = crd.Spec.StoragePool
265+
}
266+
256267
return apiv1.Resource{
257268
Name: crd.Spec.ResourceDefinitionName,
258269
NodeName: crd.Spec.NodeName,
@@ -290,5 +301,10 @@ func wireToCRDResourceSpec(in *apiv1.Resource) crdv1alpha1.ResourceSpec {
290301
DRBDOptions: typed,
291302
ExtraProps: extras,
292303
Flags: in.Flags,
304+
// Phase 10.3 step: lift Props["StorPoolName"] into the
305+
// typed slot. The legacy key stays in residual Props (it's
306+
// non-DRBD, so stripDRBDProps left it alone) for forward-
307+
// compat — readers still consulting Props will see it.
308+
StoragePool: in.Props["StorPoolName"],
293309
}
294310
}

0 commit comments

Comments
 (0)