Skip to content

Commit f971d4f

Browse files
authored
fix(usb): autodelete nodeusbdevice (#2220)
Implement automatic cleanup of stale NodeUSBDevice objects when the USB device is no longer present on the host and the object is not assigned to any namespace. The deletion flow was moved to the lifecycle handler logic: add and persist FinalizerNodeUSBDeviceCleanup first; only after that, mark orphaned NodeUSBDevice for deletion; perform USBDevice cleanup only in the standard deletion path after deletionTimestamp is set. --------- Signed-off-by: Daniil Antoshin <daniil.antoshin@flant.com>
1 parent 835f411 commit f971d4f

2 files changed

Lines changed: 68 additions & 9 deletions

File tree

images/virtualization-artifact/pkg/controller/nodeusbdevice/internal/handler/deletion.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ import (
2121
"fmt"
2222

2323
apierrors "k8s.io/apimachinery/pkg/api/errors"
24+
"k8s.io/apimachinery/pkg/api/meta"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"sigs.k8s.io/controller-runtime/pkg/client"
2627
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
2728
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2829

2930
"github.com/deckhouse/virtualization-controller/pkg/controller/nodeusbdevice/internal/state"
31+
"github.com/deckhouse/virtualization-controller/pkg/controller/reconciler"
3032
"github.com/deckhouse/virtualization/api/core/v1alpha2"
33+
"github.com/deckhouse/virtualization/api/core/v1alpha2/nodeusbdevicecondition"
3134
)
3235

3336
const (
@@ -56,7 +59,18 @@ func (h *DeletionHandler) Handle(ctx context.Context, s state.NodeUSBDeviceState
5659

5760
switch {
5861
case current.GetDeletionTimestamp().IsZero():
59-
controllerutil.AddFinalizer(changed, v1alpha2.FinalizerNodeUSBDeviceCleanup)
62+
if !controllerutil.ContainsFinalizer(current, v1alpha2.FinalizerNodeUSBDeviceCleanup) {
63+
controllerutil.AddFinalizer(changed, v1alpha2.FinalizerNodeUSBDeviceCleanup)
64+
return reconcile.Result{}, nil
65+
}
66+
67+
if shouldAutoDeleteNodeUSBDevice(current) {
68+
if err := h.client.Delete(ctx, current); err != nil && !apierrors.IsNotFound(err) {
69+
return reconcile.Result{}, fmt.Errorf("failed to delete NodeUSBDevice: %w", err)
70+
}
71+
return reconcile.Result{}, reconciler.ErrStopHandlerChain
72+
}
73+
6074
return reconcile.Result{}, nil
6175

6276
default:
@@ -92,3 +106,20 @@ func (h *DeletionHandler) cleanupOwnedUSBDevices(ctx context.Context, owner *v1a
92106
func (h *DeletionHandler) Name() string {
93107
return nameDeletionHandler
94108
}
109+
110+
func shouldAutoDeleteNodeUSBDevice(nodeUSBDevice *v1alpha2.NodeUSBDevice) bool {
111+
if nodeUSBDevice == nil || nodeUSBDevice.GetDeletionTimestamp() != nil {
112+
return false
113+
}
114+
115+
if nodeUSBDevice.Spec.AssignedNamespace != "" {
116+
return false
117+
}
118+
119+
readyCondition := meta.FindStatusCondition(nodeUSBDevice.Status.Conditions, string(nodeusbdevicecondition.ReadyType))
120+
if readyCondition == nil {
121+
return false
122+
}
123+
124+
return readyCondition.Status == metav1.ConditionFalse && readyCondition.Reason == string(nodeusbdevicecondition.NotFound)
125+
}

images/virtualization-artifact/pkg/controller/nodeusbdevice/internal/handler/deletion_test.go

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"github.com/deckhouse/virtualization-controller/pkg/controller/reconciler"
3434
"github.com/deckhouse/virtualization-controller/pkg/logger"
3535
"github.com/deckhouse/virtualization/api/core/v1alpha2"
36+
"github.com/deckhouse/virtualization/api/core/v1alpha2/nodeusbdevicecondition"
3637
)
3738

3839
var _ = Describe("DeletionHandler", func() {
@@ -43,15 +44,27 @@ var _ = Describe("DeletionHandler", func() {
4344
})
4445

4546
DescribeTable("Handle",
46-
func(deleting, withOwnedUSB bool, usbNamespace string, expectFinalizerPresent, expectOwnedUSBDeleted bool) {
47+
func(deleting, autoDelete, withOwnedUSB, withFinalizer bool, assignedNamespace, usbNamespace string, expectFinalizerPresent, expectOwnedUSBDeleted, expectNodeDeleted, expectStopChain bool) {
4748
scheme := apiruntime.NewScheme()
4849
Expect(v1alpha2.AddToScheme(scheme)).To(Succeed())
4950

50-
node := &v1alpha2.NodeUSBDevice{ObjectMeta: metav1.ObjectMeta{Name: "usb-device-1", UID: "node-usb-uid"}}
51+
node := &v1alpha2.NodeUSBDevice{
52+
ObjectMeta: metav1.ObjectMeta{Name: "usb-device-1", UID: "node-usb-uid"},
53+
Spec: v1alpha2.NodeUSBDeviceSpec{AssignedNamespace: assignedNamespace},
54+
}
55+
if autoDelete {
56+
node.Status.Conditions = []metav1.Condition{{
57+
Type: string(nodeusbdevicecondition.ReadyType),
58+
Status: metav1.ConditionFalse,
59+
Reason: string(nodeusbdevicecondition.NotFound),
60+
}}
61+
}
62+
if withFinalizer {
63+
node.Finalizers = []string{v1alpha2.FinalizerNodeUSBDeviceCleanup}
64+
}
5165
if deleting {
5266
now := metav1.Now()
5367
node.DeletionTimestamp = &now
54-
node.Finalizers = []string{v1alpha2.FinalizerNodeUSBDeviceCleanup}
5568
}
5669

5770
objects := []client.Object{node}
@@ -83,7 +96,11 @@ var _ = Describe("DeletionHandler", func() {
8396
h := NewDeletionHandler(cl)
8497
st := state.New(cl, res)
8598
_, err := h.Handle(ctx, st)
86-
Expect(err).NotTo(HaveOccurred())
99+
if expectStopChain {
100+
Expect(err).To(MatchError(reconciler.ErrStopHandlerChain))
101+
} else {
102+
Expect(err).NotTo(HaveOccurred())
103+
}
87104

88105
if expectFinalizerPresent {
89106
Expect(res.Changed().GetFinalizers()).To(ContainElement(v1alpha2.FinalizerNodeUSBDeviceCleanup))
@@ -100,10 +117,21 @@ var _ = Describe("DeletionHandler", func() {
100117
Expect(err).NotTo(HaveOccurred())
101118
}
102119
}
120+
121+
deletedNode := &v1alpha2.NodeUSBDevice{}
122+
err = cl.Get(ctx, types.NamespacedName{Name: node.Name}, deletedNode)
123+
if expectNodeDeleted {
124+
Expect(err).To(HaveOccurred())
125+
} else {
126+
Expect(err).NotTo(HaveOccurred())
127+
}
103128
},
104-
Entry("not deleting adds finalizer", false, false, "", true, false),
105-
Entry("deleting removes finalizer and owned USB", true, true, "test-namespace", false, true),
106-
Entry("deleting removes finalizer even without owned USB", true, false, "", false, false),
107-
Entry("deleting removes owned USB in different namespace", true, true, "previous-namespace", false, true),
129+
Entry("not deleting adds finalizer", false, false, false, false, "", "", true, false, false, false),
130+
Entry("auto-delete first adds finalizer without deleting node object", false, true, true, false, "", "test-namespace", true, false, false, false),
131+
Entry("auto-delete marks node object for deletion when finalizer is already present", false, true, true, true, "", "test-namespace", true, false, false, true),
132+
Entry("assigned not found device is not auto-deleted", false, true, false, true, "test-namespace", "", true, false, false, false),
133+
Entry("deleting removes finalizer and owned USB", true, false, true, true, "", "test-namespace", false, true, false, false),
134+
Entry("deleting removes finalizer even without owned USB", true, false, false, true, "", "", false, false, false, false),
135+
Entry("deleting removes owned USB in different namespace", true, false, true, true, "", "previous-namespace", false, true, false, false),
108136
)
109137
})

0 commit comments

Comments
 (0)