@@ -31,6 +31,7 @@ import (
31
31
client2 "github.com/grafana/grafana-operator/v5/controllers/client"
32
32
"github.com/grafana/grafana-operator/v5/controllers/metrics"
33
33
kuberr "k8s.io/apimachinery/pkg/api/errors"
34
+ "k8s.io/apimachinery/pkg/api/meta"
34
35
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35
36
36
37
"k8s.io/apimachinery/pkg/runtime"
@@ -41,6 +42,10 @@ import (
41
42
grafanav1beta1 "github.com/grafana/grafana-operator/v5/api/v1beta1"
42
43
)
43
44
45
+ const (
46
+ conditionFolderSynchronized = "FolderSynchronized"
47
+ )
48
+
44
49
// GrafanaFolderReconciler reconciles a GrafanaFolder object
45
50
type GrafanaFolderReconciler struct {
46
51
client.Client
@@ -176,15 +181,30 @@ func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Reques
176
181
return ctrl.Result {RequeueAfter : RequeueDelay }, err
177
182
}
178
183
184
+ if folder .Spec .ParentFolderUID == string (folder .UID ) {
185
+ setInvalidSpec (& folder .Status .Conditions , folder .Generation , "CyclicParent" , "The value of parentFolderUID must not be the uid of the current folder" )
186
+ meta .RemoveStatusCondition (& folder .Status .Conditions , conditionFolderSynchronized )
187
+ r .UpdateStatus (ctx , folder )
188
+ return ctrl.Result {}, fmt .Errorf ("cyclic folder reference" )
189
+ }
190
+ removeInvalidSpec (& folder .Status .Conditions )
191
+
179
192
instances , err := r .GetMatchingFolderInstances (ctx , folder , r .Client )
180
193
if err != nil {
181
- controllerLog .Error (err , "could not find matching instances" , "name" , folder .Name , "namespace" , folder .Namespace )
182
- return ctrl.Result {RequeueAfter : RequeueDelay }, err
194
+ setNoMatchingInstance (& folder .Status .Conditions , folder .Generation , "ErrFetchingInstances" , fmt .Sprintf ("error occurred during fetching of instances: %s" , err .Error ()))
195
+ meta .RemoveStatusCondition (& folder .Status .Conditions , conditionFolderSynchronized )
196
+ r .Log .Error (err , "could not find matching instances" )
197
+ return ctrl.Result {}, err
183
198
}
184
-
199
+ if len (instances .Items ) == 0 {
200
+ setNoMatchingInstance (& folder .Status .Conditions , folder .Generation , "EmptyAPIReply" , "Instances could not be fetched, reconciliation will be retried" )
201
+ meta .RemoveStatusCondition (& folder .Status .Conditions , conditionFolderSynchronized )
202
+ return ctrl.Result {}, fmt .Errorf ("no instances found" )
203
+ }
204
+ removeNoMatchingInstance (& folder .Status .Conditions )
185
205
controllerLog .Info ("found matching Grafana instances for folder" , "count" , len (instances .Items ))
186
206
187
- success := true
207
+ applyErrors := make ( map [ string ] string )
188
208
for _ , grafana := range instances .Items {
189
209
// check if this is a cross namespace import
190
210
if grafana .Namespace != folder .Namespace && ! folder .IsAllowCrossNamespaceImport () {
@@ -200,12 +220,14 @@ func (r *GrafanaFolderReconciler) Reconcile(ctx context.Context, req ctrl.Reques
200
220
err = r .onFolderCreated (ctx , & grafana , folder )
201
221
if err != nil {
202
222
controllerLog .Error (err , "error reconciling folder" , "folder" , folder .Name , "grafana" , grafana .Name )
203
- success = false
223
+ applyErrors [ fmt . Sprintf ( "%s/%s" , grafana . Namespace , grafana . Name )] = err . Error ()
204
224
}
205
225
}
226
+ condition := buildSynchronizedCondition ("Folder" , conditionFolderSynchronized , folder .Generation , applyErrors , len (instances .Items ))
227
+ meta .SetStatusCondition (& folder .Status .Conditions , condition )
206
228
207
- if ! success {
208
- return ctrl.Result {RequeueAfter : RequeueDelay }, nil
229
+ if len ( applyErrors ) != 0 {
230
+ return ctrl.Result {RequeueAfter : RequeueDelay }, r . UpdateStatus ( ctx , folder )
209
231
}
210
232
211
233
if folder .ResyncPeriodHasElapsed () {
@@ -295,13 +317,13 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
295
317
return err
296
318
}
297
319
298
- exists , remoteUID , err := r .Exists (grafanaClient , cr )
320
+ exists , remoteUID , remoteParent , err := r .Exists (grafanaClient , cr )
299
321
if err != nil {
300
322
return err
301
323
}
302
324
303
325
// always update after resync period has elapsed even if cr is unchanged.
304
- if exists && cr .Unchanged () && ! cr .ResyncPeriodHasElapsed () && ! cr .Moved () {
326
+ if exists && cr .Unchanged () && ! cr .ResyncPeriodHasElapsed () && cr .Spec . ParentFolderUID == remoteParent {
305
327
return nil
306
328
}
307
329
@@ -328,7 +350,7 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
328
350
}
329
351
}
330
352
331
- if cr .Moved () {
353
+ if cr .Spec . ParentFolderUID != remoteParent {
332
354
_ , err = grafanaClient .Folders .MoveFolder (remoteUID , & models.MoveFolderCommand { //nolint
333
355
ParentUID : cr .Spec .ParentFolderUID ,
334
356
})
@@ -374,30 +396,35 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
374
396
375
397
func (r * GrafanaFolderReconciler ) UpdateStatus (ctx context.Context , cr * grafanav1beta1.GrafanaFolder ) error {
376
398
cr .Status .Hash = cr .Hash ()
377
- cr .Status .ParentFolderUID = cr .Spec .ParentFolderUID
378
399
return r .Client .Status ().Update (ctx , cr )
379
400
}
380
401
381
- func (r * GrafanaFolderReconciler ) Exists (client * genapi.GrafanaHTTPAPI , cr * grafanav1beta1.GrafanaFolder ) (bool , string , error ) {
402
+ // Check if the folder exists. Matches UID first and fall back to title. Title matching only works for non-nested folders
403
+ func (r * GrafanaFolderReconciler ) Exists (client * genapi.GrafanaHTTPAPI , cr * grafanav1beta1.GrafanaFolder ) (bool , string , string , error ) {
382
404
title := cr .GetTitle ()
383
405
uid := string (cr .UID )
384
406
407
+ uidResp , err := client .Folders .GetFolderByUID (uid )
408
+ if err == nil {
409
+ return true , uidResp .Payload .UID , uidResp .Payload .ParentUID , nil
410
+ }
411
+
385
412
page := int64 (1 )
386
413
limit := int64 (10000 )
387
414
for {
388
- params := folders .NewGetFoldersParams ().WithPage (& page ).WithLimit (& limit ). WithParentUID ( & cr . Status . ParentFolderUID )
415
+ params := folders .NewGetFoldersParams ().WithPage (& page ).WithLimit (& limit )
389
416
390
417
foldersResp , err := client .Folders .GetFolders (params )
391
418
if err != nil {
392
- return false , "" , err
419
+ return false , "" , "" , err
393
420
}
394
421
for _ , folder := range foldersResp .Payload {
395
- if folder . UID == uid || strings .EqualFold (folder .Title , title ) {
396
- return true , folder .UID , nil
422
+ if strings .EqualFold (folder .Title , title ) {
423
+ return true , folder .UID , folder . ParentUID , nil
397
424
}
398
425
}
399
426
if len (foldersResp .Payload ) < int (limit ) {
400
- return false , "" , nil
427
+ return false , "" , "" , nil
401
428
}
402
429
page ++
403
430
}
0 commit comments