diff --git a/controllers/alertrulegroup_controller.go b/controllers/alertrulegroup_controller.go index 07589b545..e6d434513 100644 --- a/controllers/alertrulegroup_controller.go +++ b/controllers/alertrulegroup_controller.go @@ -114,8 +114,12 @@ func (r *GrafanaAlertRuleGroupReconciler) Reconcile(ctx context.Context, req ctr log.Info("found matching Grafana instances for group", "count", len(instances)) folderUID, err := getFolderUID(ctx, r.Client, group) - if err != nil || folderUID == "" { - return ctrl.Result{}, fmt.Errorf("folder uid not found: %w", err) + if err != nil { + return ctrl.Result{}, fmt.Errorf(ErrFetchingFolder, err) + } + + if folderUID == "" { + return ctrl.Result{}, fmt.Errorf("folder uid not found, alert rule must reference a folder") } editable := "true" //nolint:goconst diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index fe877d179..03e7b79b1 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -51,7 +51,10 @@ const ( grafanaFinalizer = "operator.grafana.com/finalizer" ) -var ErrNoMatchingInstances = fmt.Errorf("no matching instances") +var ( + ErrNoMatchingInstances = fmt.Errorf("no matching instances") + ErrFetchingFolder = "fetching folder to resolve uid: %w" +) type GrafanaCommonReconciler interface { Get(ctx context.Context, key client.ObjectKey, obj client.Object, opts ...client.GetOption) error @@ -131,7 +134,7 @@ func GetScopedMatchingInstances(ctx context.Context, k8sClient client.Client, cr return selectedList, nil } -// getFolderUID fetches the folderUID from an existing GrafanaFolder CR declared in the specified namespace +// getFolderUID returns the folderUID from an existing GrafanaFolder CR within the same namespace func getFolderUID(ctx context.Context, k8sClient client.Client, ref operatorapi.FolderReferencer) (string, error) { if ref.FolderUID() != "" { return ref.FolderUID(), nil diff --git a/controllers/dashboard_controller.go b/controllers/dashboard_controller.go index 6970c031c..93d613aef 100644 --- a/controllers/dashboard_controller.go +++ b/controllers/dashboard_controller.go @@ -157,6 +157,11 @@ func (r *GrafanaDashboardReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{Requeue: true}, nil } + folderUID, err := getFolderUID(ctx, r.Client, cr) + if err != nil { + return ctrl.Result{}, fmt.Errorf(ErrFetchingFolder, err) + } + applyHomeErrors := make(map[string]string) pluginErrors := make(map[string]string) applyErrors := make(map[string]string) @@ -174,7 +179,7 @@ func (r *GrafanaDashboardReconciler) Reconcile(ctx context.Context, req ctrl.Req } // then import the dashboard into the matching grafana instances - err = r.onDashboardCreated(ctx, &grafana, cr, dashboardModel, hash) + err = r.onDashboardCreated(ctx, &grafana, cr, dashboardModel, hash, folderUID) if err != nil { applyErrors[fmt.Sprintf("%s/%s", grafana.Namespace, grafana.Name)] = err.Error() } @@ -292,7 +297,7 @@ func (r *GrafanaDashboardReconciler) finalize(ctx context.Context, cr *v1beta1.G return nil } -func (r *GrafanaDashboardReconciler) onDashboardCreated(ctx context.Context, grafana *v1beta1.Grafana, cr *v1beta1.GrafanaDashboard, dashboardModel map[string]any, hash string) error { +func (r *GrafanaDashboardReconciler) onDashboardCreated(ctx context.Context, grafana *v1beta1.Grafana, cr *v1beta1.GrafanaDashboard, dashboardModel map[string]any, hash, folderUID string) error { log := logf.FromContext(ctx) if grafana.IsExternal() && cr.Spec.Plugins != nil { @@ -304,11 +309,6 @@ func (r *GrafanaDashboardReconciler) onDashboardCreated(ctx context.Context, gra return fmt.Errorf("creating grafana http client: %w", err) } - folderUID, err := getFolderUID(ctx, r.Client, cr) - if err != nil { - return err - } - if folderUID == "" { folderUID, err = r.GetOrCreateFolder(grafanaClient, cr) if err != nil { diff --git a/controllers/folder_controller.go b/controllers/folder_controller.go index c9d57aaec..372a7471c 100644 --- a/controllers/folder_controller.go +++ b/controllers/folder_controller.go @@ -122,12 +122,17 @@ func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Reques removeNoMatchingInstance(&folder.Status.Conditions) folder.Status.NoMatchingInstances = false + parentFolderUID, err := getFolderUID(ctx, r.Client, folder) + if err != nil { + return ctrl.Result{}, fmt.Errorf(ErrFetchingFolder, err) + } + log.Info("found matching Grafana instances for folder", "count", len(instances)) applyErrors := make(map[string]string) for _, grafana := range instances { - err = r.onFolderCreated(ctx, &grafana, folder) + err = r.onFolderCreated(ctx, &grafana, folder, parentFolderUID) if err != nil { applyErrors[fmt.Sprintf("%s/%s", grafana.Namespace, grafana.Name)] = err.Error() } @@ -183,7 +188,7 @@ func (r *GrafanaFolderReconciler) finalize(ctx context.Context, folder *grafanav return nil } -func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *grafanav1beta1.Grafana, cr *grafanav1beta1.GrafanaFolder) error { +func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *grafanav1beta1.Grafana, cr *grafanav1beta1.GrafanaFolder, parentFolderUID string) error { log := logf.FromContext(ctx) title := cr.GetTitle() @@ -194,11 +199,6 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana * return err } - parentFolderUID, err := getFolderUID(ctx, r.Client, cr) - if err != nil { - return err - } - exists, remoteUID, remoteParent, err := r.Exists(grafanaClient, cr) if err != nil { return err diff --git a/controllers/librarypanel_controller.go b/controllers/librarypanel_controller.go index e838374d8..cc7786b80 100644 --- a/controllers/librarypanel_controller.go +++ b/controllers/librarypanel_controller.go @@ -151,10 +151,15 @@ func (r *GrafanaLibraryPanelReconciler) Reconcile(ctx context.Context, req ctrl. removeNoMatchingInstance(&libraryPanel.Status.Conditions) log.Info("found matching Grafana instances for library panel", "count", len(instances)) + folderUID, err := getFolderUID(ctx, r.Client, libraryPanel) + if err != nil { + return ctrl.Result{}, fmt.Errorf(ErrFetchingFolder, err) + } + applyErrors := make(map[string]string) for _, grafana := range instances { - err := r.reconcileWithInstance(ctx, &grafana, libraryPanel, contentModel, hash) + err := r.reconcileWithInstance(ctx, &grafana, libraryPanel, contentModel, hash, folderUID) if err != nil { applyErrors[fmt.Sprintf("%s/%s", grafana.Namespace, grafana.Name)] = err.Error() } @@ -170,7 +175,7 @@ func (r *GrafanaLibraryPanelReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{RequeueAfter: libraryPanel.Spec.ResyncPeriod.Duration}, nil } -func (r *GrafanaLibraryPanelReconciler) reconcileWithInstance(ctx context.Context, instance *v1beta1.Grafana, cr *v1beta1.GrafanaLibraryPanel, model map[string]any, hash string) error { +func (r *GrafanaLibraryPanelReconciler) reconcileWithInstance(ctx context.Context, instance *v1beta1.Grafana, cr *v1beta1.GrafanaLibraryPanel, model map[string]any, hash, folderUID string) error { if instance.IsInternal() { err := ReconcilePlugins(ctx, r.Client, r.Scheme, instance, cr.Spec.Plugins, fmt.Sprintf("%v-librarypanel", cr.Name)) if err != nil { @@ -185,11 +190,6 @@ func (r *GrafanaLibraryPanelReconciler) reconcileWithInstance(ctx context.Contex return err } - folderUID, err := getFolderUID(ctx, r.Client, cr) - if err != nil { - return err - } - uid := content.CustomUIDOrUID(cr, fmt.Sprintf("%s", model["uid"])) name := fmt.Sprintf("%s", model["name"])