Skip to content

Commit fb3f44c

Browse files
committed
feat(auto-monitoring): (re)create auto-resource when it is deleted
This solves two similar but distinct use cases: - A manually managed Dash0Monitoring resource in a namespace eligible for automatic namespace monitoring blocks creating the auto resource. This is by design. When the manually managed resource is deleted, trigger creating the auto resource immediately. - An auto-resource is deleted by a user. We now restore it automatically.
1 parent e1c4c21 commit fb3f44c

3 files changed

Lines changed: 158 additions & 4 deletions

File tree

internal/controller/auto_namespace_monitoring_controller.go

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ func (r *AutoNamespaceMonitoringReconciler) ensureNamespaceWatchIsActive(labelSe
247247
if labelSelectorPredicate != nil {
248248
watchPredicates = append(watchPredicates, labelSelectorPredicate)
249249
}
250-
logger.Debug("(re)creating the namespace watch")
250+
logger.Debug("(re)creating the the namespace controller's namespace watch")
251251
if err = namespaceController.Watch(
252252
source.TypedKind[*corev1.Namespace, reconcile.Request](
253253
r.manager.GetCache(),
@@ -261,6 +261,28 @@ func (r *AutoNamespaceMonitoringReconciler) ensureNamespaceWatchIsActive(labelSe
261261
}
262262
logger.Info("successfully created a new watch for namespaces")
263263

264+
// Also watch for deletion of Dash0Monitoring resources. When a manually managed monitoring resource is removed from
265+
// a namespace that should be auto-monitored, we want to create the auto-monitoring resource immediately rather than
266+
// waiting for the next namespace reconcile. When an auto-monitoring is deleted by a user, we also want to recreate
267+
// it.
268+
logger.Debug("(re)creating the namespace controller's watch for monitoring resource deletions")
269+
if err = namespaceController.Watch(
270+
source.TypedKind[*dash0v1beta1.Dash0Monitoring, reconcile.Request](
271+
r.manager.GetCache(),
272+
&dash0v1beta1.Dash0Monitoring{},
273+
handler.TypedEnqueueRequestsFromMapFunc[*dash0v1beta1.Dash0Monitoring, reconcile.Request](
274+
func(_ context.Context, mr *dash0v1beta1.Dash0Monitoring) []reconcile.Request {
275+
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: mr.Namespace}}}
276+
},
277+
),
278+
&monitoringResourceDeletePredicate{},
279+
),
280+
); err != nil {
281+
logger.Error(err, "unable to create a new watch for monitoring resource deletions")
282+
return
283+
}
284+
logger.Info("successfully created a new watch for monitoring resource deletions")
285+
264286
// start the controller
265287
backgroundCtx := context.Background()
266288
childContextForNamespaceController, stopNamespaceController := context.WithCancel(backgroundCtx)
@@ -970,3 +992,26 @@ func namespaceMatchesLabelSelector(ns *corev1.Namespace, selectorStr string) boo
970992
}
971993
return selector.Matches(labels.Set(ns.Labels))
972994
}
995+
996+
// monitoringResourceDeletePredicate fires on the deletion of any Dash0Monitoring resource. Create/Update/Generic
997+
// events are ignored. The triggered namespace reconcile is idempotent: it (re)creates the auto-managed resource
998+
// only when the namespace should be auto-monitored, otherwise it is a no-op. Deletes initiated by the controller
999+
// itself in deleteAllAutoMonitoringResourcesInCluster happen after the namespace watch has been stopped, so they
1000+
// do not feed back into this predicate.
1001+
type monitoringResourceDeletePredicate struct{}
1002+
1003+
func (p *monitoringResourceDeletePredicate) Create(_ event.TypedCreateEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1004+
return false
1005+
}
1006+
1007+
func (p *monitoringResourceDeletePredicate) Update(_ event.TypedUpdateEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1008+
return false
1009+
}
1010+
1011+
func (p *monitoringResourceDeletePredicate) Delete(_ event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1012+
return true
1013+
}
1014+
1015+
func (p *monitoringResourceDeletePredicate) Generic(_ event.TypedGenericEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1016+
return false
1017+
}

