diff --git a/api/v1beta1/namespaced_resource.go b/api/v1beta1/namespaced_resource.go index 5a465e8ba..4dcf4d87e 100644 --- a/api/v1beta1/namespaced_resource.go +++ b/api/v1beta1/namespaced_resource.go @@ -9,6 +9,11 @@ type NamespacedResource string type NamespacedResourceList []NamespacedResource +// +kubebuilder:object:generate=false +type NamespacedResourceImpl[T interface{}] interface { + Find(namespace string, name string) *T +} + func (in NamespacedResource) Split() (string, string, string) { parts := strings.Split(string(in), "/") return parts[0], parts[1], parts[2] diff --git a/controllers/contactpoint_controller.go b/controllers/contactpoint_controller.go index a59129dcc..cc892f5f3 100644 --- a/controllers/contactpoint_controller.go +++ b/controllers/contactpoint_controller.go @@ -21,7 +21,6 @@ import ( "encoding/json" "fmt" "strings" - "time" kuberr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -37,7 +36,6 @@ import ( "github.com/grafana/grafana-openapi-client-go/models" grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1" client2 "github.com/grafana/grafana-operator/v5/controllers/client" - "github.com/grafana/grafana-operator/v5/controllers/metrics" ) const ( @@ -54,55 +52,6 @@ type GrafanaContactPointReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanacontactpoints/status,verbs=get;update;patch //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanacontactpoints/finalizers,verbs=update -func (r *GrafanaContactPointReconciler) syncStatuses(ctx context.Context) error { - log := logf.FromContext(ctx) - - // get all grafana instances - grafanas := &grafanav1beta1.GrafanaList{} - var opts []client.ListOption - err := r.List(ctx, grafanas, opts...) - if err != nil { - return err - } - // no instances, no need to sync - if len(grafanas.Items) == 0 { - return nil - } - - // get all contact points - allContactPoints := &grafanav1beta1.GrafanaContactPointList{} - err = r.List(ctx, allContactPoints, opts...) - if err != nil { - return err - } - - // delete contact points from grafana statuses that do no longer have a cr - contactpointsSynced := 0 - for _, grafana := range grafanas.Items { - statusUpdated := false - for _, contactpoint := range grafana.Status.ContactPoints { - namespace, name, _ := contactpoint.Split() - if allContactPoints.Find(namespace, name) == nil { - grafana.Status.ContactPoints = grafana.Status.ContactPoints.Remove(namespace, name) - contactpointsSynced += 1 - statusUpdated = true - } - } - - if statusUpdated { - err = r.Client.Status().Update(ctx, &grafana) - if err != nil { - return err - } - } - } - - if contactpointsSynced > 0 { - log.Info("successfully synced contact points", "contactpoints", contactpointsSynced) - } - return nil -} - func (r *GrafanaContactPointReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx).WithName("GrafanaContactPointReconciler") ctx = logf.IntoContext(ctx, log) @@ -290,36 +239,9 @@ func (r *GrafanaContactPointReconciler) finalize(ctx context.Context, contactPoi } // SetupWithManager sets up the controller with the Manager. -func (r *GrafanaContactPointReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - err := ctrl.NewControllerManagedBy(mgr). +func (r *GrafanaContactPointReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). For(&grafanav1beta1.GrafanaContactPoint{}). WithEventFilter(ignoreStatusUpdates()). Complete(r) - if err != nil { - return err - } - - go func() { - log := logf.FromContext(ctx).WithName("GrafanaContactPointReconciler") - for { - select { - case <-ctx.Done(): - return - case <-time.After(initialSyncDelay): - start := time.Now() - err := r.syncStatuses(ctx) - elapsed := time.Since(start).Milliseconds() - metrics.InitialContactPointSyncDuration.Set(float64(elapsed)) - if err != nil { - log.Error(err, "error synchronizing contact points") - continue - } - - log.Info("contact point sync complete") - return - } - } - }() - - return nil } diff --git a/controllers/dashboard_controller.go b/controllers/dashboard_controller.go index 55ed8c1d3..b67a5946c 100644 --- a/controllers/dashboard_controller.go +++ b/controllers/dashboard_controller.go @@ -23,7 +23,6 @@ import ( "net/http" "reflect" "strings" - "time" "k8s.io/utils/strings/slices" @@ -35,7 +34,6 @@ import ( "github.com/grafana/grafana-operator/v5/api/v1beta1" client2 "github.com/grafana/grafana-operator/v5/controllers/client" "github.com/grafana/grafana-operator/v5/controllers/content" - "github.com/grafana/grafana-operator/v5/controllers/metrics" corev1 "k8s.io/api/core/v1" kuberr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -65,55 +63,6 @@ type GrafanaDashboardReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadashboards/status,verbs=get;update;patch //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadashboards/finalizers,verbs=update -func (r *GrafanaDashboardReconciler) syncStatuses(ctx context.Context) error { - log := logf.FromContext(ctx) - - // get all grafana instances - grafanas := &v1beta1.GrafanaList{} - var opts []client.ListOption - err := r.List(ctx, grafanas, opts...) - if err != nil { - return err - } - // no instances, no need to sync - if len(grafanas.Items) == 0 { - return nil - } - - // get all dashboards - allDashboards := &v1beta1.GrafanaDashboardList{} - err = r.List(ctx, allDashboards, opts...) - if err != nil { - return err - } - - // delete dashboards from grafana statuses that do no longer have a cr - dashboardsSynced := 0 - for _, grafana := range grafanas.Items { - statusUpdated := false - for _, dashboard := range grafana.Status.Dashboards { - namespace, name, _ := dashboard.Split() - if allDashboards.Find(namespace, name) == nil { - grafana.Status.Dashboards = grafana.Status.Dashboards.Remove(namespace, name) - dashboardsSynced += 1 - statusUpdated = true - } - } - - if statusUpdated { - err = r.Client.Status().Update(ctx, &grafana) - if err != nil { - return err - } - } - } - - if dashboardsSynced > 0 { - log.Info("successfully synced dashboards", "dashboards", dashboardsSynced) - } - return nil -} - func (r *GrafanaDashboardReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { //nolint:gocyclo log := logf.FromContext(ctx).WithName("GrafanaDashboardReconciler") ctx = logf.IntoContext(ctx, log) @@ -576,7 +525,7 @@ func (r *GrafanaDashboardReconciler) SetupWithManager(ctx context.Context, mgr c return fmt.Errorf("failed setting index fields: %w", err) } - err := ctrl.NewControllerManagedBy(mgr). + return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.GrafanaDashboard{}, builder.WithPredicates( ignoreStatusUpdates(), )). @@ -585,33 +534,6 @@ func (r *GrafanaDashboardReconciler) SetupWithManager(ctx context.Context, mgr c handler.EnqueueRequestsFromMapFunc(r.requestsForChangeByField(configMapIndexKey)), ). Complete(r) - if err != nil { - return err - } - - go func() { - log := logf.FromContext(ctx).WithName("GrafanaDashboardReconciler") - for { - select { - case <-ctx.Done(): - return - case <-time.After(initialSyncDelay): - start := time.Now() - err := r.syncStatuses(ctx) - elapsed := time.Since(start).Milliseconds() - metrics.InitialDashboardSyncDuration.Set(float64(elapsed)) - if err != nil { - log.Error(err, "error synchronizing dashboards") - continue - } - - log.Info("dashboard sync complete") - return - } - } - }() - - return nil } func (r *GrafanaDashboardReconciler) indexConfigMapSource() func(o client.Object) []string { diff --git a/controllers/datasource_controller.go b/controllers/datasource_controller.go index ad40d0b56..cc6c21036 100644 --- a/controllers/datasource_controller.go +++ b/controllers/datasource_controller.go @@ -23,14 +23,11 @@ import ( "errors" "fmt" "strings" - "time" simplejson "github.com/bitly/go-simplejson" "github.com/grafana/grafana-openapi-client-go/client/datasources" "github.com/grafana/grafana-openapi-client-go/models" - "github.com/grafana/grafana-operator/v5/controllers/metrics" - genapi "github.com/grafana/grafana-openapi-client-go/client" client2 "github.com/grafana/grafana-operator/v5/controllers/client" kuberr "k8s.io/apimachinery/pkg/api/errors" @@ -58,55 +55,6 @@ type GrafanaDatasourceReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadatasources/status,verbs=get;update;patch //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanadatasources/finalizers,verbs=update -func (r *GrafanaDatasourceReconciler) syncStatuses(ctx context.Context) error { - log := logf.FromContext(ctx) - - // get all grafana instances - grafanas := &v1beta1.GrafanaList{} - var opts []client.ListOption - err := r.List(ctx, grafanas, opts...) - if err != nil { - return err - } - // no instances, no need to sync - if len(grafanas.Items) == 0 { - return nil - } - - // get all datasources - allDatasource := &v1beta1.GrafanaDatasourceList{} - err = r.List(ctx, allDatasource, opts...) - if err != nil { - return err - } - - // delete datasources datasourcegrafana statuses that no longer have a CR - datasourcesSynced := 0 - for _, grafana := range grafanas.Items { - statusUpdated := false - for _, ds := range grafana.Status.Datasources { - namespace, name, _ := ds.Split() - if allDatasource.Find(namespace, name) == nil { - grafana.Status.Datasources = grafana.Status.Datasources.Remove(namespace, name) - datasourcesSynced += 1 - statusUpdated = true - } - } - - if statusUpdated { - err = r.Status().Update(ctx, &grafana) - if err != nil { - return err - } - } - } - - if datasourcesSynced > 0 { - log.Info("successfully synced datasources", "datasources", datasourcesSynced) - } - return nil -} - func (r *GrafanaDatasourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx).WithName("GrafanaDatasourceReconciler") ctx = logf.IntoContext(ctx, log) @@ -374,40 +322,11 @@ func (r *GrafanaDatasourceReconciler) Exists(client *genapi.GrafanaHTTPAPI, uid, } // SetupWithManager sets up the controller with the Manager. -func (r *GrafanaDatasourceReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - err := ctrl.NewControllerManagedBy(mgr). +func (r *GrafanaDatasourceReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.GrafanaDatasource{}). WithEventFilter(ignoreStatusUpdates()). Complete(r) - if err != nil { - return err - } - - go func() { - // periodic sync reconcile - log := logf.FromContext(ctx).WithName("GrafanaDatasourceReconciler") - - for { - select { - case <-ctx.Done(): - return - case <-time.After(initialSyncDelay): - start := time.Now() - err := r.syncStatuses(ctx) - elapsed := time.Since(start).Milliseconds() - metrics.InitialDatasourceSyncDuration.Set(float64(elapsed)) - if err != nil { - log.Error(err, "error synchronizing datasources") - continue - } - - log.Info("datasource sync complete") - return - } - } - }() - - return nil } func (r *GrafanaDatasourceReconciler) buildDatasourceModel(ctx context.Context, cr *v1beta1.GrafanaDatasource) (*models.UpdateDataSourceCommand, string, error) { diff --git a/controllers/folder_controller.go b/controllers/folder_controller.go index 02997a1e8..435d6507d 100644 --- a/controllers/folder_controller.go +++ b/controllers/folder_controller.go @@ -22,13 +22,11 @@ import ( "errors" "fmt" "strings" - "time" genapi "github.com/grafana/grafana-openapi-client-go/client" "github.com/grafana/grafana-openapi-client-go/client/folders" "github.com/grafana/grafana-openapi-client-go/models" client2 "github.com/grafana/grafana-operator/v5/controllers/client" - "github.com/grafana/grafana-operator/v5/controllers/metrics" kuberr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -55,55 +53,6 @@ type GrafanaFolderReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanafolders/status,verbs=get;update;patch //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanafolders/finalizers,verbs=update -func (r *GrafanaFolderReconciler) syncStatuses(ctx context.Context) error { - log := logf.FromContext(ctx) - - // get all grafana instances - grafanas := &grafanav1beta1.GrafanaList{} - var opts []client.ListOption - err := r.List(ctx, grafanas, opts...) - if err != nil { - return err - } - // no instances, no need to sync - if len(grafanas.Items) == 0 { - return nil - } - - // get all folders - allFolders := &grafanav1beta1.GrafanaFolderList{} - err = r.List(ctx, allFolders, opts...) - if err != nil { - return err - } - - // delete folders from grafana statuses that no longer have a CR - foldersSynced := 0 - for _, grafana := range grafanas.Items { - statusUpdated := false - for _, folder := range grafana.Status.Folders { - namespace, name, _ := folder.Split() - if allFolders.Find(namespace, name) == nil { - grafana.Status.Folders = grafana.Status.Folders.Remove(namespace, name) - foldersSynced += 1 - statusUpdated = true - } - } - - if statusUpdated { - err = r.Status().Update(ctx, &grafana) - if err != nil { - return err - } - } - } - - if foldersSynced > 0 { - log.Info("successfully synced folders", "folders", foldersSynced) - } - return nil -} - func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx).WithName("GrafanaFolderReconciler") ctx = logf.IntoContext(ctx, log) @@ -349,36 +298,9 @@ func (r *GrafanaFolderReconciler) Exists(client *genapi.GrafanaHTTPAPI, cr *graf } // SetupWithManager sets up the controller with the Manager. -func (r *GrafanaFolderReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { - err := ctrl.NewControllerManagedBy(mgr). +func (r *GrafanaFolderReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). For(&grafanav1beta1.GrafanaFolder{}). WithEventFilter(ignoreStatusUpdates()). Complete(r) - if err != nil { - return err - } - - go func() { - log := logf.FromContext(ctx).WithName("GrafanaFolderReconciler") - for { - select { - case <-ctx.Done(): - return - case <-time.After(initialSyncDelay): - start := time.Now() - err := r.syncStatuses(ctx) - elapsed := time.Since(start).Milliseconds() - metrics.InitialFoldersSyncDuration.Set(float64(elapsed)) - if err != nil { - log.Error(err, "error synchronizing folders") - continue - } - - log.Info("folder sync complete") - return - } - } - }() - - return nil } diff --git a/controllers/grafana_controller.go b/controllers/grafana_controller.go index 7318ed010..e0786a987 100644 --- a/controllers/grafana_controller.go +++ b/controllers/grafana_controller.go @@ -21,6 +21,7 @@ import ( "fmt" "os" "strings" + "time" "github.com/grafana/grafana-operator/v5/controllers/config" "github.com/grafana/grafana-operator/v5/controllers/metrics" @@ -134,14 +135,135 @@ func (r *GrafanaReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct return ctrl.Result{}, nil } +func removeMissingCRs[T interface{}](statusList *grafanav1beta1.NamespacedResourceList, crs grafanav1beta1.NamespacedResourceImpl[T], updateStatus *bool) { + for _, namespacedCR := range *statusList { + namespace, name, _ := namespacedCR.Split() + if crs.Find(namespace, name) == nil { + *statusList = statusList.Remove(namespace, name) + *updateStatus = true + } + } +} + +func (r *GrafanaReconciler) syncStatuses(ctx context.Context) error { + log := logf.FromContext(ctx) + + // get all grafana instances + grafanas := &grafanav1beta1.GrafanaList{} + err := r.List(ctx, grafanas) + if err != nil { + return err + } + // no instances, skip sync + if len(grafanas.Items) == 0 { + return nil + } + + // folders + folders := &grafanav1beta1.GrafanaFolderList{} + err = r.List(ctx, folders) + if err != nil { + return err + } + + // dashboards + dashboards := &grafanav1beta1.GrafanaDashboardList{} + err = r.List(ctx, dashboards) + if err != nil { + return err + } + + // library libraryPanels + libraryPanels := &grafanav1beta1.GrafanaLibraryPanelList{} + err = r.List(ctx, libraryPanels) + if err != nil { + return err + } + + // datasources + datasources := &grafanav1beta1.GrafanaDatasourceList{} + err = r.List(ctx, datasources) + if err != nil { + return err + } + + // contact points + contactPoints := &grafanav1beta1.GrafanaContactPointList{} + err = r.List(ctx, contactPoints) + if err != nil { + return err + } + + // delete resources from grafana statuses that no longer have a CR + statusUpdates := 0 + for _, grafana := range grafanas.Items { + updateStatus := false + + removeMissingCRs(&grafana.Status.Folders, folders, &updateStatus) + removeMissingCRs(&grafana.Status.Dashboards, dashboards, &updateStatus) + removeMissingCRs(&grafana.Status.LibraryPanels, libraryPanels, &updateStatus) + removeMissingCRs(&grafana.Status.Datasources, datasources, &updateStatus) + removeMissingCRs(&grafana.Status.ContactPoints, contactPoints, &updateStatus) + + if updateStatus { + statusUpdates += 1 + err = r.Client.Status().Update(ctx, &grafana) + if err != nil { + return err + } + } + } + + if statusUpdates > 0 { + log.Info("successfully synced grafana statuses", "update count", statusUpdates) + } + return nil +} + // SetupWithManager sets up the controller with the Manager. -func (r *GrafanaReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). +func (r *GrafanaReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager) error { + err := ctrl.NewControllerManagedBy(mgr). For(&grafanav1beta1.Grafana{}, builder.WithPredicates(ignoreStatusUpdates())). Owns(&appsv1.Deployment{}, builder.WithPredicates(ignoreStatusUpdates())). Owns(&corev1.ConfigMap{}). WithOptions(controller.Options{RateLimiter: defaultRateLimiter()}). Complete(r) + if err != nil { + return err + } + + go func() { + // Wait with sync until elected as leader + select { + case <-ctx.Done(): + return + case <-mgr.Elected(): + } + + // periodic sync reconcile + log := logf.FromContext(ctx).WithName("GrafanaReconciler") + + for { + select { + case <-ctx.Done(): + return + case <-time.After(initialSyncDelay): + start := time.Now() + err := r.syncStatuses(ctx) + elapsed := time.Since(start).Milliseconds() + metrics.InitialStatusSyncDuration.Set(float64(elapsed)) + if err != nil { + log.Error(err, "error synchronizing grafana statuses") + continue + } + + log.Info("Grafana status sync complete") + return + } + } + }() + + return nil } func getInstallationStages() []grafanav1beta1.OperatorStageName { diff --git a/controllers/grafana_controller_test.go b/controllers/grafana_controller_test.go new file mode 100644 index 000000000..0a450c0ed --- /dev/null +++ b/controllers/grafana_controller_test.go @@ -0,0 +1,47 @@ +package controllers + +import ( + "testing" + + v1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestRemoveMissingCRs(t *testing.T) { + statusList := v1beta1.NamespacedResourceList{ + "default/present/uid", + "default/missing/uid", + "other/missing/uid", + } + + dashboards := v1beta1.GrafanaDashboardList{ + Items: []v1beta1.GrafanaDashboard{ + { + ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "present"}, + }, + { + ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "unrelated-dashboard"}, + }, + }, + } + + // Sanity checks before test + assert.Len(t, statusList, 3) + assert.Contains(t, statusList, v1beta1.NamespacedResource("default/present/uid")) + assert.Contains(t, statusList, v1beta1.NamespacedResource("default/missing/uid")) + assert.Contains(t, statusList, v1beta1.NamespacedResource("other/missing/uid")) + + updateStatus := false + removeMissingCRs(&statusList, &dashboards, &updateStatus) + + assert.True(t, updateStatus, "Entries were removed but status change was not detected") + + assert.Len(t, statusList, 1) + assert.Contains(t, statusList, v1beta1.NamespacedResource("default/present/uid")) + assert.NotContains(t, statusList, v1beta1.NamespacedResource("default/missing/uid")) + assert.NotContains(t, statusList, v1beta1.NamespacedResource("other/missing/uid")) + + found, _ := statusList.Find("default", "unrelated-dashboard") + assert.False(t, found, "Dashboard is not in status and should not be") +} diff --git a/controllers/librarypanel_controller.go b/controllers/librarypanel_controller.go index d14ac5bd5..890f5692a 100644 --- a/controllers/librarypanel_controller.go +++ b/controllers/librarypanel_controller.go @@ -20,14 +20,12 @@ import ( "context" "errors" "fmt" - "time" "github.com/grafana/grafana-openapi-client-go/client/library_elements" "github.com/grafana/grafana-openapi-client-go/models" "github.com/grafana/grafana-operator/v5/api/v1beta1" client2 "github.com/grafana/grafana-operator/v5/controllers/client" "github.com/grafana/grafana-operator/v5/controllers/content" - "github.com/grafana/grafana-operator/v5/controllers/metrics" corev1 "k8s.io/api/core/v1" kuberr "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -64,55 +62,6 @@ type GrafanaLibraryPanelReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanalibrarypanels/status,verbs=get;update;patch //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanalibrarypanels/finalizers,verbs=update -func (r *GrafanaLibraryPanelReconciler) syncStatuses(ctx context.Context) error { - log := logf.FromContext(ctx) - - // get all grafana instances - grafanas := &v1beta1.GrafanaList{} - var opts []client.ListOption - err := r.List(ctx, grafanas, opts...) - if err != nil { - return err - } - // no instances, no need to sync - if len(grafanas.Items) == 0 { - return nil - } - - // get all panels - allPanels := &v1beta1.GrafanaLibraryPanelList{} - err = r.List(ctx, allPanels, opts...) - if err != nil { - return err - } - - // delete panels from grafana statuses that no longer have a CR - panelsSynced := 0 - for _, grafana := range grafanas.Items { - statusUpdated := false - for _, panel := range grafana.Status.LibraryPanels { - namespace, name, _ := panel.Split() - if allPanels.Find(namespace, name) == nil { - grafana.Status.LibraryPanels = grafana.Status.LibraryPanels.Remove(namespace, name) - panelsSynced += 1 - statusUpdated = true - } - } - - if statusUpdated { - err = r.Client.Status().Update(ctx, &grafana) - if err != nil { - return err - } - } - } - - if panelsSynced > 0 { - log.Info("successfully synced library panels", "library panels", panelsSynced) - } - return nil -} - func (r *GrafanaLibraryPanelReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := logf.FromContext(ctx).WithName("GrafanaLibraryPanelReconciler") ctx = logf.IntoContext(ctx, log) @@ -338,7 +287,7 @@ func (r *GrafanaLibraryPanelReconciler) SetupWithManager(ctx context.Context, mg return fmt.Errorf("failed setting index fields: %w", err) } - err := ctrl.NewControllerManagedBy(mgr). + return ctrl.NewControllerManagedBy(mgr). For(&v1beta1.GrafanaLibraryPanel{}, builder.WithPredicates( ignoreStatusUpdates(), )). @@ -347,35 +296,6 @@ func (r *GrafanaLibraryPanelReconciler) SetupWithManager(ctx context.Context, mg handler.EnqueueRequestsFromMapFunc(r.requestsForChangeByField(configMapIndexKey)), ). Complete(r) - if err != nil { - return err - } - - go func() { - // periodic sync reconcile - log := logf.FromContext(ctx).WithName("GrafanaLibraryPanelReconciler") - - for { - select { - case <-ctx.Done(): - return - case <-time.After(initialSyncDelay): - start := time.Now() - err := r.syncStatuses(ctx) - elapsed := time.Since(start).Milliseconds() - metrics.InitialLibraryPanelSyncDuration.Set(float64(elapsed)) - if err != nil { - log.Error(err, "error synchronizing library panels") - continue - } - - log.Info("library panel sync complete") - return - } - } - }() - - return nil } func (r *GrafanaLibraryPanelReconciler) indexConfigMapSource() func(o client.Object) []string { diff --git a/controllers/metrics/metrics.go b/controllers/metrics/metrics.go index 031337e98..3a4daef3e 100644 --- a/controllers/metrics/metrics.go +++ b/controllers/metrics/metrics.go @@ -50,6 +50,14 @@ var ( Help: "requests to list content revisions on grafana.com", }, []string{"kind", "resource", "method", "status"}) + InitialStatusSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "grafana_operator", + Subsystem: "reconciler", + Name: "initial_sync_duration", + Help: "time in ms to sync statuses after operator restart", + }) + + // Deprecated: All Initial Sync Duration metrics have merged into a single metric InitialContactPointSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "grafana_operator", Subsystem: "contactpoints", @@ -57,6 +65,7 @@ var ( Help: "time in ms to sync contact-points after operator restart", }) + // Deprecated: All Initial Sync Duration metrics have merged into a single metric InitialDashboardSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "grafana_operator", Subsystem: "dashboards", @@ -64,6 +73,7 @@ var ( Help: "time in ms to sync dashboards after operator restart", }) + // Deprecated: All Initial Sync Duration metrics have merged into a single metric InitialLibraryPanelSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "grafana_operator", Subsystem: "librarypanels", @@ -71,6 +81,7 @@ var ( Help: "time in ms to sync library panels after operator restart", }) + // Deprecated: All Initial Sync Duration metrics have merged into a single metric InitialDatasourceSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "grafana_operator", Subsystem: "datasources", @@ -78,6 +89,7 @@ var ( Help: "time in ms to sync datasources after operator restart", }) + // Deprecated: All Initial Sync Duration metrics have merged into a single metric InitialFoldersSyncDuration = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "grafana_operator", Subsystem: "folders", @@ -93,6 +105,8 @@ func init() { metrics.Registry.MustRegister(GrafanaComAPIRevisionRequests) metrics.Registry.MustRegister(DashboardURLRequests) metrics.Registry.MustRegister(ContentURLRequests) + metrics.Registry.MustRegister(InitialStatusSyncDuration) + // TODO Remvoe below registrations metrics.Registry.MustRegister(InitialContactPointSyncDuration) metrics.Registry.MustRegister(InitialDashboardSyncDuration) metrics.Registry.MustRegister(InitialLibraryPanelSyncDuration) diff --git a/main.go b/main.go index b5593fe3c..0efe447e9 100644 --- a/main.go +++ b/main.go @@ -268,7 +268,7 @@ func main() { // nolint:gocyclo Scheme: mgr.GetScheme(), IsOpenShift: isOpenShift, ClusterDomain: clusterDomain, - }).SetupWithManager(mgr); err != nil { + }).SetupWithManager(ctx, mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Grafana") os.Exit(1) } @@ -282,14 +282,14 @@ func main() { // nolint:gocyclo if err = (&controllers.GrafanaDatasourceReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - }).SetupWithManager(ctx, mgr); err != nil { + }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "GrafanaDatasource") os.Exit(1) } if err = (&controllers.GrafanaFolderReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - }).SetupWithManager(ctx, mgr); err != nil { + }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "GrafanaFolder") os.Exit(1) } @@ -310,7 +310,7 @@ func main() { // nolint:gocyclo if err = (&controllers.GrafanaContactPointReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), - }).SetupWithManager(ctx, mgr); err != nil { + }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "GrafanaContactPoint") os.Exit(1) }