Skip to content

Commit 3b03e37

Browse files
committed
Remove the DaemonSet reconciler (#39)
Run the DaemonSet garbage collection in the Module reconciliation loop.
1 parent ae6e301 commit 3b03e37

9 files changed

+170
-176
lines changed

.github/workflows/e2e.yaml

+4-17
Original file line numberDiff line numberDiff line change
@@ -288,26 +288,13 @@ jobs:
288288
done
289289
timeout-minutes: 1
290290

291-
- name: Verify that the DaemonSet controller works
291+
- name: Verify that the DaemonSet gets garbage collected
292292
run: |
293-
set -eux
294-
295-
# Get the DaemonSet's name, as we cannot patch by label
296-
DS_NAME=$(kubectl get daemonsets.apps -l oot.node.kubernetes.io/module.name=ooto-ci-b --template='{{ (index .items 0).metadata.name }}')
297-
298-
# Verify that we got the name
299-
test -n "${DS_NAME}"
300-
301-
# Wait one minute for the DaemonSet to be considered old and deletable
302-
sleep 60
303-
304-
# Patch the DaemonSet to trigger a reconciliation
305-
kubectl patch daemonset.apps "${DS_NAME}" --patch '{"metadata":{"labels": {"trigger-reconcile":""}}}'
306-
307-
until ! kubectl get daemonsets.apps "${DS_NAME}"; do
293+
# Cannot use kubectl wait because it will fail if there is no initial match
294+
until [ $(kubectl get daemonsets.apps -l oot.node.kubernetes.io/module.name=ooto-ci-b -o go-template='{{ len .items }}') -eq 0 ]; do
308295
sleep 3
309296
done
310-
timeout-minutes: 2
297+
timeout-minutes: 1
311298

312299
- name: Get all resources in the oot-operator-system namespace
313300
run: kubectl get all -n oot-operator-system

controllers/daemonset.go

+18
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import (
1111
v1 "k8s.io/api/core/v1"
1212
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1313
"k8s.io/apimachinery/pkg/runtime"
14+
"k8s.io/apimachinery/pkg/util/sets"
1415
"sigs.k8s.io/controller-runtime/pkg/client"
1516
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
1617
)
1718

1819
//go:generate mockgen -source=daemonset.go -package=controllers -destination=mock_daemonset.go
1920

2021
type DaemonSetCreator interface {
22+
GarbageCollect(ctx context.Context, existingDS map[string]*appsv1.DaemonSet, validKernels sets.String) ([]string, error)
2123
ModuleDaemonSetsByKernelVersion(ctx context.Context, mod ootov1alpha1.Module) (map[string]*appsv1.DaemonSet, error)
2224
SetAsDesired(ds *appsv1.DaemonSet, image string, mod ootov1alpha1.Module, kernelVersion string) error
2325
}
@@ -38,6 +40,22 @@ func NewDaemonSetCreator(client client.Client, kernelLabel, namespace string, sc
3840
}
3941
}
4042

43+
func (dc *daemonSetGenerator) GarbageCollect(ctx context.Context, existingDS map[string]*appsv1.DaemonSet, validKernels sets.String) ([]string, error) {
44+
deleted := make([]string, 0)
45+
46+
for kernelVersion, ds := range existingDS {
47+
if !validKernels.Has(kernelVersion) {
48+
if err := dc.client.Delete(ctx, ds); err != nil {
49+
return nil, fmt.Errorf("could not delete DaemonSet %s: %v", ds.Name, err)
50+
}
51+
52+
deleted = append(deleted, ds.Name)
53+
}
54+
}
55+
56+
return deleted, nil
57+
}
58+
4159
func (dc *daemonSetGenerator) ModuleDaemonSetsByKernelVersion(ctx context.Context, mod ootov1alpha1.Module) (map[string]*appsv1.DaemonSet, error) {
4260
dsList := appsv1.DaemonSetList{}
4361

controllers/daemonset_reconciler.go

-57
This file was deleted.

controllers/daemonset_reconciler_test.go

-72
This file was deleted.

controllers/daemonset_test.go

+63
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
appsv1 "k8s.io/api/apps/v1"
1313
v1 "k8s.io/api/core/v1"
1414
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
15+
"k8s.io/apimachinery/pkg/util/sets"
1516
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1617
)
1718

@@ -235,6 +236,68 @@ var _ = Describe("daemonSetGenerator", func() {
235236
})
236237
})
237238

