Skip to content

Commit c1647dc

Browse files
kvapsclaude
andcommitted
feat(rest): add GET /v1/resource-definitions/{rd}/resources[/{node}]
linstor-csi polls the per-RD resource list during ControllerPublishVolume ("is this volume on this node?"). Without these handlers we returned a 404 for the parent path and the publish call surfaced as "volume not present in storage backend". Add the GET list and GET-by-node so autoplace's placement is visible to the client. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent 44012e2 commit c1647dc

1 file changed

Lines changed: 50 additions & 1 deletion

File tree

pkg/rest/autoplace.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,65 @@ import (
3030
)
3131

3232
// registerAutoplace wires `POST /v1/resource-definitions/{rd}/autoplace` and
33-
// the per-resource POST/DELETE used by linstor-csi for explicit placement.
33+
// the per-resource list/POST/DELETE used by linstor-csi for explicit placement.
3434
func (s *Server) registerAutoplace(mux *http.ServeMux) {
3535
mux.HandleFunc("POST /v1/resource-definitions/{rd}/autoplace",
3636
s.requireStore(s.handleAutoplace))
37+
mux.HandleFunc("GET /v1/resource-definitions/{rd}/resources",
38+
s.requireStore(s.handleResourceList))
39+
mux.HandleFunc("GET /v1/resource-definitions/{rd}/resources/{node}",
40+
s.requireStore(s.handleResourceGet))
3741
mux.HandleFunc("POST /v1/resource-definitions/{rd}/resources",
3842
s.requireStore(s.handleResourceCreate))
3943
mux.HandleFunc("DELETE /v1/resource-definitions/{rd}/resources/{node}",
4044
s.requireStore(s.handleResourceDelete))
4145
}
4246

47+
// handleResourceList answers `GET /v1/resource-definitions/{rd}/resources`,
48+
// the per-RD aggregate linstor-csi polls during ControllerPublishVolume to
49+
// answer "is the resource on this node?". Wraps each Resource in
50+
// ResourceWithVolumes so the wire shape matches /v1/view/resources.
51+
func (s *Server) handleResourceList(w http.ResponseWriter, r *http.Request) {
52+
rdName := r.PathValue("rd")
53+
54+
_, err := s.Store.ResourceDefinitions().Get(r.Context(), rdName)
55+
if err != nil {
56+
writeStoreError(w, err)
57+
58+
return
59+
}
60+
61+
resList, err := s.Store.Resources().ListByDefinition(r.Context(), rdName)
62+
if err != nil {
63+
writeError(w, http.StatusInternalServerError, err.Error())
64+
65+
return
66+
}
67+
68+
out := make([]apiv1.ResourceWithVolumes, 0, len(resList))
69+
for i := range resList {
70+
out = append(out, apiv1.ResourceWithVolumes{Resource: resList[i]})
71+
}
72+
73+
writeJSON(w, http.StatusOK, out)
74+
}
75+
76+
// handleResourceGet answers `GET /v1/resource-definitions/{rd}/resources/{node}`,
77+
// returning the single Resource on that node or 404.
78+
func (s *Server) handleResourceGet(w http.ResponseWriter, r *http.Request) {
79+
rdName := r.PathValue("rd")
80+
node := r.PathValue("node")
81+
82+
res, err := s.Store.Resources().Get(r.Context(), rdName, node)
83+
if err != nil {
84+
writeStoreError(w, err)
85+
86+
return
87+
}
88+
89+
writeJSON(w, http.StatusOK, apiv1.ResourceWithVolumes{Resource: res})
90+
}
91+
4392
// handleAutoplace selects up to `place_count` nodes that have a storage
4493
// pool of the requested kind/name and creates Resource objects on them.
4594
//

0 commit comments

Comments
 (0)