Skip to content

Commit 525a0e4

Browse files
committed
networks: Use owner annotation instead of finalizers
Using finalizers to remove dependant objects might lock a user if the operator is not running. Using the owner reference to garbage collect dangling NetworkAttachmentDefinition might clean the above use case. Signed-off-by: Andrea Panattoni <[email protected]>
1 parent 222b898 commit 525a0e4

File tree

3 files changed

+113
-65
lines changed

3 files changed

+113
-65
lines changed

api/v1/helper.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
uns "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2020
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
2121
"k8s.io/client-go/kubernetes"
22+
"sigs.k8s.io/controller-runtime/pkg/client"
2223
logf "sigs.k8s.io/controller-runtime/pkg/log"
2324

2425
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
@@ -28,7 +29,7 @@ import (
2829

2930
const (
3031
LASTNETWORKNAMESPACE = "operator.sriovnetwork.openshift.io/last-network-namespace"
31-
NETATTDEFFINALIZERNAME = "netattdef.finalizers.sriovnetwork.openshift.io"
32+
NETATTDEFFINALIZERNAME = "netattdef.finalizers.sriovnetwork.openshift.io" // Deprecated. Keeping this to migrate old cluster through upgrades
3233
POOLCONFIGFINALIZERNAME = "poolconfig.finalizers.sriovnetwork.openshift.io"
3334
OPERATORCONFIGFINALIZERNAME = "operatorconfig.finalizers.sriovnetwork.openshift.io"
3435
ESwithModeLegacy = "legacy"
@@ -651,7 +652,7 @@ func (cr *SriovIBNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
651652
} else {
652653
data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
653654
}
654-
data.Data["Owner"] = cr.GroupVersionKind().GroupKind().String() + "/" + cr.GetNamespace() + "/" + cr.Name
655+
data.Data["Owner"] = OwnerRefToString(cr)
655656
data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
656657

657658
data.Data["StateConfigured"] = true
@@ -720,7 +721,7 @@ func (cr *SriovNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
720721
} else {
721722
data.Data["SriovNetworkNamespace"] = cr.Spec.NetworkNamespace
722723
}
723-
data.Data["Owner"] = cr.GroupVersionKind().GroupKind().String() + "/" + cr.GetNamespace() + "/" + cr.Name
724+
data.Data["Owner"] = OwnerRefToString(cr)
724725
data.Data["SriovCniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
725726
data.Data["SriovCniVlan"] = cr.Spec.Vlan
726727

@@ -839,7 +840,7 @@ func (cr *OVSNetwork) RenderNetAttDef() (*uns.Unstructured, error) {
839840
} else {
840841
data.Data["NetworkNamespace"] = cr.Spec.NetworkNamespace
841842
}
842-
data.Data["Owner"] = cr.GroupVersionKind().GroupKind().String() + "/" + cr.GetNamespace() + "/" + cr.Name
843+
data.Data["Owner"] = OwnerRefToString(cr)
843844
data.Data["CniResourceName"] = os.Getenv("RESOURCE_PREFIX") + "/" + cr.Spec.ResourceName
844845

845846
if cr.Spec.Capabilities == "" {
@@ -997,3 +998,34 @@ func (s *SriovNetworkNodeState) ResetKeepUntilTime() bool {
997998
s.SetAnnotations(annotations)
998999
return true
9991000
}
1001+
1002+
func OwnerRefToString(cr client.Object) string {
1003+
if cr == nil {
1004+
return "owner-object-not-found"
1005+
}
1006+
if cr.GetObjectKind().GroupVersionKind().Empty() {
1007+
return "unknown/" + cr.GetNamespace() + "/" + cr.GetName()
1008+
}
1009+
1010+
return cr.GetObjectKind().GroupVersionKind().GroupKind().String() + "/" + cr.GetNamespace() + "/" + cr.GetName()
1011+
}
1012+
1013+
func StringToOwnerRef(ownerRef string) (client.Object, string, string, error) {
1014+
chunks := strings.Split(ownerRef, "/")
1015+
if len(chunks) != 3 {
1016+
return nil, "", "", fmt.Errorf("invalid ownerRef %s", ownerRef)
1017+
}
1018+
groupKind, namespace, name := chunks[0], chunks[1], chunks[2]
1019+
switch groupKind {
1020+
case "SriovNetwork.sriovnetwork.openshift.io":
1021+
return &SriovNetwork{}, namespace, name, nil
1022+
1023+
case "SriovIBNetwork.sriovnetwork.openshift.io":
1024+
return &SriovIBNetwork{}, namespace, name, nil
1025+
1026+
case "OVSNetwork.sriovnetwork.openshift.io":
1027+
return &OVSNetwork{}, namespace, name, nil
1028+
}
1029+
1030+
return nil, "", "", fmt.Errorf("unknown ownerRef groupKind %s", groupKind)
1031+
}

controllers/generic_network_controller.go

Lines changed: 53 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,15 @@ func (r *genericNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Reque
8888
// Request object not found, could have been deleted after reconcile request.
8989
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
9090
// Return and don't requeue
91-
return reconcile.Result{}, nil
91+
return reconcile.Result{}, r.garbageCollect(ctx)
9292
}
9393
// Error reading the object - requeue the request.
9494
return reconcile.Result{}, err
9595
}
9696

97-
if instance == nil {
98-
// Request object not found, could have been deleted after reconcile request.
99-
// Owned objects are automatically garbage collected. For additional cleanup logic use finalizers.
100-
// Return and don't requeue
101-
return reconcile.Result{}, nil
97+
err = r.cleanOldFinalizers(ctx, instance)
98+
if err != nil {
99+
return reconcile.Result{}, err
102100
}
103101

104102
if instance.NetworkNamespace() != "" && instance.GetNamespace() != vars.Namespace {
@@ -112,20 +110,6 @@ func (r *genericNetworkReconciler) Reconcile(ctx context.Context, req ctrl.Reque
112110
return reconcile.Result{}, nil
113111
}
114112

115-
// examine DeletionTimestamp to determine if object is under deletion
116-
if instance.GetDeletionTimestamp().IsZero() {
117-
// The object is not being deleted, so if it does not have our finalizer,
118-
// then lets add the finalizer and update the object. This is equivalent
119-
// registering our finalizer.
120-
err = r.updateFinalizers(ctx, instance)
121-
if err != nil {
122-
return reconcile.Result{}, err
123-
}
124-
} else {
125-
// The object is being deleted
126-
err = r.cleanResourcesAndFinalizers(ctx, instance)
127-
return reconcile.Result{}, err
128-
}
129113
raw, err := instance.RenderNetAttDef()
130114
if err != nil {
131115
return reconcile.Result{}, err
@@ -286,61 +270,69 @@ func (r *genericNetworkReconciler) namespaceHandlerCreate(ctx context.Context, e
286270
})
287271
}
288272

289-
// deleteNetAttDef deletes the generated net-att-def CR
290-
func (r *genericNetworkReconciler) deleteNetAttDef(ctx context.Context, cr NetworkCRInstance) error {
291-
// Fetch the NetworkAttachmentDefinition instance
292-
namespace := cr.NetworkNamespace()
293-
if namespace == "" {
294-
namespace = cr.GetNamespace()
295-
}
296-
instance := &netattdefv1.NetworkAttachmentDefinition{ObjectMeta: metav1.ObjectMeta{Name: cr.GetName(), Namespace: namespace}}
297-
err := r.Delete(ctx, instance)
273+
// garbageCollect searches all NetworkAttachmentDefinition objects that has a `sriovnetwork.openshift.io/owner`
274+
// annotation pointing to non existing objects
275+
func (r *genericNetworkReconciler) garbageCollect(ctx context.Context) error {
276+
logger := log.Log.WithName("garbageCollect")
277+
278+
netAttachDefs := &netattdefv1.NetworkAttachmentDefinitionList{}
279+
err := r.Client.List(ctx, netAttachDefs)
298280
if err != nil {
299-
if errors.IsNotFound(err) {
300-
return nil
301-
}
302-
return err
281+
return fmt.Errorf("garbageCollect: failed to list NetworkAttachmentDefinition: %w", err)
303282
}
304-
return nil
305-
}
283+
logger.Info("xxx")
306284

307-
func (r *genericNetworkReconciler) updateFinalizers(ctx context.Context, instance NetworkCRInstance) error {
308-
if instance.GetNamespace() != vars.Namespace {
309-
// If the resource is in a namespace different than the operator one, then the NetworkAttachmentDefinition will
310-
// be created in the same namespace and its deletion can be handled by OwnerReferences. There is no need for finalizers
311-
return nil
312-
}
285+
for _, netAttDef := range netAttachDefs.Items {
286+
logger.Info("xxx1", "obj", netAttDef)
313287

314-
instanceFinalizers := instance.GetFinalizers()
315-
if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
316-
instance.SetFinalizers(append(instanceFinalizers, sriovnetworkv1.NETATTDEFFINALIZERNAME))
317-
if err := r.Update(ctx, instance); err != nil {
318-
return err
288+
owner, ok := netAttDef.GetAnnotations()[consts.OwnerRefAnnotation]
289+
if !ok {
290+
continue
291+
}
292+
293+
obj, namespace, name, err := sriovnetworkv1.StringToOwnerRef(owner)
294+
if err != nil {
295+
logger.Error(err, "bad value for `sriovnetwork.openshift.io/owner`", "owner", owner)
296+
continue
297+
}
298+
299+
err = r.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, obj)
300+
if err != nil {
301+
if !errors.IsNotFound(err) {
302+
logger.Error(err, "failed to get owner object", "owner", owner)
303+
continue
304+
}
305+
306+
logger.Info("owner object not found. deleting NetworkAttachmentDefinition", "obj", netAttDef)
307+
308+
err := r.Delete(ctx, &netAttDef)
309+
if err != nil {
310+
logger.Error(err, "can't delete NetworkAttachmentDefinition", "obj", netAttDef)
311+
}
319312
}
320313
}
321314

322315
return nil
323316
}
324317

325-
func (r *genericNetworkReconciler) cleanResourcesAndFinalizers(ctx context.Context, instance NetworkCRInstance) error {
318+
func (r *genericNetworkReconciler) cleanOldFinalizers(ctx context.Context, instance NetworkCRInstance) error {
326319
instanceFinalizers := instance.GetFinalizers()
327320

328-
if sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
329-
// our finalizer is present, so lets handle any external dependency
330-
log.FromContext(ctx).Info("delete NetworkAttachmentDefinition CR", "Namespace", instance.NetworkNamespace(), "Name", instance.GetName())
331-
if err := r.deleteNetAttDef(ctx, instance); err != nil {
332-
// if fail to delete the external dependency here, return with error
333-
// so that it can be retried
321+
if !sriovnetworkv1.StringInArray(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers) {
322+
return nil
323+
}
324+
325+
// remove our finalizer from the list and update it.
326+
newFinalizers, found := sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers)
327+
if found {
328+
logger := log.Log.WithName("cleanFinalizers")
329+
330+
instance.SetFinalizers(newFinalizers)
331+
logger.Info("Updating network instance to remove finalizer `netattdef.finalizers.sriovnetwork.openshift.io`", "obj", instance)
332+
if err := r.Update(ctx, instance); err != nil {
334333
return err
335334
}
336-
// remove our finalizer from the list and update it.
337-
newFinalizers, found := sriovnetworkv1.RemoveString(sriovnetworkv1.NETATTDEFFINALIZERNAME, instanceFinalizers)
338-
if found {
339-
instance.SetFinalizers(newFinalizers)
340-
if err := r.Update(ctx, instance); err != nil {
341-
return err
342-
}
343-
}
344335
}
336+
345337
return nil
346338
}

controllers/sriovnetwork_controller_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,30 @@ var _ = Describe("SriovNetwork Controller", Ordered, func() {
427427
})
428428

429429
})
430+
431+
Context("Migration upgrades", func() {
432+
It("should remove finalizer in existing SriovNetworks", func() {
433+
cr := sriovnetworkv1.SriovNetwork{
434+
ObjectMeta: metav1.ObjectMeta{
435+
Name: "sriovnet-old",
436+
Namespace: testNamespace,
437+
Finalizers: []string{
438+
sriovnetworkv1.NETATTDEFFINALIZERNAME,
439+
},
440+
},
441+
}
442+
443+
err := k8sClient.Create(ctx, &cr)
444+
Expect(err).NotTo(HaveOccurred())
445+
446+
Eventually(func(g Gomega) {
447+
err := k8sClient.Get(ctx, types.NamespacedName{Namespace: cr.Namespace, Name: cr.Name}, &cr)
448+
g.Expect(err).ToNot(HaveOccurred())
449+
g.Expect(cr.Finalizers).To(BeEmpty())
450+
}).WithTimeout(time.Second).Should(Succeed())
451+
})
452+
})
453+
430454
})
431455
})
432456

0 commit comments

Comments
 (0)