Skip to content

Commit c581a74

Browse files
committed
feat(auto-monitoring): create auto-resource when manual r is deleted
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.
1 parent 0771cc7 commit c581a74

2 files changed

Lines changed: 119 additions & 2 deletions

File tree

internal/controller/auto_namespace_monitoring_controller.go

Lines changed: 44 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,27 @@ func (r *AutoNamespaceMonitoringReconciler) ensureNamespaceWatchIsActive(labelSe
261261
}
262262
logger.Info("successfully created a new watch for namespaces")
263263

264+
// Also watch for deletion of manually managed Dash0Monitoring resources. When a manually managed monitoring
265+
// resource is removed from a namespace that should be auto-monitored, we want to create the auto-monitoring
266+
// resource immediately rather than waiting for the next namespace reconcile.
267+
logger.Debug("(re)creating the namespace controller's watch for monitoring resource deletions")
268+
if err = namespaceController.Watch(
269+
source.TypedKind[*dash0v1beta1.Dash0Monitoring, reconcile.Request](
270+
r.manager.GetCache(),
271+
&dash0v1beta1.Dash0Monitoring{},
272+
handler.TypedEnqueueRequestsFromMapFunc[*dash0v1beta1.Dash0Monitoring, reconcile.Request](
273+
func(_ context.Context, mr *dash0v1beta1.Dash0Monitoring) []reconcile.Request {
274+
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: mr.Namespace}}}
275+
},
276+
),
277+
&manuallyManagedMonitoringResourceDeletePredicate{},
278+
),
279+
); err != nil {
280+
logger.Error(err, "unable to create a new watch for monitoring resource deletions")
281+
return
282+
}
283+
logger.Info("successfully created a new watch for monitoring resource deletions")
284+
264285
// start the controller
265286
backgroundCtx := context.Background()
266287
childContextForNamespaceController, stopNamespaceController := context.WithCancel(backgroundCtx)
@@ -970,3 +991,25 @@ func namespaceMatchesLabelSelector(ns *corev1.Namespace, selectorStr string) boo
970991
}
971992
return selector.Matches(labels.Set(ns.Labels))
972993
}
994+
995+
// manuallyManagedMonitoringResourceDeletePredicate fires only on the deletion of monitoring resources that are not
996+
// auto-managed (i.e. they do not carry the dash0.com/auto-monitored-namespace=true label). Create/Update/Generic
997+
// events are ignored, and deletions of auto-managed resources are ignored to avoid loops with the auto-namespace
998+
// monitoring controller's own deletes.
999+
type manuallyManagedMonitoringResourceDeletePredicate struct{}
1000+
1001+
func (p *manuallyManagedMonitoringResourceDeletePredicate) Create(_ event.TypedCreateEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1002+
return false
1003+
}
1004+
1005+
func (p *manuallyManagedMonitoringResourceDeletePredicate) Update(_ event.TypedUpdateEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1006+
return false
1007+
}
1008+
1009+
func (p *manuallyManagedMonitoringResourceDeletePredicate) Delete(e event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1010+
return e.Object.Labels[util.AutoMonitoredNamespaceLabel] != util.TrueString
1011+
}
1012+
1013+
func (p *manuallyManagedMonitoringResourceDeletePredicate) Generic(_ event.TypedGenericEvent[*dash0v1beta1.Dash0Monitoring]) bool {
1014+
return false
1015+
}

internal/controller/auto_namespace_monitoring_controller_test.go

Lines changed: 75 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,30 @@ 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+
320345
It("uses custom settings from the MonitoringTemplate when set", func() {
321346
createOperatorConfigurationResourceWithAutoMonitorNamespaces(
322347
ctx,
@@ -1021,6 +1046,55 @@ var _ = Describe("The auto-namespace-monitoring controller", Ordered, func() {
10211046
Expect(t2.Labels).To(Equal(map[string]string{"key": "value-2"}))
10221047
})
10231048
})
1049+
1050+
Context("manuallyManagedMonitoringResourceDeletePredicate", func() {
1051+
var p manuallyManagedMonitoringResourceDeletePredicate
1052+
1053+
It("ignores Create events", func() {
1054+
Expect(p.Create(event.TypedCreateEvent[*dash0v1beta1.Dash0Monitoring]{
1055+
Object: &dash0v1beta1.Dash0Monitoring{},
1056+
})).To(BeFalse())
1057+
})
1058+
1059+
It("ignores Update events", func() {
1060+
Expect(p.Update(event.TypedUpdateEvent[*dash0v1beta1.Dash0Monitoring]{
1061+
ObjectOld: &dash0v1beta1.Dash0Monitoring{},
1062+
ObjectNew: &dash0v1beta1.Dash0Monitoring{},
1063+
})).To(BeFalse())
1064+
})
1065+
1066+
It("ignores Generic events", func() {
1067+
Expect(p.Generic(event.TypedGenericEvent[*dash0v1beta1.Dash0Monitoring]{
1068+
Object: &dash0v1beta1.Dash0Monitoring{},
1069+
})).To(BeFalse())
1070+
})
1071+
1072+
It("fires on Delete of a manually managed monitoring resource (no auto-monitored label)", func() {
1073+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1074+
Object: &dash0v1beta1.Dash0Monitoring{},
1075+
})).To(BeTrue())
1076+
})
1077+
1078+
It("fires on Delete of a monitoring resource with unrelated labels", func() {
1079+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1080+
Object: &dash0v1beta1.Dash0Monitoring{
1081+
ObjectMeta: metav1.ObjectMeta{
1082+
Labels: map[string]string{"some": "label"},
1083+
},
1084+
},
1085+
})).To(BeTrue())
1086+
})
1087+
1088+
It("does not fire on Delete of an auto-managed monitoring resource", func() {
1089+
Expect(p.Delete(event.TypedDeleteEvent[*dash0v1beta1.Dash0Monitoring]{
1090+
Object: &dash0v1beta1.Dash0Monitoring{
1091+
ObjectMeta: metav1.ObjectMeta{
1092+
Labels: map[string]string{util.AutoMonitoredNamespaceLabel: util.TrueString},
1093+
},
1094+
},
1095+
})).To(BeFalse())
1096+
})
1097+
})
10241098
})
10251099

10261100
func createOperatorConfigurationResourceWithAutoMonitorNamespaces(

0 commit comments

Comments
 (0)