Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement finalizers for GrafanaFolder #1807

Merged
merged 5 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading