Skip to content

Commit

Permalink
Implement finalizers for GrafanaFolder (#1807)
Browse files Browse the repository at this point in the history
* feat: Add finalizers to folder cr

* chore: Remove controllerLog/syncLog in folder reconciler

* chore: Update returned results on errors

* chore: Make checks more idiomatic

* nit: Improve err accuracy

Co-authored-by: Dominik Süß <[email protected]>

---------

Co-authored-by: Dominik Süß <[email protected]>
  • Loading branch information
Baarsgaard and theSuess authored Jan 8, 2025
1 parent 9bdf1c7 commit ed11c7d
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 27 deletions.
12 changes: 10 additions & 2 deletions controllers/controller_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
70 changes: 45 additions & 25 deletions controllers/grafanafolder_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -58,17 +59,14 @@ 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
grafanas := &grafanav1beta1.GrafanaList{}
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
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -119,9 +115,9 @@ func (r *GrafanaFolderReconciler) syncFolders(ctx context.Context) (ctrl.Result,
if err != nil {
var notFound *folders.DeleteFolderNotFound
if errors.As(err, &notFound) {
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
}
}

Expand All @@ -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
}
Expand All @@ -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/[email protected]/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 == "" {
Expand All @@ -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() {
Expand All @@ -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 {
Expand Down Expand Up @@ -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...)
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit ed11c7d

Please sign in to comment.