diff --git a/controllers/controller_shared.go b/controllers/controller_shared.go index b8a45a72e..9663b7960 100644 --- a/controllers/controller_shared.go +++ b/controllers/controller_shared.go @@ -84,10 +84,14 @@ func GetScopedMatchingInstances(log logr.Logger, ctx context.Context, k8sClient var list v1beta1.GrafanaList err := k8sClient.List(ctx, &list, opts...) - if err != nil || len(list.Items) == 0 { + if err != nil { return []v1beta1.Grafana{}, err } + if len(list.Items) == 0 { + return []v1beta1.Grafana{}, nil + } + selectedList := []v1beta1.Grafana{} var unready_instances []string for _, instance := range list.Items { @@ -124,10 +128,14 @@ func GetAllMatchingInstances(ctx context.Context, k8sClient client.Client, cr v1 var list v1beta1.GrafanaList err := k8sClient.List(ctx, &list, client.MatchingLabels(instanceSelector.MatchLabels)) - if err != nil || len(list.Items) == 0 { + if err != nil { return []v1beta1.Grafana{}, err } + if len(list.Items) == 0 { + return []v1beta1.Grafana{}, nil + } + selectedList := []v1beta1.Grafana{} for _, instance := range list.Items { // Matches all instances when MatchExpressions is undefined diff --git a/controllers/grafanafolder_controller.go b/controllers/grafanafolder_controller.go index 2272dfe45..643b23e4b 100644 --- a/controllers/grafanafolder_controller.go +++ b/controllers/grafanafolder_controller.go @@ -37,6 +37,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1" @@ -58,7 +59,6 @@ type GrafanaFolderReconciler struct { //+kubebuilder:rbac:groups=grafana.integreatly.org,resources=grafanafolders/finalizers,verbs=update func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, error) { - syncLog := log.FromContext(ctx).WithName("GrafanaFolderReconciler") foldersSynced := 0 // get all grafana instances @@ -66,9 +66,7 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, var opts []client.ListOption err := r.Client.List(ctx, grafanas, opts...) if err != nil { - return ctrl.Result{ - Requeue: true, - }, err + return ctrl.Result{}, err } // no instances, no need to sync @@ -80,9 +78,7 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, allFolders := &grafanav1beta1.GrafanaFolderList{} err = r.Client.List(ctx, allFolders, opts...) if err != nil { - return ctrl.Result{ - Requeue: true, - }, err + return ctrl.Result{}, err } // sync folders, delete folders from grafana that do no longer have a cr @@ -100,7 +96,7 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, for grafana, existingFolders := range foldersToDelete { grafanaClient, err := client2.NewGeneratedGrafanaClient(ctx, r.Client, grafana) if err != nil { - return ctrl.Result{Requeue: true}, err + return ctrl.Result{}, err } for _, folder := range existingFolders { @@ -119,9 +115,9 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, if err != nil { var notFound *folders.DeleteFolderNotFound if errors.As(err, ¬Found) { - syncLog.Info("folder no longer exists", "namespace", namespace, "name", name) + r.Log.Info("folder no longer exists", "namespace", namespace, "name", name) } else { - return ctrl.Result{Requeue: false}, err + return ctrl.Result{}, err } } @@ -133,12 +129,12 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, // so we should minimize those updates err = r.Client.Status().Update(ctx, grafana) if err != nil { - return ctrl.Result{Requeue: false}, err + return ctrl.Result{}, err } } if foldersSynced > 0 { - syncLog.Info("successfully synced folders", "folders", foldersSynced) + r.Log.Info("successfully synced folders", "folders", foldersSynced) } return ctrl.Result{Requeue: false}, nil } @@ -153,8 +149,7 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result, // For more details, check Reconcile and its Result here: // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - controllerLog := log.FromContext(ctx).WithName("GrafanaFolderReconciler") - r.Log = controllerLog + r.Log = log.FromContext(ctx).WithName("GrafanaFolderReconciler") // periodic sync reconcile if req.Namespace == "" && req.Name == "" { @@ -166,26 +161,45 @@ func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Reques } folder := &grafanav1beta1.GrafanaFolder{} - err := r.Client.Get(ctx, client.ObjectKey{ Namespace: req.Namespace, Name: req.Name, }, folder) if err != nil { if kuberr.IsNotFound(err) { - if err := r.onFolderDeleted(ctx, req.Namespace, req.Name); err != nil { - return ctrl.Result{RequeueAfter: RequeueDelay}, err - } return ctrl.Result{}, nil } return ctrl.Result{}, fmt.Errorf("error getting grafana folder cr: %w", err) } + + if folder.GetDeletionTimestamp() != nil { + // Check if resource needs clean up + if controllerutil.ContainsFinalizer(folder, grafanaFinalizer) { + if err := r.finalize(ctx, folder); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to finalize GrafanaFolder: %w", err) + } + if err := removeFinalizer(ctx, r.Client, folder); err != nil { + return ctrl.Result{}, fmt.Errorf("failed to remove finalizer: %w", err) + } + } + return ctrl.Result{}, nil + } + defer func() { folder.Status.Hash = folder.Hash() folder.Status.LastResync = metav1.Time{Time: time.Now()} if err := r.Status().Update(ctx, folder); err != nil { r.Log.Error(err, "updating status") } + if meta.IsStatusConditionTrue(folder.Status.Conditions, conditionNoMatchingInstance) { + if err := removeFinalizer(ctx, r.Client, folder); err != nil { + r.Log.Error(err, "failed to remove finalizer") + } + } else { + if err := addFinalizer(ctx, r.Client, folder); err != nil { + r.Log.Error(err, "failed to set finalizer") + } + } }() if folder.Spec.ParentFolderUID == folder.CustomUIDOrUID() { @@ -195,18 +209,24 @@ func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Reques } removeInvalidSpec(&folder.Status.Conditions) - instances, err := GetScopedMatchingInstances(controllerLog, ctx, r.Client, folder) - if err != nil || len(instances) == 0 { + instances, err := GetScopedMatchingInstances(r.Log, ctx, r.Client, folder) + if err != nil { setNoMatchingInstancesCondition(&folder.Status.Conditions, folder.Generation, err) + meta.RemoveStatusCondition(&folder.Status.Conditions, conditionFolderSynchronized) folder.Status.NoMatchingInstances = true + return ctrl.Result{}, fmt.Errorf("failed fetching instances: %w", err) + } + + if len(instances) == 0 { + setNoMatchingInstancesCondition(&folder.Status.Conditions, folder.Generation, err) meta.RemoveStatusCondition(&folder.Status.Conditions, conditionFolderSynchronized) - controllerLog.Error(err, "could not find matching instances", "name", folder.Name, "namespace", folder.Namespace) + folder.Status.NoMatchingInstances = true return ctrl.Result{RequeueAfter: RequeueDelay}, nil } removeNoMatchingInstance(&folder.Status.Conditions) folder.Status.NoMatchingInstances = false - controllerLog.Info("found matching Grafana instances for folder", "count", len(instances)) + r.Log.Info("found matching Grafana instances for folder", "count", len(instances)) applyErrors := make(map[string]string) for _, grafana := range instances { @@ -266,7 +286,7 @@ func (r *GrafanaFolderReconciler) SetupWithManager(mgr ctrl.Manager, ctx context return err } -func (r *GrafanaFolderReconciler) onFolderDeleted(ctx context.Context, namespace string, name string) error { +func (r *GrafanaFolderReconciler) finalize(ctx context.Context, folder *grafanav1beta1.GrafanaFolder) error { list := grafanav1beta1.GrafanaList{} var opts []client.ListOption err := r.Client.List(ctx, &list, opts...) @@ -276,7 +296,7 @@ func (r *GrafanaFolderReconciler) onFolderDeleted(ctx context.Context, namespace for _, grafana := range list.Items { grafana := grafana - if found, uid := grafana.Status.Folders.Find(namespace, name); found { + if found, uid := grafana.Status.Folders.Find(folder.Namespace, folder.Name); found { grafanaClient, err := client2.NewGeneratedGrafanaClient(ctx, r.Client, &grafana) if err != nil { return err @@ -291,7 +311,7 @@ func (r *GrafanaFolderReconciler) onFolderDeleted(ctx context.Context, namespace } } - grafana.Status.Folders = grafana.Status.Folders.Remove(namespace, name) + grafana.Status.Folders = grafana.Status.Folders.Remove(folder.Namespace, folder.Name) err = r.Client.Status().Update(ctx, &grafana) if err != nil { return err