239+
Describe("GarbageCollect", func() {
240+
It("should only delete one of the two DaemonSets if only one is not used", func() {
241+
const (
242+
legitKernelVersion = "legit-kernel-version"
243+
legitName = "legit"
244+
notLegitKernelVersion = "not-legit-kernel-version"
245+
notLegitName = "not-legit"
246+
)
247+
248+
dsLegit := appsv1.DaemonSet{
249+
ObjectMeta: metav1.ObjectMeta{Name: legitName, Namespace: dsNamespace},
250+
}
251+
252+
dsNotLegit := appsv1.DaemonSet{
253+
ObjectMeta: metav1.ObjectMeta{Name: notLegitName, Namespace: dsNamespace},
254+
}
255+
256+
client := fake.NewClientBuilder().WithObjects(&dsLegit, &dsNotLegit).Build()
257+
258+
dc := controllers.NewDaemonSetCreator(client, "", dsNamespace, scheme)
259+
260+
existingDS := map[string]*appsv1.DaemonSet{
261+
legitKernelVersion: &dsLegit,
262+
notLegitKernelVersion: &dsNotLegit,
263+
}
264+
265+
validKernels := sets.NewString(legitKernelVersion)
266+
267+
Expect(
268+
dc.GarbageCollect(context.TODO(), existingDS, validKernels),
269+
).To(
270+
Equal([]string{notLegitName}),
271+
)
272+
273+
afterDeleteDaemonSetList := appsv1.DaemonSetList{}
274+
275+
Expect(
276+
client.List(context.TODO(), &afterDeleteDaemonSetList),
277+
).To(
278+
Succeed(),
279+
)
280+
281+
Expect(afterDeleteDaemonSetList.Items).To(HaveLen(1))
282+
Expect(afterDeleteDaemonSetList.Items[0].Name).To(Equal(legitName))
283+
})
284+
285+
It("should return an error if a deletion failed", func() {
286+
dc := controllers.NewDaemonSetCreator(
287+
fake.NewClientBuilder().Build(),
288+
"",
289+
dsNamespace,
290+
scheme)
291+
292+
existingDS := map[string]*appsv1.DaemonSet{
293+
"some-kernel-version": {},
294+
}
295+
296+
_, err := dc.GarbageCollect(context.TODO(), existingDS, sets.NewString())
297+
Expect(err).To(HaveOccurred())
298+
})
299+
})
300+
238301
Describe("ModuleDaemonSetsByKernelVersion", func() {
239302
It("should return an empty map if no DaemonSets are present", func() {
240303
dc := controllers.NewDaemonSetCreator(

controllers/mock_daemonset.go

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controllers/module_reconciler.go

+13-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
batchv1 "k8s.io/api/batch/v1"
2929
v1 "k8s.io/api/core/v1"
3030
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/util/sets"
3132
ctrl "sigs.k8s.io/controller-runtime"
3233
"sigs.k8s.io/controller-runtime/pkg/builder"
3334
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -70,7 +71,7 @@ func NewModuleReconciler(
7071
//+kubebuilder:rbac:groups=ooto.sigs.k8s.io,resources=modules,verbs=get;list;watch;create;update;patch;delete
7172
//+kubebuilder:rbac:groups=ooto.sigs.k8s.io,resources=modules/status,verbs=get;update;patch
7273
//+kubebuilder:rbac:groups=ooto.sigs.k8s.io,resources=modules/finalizers,verbs=update
73-
//+kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=create;get;list;patch;watch
74+
//+kubebuilder:rbac:groups=apps,resources=daemonsets,verbs=create;delete;get;list;patch;watch
7475
//+kubebuilder:rbac:groups="core",resources=nodes,verbs=get;list;watch
7576
//+kubebuilder:rbac:groups="batch",resources=jobs,verbs=create;list;watch
7677

@@ -100,11 +101,6 @@ func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
100101
return res, fmt.Errorf("could not list nodes: %v", err)
101102
}
102103

103-
if len(nodes.Items) == 0 {
104-
logger.Info("No nodes matching the selector; skipping module")
105-
return res, nil
106-
}
107-
108104
mappings := make(map[string]*ootov1alpha1.KernelMapping)
109105

110106
for _, node := range nodes.Items {
@@ -185,8 +181,17 @@ func (r *ModuleReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
185181
logger.Info("Reconciled DaemonSet", "name", ds.Name, "result", opRes)
186182
}
187183

188-
// TODO we need to garbage collect DaemonSets here.
189-
// If we removed a kernel mapping from the Module, the corresponding DaemonSet is currently not deleted.
184+
logger.Info("Garbage-collecting DaemonSets")
185+
186+
// Garbage collect old DaemonSets for which there are no nodes.
187+
validKernels := sets.StringKeySet(mappings)
188+
189+
deleted, err := r.dc.GarbageCollect(ctx, dsByKernelVersion, validKernels)
190+
if err != nil {
191+
return res, fmt.Errorf("could not garbage collect DaemonSets: %v", err)
192+
}
193+
194+
logger.Info("Garbage-collected DaemonSets", "names", deleted)
190195

191196
return res, nil
192197
}

0 commit comments

Comments
 (0)