Skip to content

Commit a96374e

Browse files
committed
fix(substrate): degrade gracefully when ate.dev CRDs are absent
GET /api/substrate/status returned 500 on clusters without the agent-substrate CRDs installed because listSubstrateCRs propagated the REST mapper NoKindMatchError as a server error. Treat IsNoMatchError as an empty result so the endpoint returns 200 with Enabled:false when substrate is not configured. Signed-off-by: QuentinBisson <quentin@giantswarm.io>
1 parent 14d6840 commit a96374e

2 files changed

Lines changed: 44 additions & 0 deletions

File tree

go/core/internal/httpserver/handlers/substrate.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/kagent-dev/kagent/go/core/internal/httpserver/errors"
1414
"github.com/kagent-dev/kagent/go/core/pkg/auth"
1515
"github.com/kagent-dev/kagent/go/core/pkg/sandboxbackend/substrate"
16+
"k8s.io/apimachinery/pkg/api/meta"
1617
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
1718
"sigs.k8s.io/controller-runtime/pkg/client"
1819
ctrllog "sigs.k8s.io/controller-runtime/pkg/log"
@@ -110,10 +111,16 @@ func (h *SubstrateHandler) listSubstrateCRs(ctx context.Context, namespace strin
110111

111112
wpList := &atev1alpha1.WorkerPoolList{}
112113
if err := h.KubeClient.List(ctx, wpList, listOpts...); err != nil {
114+
if meta.IsNoMatchError(err) {
115+
return nil, nil, nil
116+
}
113117
return nil, nil, err
114118
}
115119
tmplList := &atev1alpha1.ActorTemplateList{}
116120
if err := h.KubeClient.List(ctx, tmplList, listOpts...); err != nil {
121+
if meta.IsNoMatchError(err) {
122+
return nil, nil, nil
123+
}
117124
return nil, nil, err
118125
}
119126

go/core/internal/httpserver/handlers/substrate_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,50 @@ import (
1616
"github.com/stretchr/testify/require"
1717
"google.golang.org/grpc"
1818
corev1 "k8s.io/api/core/v1"
19+
apimeta "k8s.io/apimachinery/pkg/api/meta"
1920
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2021
"k8s.io/apimachinery/pkg/runtime"
2122
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2223
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
24+
"sigs.k8s.io/controller-runtime/pkg/client"
2325
"sigs.k8s.io/controller-runtime/pkg/client/fake"
2426
)
2527

28+
// noMatchKubeClient is a minimal client.Client stub whose List always returns
29+
// a *meta.NoKindMatchError, simulating a cluster where the listed CRDs are absent.
30+
type noMatchKubeClient struct {
31+
client.Client
32+
}
33+
34+
func (noMatchKubeClient) List(_ context.Context, _ client.ObjectList, _ ...client.ListOption) error {
35+
return &apimeta.NoKindMatchError{}
36+
}
37+
38+
// TestHandleGetSubstrateStatus_NoCRDs verifies that the endpoint returns 200 with
39+
// Enabled:false and empty slices when the ate.dev CRDs are not installed on the cluster.
40+
func TestHandleGetSubstrateStatus_NoCRDs(t *testing.T) {
41+
t.Parallel()
42+
43+
// AteClient is nil — substrate is not configured.
44+
base := &handlers.Base{KubeClient: noMatchKubeClient{}, Authorizer: &auth.NoopAuthorizer{}}
45+
h := handlers.NewSubstrateHandler(base, nil)
46+
47+
req := httptest.NewRequest(http.MethodGet, "/api/substrate/status?namespace=kagent", nil)
48+
req = setUser(req, "test-user")
49+
rec := httptest.NewRecorder()
50+
h.HandleGetSubstrateStatus(&testErrorResponseWriter{ResponseWriter: rec}, req)
51+
52+
require.Equal(t, http.StatusOK, rec.Code)
53+
54+
var wrapped api.StandardResponse[api.SubstrateStatusResponse]
55+
require.NoError(t, json.Unmarshal(rec.Body.Bytes(), &wrapped))
56+
require.False(t, wrapped.Data.Enabled)
57+
require.Empty(t, wrapped.Data.WorkerPools)
58+
require.Empty(t, wrapped.Data.ActorTemplates)
59+
require.Empty(t, wrapped.Data.Actors)
60+
require.Empty(t, wrapped.Data.Workers)
61+
}
62+
2663
type stubAteControl struct {
2764
ateapipb.ControlClient
2865
actors []*ateapipb.Actor

0 commit comments

Comments
 (0)