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

k8snetworkplumbingwg / sriov-network-operator / 17261676753

27 Aug 2025 08:36AM UTC coverage: 61.842% (-0.2%) from 62.077%
17261676753

push

github

web-flow
Merge pull request #894 from zeeke/us/namespaced-networks

Support namespaced {Sriov,SriovIB,OVS}Networks

73 of 141 new or added lines in 3 files covered. (51.77%)

14 existing lines in 3 files now uncovered.

8653 of 13992 relevant lines covered (61.84%)

0.69 hits per line

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

78.44
/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/utils"
42
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
43
)
44

45
type NetworkCRInstance interface {
46
        client.Object
47
        // renders NetAttDef from the network instance
48
        RenderNetAttDef() (*uns.Unstructured, error)
49
        // return name of the target namespace for the network
50
        NetworkNamespace() string
51
}
52

53
// interface which controller should implement to be compatible with genericNetworkReconciler
54
type networkController interface {
55
        reconcile.Reconciler
56
        // GetObject should return CR type which implements networkCRInstance
57
        // interface
58
        GetObject() NetworkCRInstance
59
        // should return CR list type
60
        GetObjectList() client.ObjectList
61
        // should return name of the controller
62
        Name() string
63
}
64

65
func newGenericNetworkReconciler(c client.Client, s *runtime.Scheme, controller networkController) *genericNetworkReconciler {
1✔
66
        return &genericNetworkReconciler{Client: c, Scheme: s, controller: controller}
1✔
67
}
1✔
68

69
// genericNetworkReconciler provide common code for all network controllers
70
type genericNetworkReconciler struct {
71
        client.Client
72
        Scheme     *runtime.Scheme
73
        controller networkController
74
}
75

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

1✔
79
        reqLogger.Info("Reconciling " + r.controller.Name())
1✔
80
        var err error
1✔
81

1✔
82
        // Fetch instance of the network object
1✔
83
        instance := r.controller.GetObject()
1✔
84
        err = r.Get(ctx, req.NamespacedName, instance)
1✔
85
        if err != nil {
2✔
86
                if errors.IsNotFound(err) {
2✔
87
                        // Request object not found, could have been deleted after reconcile request.
1✔
88
                        // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
1✔
89
                        // Return and don't requeue
1✔
90
                        return reconcile.Result{}, nil
1✔
91
                }
1✔
92
                // Error reading the object - requeue the request.
93
                return reconcile.Result{}, err
×
94
        }
95

96
        if instance == nil {
1✔
NEW
97
                // Request object not found, could have been deleted after reconcile request.
×
NEW
98
                // Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
×
NEW
99
                // Return and don't requeue
×
NEW
100
                return reconcile.Result{}, nil
×
NEW
101
        }
×
102

103
        if instance.NetworkNamespace() != "" && instance.GetNamespace() != vars.Namespace {
2✔
104
                reqLogger.Error(
1✔
105
                        fmt.Errorf("bad value for NetworkNamespace"),
1✔
106
                        ".spec.networkNamespace can't be specified if the resource belongs to a namespace other than the operator's",
1✔
107
                        "operatorNamespace", vars.Namespace,
1✔
108
                        ".metadata.namespace", instance.GetNamespace(),
1✔
109
                        ".spec.networkNamespace", instance.NetworkNamespace(),
1✔
110
                )
1✔
111
                return reconcile.Result{}, nil
1✔
112
        }
1✔
113

114
        // examine DeletionTimestamp to determine if object is under deletion
115
        if instance.GetDeletionTimestamp().IsZero() {
2✔
116
                // The object is not being deleted, so if it does not have our finalizer,
1✔
117
                // then lets add the finalizer and update the object. This is equivalent
1✔
118
                // registering our finalizer.
1✔
119
                err = r.updateFinalizers(ctx, instance)
1✔
120
                if err != nil {
2✔
121
                        return reconcile.Result{}, err
1✔
122
                }
1✔
123
        } else {
1✔
124
                // The object is being deleted
1✔
125
                err = r.cleanResourcesAndFinalizers(ctx, instance)
1✔
126
                return reconcile.Result{}, err
1✔
127
        }
1✔
128
        raw, err := instance.RenderNetAttDef()
1✔
129
        if err != nil {
1✔
130
                return reconcile.Result{}, err
×
131
        }
×
132
        netAttDef := &netattdefv1.NetworkAttachmentDefinition{}
1✔
133
        err = r.Scheme.Convert(raw, netAttDef, nil)
1✔
134
        if err != nil {
1✔
135
                return reconcile.Result{}, err
×
136
        }
×
137
        // format CNI config json in CR for easier readability
138
        netAttDef.Spec.Config, err = formatJSON(netAttDef.Spec.Config)
