• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 20828114838

08 Jan 2026 06:52PM UTC coverage: 64.358% (+1.1%) from 63.274%
20828114838

Pull #1007

github

web-flow
Merge 353dd128d into 28eea4ead
Pull Request #1007: Add Kubernetes Conditions to SR-IOV Network Operator CRDs

846 of 1066 new or added lines in 15 files covered. (79.36%)

27 existing lines in 7 files now uncovered.

9951 of 15462 relevant lines covered (64.36%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

73.15
/controllers/generic_network_controller.go
1
/*
2
Copyright 2021.
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 controllers
18

19
import (
20
        "context"
21
        "fmt"
22

23
        netattdefv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1"
24
        corev1 "k8s.io/api/core/v1"
25
        "k8s.io/apimachinery/pkg/api/equality"
26
        "k8s.io/apimachinery/pkg/api/errors"
27
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
        uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29
        "k8s.io/apimachinery/pkg/runtime"
30
        "k8s.io/apimachinery/pkg/types"
31
        "k8s.io/client-go/util/workqueue"
32
        ctrl "sigs.k8s.io/controller-runtime"
33
        "sigs.k8s.io/controller-runtime/pkg/client"
34
        "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
35
        "sigs.k8s.io/controller-runtime/pkg/event"
36
        "sigs.k8s.io/controller-runtime/pkg/handler"
37
        "sigs.k8s.io/controller-runtime/pkg/log"
38
        "sigs.k8s.io/controller-runtime/pkg/reconcile"
39

40
        sriovnetworkv1 "github.com/k8snetworkplumbingwg/sriov-network-operator/api/v1"
41
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
42
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/status"
43
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
44
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
45
)
46

47
type NetworkCRInstance interface {
48
        client.Object
49
        // renders NetAttDef from the network instance
50
        RenderNetAttDef() (*uns.Unstructured, error)
51
        // return name of the target namespace for the network
52
        NetworkNamespace() string
53
        // GetConditions returns the conditions from the status
54
        GetConditions() []metav1.Condition
55
        // SetConditions sets the conditions in the status
56
        SetConditions(conditions []metav1.Condition)
57
}
58

59
// interface which controller should implement to be compatible with genericNetworkReconciler
60
type networkController interface {
61
        reconcile.Reconciler
62
        // GetObject should return CR type which implements networkCRInstance
63
        // interface
64
        GetObject() NetworkCRInstance
65
        // should return CR list type
66
        GetObjectList() client.ObjectList
67
        // should return name of the controller
68
        Name() string
69
}
70

71
func newGenericNetworkReconciler(c client.Client, s *runtime.Scheme, controller networkController, statusPatcher status.Interface) *genericNetworkReconciler {
1✔
72
        return &genericNetworkReconciler{
1✔
73
                Client:        c,
1✔
74
                Scheme:        s,
1✔
75
                controller:    controller,
1✔
76
                statusPatcher: statusPatcher,
1✔
77
        }
1✔
78
}
1✔
79

80
// genericNetworkReconciler provide common code for all network controllers
81
type genericNetworkReconciler struct {
82
        client.Client
83
        Scheme        *runtime.Scheme
84
        controller    networkController
85
        statusPatcher status.Interface
86
}
87

88
func (r *genericNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
1✔
89
        reqLogger := log.FromContext(ctx).WithValues(r.controller.Name(), req.NamespacedName)
1✔
90

1✔
91
        reqLogger.Info("Reconciling " + r.controller.Name())
1✔
92
        var err error
1✔
93

1✔
94
        // Fetch instance of the network object
1✔
95
        instance := r.controller.GetObject()
1✔
96
        err = r.Get(ctx, req.NamespacedName, instance)
1✔
97
        if err != nil {
2✔
98
                if errors.IsNotFound(err) {
2✔
99
                        // Request object not found, could have been deleted after reconcile request.
1✔
100
                        // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
1✔
101
                        // Return and don't requeue
1✔
102
                        return reconcile.Result{}, nil
1✔
103
                }
1✔
104
                // Error reading the object - requeue the request.
105
                return reconcile.Result{}, err
×
106
        }
107

108
        if instance == nil {
1✔
109
                // Request object not found, could have been deleted after reconcile request.
×
110
                // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
×
111
                // Return and don't requeue
×
112
                return reconcile.Result{}, nil
×
113
        }
×
114

115
        if instance.NetworkNamespace() != "" && instance.GetNamespace() != vars.Namespace {
2✔
116
                errMsg := fmt.Errorf("spec.networkNamespace cannot be set when the resource is not in the operator namespace (%s)", vars.Namespace)
1✔
117
                reqLogger.Error(
1✔
118
                        errMsg,
1✔
119
                        ".spec.networkNamespace can't be specified if the resource belongs to a namespace other than the operator's",
1✔
120
                        "operatorNamespace", vars.Namespace,
1✔
121
                        ".metadata.namespace", instance.GetNamespace(),
1✔
122
                        ".spec.networkNamespace", instance.NetworkNamespace(),
1✔
123
                )
1✔
124
                // Update conditions to reflect the error
1✔
125
                err = r.updateConditions(ctx, instance, false, errMsg)
1✔
126
                if err != nil {
1✔
NEW
127
                        reqLogger.Error(err, "Failed to update conditions")
×
NEW
128
                }
×
129
                return reconcile.Result{}, err
1✔
130
        }
131

132
        // examine DeletionTimestamp to determine if object is under deletion
133
        if instance.GetDeletionTimestamp().IsZero() {
2✔
134
                // The object is not being deleted, so if it does not have our finalizer,
1✔
135
                // then lets add the finalizer and update the object. This is equivalent
1✔
136
                // registering our finalizer.
1✔
137
                err = r.updateFinalizers(ctx, instance)
1✔
138
                if err != nil {
1✔
UNCOV
139
                        return reconcile.Result{}, err
×
UNCOV
140
                }
×
141
        } else {
1✔
142
                // The object is being deleted
1✔
143
                err = r.cleanResourcesAndFinalizers(ctx, instance)
1✔
144
                return reconcile.Result{}, err
1✔
145
        }
1✔
146
        raw, err := instance.RenderNetAttDef()
1✔
147
        if err != nil {
1✔
NEW
148
                // Update conditions to reflect rendering failure
×
NEW
149
                updateErr := r.updateConditions(ctx, instance, false, err)
×
NEW
150
                if updateErr != nil {
×
NEW
151
                        reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
152
                }
×
153
                return reconcile.Result{}, err
×
154
        }
155
        netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
1✔
156
        err = r.Scheme.Convert(raw, netAttDef, nil)
1✔
157
        if err != nil {
1✔
158
                return reconcile.Result{}, err
×
159
        }
×
160
        // format CNI config json in CR for easier readability
161
        netAttDef.Spec.Config, err = formatJSON(netAttDef.Spec.Config)
1✔
162
        if err != nil {
1✔
163
                reqLogger.Error(err, "Couldn't process rendered NetworkAttachmentDefinition config", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
164
                return reconcile.Result{}, err
×
165
        }
×
166
        if lnns, ok := instance.GetAnnotations()[sriovnetworkv1.LASTNETWORKNAMESPACE]; ok && netAttDef.GetNamespace() != lnns {
1✔
167
                err = r.Delete(ctx, &netattdefv1.NetworkAttachmentDefinition{
×
168
                        ObjectMeta: metav1.ObjectMeta{
×
169
                                Name:      instance.GetName(),
×
170
                                Namespace: lnns,
×
171
                        },
×
172
                })
×
173
                if err != nil {
×
174
                        reqLogger.Error(err, "Couldn't delete NetworkAttachmentDefinition CR", "Namespace", instance.GetName(), "Name", lnns)
×
175
                        return reconcile.Result{}, err
×
176
                }
×
177
        }
178

179
        if instance.GetNamespace() == netAttDef.Namespace {
2✔
180
                // If the NetAttachDef is in the same namespace of the resource, then we can leverage the OwnerReference field for garbage collector
1✔
181
                if err := controllerutil.SetOwnerReference(instance, netAttDef, r.Scheme); err != nil {
1✔
182
                        return reconcile.Result{}, err
×
183
                }
×
184
        }
185

186
        // Check if this NetworkAttachmentDefinition already exists
187
        found := &netattdefv1.NetworkAttachmentDefinition{}
1✔
188
        err = r.Get(ctx, types.NamespacedName{Name: netAttDef.Name, Namespace: netAttDef.Namespace}, found)
1✔
189
        if err != nil {
2✔
190
                if errors.IsNotFound(err) {
2✔
191
                        targetNamespace := &corev1.Namespace{}
1✔
192
                        err = r.Get(ctx, types.NamespacedName{Name: netAttDef.Namespace}, targetNamespace)
1✔
193
                        if errors.IsNotFound(err) {
2✔
194
                                reqLogger.Info("Target namespace doesn't exist, NetworkAttachmentDefinition will be created when namespace is available", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
1✔
195
                                // Update conditions to reflect waiting state
1✔
196
                                updateErr := r.updateConditions(ctx, instance, false, nil)
1✔
197
                                if updateErr != nil {
1✔
NEW
198
                                        reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
199
                                        return reconcile.Result{}, updateErr
×
NEW
200
                                }
×
201
                                return reconcile.Result{}, nil
1✔
202
                        }
203

204
                        reqLogger.Info("NetworkAttachmentDefinition CR not exist, creating")
1✔
205
                        err = r.Create(ctx, netAttDef)
1✔
206
                        if err != nil {
1✔
207
                                reqLogger.Error(err, "Couldn't create NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
NEW
208
                                // Update conditions to reflect creation failure
×
NEW
209
                                updateErr := r.updateConditions(ctx, instance, false, err)
×
NEW
210
                                if updateErr != nil {
×
NEW
211
                                        reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
212
                                        return reconcile.Result{}, updateErr
×
NEW
213
                                }
×
214
                                return reconcile.Result{}, err
×
215
                        }
216

217
                        err = utils.AnnotateObject(ctx, instance, sriovnetworkv1.LASTNETWORKNAMESPACE, netAttDef.Namespace, r.Client)
1✔
218
                        if err != nil {
1✔
219
                                return reconcile.Result{}, err
×
220
                        }
×
221

222
                        // Update conditions to reflect successful creation
223
                        updateErr := r.updateConditions(ctx, instance, false, err)
1✔
224
                        if updateErr != nil {
1✔
NEW
225
                                reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
226
                                return reconcile.Result{}, updateErr
×
NEW
227
                        }
×
228
                } else {
×
229
                        reqLogger.Error(err, "Couldn't get NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
NEW
230
                        // Update conditions to reflect get failure
×
NEW
231
                        updateErr := r.updateConditions(ctx, instance, false, err)
×
NEW
232
                        if updateErr != nil {
×
NEW
233
                                reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
234
                        }
×
235
                        return reconcile.Result{}, err
×
236
                }
237
        } else {
1✔
238
                reqLogger.Info("NetworkAttachmentDefinition CR already exist")
1✔
239

1✔
240
                foundOwner := found.GetAnnotations()[consts.OwnerRefAnnotation]
1✔
241
                expectedOwner := netAttDef.GetAnnotations()[consts.OwnerRefAnnotation]
1✔
242

1✔
243
                // Note for the future: the `foundOwner != ""` condition can be removed to make the operator not touching the NetworkAttachmentDefinition created
1✔
244
                // by the user.
1✔
245
                if foundOwner != "" && foundOwner != expectedOwner {
2✔
246
                        reqLogger.Info("A NetworkAttachmentDefinition with the same name already exists and it does not belong to this resource",
1✔
247
                                "Namespace", netAttDef.Namespace, "Name", netAttDef.Name,
1✔
248
                                "CurrentOwner", foundOwner, "ExpectedOwner", expectedOwner,
1✔
249
                        )
1✔
250
                        // Update conditions to reflect ownership conflict
1✔
251
                        ownershipErr := fmt.Errorf("NetworkAttachmentDefinition '%s/%s' already exists and is owned by %s", netAttDef.Namespace, netAttDef.Name, foundOwner)
1✔
252
                        updateErr := r.updateConditions(ctx, instance, false, ownershipErr)
1✔
253
                        if updateErr != nil {
1✔
NEW
254
                                reqLogger.Error(updateErr, "Failed to update conditions")
×
NEW
255
                                return reconcile.Result{}, updateErr
×
NEW
256
                        }
×
257
                        return reconcile.Result{}, nil
1✔
258
                }
259

260
                if !equality.Semantic.DeepEqual(found.Spec, netAttDef.Spec) || !equality.Semantic.DeepEqual(found.GetAnnotations(), netAttDef.GetAnnotations()) {
2✔
261
                        reqLogger.Info("Update NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
1✔
262
                        netAttDef.SetResourceVersion(found.GetResourceVersion())
1✔
263

1✔
264
                        err = r.Update(ctx, netAttDef)
1✔
265
                        if err != nil {
1✔
266
                                reqLogger.Error(err, "Couldn't update NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
NEW
267
                                // Update conditions to reflect update failure
×
NEW
268
                                updateErr := r.updateConditions(ctx, instance, false, err)
×
NEW
269
                                if updateErr != nil {
×
NEW
270
                                        reqLogger.Error(updateErr, "Failed to update conditions", "Namespace", instance.GetNamespace(), "Name", instance.GetName())
×
NEW
271
                                }
×
272
                                return reconcile.Result{}, err
×
273
                        }
274
                }
275

276
                // NetworkAttachmentDefinition exists and is up to date
277
                err = r.updateConditions(ctx, instance, true, nil)
1✔
278
                if err != nil {
1✔
NEW
279
                        reqLogger.Error(err, "Failed to update conditions")
×
NEW
280
                        return reconcile.Result{}, err
×
NEW
281
                }
×
282
        }
283

284
        return ctrl.Result{}, nil
1✔
285
}
286

287
// SetupWithManager sets up the controller with the Manager.
288
func (r *genericNetworkReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
289
        // Reconcile when the target namespace is created after the network object.
1✔
290
        namespaceHandler := handler.Funcs{
1✔
291
                CreateFunc: r.namespaceHandlerCreate,
1✔
292
        }
1✔
293
        return ctrl.NewControllerManagedBy(mgr).
1✔
294
                For(r.controller.GetObject()).
1✔
295
                Watches(&netattdefv1.NetworkAttachmentDefinition{}, handler.EnqueueRequestsFromMapFunc(r.handleNetAttDef)).
1✔
296
                Watches(&corev1.Namespace{}, &namespaceHandler).
1✔
297
                Complete(r.controller)
1✔
298
}
1✔
299

300
func (r *genericNetworkReconciler) handleNetAttDef(ctx context.Context, obj client.Object) []reconcile.Request {
1✔
301
        ret := []reconcile.Request{}
1✔
302
        instance := r.controller.GetObject()
1✔
303
        nadNamespacedName := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
1✔
304

1✔
305
        err := r.Get(ctx, nadNamespacedName, instance)
1✔
306
        if err == nil {
2✔
307
                // Found a NetworkObject in the same namespace as the NetworkAttachmentDefinition, reconcile it
1✔
308
                ret = append(ret, reconcile.Request{NamespacedName: nadNamespacedName})
1✔
309
        } else if !errors.IsNotFound(err) {
2✔
310
                log.Log.WithName(r.controller.Name()+" handleNetAttDef").Error(err, "can't get object", "object", nadNamespacedName)
×
311
        }
×
312

313
        // Not found, try to find the NetworkObject in the operator's namespace
314
        operatorNamespacedName := types.NamespacedName{Namespace: vars.Namespace, Name: obj.GetName()}
1✔
315
        err = r.Get(ctx, operatorNamespacedName, instance)
1✔
316
        if err == nil {
2✔
317
                // Found a NetworkObject in the operator's namespace, reconcile it
1✔
318
                ret = append(ret, reconcile.Request{NamespacedName: operatorNamespacedName})
1✔
319
        } else if !errors.IsNotFound(err) {
2✔
320
                log.Log.WithName(r.controller.Name()+" handleNetAttDef").Error(err, "can't get object", "object", operatorNamespacedName)
×
321
        }
×
322

323
        return ret
1✔
324
}
325

326
func (r *genericNetworkReconciler) namespaceHandlerCreate(ctx context.Context, e event.TypedCreateEvent[client.Object], w workqueue.TypedRateLimitingInterface[reconcile.Request]) {
1✔
327
        networkList := r.controller.GetObjectList()
1✔
328
        err := r.List(ctx,
1✔
329
                networkList,
1✔
330
                client.MatchingFields{"spec.networkNamespace": e.Object.GetName()},
1✔
331
        )
1✔
332
        logger := log.Log.WithName(r.controller.Name() + " reconciler")
1✔
333
        if err != nil {
1✔
334
                logger.Info("Can't list networks for namespace", "resource", e.Object.GetName(), "error", err)
×
335
                return
×
336
        }
×
337
        unsContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(networkList)
1✔
338
        if err != nil {
1✔
339
                logger.Info("Can't convert network list to unstructured object", "resource", e.Object.GetName(), "error", err)
×
340
                return
×
341
        }
×
342
        unsList := &uns.Unstructured{}
1✔
343
        unsList.SetUnstructuredContent(unsContent)
1✔
344
        _ = unsList.EachListItem(func(o runtime.Object) error {
2✔
345
                unsObj := o.(*uns.Unstructured)
1✔
346
                w.Add(reconcile.Request{NamespacedName: types.NamespacedName{
1✔
347
                        Namespace: unsObj.GetNamespace(),
1✔
348
                        Name:      unsObj.GetName(),
1✔
349
                }})
1✔
350
                return nil
1✔
351
        })
1✔
352
}
353

354
// deleteNetAttDef deletes the generated net-att-def CR
355
func (r *genericNetworkReconciler) deleteNetAttDef(ctx context.Context, cr NetworkCRInstance) error {
1✔
356
        // Fetch the NetworkAttachmentDefinition instance
1✔
357
        namespace := cr.NetworkNamespace()
1✔
358
        if namespace == "" {
2✔
359
                namespace = cr.GetNamespace()
1✔
360
        }
1✔
361
        instance := &netattdefv1.NetworkAttachmentDefinition{ObjectMeta: metav1.ObjectMeta{Name: cr.GetName(), Namespace: namespace}}
1✔
362
        err := r.Delete(ctx, instance)
1✔
363
        if err != nil {
2✔
364
                if errors.IsNotFound(err) {
2✔
365
                        return nil
1✔
366
                }
1✔
367
                return err
1✔
368
        }
369
        return nil
1✔
370
}
371

372
func (r *genericNetworkReconciler) updateFinalizers(ctx context.Context, instance NetworkCRInstance) error {
1✔
373
        if instance.GetNamespace() != vars.Namespace {
2✔
374
                // If the resource is in a namespace different than the operator one, then the NetworkAttachmentDefinition will
1✔
375
                // be created in the same namespace and its deletion can be handled by OwnerReferences. There is no need for finalizers
1✔
376
                return nil
1✔
377
        }
1✔
378

379
        instanceFinalizers := instance.GetFinalizers()
1✔
380
        if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
2✔
381
                instance.SetFinalizers(append(instanceFinalizers, sriovnetworkv1.NETATTDEFFINALIZERNAME))
1✔
382
                if err := r.Update(ctx, instance); err != nil {
1✔
UNCOV
383
                        return err
×
UNCOV
384
                }
×
385
        }
386

387
        return nil
1✔
388
}
389

390
func (r *genericNetworkReconciler) cleanResourcesAndFinalizers(ctx context.Context, instance NetworkCRInstance) error {
1✔
391
        instanceFinalizers := instance.GetFinalizers()
1✔
392

1✔
393
        if sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
2✔
394
                // our finalizer is present, so lets handle any external dependency
1✔
395
                log.FromContext(ctx).Info("delete NetworkAttachmentDefinition CR", "Namespace", instance.NetworkNamespace(), "Name", instance.GetName())
1✔
396
                if err := r.deleteNetAttDef(ctx, instance); err != nil {
2✔
397
                        // if fail to delete the external dependency here, return with error
1✔
398
                        // so that it can be retried
1✔
399
                        return err
1✔
400
                }
1✔
401
                // remove our finalizer from the list and update it.
402
                newFinalizers, found := sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers)
1✔
403
                if found {
2✔
404
                        instance.SetFinalizers(newFinalizers)
1✔
405
                        if err := r.Update(ctx, instance); err != nil {
2✔
406
                                return err
1✔
407
                        }
1✔
408
                }
409
        }
410
        return nil
1✔
411
}
412

413
// updateConditions updates the conditions in the instance status
414
func (r *genericNetworkReconciler) updateConditions(ctx context.Context, instance NetworkCRInstance, nadFound bool, err error) error {
1✔
415
        updateInstance := r.controller.GetObject()
1✔
416
        getErr := r.Get(ctx, types.NamespacedName{Namespace: instance.GetNamespace(), Name: instance.GetName()}, updateInstance)
1✔
417
        if getErr != nil {
1✔
NEW
418
                return getErr
×
NEW
419
        }
×
420

421
        oldConditions := updateInstance.GetConditions()
1✔
422
        newConditions := make([]metav1.Condition, len(oldConditions))
1✔
423
        copy(newConditions, oldConditions)
1✔
424

1✔
425
        if err != nil {
2✔
426
                // Network provisioning failed
1✔
427
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionReady, metav1.ConditionFalse,
1✔
428
                        sriovnetworkv1.ReasonProvisioningFailed, fmt.Sprintf("Failed to provision network: %v", err),
1✔
429
                        instance.GetGeneration())
1✔
430
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionDegraded, metav1.ConditionTrue,
1✔
431
                        sriovnetworkv1.ReasonProvisioningFailed, fmt.Sprintf("NetworkAttachmentDefinition provisioning failed: %v", err),
1✔
432
                        instance.GetGeneration())
1✔
433
        } else if nadFound {
3✔
434
                // Network is ready
1✔
435
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionReady, metav1.ConditionTrue,
1✔
436
                        sriovnetworkv1.ReasonNetworkReady, "NetworkAttachmentDefinition is provisioned and ready",
1✔
437
                        instance.GetGeneration())
1✔
438
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionDegraded, metav1.ConditionFalse,
1✔
439
                        sriovnetworkv1.ReasonNotDegraded, "Network is functioning correctly",
1✔
440
                        instance.GetGeneration())
1✔
441
        } else {
2✔
442
                // NAD not found yet (waiting for namespace) - this is a degraded state
1✔
443
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionReady, metav1.ConditionFalse,
1✔
444
                        sriovnetworkv1.ReasonNotReady, "Waiting for target namespace to be created",
1✔
445
                        instance.GetGeneration())
1✔
446
                r.statusPatcher.SetCondition(&newConditions, sriovnetworkv1.ConditionDegraded, metav1.ConditionTrue,
1✔
447
                        sriovnetworkv1.ReasonNamespaceNotFound, "Target namespace does not exist",
1✔
448
                        instance.GetGeneration())
1✔
449
        }
1✔
450

451
        // Only update status if conditions changed
452
        if !equality.Semantic.DeepEqual(oldConditions, newConditions) {
2✔
453
                // Use the StatusPatcher to update status with automatic retry and event emission
1✔
454
                if updateErr := r.statusPatcher.UpdateStatusWithEvents(ctx, updateInstance, oldConditions, newConditions, func() error {
2✔
455
                        updateInstance.SetConditions(newConditions)
1✔
456
                        return nil
1✔
457
                }); updateErr != nil {
1✔
NEW
458
                        log.FromContext(ctx).Error(updateErr, "Failed to update status conditions")
×
NEW
459
                        return updateErr
×
NEW
460
                }
×
461
        }
462

463
        return nil
1✔
464
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc