From a0877ba8333c0b6f49f5717509114fd4b97fed81 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Wed, 8 Jul 2026 15:04:50 +0200 Subject: [PATCH 1/5] fix(datasource): change NumberOfObjects to a pointer type for better null handling --- api/v1alpha1/datasource_types.go | 2 +- .../knowledge/datasources/plugins/openstack/controller.go | 4 ++-- .../knowledge/datasources/plugins/prometheus/controller.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/api/v1alpha1/datasource_types.go b/api/v1alpha1/datasource_types.go index fff321c48..6ab7d962e 100644 --- a/api/v1alpha1/datasource_types.go +++ b/api/v1alpha1/datasource_types.go @@ -232,7 +232,7 @@ type DatasourceStatus struct { // When the datasource was last successfully synced. LastSynced metav1.Time `json:"lastSynced,omitempty"` // The number of objects currently stored for this datasource. - NumberOfObjects int64 `json:"numberOfObjects,omitempty"` + NumberOfObjects *int64 `json:"numberOfObjects,omitempty"` // Planned time for the next sync. NextSyncTime metav1.Time `json:"nextSyncTime,omitempty"` diff --git a/internal/knowledge/datasources/plugins/openstack/controller.go b/internal/knowledge/datasources/plugins/openstack/controller.go index 96d056a15..07277ce79 100644 --- a/internal/knowledge/datasources/plugins/openstack/controller.go +++ b/internal/knowledge/datasources/plugins/openstack/controller.go @@ -95,7 +95,7 @@ func (r *OpenStackDatasourceReconciler) Reconcile(ctx context.Context, req ctrl. log.Info("skipping datasource, not an openstack datasource", "name", datasource.Name) return ctrl.Result{}, nil } - if datasource.Status.NextSyncTime.After(time.Now()) && datasource.Status.NumberOfObjects != 0 { + if datasource.Status.NextSyncTime.After(time.Now()) && datasource.Status.NumberOfObjects != nil { if _, seen := r.reconciledOnce.Load(req.NamespacedName); seen { log.Info("skipping datasource sync, not yet time", "name", datasource.Name) return ctrl.Result{RequeueAfter: time.Until(datasource.Status.NextSyncTime.Time)}, nil @@ -262,7 +262,7 @@ func (r *OpenStackDatasourceReconciler) Reconcile(ctx context.Context, req ctrl. datasource.Status.LastSynced = metav1.NewTime(time.Now()) nextTime := time.Now().Add(datasource.Spec.OpenStack.SyncInterval.Duration) datasource.Status.NextSyncTime = metav1.NewTime(nextTime) - datasource.Status.NumberOfObjects = nResults + datasource.Status.NumberOfObjects = &nResults patch := client.MergeFrom(old) if err := r.Status().Patch(ctx, datasource, patch); err != nil { log.Error(err, "failed to patch datasource status", "name", datasource.Name) diff --git a/internal/knowledge/datasources/plugins/prometheus/controller.go b/internal/knowledge/datasources/plugins/prometheus/controller.go index dab9ea3b3..bccc1fcf1 100644 --- a/internal/knowledge/datasources/plugins/prometheus/controller.go +++ b/internal/knowledge/datasources/plugins/prometheus/controller.go @@ -71,7 +71,7 @@ func (r *PrometheusDatasourceReconciler) Reconcile(ctx context.Context, req ctrl log.Info("skipping datasource, not a prometheus datasource", "name", datasource.Name) return ctrl.Result{}, nil } - if datasource.Status.NextSyncTime.After(time.Now()) && datasource.Status.NumberOfObjects != 0 { + if datasource.Status.NextSyncTime.After(time.Now()) && datasource.Status.NumberOfObjects != nil { if _, seen := r.reconciledOnce.Load(req.NamespacedName); seen { log.Info("skipping datasource sync, not yet time", "name", datasource.Name) return ctrl.Result{RequeueAfter: time.Until(datasource.Status.NextSyncTime.Time)}, nil @@ -201,7 +201,7 @@ func (r *PrometheusDatasourceReconciler) Reconcile(ctx context.Context, req ctrl }) datasource.Status.LastSynced = metav1.NewTime(time.Now()) datasource.Status.NextSyncTime = metav1.NewTime(nextSync) - datasource.Status.NumberOfObjects = nResults + datasource.Status.NumberOfObjects = &nResults patch := client.MergeFrom(old) if err := r.Status().Patch(ctx, datasource, patch); err != nil { log.Error(err, "failed to patch datasource status", "name", datasource.Name) From ea6d1132b139b3f542d5e17d435d561adf25ded6 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Wed, 8 Jul 2026 15:05:00 +0200 Subject: [PATCH 2/5] fix(alerts): enhance sync object drop alerts with historical check --- helm/bundles/cortex-cinder/templates/alerts.yaml | 2 +- helm/bundles/cortex-manila/templates/alerts.yaml | 2 +- helm/bundles/cortex-nova/templates/alerts.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/helm/bundles/cortex-cinder/templates/alerts.yaml b/helm/bundles/cortex-cinder/templates/alerts.yaml index 4beea8b53..941c33546 100644 --- a/helm/bundles/cortex-cinder/templates/alerts.yaml +++ b/helm/bundles/cortex-cinder/templates/alerts.yaml @@ -156,7 +156,7 @@ spec: time the service will have a less recent view of the datacenter. - alert: CortexCinderSyncObjectsDroppedToZero - expr: cortex_sync_objects{service="cortex-cinder-metrics"} == 0 + expr: cortex_sync_objects{service="cortex-cinder-metrics"} == 0 and (cortex_sync_objects{service="cortex-cinder-metrics"} offset 1h > 0) for: 60m labels: context: syncobjects diff --git a/helm/bundles/cortex-manila/templates/alerts.yaml b/helm/bundles/cortex-manila/templates/alerts.yaml index ef36fe983..769730c1e 100644 --- a/helm/bundles/cortex-manila/templates/alerts.yaml +++ b/helm/bundles/cortex-manila/templates/alerts.yaml @@ -162,7 +162,7 @@ spec: time the service will have a less recent view of the datacenter. - alert: CortexManilaSyncObjectsDroppedToZero - expr: cortex_sync_objects{service="cortex-manila-metrics"} == 0 + expr: cortex_sync_objects{service="cortex-manila-metrics"} == 0 and (cortex_sync_objects{service="cortex-manila-metrics"} offset 1h > 0) for: 60m labels: context: syncobjects diff --git a/helm/bundles/cortex-nova/templates/alerts.yaml b/helm/bundles/cortex-nova/templates/alerts.yaml index 840e969f2..76fe545e1 100644 --- a/helm/bundles/cortex-nova/templates/alerts.yaml +++ b/helm/bundles/cortex-nova/templates/alerts.yaml @@ -179,7 +179,7 @@ spec: time the service will have a less recent view of the datacenter. - alert: CortexNovaSyncObjectsDroppedToZero - expr: cortex_sync_objects{service="cortex-nova-metrics", datasource!="openstack_migrations"} == 0 + expr: cortex_sync_objects{service="cortex-nova-metrics", datasource!="openstack_migrations"} == 0 and (cortex_sync_objects{service="cortex-nova-metrics", datasource!="openstack_migrations"} offset 1h > 0) for: 60m labels: context: syncobjects From 2bc93c87b47b60516647afec0aee75d5d2955cda Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Wed, 8 Jul 2026 15:16:41 +0200 Subject: [PATCH 3/5] fix: make deepcopy --- api/v1alpha1/zz_generated.deepcopy.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 9f654186a..0fa21d2bb 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -353,6 +353,11 @@ func (in *DatasourceSpec) DeepCopy() *DatasourceSpec { func (in *DatasourceStatus) DeepCopyInto(out *DatasourceStatus) { *out = *in in.LastSynced.DeepCopyInto(&out.LastSynced) + if in.NumberOfObjects != nil { + in, out := &in.NumberOfObjects, &out.NumberOfObjects + *out = new(int64) + **out = **in + } in.NextSyncTime.DeepCopyInto(&out.NextSyncTime) if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions From 4b0ba75a35ac51b9a530e4bc32f44211cca47d31 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Wed, 8 Jul 2026 15:26:21 +0200 Subject: [PATCH 4/5] fix(datasource): change NumberOfObjects to pointer type for better null handling in tests --- .../datasources/plugins/openstack/controller_test.go | 4 ++-- internal/knowledge/kpis/controller_test.go | 10 +++++----- .../kpis/plugins/deployment/datasource_state_test.go | 11 ++++++----- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/internal/knowledge/datasources/plugins/openstack/controller_test.go b/internal/knowledge/datasources/plugins/openstack/controller_test.go index 899e83237..a809e099b 100644 --- a/internal/knowledge/datasources/plugins/openstack/controller_test.go +++ b/internal/knowledge/datasources/plugins/openstack/controller_test.go @@ -495,11 +495,11 @@ func TestUpdatePredicateIgnoresStatusConditionChanges(t *testing.T) { name: "status NumberOfObjects changes - should trigger reconcile", oldObj: &v1alpha1.Datasource{ ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, - Status: v1alpha1.DatasourceStatus{NumberOfObjects: 10}, + Status: v1alpha1.DatasourceStatus{NumberOfObjects: new(int64(10))}, }, newObj: &v1alpha1.Datasource{ ObjectMeta: metav1.ObjectMeta{Name: "test", Namespace: "default"}, - Status: v1alpha1.DatasourceStatus{NumberOfObjects: 20}, + Status: v1alpha1.DatasourceStatus{NumberOfObjects: new(int64(20))}, }, expected: true, }, diff --git a/internal/knowledge/kpis/controller_test.go b/internal/knowledge/kpis/controller_test.go index 708a19cd0..0aeb96485 100644 --- a/internal/knowledge/kpis/controller_test.go +++ b/internal/knowledge/kpis/controller_test.go @@ -333,7 +333,7 @@ func TestController_Reconcile(t *testing.T) { }, }, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 1, + NumberOfObjects: new(int64(1)), Conditions: []metav1.Condition{ { Type: v1alpha1.DatasourceConditionReady, @@ -393,7 +393,7 @@ func TestController_Reconcile(t *testing.T) { }, }, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 0, + NumberOfObjects: new(int64(0)), Conditions: []metav1.Condition{ { Type: v1alpha1.DatasourceConditionReady, @@ -622,7 +622,7 @@ func TestController_handleKPIChange(t *testing.T) { DatabaseSecretRef: corev1.SecretReference{Name: "db-secret", Namespace: "default"}, }, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 1, + NumberOfObjects: new(int64(1)), Conditions: []metav1.Condition{ { Type: v1alpha1.DatasourceConditionReady, @@ -638,7 +638,7 @@ func TestController_handleKPIChange(t *testing.T) { DatabaseSecretRef: corev1.SecretReference{Name: "db-secret", Namespace: "default"}, }, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 0, + NumberOfObjects: new(int64(0)), Conditions: []metav1.Condition{ { Type: v1alpha1.DatasourceConditionReady, @@ -703,7 +703,7 @@ func TestController_handleKPIChange(t *testing.T) { DatabaseSecretRef: corev1.SecretReference{Name: "db-secret", Namespace: "default"}, }, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 1, + NumberOfObjects: new(int64(1)), Conditions: []metav1.Condition{ { Type: v1alpha1.DatasourceConditionReady, diff --git a/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go b/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go index d6e56bd35..685c14203 100644 --- a/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go +++ b/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go @@ -12,6 +12,7 @@ import ( "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -49,7 +50,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds1"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "test-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 10, + NumberOfObjects: new(int64(10)), Conditions: []v1.Condition{}, }, }, @@ -87,7 +88,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds-ready"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "test-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 10, + NumberOfObjects: ptr.To(int64(10)), Conditions: []v1.Condition{}, }, }, @@ -127,7 +128,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds-correct-operator"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "test-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 10, + NumberOfObjects: new(int64(10)), Conditions: []v1.Condition{}, }, }, @@ -135,7 +136,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds-wrong-operator"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "other-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 10, + NumberOfObjects: new(int64(10)), Conditions: []v1.Condition{}, }, }, @@ -151,7 +152,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds-unknown"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "test-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: 0, + NumberOfObjects: new(int64(0)), Conditions: []v1.Condition{}, }, }, From cc3193a355d74b4491266ee4af11575a69e5c4a3 Mon Sep 17 00:00:00 2001 From: Markus Wieland Date: Wed, 8 Jul 2026 15:34:18 +0200 Subject: [PATCH 5/5] fix(datasource): update NumberOfObjects initialization to use new() for consistency --- .../knowledge/kpis/plugins/deployment/datasource_state_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go b/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go index 685c14203..e143bf5f9 100644 --- a/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go +++ b/internal/knowledge/kpis/plugins/deployment/datasource_state_test.go @@ -12,7 +12,6 @@ import ( "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -88,7 +87,7 @@ func TestDatasourceStateKPI_Collect(t *testing.T) { ObjectMeta: v1.ObjectMeta{Name: "ds-ready"}, Spec: v1alpha1.DatasourceSpec{SchedulingDomain: "test-operator"}, Status: v1alpha1.DatasourceStatus{ - NumberOfObjects: ptr.To(int64(10)), + NumberOfObjects: new(int64(10)), Conditions: []v1.Condition{}, }, },