1✔
139
        if err != nil {
1✔
140
                reqLogger.Error(err, "Couldn't process rendered NetworkAttachmentDefinition config", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
141
                return reconcile.Result{}, err
×
142
        }
×
143
        if lnns, ok := instance.GetAnnotations()[sriovnetworkv1.LASTNETWORKNAMESPACE]; ok && netAttDef.GetNamespace() != lnns {
1✔
144
                err = r.Delete(ctx, &netattdefv1.NetworkAttachmentDefinition{
×
145
                        ObjectMeta: metav1.ObjectMeta{
×
146
                                Name:      instance.GetName(),
×
147
                                Namespace: lnns,
×
148
                        },
×
149
                })
×
150
                if err != nil {
×
151
                        reqLogger.Error(err, "Couldn't delete NetworkAttachmentDefinition CR", "Namespace", instance.GetName(), "Name", lnns)
×
152
                        return reconcile.Result{}, err
×
153
                }
×
154
        }
155

156
        if instance.GetNamespace() == netAttDef.Namespace {
2✔
157
                // If the NetAttachDef is in the same namespace of the resource, then we can leverage the OwnerReference field for garbage collector
1✔
158
                if err := controllerutil.SetOwnerReference(instance, netAttDef, r.Scheme); err != nil {
1✔
NEW
159
                        return reconcile.Result{}, err
×
NEW
160
                }
×
161
        }
162

163
        // Check if this NetworkAttachmentDefinition already exists
164
        found := &netattdefv1.NetworkAttachmentDefinition{}
1✔
165
        err = r.Get(ctx, types.NamespacedName{Name: netAttDef.Name, Namespace: netAttDef.Namespace}, found)
1✔
166
        if err != nil {
2✔
167
                if errors.IsNotFound(err) {
2✔
168
                        targetNamespace := &corev1.Namespace{}
1✔
169
                        err = r.Get(ctx, types.NamespacedName{Name: netAttDef.Namespace}, targetNamespace)
1✔
170
                        if errors.IsNotFound(err) {
2✔
171
                                reqLogger.Info("Target namespace doesn't exist, NetworkAttachmentDefinition will be created when namespace is available", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
1✔
172
                                return reconcile.Result{}, nil
1✔
173
                        }
1✔
174

175
                        reqLogger.Info("NetworkAttachmentDefinition CR not exist, creating")
1✔
176
                        err = r.Create(ctx, netAttDef)
1✔
177
                        if err != nil {
1✔
178
                                reqLogger.Error(err, "Couldn't create NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
179
                                return reconcile.Result{}, err
×
180
                        }
×
181

182
                        err = utils.AnnotateObject(ctx, instance, sriovnetworkv1.LASTNETWORKNAMESPACE, netAttDef.Namespace, r.Client)
1✔
183
                        if err != nil {
1✔
184
                                return reconcile.Result{}, err
×
185
                        }
×
186
                } else {
×
187
                        reqLogger.Error(err, "Couldn't get NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
188
                        return reconcile.Result{}, err
×
189
                }
×
190
        } else {
1✔
191
                reqLogger.Info("NetworkAttachmentDefinition CR already exist")
1✔
192
                if !equality.Semantic.DeepEqual(found.Spec, netAttDef.Spec) || !equality.Semantic.DeepEqual(found.GetAnnotations(), netAttDef.GetAnnotations()) {
2✔
193
                        reqLogger.Info("Update NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
1✔
194
                        netAttDef.SetResourceVersion(found.GetResourceVersion())
1✔
195

1✔
196
                        err = r.Update(ctx, netAttDef)
1✔
197
                        if err != nil {
1✔
198
                                reqLogger.Error(err, "Couldn't update NetworkAttachmentDefinition CR", "Namespace", netAttDef.Namespace, "Name", netAttDef.Name)
×
199
                                return reconcile.Result{}, err
×
200
                        }
×
201
                }
202
        }
203

204
        return ctrl.Result{}, nil
1✔
205
}
206

207
// SetupWithManager sets up the controller with the Manager.
208
func (r *genericNetworkReconciler) SetupWithManager(mgr ctrl.Manager) error {
1✔
209
        // Reconcile when the target namespace is created after the network object.
1✔
210
        namespaceHandler := handler.Funcs{
1✔
211
                CreateFunc: r.namespaceHandlerCreate,
1✔
212
        }
1✔
213
        return ctrl.NewControllerManagedBy(mgr).
1✔
214
                For(r.controller.GetObject()).
1✔
215
                Watches(&netattdefv1.NetworkAttachmentDefinition{}, handler.EnqueueRequestsFromMapFunc(r.handleNetAttDef)).
1✔
216
                Watches(&corev1.Namespace{}, &namespaceHandler).
1✔
217
                Complete(r.controller)
1✔
218
}
1✔
219

220
func (r *genericNetworkReconciler) handleNetAttDef(ctx context.Context, obj client.Object) []reconcile.Request {
1✔
221
        ret := []reconcile.Request{}
1✔
222
        instance := r.controller.GetObject()
1✔
223
        nadNamespacedName := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
1✔
224

1✔
225
        err := r.Get(ctx, nadNamespacedName, instance)
1✔
226
        if err == nil {
2✔
227
                // Found a NetworkObject in the same namespace as the NetworkAttachmentDefinition, reconcile it
1✔
228
                ret = append(ret, reconcile.Request{NamespacedName: nadNamespacedName})
1✔
229
        } else if !errors.IsNotFound(err) {
2✔
NEW
230
                log.Log.WithName(r.controller.Name()+" handleNetAttDef").Error(err, "can't get object", "object", nadNamespacedName)
×
NEW
231
        }
×
232

233
        // Not found, try to find the NetworkObject in the operator's namespace
234
        operatorNamespacedName := types.NamespacedName{Namespace: vars.Namespace, Name: obj.GetName()}
1✔
235
        err = r.Get(ctx, operatorNamespacedName, instance)
1✔
236
        if err == nil {
2✔
237
                // Found a NetworkObject in the operator's namespace, reconcile it
1✔
238
                ret = append(ret, reconcile.Request{NamespacedName: operatorNamespacedName})
1✔
239
        } else if !errors.IsNotFound(err) {
2✔
NEW
240
                log.Log.WithName(r.controller.Name()+" handleNetAttDef").Error(err, "can't get object", "object", operatorNamespacedName)
×
NEW
241
        }
×
242

243
        return ret
1✔
244
}
245

246
func (r *genericNetworkReconciler) namespaceHandlerCreate(ctx context.Context, e event.TypedCreateEvent[client.Object], w workqueue.TypedRateLimitingInterface[reconcile.Request]) {
1✔
247
        networkList := r.controller.GetObjectList()
1✔
248
        err := r.List(ctx,
1✔
249
                networkList,
1✔
250
                client.MatchingFields{"spec.networkNamespace": e.Object.GetName()},
1✔
251
        )
1✔
252
        logger := log.Log.WithName(r.controller.Name() + " reconciler")
1✔
253
        if err != nil {
1✔
254
                logger.Info("Can't list networks for namespace", "resource", e.Object.GetName(), "error", err)
×
255
                return
×
256
        }
×
257
        unsContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(networkList)
1✔
258
        if err != nil {
1✔
259
                logger.Info("Can't convert network list to unstructured object", "resource", e.Object.GetName(), "error", err)
×
260
                return
×
261
        }
×
262
        unsList := &uns.Unstructured{}
1✔
263
        unsList.SetUnstructuredContent(unsContent)
1✔
264
        _ = unsList.EachListItem(func(o runtime.Object) error {
2✔
265
                unsObj := o.(*uns.Unstructured)
1✔
266
                w.Add(reconcile.Request{NamespacedName: types.NamespacedName{
1✔
267
                        Namespace: unsObj.GetNamespace(),
1✔
268
                        Name:      unsObj.GetName(),
1✔
269
                }})
1✔
270
                return nil
1✔
271
        })
1✔
272
}
273

274
// deleteNetAttDef deletes the generated net-att-def CR
275
func (r *genericNetworkReconciler) deleteNetAttDef(ctx context.Context, cr NetworkCRInstance) error {
1✔
276
        // Fetch the NetworkAttachmentDefinition instance
1✔
277
        namespace := cr.NetworkNamespace()
1✔
278
        if namespace == "" {
2✔
279
                namespace = cr.GetNamespace()
1✔
280
        }
1✔
281
        instance := &netattdefv1.NetworkAttachmentDefinition{ObjectMeta: metav1.ObjectMeta{Name: cr.GetName(), Namespace: namespace}}
1✔
282
        err := r.Delete(ctx, instance)
1✔
283
        if err != nil {
2✔
284
                if errors.IsNotFound(err) {
2✔
285
                        return nil
1✔
286
                }
1✔
287
                return err
1✔
288
        }
289
        return nil
1✔
290
}
291

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

299
        instanceFinalizers := instance.GetFinalizers()
1✔
300
        if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
2✔
301
                instance.SetFinalizers(append(instanceFinalizers, sriovnetworkv1.NETATTDEFFINALIZERNAME))
1✔
302
                if err := r.Update(ctx, instance); err != nil {
2✔
303
                        return err
1✔
304
                }
1✔
305
        }
306

307
        return nil
1✔
308
}
309

310
func (r *genericNetworkReconciler) cleanResourcesAndFinalizers(ctx context.Context, instance NetworkCRInstance) error {
1✔
311
        instanceFinalizers := instance.GetFinalizers()
1✔
312

1✔
313
        if sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
2✔
314
                // our finalizer is present, so lets handle any external dependency
1✔
315
                log.FromContext(ctx).Info("delete NetworkAttachmentDefinition CR", "Namespace", instance.NetworkNamespace(), "Name", instance.GetName())
1✔
316
                if err := r.deleteNetAttDef(ctx, instance); err != nil {
2✔
317
                        // if fail to delete the external dependency here, return with error
1✔
318
                        // so that it can be retried
1✔
319
                        return err
1✔
320
                }
1✔
321
                // remove our finalizer from the list and update it.
322
                newFinalizers, found := sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers)
1✔
323
                if found {
2✔
324
                        instance.SetFinalizers(newFinalizers)
1✔
325
                        if err := r.Update(ctx, instance); err != nil {
2✔
326
                                return err
1✔
327
                        }
1✔
328
                }
329
        }
330
        return nil
1✔
331
}
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