Skip to content

Commit 6c22d92

Browse files
authored
Adds ErrorNotFound Handling for Reconciler (#286)
Signed-off-by: Daneyon Hansen <[email protected]>
1 parent b5ffb66 commit 6c22d92

File tree

2 files changed

+138
-29
lines changed

2 files changed

+138
-29
lines changed

pkg/ext-proc/backend/inferencemodel_reconciler.go

+20-14
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
"inference.networking.x-k8s.io/gateway-api-inference-extension/api/v1alpha1"
77
logutil "inference.networking.x-k8s.io/gateway-api-inference-extension/pkg/ext-proc/util/logging"
8+
"k8s.io/apimachinery/pkg/api/errors"
89
"k8s.io/apimachinery/pkg/runtime"
910
"k8s.io/apimachinery/pkg/types"
1011
"k8s.io/client-go/tools/record"
@@ -25,32 +26,37 @@ func (c *InferenceModelReconciler) Reconcile(ctx context.Context, req ctrl.Reque
2526
if req.Namespace != c.PoolNamespacedName.Namespace {
2627
return ctrl.Result{}, nil
2728
}
28-
klog.V(1).Infof("reconciling InferenceModel %v", req.NamespacedName)
29-
30-
service := &v1alpha1.InferenceModel{}
31-
if err := c.Get(ctx, req.NamespacedName, service); err != nil {
32-
klog.Error(err, "unable to get InferencePool")
29+
klog.V(1).Infof("Reconciling InferenceModel %v", req.NamespacedName)
30+
31+
infModel := &v1alpha1.InferenceModel{}
32+
if err := c.Get(ctx, req.NamespacedName, infModel); err != nil {
33+
if errors.IsNotFound(err) {
34+
klog.V(1).Infof("InferenceModel %v not found. Removing from datastore since object must be deleted", req.NamespacedName)
35+
c.Datastore.InferenceModels.Delete(infModel.Spec.ModelName)
36+
return ctrl.Result{}, nil
37+
}
38+
klog.Error(err, "Unable to get InferenceModel")
3339
return ctrl.Result{}, err
3440
}
3541

36-
c.updateDatastore(service)
42+
c.updateDatastore(infModel)
3743
return ctrl.Result{}, nil
3844
}
3945

40-
func (c *InferenceModelReconciler) SetupWithManager(mgr ctrl.Manager) error {
41-
return ctrl.NewControllerManagedBy(mgr).
42-
For(&v1alpha1.InferenceModel{}).
43-
Complete(c)
44-
}
45-
4646
func (c *InferenceModelReconciler) updateDatastore(infModel *v1alpha1.InferenceModel) {
4747
if infModel.Spec.PoolRef.Name == c.PoolNamespacedName.Name {
4848
klog.V(1).Infof("Incoming pool ref %v, server pool name: %v", infModel.Spec.PoolRef, c.PoolNamespacedName.Name)
49-
klog.V(1).Infof("Adding/Updating inference model: %v", infModel.Spec.ModelName)
49+
klog.V(1).Infof("Adding/Updating InferenceModel: %v", infModel.Spec.ModelName)
5050
c.Datastore.InferenceModels.Store(infModel.Spec.ModelName, infModel)
5151
return
5252
}
53-
klog.V(logutil.DEFAULT).Infof("Removing/Not adding inference model: %v", infModel.Spec.ModelName)
53+
klog.V(logutil.DEFAULT).Infof("Removing/Not adding InferenceModel: %v", infModel.Spec.ModelName)
5454
// If we get here. The model is not relevant to this pool, remove.
5555
c.Datastore.InferenceModels.Delete(infModel.Spec.ModelName)
5656
}
57+
58+
func (c *InferenceModelReconciler) SetupWithManager(mgr ctrl.Manager) error {
59+
return ctrl.NewControllerManagedBy(mgr).
60+
For(&v1alpha1.InferenceModel{}).
61+
Complete(c)
62+
}

pkg/ext-proc/backend/inferencemodel_reconciler_test.go

+118-15
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
11
package backend
22

33
import (
4+
"context"
45
"sync"
56
"testing"
67

8+
"k8s.io/apimachinery/pkg/runtime"
9+
"k8s.io/client-go/tools/record"
10+
ctrl "sigs.k8s.io/controller-runtime"
11+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
12+
713
"inference.networking.x-k8s.io/gateway-api-inference-extension/api/v1alpha1"
814
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
915
"k8s.io/apimachinery/pkg/types"
1016
)
1117

1218
var (
13-
service1 = &v1alpha1.InferenceModel{
19+
infModel1 = &v1alpha1.InferenceModel{
1420
Spec: v1alpha1.InferenceModelSpec{
1521
ModelName: "fake model1",
1622
PoolRef: v1alpha1.PoolObjectReference{Name: "test-pool"},
@@ -19,7 +25,7 @@ var (
1925
Name: "test-service",
2026
},
2127
}
22-
service1Modified = &v1alpha1.InferenceModel{
28+
infModel1Modified = &v1alpha1.InferenceModel{
2329
Spec: v1alpha1.InferenceModelSpec{
2430
ModelName: "fake model1",
2531
PoolRef: v1alpha1.PoolObjectReference{Name: "test-poolio"},
@@ -28,7 +34,7 @@ var (
2834
Name: "test-service",
2935
},
3036
}
31-
service2 = &v1alpha1.InferenceModel{
37+
infModel2 = &v1alpha1.InferenceModel{
3238
Spec: v1alpha1.InferenceModelSpec{
3339
ModelName: "fake model",
3440
PoolRef: v1alpha1.PoolObjectReference{Name: "test-pool"},
@@ -60,8 +66,8 @@ func TestUpdateDatastore_InferenceModelReconciler(t *testing.T) {
6066
},
6167
InferenceModels: &sync.Map{},
6268
},
63-
incomingService: service1,
64-
wantInferenceModels: populateServiceMap(service1),
69+
incomingService: infModel1,
70+
wantInferenceModels: populateServiceMap(infModel1),
6571
},
6672
{
6773
name: "Removing existing service.",
@@ -75,9 +81,9 @@ func TestUpdateDatastore_InferenceModelReconciler(t *testing.T) {
7581
ResourceVersion: "Old and boring",
7682
},
7783
},
78-
InferenceModels: populateServiceMap(service1),
84+
InferenceModels: populateServiceMap(infModel1),
7985
},
80-
incomingService: service1Modified,
86+
incomingService: infModel1Modified,
8187
wantInferenceModels: populateServiceMap(),
8288
},
8389
{
@@ -92,7 +98,7 @@ func TestUpdateDatastore_InferenceModelReconciler(t *testing.T) {
9298
ResourceVersion: "Old and boring",
9399
},
94100
},
95-
InferenceModels: populateServiceMap(service1),
101+
InferenceModels: populateServiceMap(infModel1),
96102
},
97103
incomingService: &v1alpha1.InferenceModel{
98104
Spec: v1alpha1.InferenceModelSpec{
@@ -103,7 +109,7 @@ func TestUpdateDatastore_InferenceModelReconciler(t *testing.T) {
103109
Name: "unrelated-service",
104110
},
105111
},
106-
wantInferenceModels: populateServiceMap(service1),
112+
wantInferenceModels: populateServiceMap(infModel1),
107113
},
108114
{
109115
name: "Add to existing",
@@ -117,27 +123,124 @@ func TestUpdateDatastore_InferenceModelReconciler(t *testing.T) {
117123
ResourceVersion: "Old and boring",
118124
},
119125
},
120-
InferenceModels: populateServiceMap(service1),
126+
InferenceModels: populateServiceMap(infModel1),
121127
},
122-
incomingService: service2,
123-
wantInferenceModels: populateServiceMap(service1, service2),
128+
incomingService: infModel2,
129+
wantInferenceModels: populateServiceMap(infModel1, infModel2),
124130
},
125131
}
126132
for _, test := range tests {
127133
t.Run(test.name, func(t *testing.T) {
128-
InferenceModelReconciler := &InferenceModelReconciler{
134+
reconciler := &InferenceModelReconciler{
129135
Datastore: test.datastore,
130136
PoolNamespacedName: types.NamespacedName{Name: test.datastore.inferencePool.Name},
131137
}
132-
InferenceModelReconciler.updateDatastore(test.incomingService)
138+
reconciler.updateDatastore(test.incomingService)
133139

134-
if ok := mapsEqual(InferenceModelReconciler.Datastore.InferenceModels, test.wantInferenceModels); !ok {
140+
if ok := mapsEqual(reconciler.Datastore.InferenceModels, test.wantInferenceModels); !ok {
135141
t.Error("Maps are not equal")
136142
}
137143
})
138144
}
139145
}
140146

147+
func TestReconcile_ResourceNotFound(t *testing.T) {
148+
// Set up the scheme.
149+
scheme := runtime.NewScheme()
150+
_ = v1alpha1.AddToScheme(scheme)
151+
152+
// Create a fake client with no InferenceModel objects.
153+
fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
154+
155+
// Create a minimal datastore.
156+
datastore := &K8sDatastore{
157+
InferenceModels: &sync.Map{},
158+
inferencePool: &v1alpha1.InferencePool{
159+
ObjectMeta: metav1.ObjectMeta{Name: "test-pool"},
160+
},
161+
}
162+
163+
// Create the reconciler.
164+
reconciler := &InferenceModelReconciler{
165+
Client: fakeClient,
166+
Scheme: scheme,
167+
Record: record.NewFakeRecorder(10),
168+
Datastore: datastore,
169+
PoolNamespacedName: types.NamespacedName{Name: "test-pool"},
170+
}
171+
172+
// Create a request for a non-existent resource.
173+
req := ctrl.Request{NamespacedName: types.NamespacedName{Name: "non-existent-model", Namespace: "default"}}
174+
175+
// Call Reconcile.
176+
result, err := reconciler.Reconcile(context.Background(), req)
177+
if err != nil {
178+
t.Fatalf("expected no error when resource is not found, got %v", err)
179+
}
180+
181+
// Check that no requeue is requested.
182+
if result.Requeue || result.RequeueAfter != 0 {
183+
t.Errorf("expected no requeue, got %+v", result)
184+
}
185+
}
186+
187+
func TestReconcile_ResourceExists(t *testing.T) {
188+
// Set up the scheme.
189+
scheme := runtime.NewScheme()
190+
_ = v1alpha1.AddToScheme(scheme)
191+
192+
// Create an InferenceModel object.
193+
existingModel := &v1alpha1.InferenceModel{
194+
ObjectMeta: metav1.ObjectMeta{
195+
Name: "existing-model",
196+
Namespace: "default",
197+
},
198+
Spec: v1alpha1.InferenceModelSpec{
199+
ModelName: "fake-model",
200+
PoolRef: v1alpha1.PoolObjectReference{Name: "test-pool"},
201+
},
202+
}
203+
204+
// Create a fake client with the existing model.
205+
fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(existingModel).Build()
206+
207+
// Create a minimal datastore.
208+
datastore := &K8sDatastore{
209+
InferenceModels: &sync.Map{},
210+
inferencePool: &v1alpha1.InferencePool{
211+
ObjectMeta: metav1.ObjectMeta{Name: "test-pool"},
212+
},
213+
}
214+
215+
// Create the reconciler.
216+
reconciler := &InferenceModelReconciler{
217+
Client: fakeClient,
218+
Scheme: scheme,
219+
Record: record.NewFakeRecorder(10),
220+
Datastore: datastore,
221+
PoolNamespacedName: types.NamespacedName{Name: "test-pool", Namespace: "default"},
222+
}
223+
224+
// Create a request for the existing resource.
225+
req := ctrl.Request{NamespacedName: types.NamespacedName{Name: "existing-model", Namespace: "default"}}
226+
227+
// Call Reconcile.
228+
result, err := reconciler.Reconcile(context.Background(), req)
229+
if err != nil {
230+
t.Fatalf("expected no error when resource exists, got %v", err)
231+
}
232+
233+
// Check that no requeue is requested.
234+
if result.Requeue || result.RequeueAfter != 0 {
235+
t.Errorf("expected no requeue, got %+v", result)
236+
}
237+
238+
// Verify that the datastore was updated.
239+
if _, ok := datastore.InferenceModels.Load(existingModel.Spec.ModelName); !ok {
240+
t.Errorf("expected datastore to contain model %q", existingModel.Spec.ModelName)
241+
}
242+
}
243+
141244
func populateServiceMap(services ...*v1alpha1.InferenceModel) *sync.Map {
142245
returnVal := &sync.Map{}
143246

0 commit comments

Comments
 (0)