Skip to content

Commit 1eeb29a

Browse files
kvapsclaude
andcommitted
feat(rest): error-reports endpoints stub
LIST returns an empty array, GET /{id} returns 404. Lets the linstor CLI's `error-reports list` work without 404-ing the whole endpoint. Durable per-error capture lands when the controller starts buffering reports. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
1 parent c724779 commit 1eeb29a

4 files changed

Lines changed: 112 additions & 1 deletion

File tree

PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ Full scope list lives in `docs/csi-api-surface.md` (to be created in Phase 1).
267267
- [ ] Cluster passphrase management
268268
- [ ] Satellite eviction / restoration / lost-and-recover
269269
- [x] Stats endpoint (`GET /v1/stats`): cluster-wide counters (nodes, RDs, resources, storage pools, snapshots). 2 contract tests.
270-
- [ ] Error reports, SOS-report
270+
- [x] Error reports stub (`/v1/error-reports` LIST returns []; GET /{id} → 404). Empty-but-present so `linstor error-reports list` doesn't choke. Real persistence lands when the controller starts buffering reports.
271271
- [ ] All `/v1/view/*` aggregates
272272
- [x] Controller properties endpoints (`/v1/controller/properties` GET/POST) — backed by KV-store instance "ControllerProps". Covers `linstor controller list-properties` / `set-property`. 3 contract tests.
273273
- [ ] Property-info endpoints (`*/properties/info`)

pkg/rest/error_reports.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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 rest
18+
19+
import (
20+
"net/http"
21+
)
22+
23+
// registerErrorReports wires the error-reports endpoints. linstor CLI's
24+
// `error-reports list` and `error-reports show` hit them.
25+
//
26+
// We don't yet capture controller-side errors as durable reports — the
27+
// LIST endpoint returns an empty array, GET returns 404. When the
28+
// controller starts buffering reports (Phase 6), this is the surface.
29+
func (s *Server) registerErrorReports(mux *http.ServeMux) {
30+
mux.HandleFunc("GET /v1/error-reports", handleErrorReportsList)
31+
mux.HandleFunc("GET /v1/error-reports/{id}", handleErrorReportGet)
32+
}
33+
34+
// handleErrorReportsList returns an empty array. We don't 503 the way
35+
// requireStore would — the endpoint stays available even if the store
36+
// is offline so client tooling doesn't choke.
37+
func handleErrorReportsList(w http.ResponseWriter, _ *http.Request) {
38+
writeJSON(w, http.StatusOK, []any{})
39+
}
40+
41+
// handleErrorReportGet always 404s for now; reports aren't persisted.
42+
func handleErrorReportGet(w http.ResponseWriter, _ *http.Request) {
43+
writeError(w, http.StatusNotFound, "error report not found")
44+
}

pkg/rest/error_reports_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 rest
18+
19+
import (
20+
"encoding/json"
21+
"net/http"
22+
"testing"
23+
24+
"github.com/cozystack/blockstor/pkg/store"
25+
)
26+
27+
// TestErrorReportsListEmpty: linstor CLI calls /v1/error-reports on
28+
// every `error-reports list`; an empty list is the right answer when
29+
// no errors have been recorded.
30+
func TestErrorReportsListEmpty(t *testing.T) {
31+
base, stop := startServerWithStore(t, store.NewInMemory())
32+
defer stop()
33+
34+
resp := httpGet(t, base+"/v1/error-reports")
35+
defer func() { _ = resp.Body.Close() }()
36+
37+
if resp.StatusCode != http.StatusOK {
38+
t.Fatalf("status: got %d, want 200", resp.StatusCode)
39+
}
40+
41+
var got []any
42+
43+
err := json.NewDecoder(resp.Body).Decode(&got)
44+
if err != nil {
45+
t.Fatalf("decode: %v", err)
46+
}
47+
48+
if len(got) != 0 {
49+
t.Errorf("expected empty list; got %d items", len(got))
50+
}
51+
}
52+
53+
// TestErrorReportGetMissing: an unknown error report id → 404. Upstream
54+
// returns 404 here too; clients depend on it for the "report expired"
55+
// branch.
56+
func TestErrorReportGetMissing(t *testing.T) {
57+
base, stop := startServerWithStore(t, store.NewInMemory())
58+
defer stop()
59+
60+
resp := httpGet(t, base+"/v1/error-reports/abcdef-1234")
61+
_ = resp.Body.Close()
62+
63+
if resp.StatusCode != http.StatusNotFound {
64+
t.Errorf("status: got %d, want 404", resp.StatusCode)
65+
}
66+
}

pkg/rest/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func (s *Server) Start(ctx context.Context) error {
7070
s.registerControllerProperties(mux)
7171
s.registerAdjust(mux)
7272
s.registerStats(mux)
73+
s.registerErrorReports(mux)
7374

7475
srv := &http.Server{
7576
Addr: s.Addr,

0 commit comments

Comments
 (0)