internal/controller/auto_namespace_monitoring_controller_test.go

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"k8s.io/apimachinery/pkg/types"
1414
"k8s.io/utils/ptr"
1515
"sigs.k8s.io/controller-runtime/pkg/client"
16+
"sigs.k8s.io/controller-runtime/pkg/event"
1617
"sigs.k8s.io/controller-runtime/pkg/reconcile"
1718

1819
dash0common "github.com/dash0hq/dash0-operator/api/operator/common"
@@ -299,7 +300,7 @@ var _ = Describe("The auto-namespace-monitoring controller", Ordered, func() {
299300
verifyNamespaceHasAutoMonitoringResource(ctx, Default, testAutoNamespace1)
300301
})
301302

302-
It("does not create a Dash0Monitoring with dash0.com/auto-monitored-namespace when the namespace already contains non-auto monitoring resource", func() {
303+
It("does not create a Dash0Monitoring resource with dash0.com/auto-monitored-namespace when the namespace already contains a non-auto monitoring resource", func() {
303304
createOperatorConfigurationResourceWithAutoMonitorNamespaces(ctx, new(true), "", nil)
304305
EnsureMonitoringResourceWithSpecExistsInNamespace(
305306
ctx,
@@ -317,6 +318,49 @@ var _ = Describe("The auto-namespace-monitoring controller", Ordered, func() {
317318
Expect(hasAutoMonitoredLabel).To(BeFalse())
318319
})
319320

321+
It("creates the auto-monitoring resource when a manually managed monitoring resource is deleted", func() {
322+
createOperatorConfigurationResourceWithAutoMonitorNamespaces(ctx, new(true), "", nil)
323+
EnsureMonitoringResourceWithSpecExistsInNamespace(
324+
ctx,
325+
k8sClient,
326+
MonitoringResourceDefaultSpec,
327+
manualMonitoringResourceName,
328+
)
329+
triggerNamespaceWatcherReconcile(ctx, namespaceWatcher, testAutoNamespace1)
330+
331+
// While the manual resource is in place, no auto-monitoring resource is created.
332+
monitoringResources := listMonitoringResources(ctx, Default, testAutoNamespace1)
333+
Expect(monitoringResources).To(HaveLen(1))
334+
Expect(monitoringResources[0].Name).To(Equal(manualMonitoringResourceName.Name))
335+
336+
// Simulate the user deleting the manually managed monitoring resource. Once the watch on Dash0Monitoring
337+
// deletions observes this, it enqueues a reconcile for the namespace; this test exercises that reconcile
338+
// directly.
339+
DeleteMonitoringResourceByName(ctx, k8sClient, manualMonitoringResourceName, false)
340+
triggerNamespaceWatcherReconcile(ctx, namespaceWatcher, testAutoNamespace1)
341+
342+
verifyNamespaceHasAutoMonitoringResource(ctx, Default, testAutoNamespace1)
343+
})
344+
345+
It("recreates the auto-monitoring resource when the auto-managed resource is deleted by the user", func() {
346+
createOperatorConfigurationResourceWithAutoMonitorNamespaces(ctx, new(true), "", nil)
347+
triggerNamespaceWatcherReconcile(ctx, namespaceWatcher, testAutoNamespace1)
348+
verifyNamespaceHasAutoMonitoringResource(ctx, Default, testAutoNamespace1)
349+
350+
// Simulate the user deleting the auto-managed monitoring resource. The monitoringResourceDeletePredicate
351+
// triggers a namespace reconcile, which should recreate the auto-managed resource.
352+
autoMonitoringResourceName := types.NamespacedName{
353+
Name: util.MonitoringAutoResourceDefaultName,
354+
Namespace: testAutoNamespace1,
355+
}
356+
DeleteMonitoringResourceByName(ctx, k8sClient, autoMonitoringResourceName, false)
357+
Expect(listMonitoringResources(ctx, Default, testAutoNamespace1)).To(BeEmpty())
358+
359+
triggerNamespaceWatcherReconcile(ctx, namespaceWatcher, testAutoNamespace1)
360+
361+
verifyNamespaceHasAutoMonitoringResource(ctx, Default, testAutoNamespace1)
362+
})
363+
320364
It("uses custom settings from the MonitoringTemplate when set", func() {
321365
createOperatorConfigurationResourceWithAutoMonitorNamespaces(
322366
ctx,
@@ -1021,6 +1065,55 @@ var _ = Describe("The auto-namespace-monitoring controller", Ordered, func() {
10211065
Expect(t2.Labels).To(Equal(map[string]string{"key": "value-2"}))
10221066
})
10231067
})
1068+
1069+
Context("monitoringResourceDeletePredicate", func() {
1070+
var p monitoringResourceDeletePredicate
1071+
1072+
It("ignores Create events", func() {
1073+
Expect(p.Create(event.TypedCreateEvent[*dash0v1beta1.Dash0Monitoring]{
1074+
Object: &dash0v1beta1.Dash0Monitoring{},
1075+
})).To(BeFalse())
1076+
})
1077+
1078+
It("ignores Update events", func() {
1079+
Expect(p.Update(event.TypedUpdateEvent[*dash0v1beta1.Dash0Monitoring]{
1080+
ObjectOld: &dash0v1beta1.Dash0Monitoring{},
1081+
ObjectNew: &dash0v1beta1.Dash0Monitoring{},
1082+
})).To(BeFalse())
1083+
})
1084+
1085+
It("ignores Generic events", func() {
1086+
Expect(p.Generic(event.TypedGenericEvent[*dash0v1beta1.Dash0Monitoring]{
1087+
Object: &dash0v1beta1.Dash0Monitoring{},
1088+
})).To(BeFalse())
1089+
})
1090+
1091+
It("fires on Delete of a manually managed monitoring resource (no auto-monitored label)", func() {
1092+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1093+
Object: &dash0v1beta1.Dash0Monitoring{},
1094+
})).To(BeTrue())
1095+
})
1096+
1097+
It("fires on Delete of a monitoring resource with unrelated labels", func() {
1098+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1099+
Object: &dash0v1beta1.Dash0Monitoring{
1100+
ObjectMeta: metav1.ObjectMeta{
1101+
Labels: map[string]string{"some": "label"},
1102+
},
1103+
},
1104+
})).To(BeTrue())
1105+
})
1106+
1107+
It("fires on Delete of an auto-managed monitoring resource", func() {
1108+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1109+
Object: &dash0v1beta1.Dash0Monitoring{
1110+
ObjectMeta: metav1.ObjectMeta{
1111+
Labels: map[string]string{util.AutoMonitoredNamespaceLabel: util.TrueString},
1112+
},
1113+
},
1114+
})).To(BeTrue())
1115+
})
1116+
})
10241117
})
10251118

