Skip to content

Commit 1bb94a3

Browse files
kvapsclaude
andcommitted
feat: Resource CRD + /v1/view/resources
Phase 2 fourth slice. linstor-csi calls GetResourceView in its volume reconciliation loop — without it volume operations stall. - pkg/api/v1/resource.go: Resource, ResourceWithVolumes, Volume, state types. - api/v1alpha1/resource_types.go: CRD with composite (rd, node) key encoded as metadata.name = '<rd>.<node>'; status carries inUse and per-volume runtime state. - pkg/store: ResourceStore interface; InMemory + k8s impls (k8s indexed by blockstor.io/resource-definition + blockstor.io/node-name labels for ListByDefinition); 5-case shared suite green on both. - pkg/rest/resources.go: /v1/view/resources handler. - 3 contract tests (empty, multi-node, no-store→503). CSI MVP api-surface.md: /v1/view/resources ✅. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent ab12ebf commit 1bb94a3

29 files changed

Lines changed: 1329 additions & 3 deletions

.golangci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,15 @@ linters:
125125
- linters:
126126
- dupl
127127
path: pkg/store/k8s/
128-
# Same applies to the in-memory store implementations.
128+
# Same applies to the in-memory store implementations and the
129+
# interface definitions in pkg/store/store.go (per-resource Store
130+
# interfaces share the same shape on purpose).
129131
- linters:
130132
- dupl
131133
path: pkg/store/inmemory_
134+
- linters:
135+
- dupl
136+
path: pkg/store/store\.go
132137
# REST handlers follow the same Get/Create/Update/Delete shape per
133138
# resource for the same reason.
134139
- linters:

PROJECT

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ resources:
4141
kind: ResourceDefinition
4242
path: github.com/cozystack/blockstor/api/v1alpha1
4343
version: v1alpha1
44+
- api:
45+
crdVersion: v1
46+
controller: true
47+
domain: blockstor.io
48+
group: blockstor.io
49+
kind: Resource
50+
path: github.com/cozystack/blockstor/api/v1alpha1
51+
version: v1alpha1
4452
version: "3"

api/v1alpha1/resource_types.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
Copyright 2026 Cozystack contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1alpha1
18+
19+
import (
20+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21+
)
22+
23+
// ResourceSpec is the desired state of one replica of a ResourceDefinition
24+
// placed on a node. The composite key is (resourceDefinitionName, nodeName);
25+
// metadata.name encodes that as `<rd>.<node>`.
26+
type ResourceSpec struct {
27+
// resourceDefinitionName is the parent ResourceDefinition.
28+
// +required
29+
ResourceDefinitionName string `json:"resourceDefinitionName"`
30+
31+
// nodeName is the satellite hosting this replica.
32+
// +required
33+
NodeName string `json:"nodeName"`
34+
35+
// props is the LINSTOR per-resource property map.
36+
// +optional
37+
Props map[string]string `json:"props,omitempty"`
38+
39+
// flags carries the user-controlled placement flags.
40+
// +optional
41+
Flags []string `json:"flags,omitempty"`
42+
}
43+
44+
// ResourceStatus is the observed state of a placed resource.
45+
type ResourceStatus struct {
46+
// inUse is whether DRBD reports the resource as primary anywhere.
47+
// +optional
48+
InUse bool `json:"inUse,omitempty"`
49+
50+
// volumes is the per-volume runtime state reported by the satellite.
51+
// +optional
52+
Volumes []ResourceVolumeStatus `json:"volumes,omitempty"`
53+
54+
// conditions represent the current state of the Resource.
55+
// +listType=map
56+
// +listMapKey=type
57+
// +optional
58+
Conditions []metav1.Condition `json:"conditions,omitempty"`
59+
}
60+
61+
// ResourceVolumeStatus is the runtime state of one volume of a Resource.
62+
type ResourceVolumeStatus struct {
63+
VolumeNumber int32 `json:"volumeNumber"`
64+
// +optional
65+
StoragePool string `json:"storagePool,omitempty"`
66+
// +optional
67+
DevicePath string `json:"devicePath,omitempty"`
68+
// +optional
69+
AllocatedKib int64 `json:"allocatedKib,omitempty"`
70+
// +optional
71+
UsableKib int64 `json:"usableKib,omitempty"`
72+
// +optional
73+
DiskState string `json:"diskState,omitempty"`
74+
}
75+
76+
// +kubebuilder:object:root=true
77+
// +kubebuilder:subresource:status
78+
// +kubebuilder:resource:scope=Cluster
79+
80+
// Resource is the Schema for the resources API
81+
type Resource struct {
82+
metav1.TypeMeta `json:",inline"`
83+
84+
// metadata is a standard object metadata
85+
// +optional
86+
metav1.ObjectMeta `json:"metadata,omitzero"`
87+
88+
// spec defines the desired state of Resource
89+
// +required
90+
Spec ResourceSpec `json:"spec"`
91+
92+
// status defines the observed state of Resource
93+
// +optional
94+
Status ResourceStatus `json:"status,omitzero"`
95+
}
96+
97+
// +kubebuilder:object:root=true
98+
99+
// ResourceList contains a list of Resource
100+
type ResourceList struct {
101+
metav1.TypeMeta `json:",inline"`
102+
metav1.ListMeta `json:"metadata,omitzero"`
103+
Items []Resource `json:"items"`
104+
}
105+
106+
func init() {
107+
SchemeBuilder.Register(&Resource{}, &ResourceList{})
108+
}

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 128 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ func main() {
214214
setupLog.Error(err, "Failed to create controller", "controller", "resourcedefinition")
215215
os.Exit(1)
216216
}
217+
if err := (&controller.ResourceReconciler{
218+
Client: mgr.GetClient(),
219+
Scheme: mgr.GetScheme(),
220+
}).SetupWithManager(mgr); err != nil {
221+
setupLog.Error(err, "Failed to create controller", "controller", "resource")
222+
os.Exit(1)
223+
}
217224
// +kubebuilder:scaffold:builder
218225

219226
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

0 commit comments

Comments
 (0)