10261119
func createOperatorConfigurationResourceWithAutoMonitorNamespaces(

test/e2e/e2e_test.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3202,10 +3202,26 @@ trace_statements:
32023202
false,
32033203
)
32043204

3205-
// Part Five: Disable automatic namespace monitoring entirely. This deletes all remaining auto-monitoring
3205+
// Part Five: Delete the auto-monitoring resource in a namespace and verify that it is recreated automatically.
3206+
3207+
By("auto-monitoring test, part V: deleting the auto-monitoring resource in namespaceExisting")
3208+
Expect(runAndIgnoreOutput(
3209+
exec.Command(
3210+
"kubectl",
3211+
"delete",
3212+
"--namespace",
3213+
namespaceExisting,
3214+
"dash0monitoring",
3215+
util.MonitoringAutoResourceDefaultName,
3216+
"--wait",
3217+
))).To(Succeed())
3218+
3219+
waitForMonitoringResourceToBecomeAvailable(namespaceExisting, util.MonitoringAutoResourceDefaultName)
3220+
3221+
// Part Six: Disable automatic namespace monitoring entirely. This deletes all remaining auto-monitoring
32063222
// resources, and the workloads in those namespaces will be uninstrumented.
32073223

3208-
By("auto-monitoring test, part V: disabling automatic namespace monitoring")
3224+
By("auto-monitoring test, part VI: disabling automatic namespace monitoring")
32093225
updateOperatorConfigurationAutoNamespaceMonitoringEnabled(false)
32103226

32113227
// Namespaces that still had auto-monitoring resources at the end of part IV, which now should become unmonitored.

0 commit comments

Comments
 (0)