Skip to content

Commit df1a50e

Browse files
authored
Merge pull request #1566 from aboulay-numspot/feat/add-parent-folder-uid-field-in-grafana-folder
Feat: add parentFolderUID field in GrafanaFolder CRD
2 parents a1d05be + 9d9ec87 commit df1a50e

File tree

7 files changed

+75
-6
lines changed

7 files changed

+75
-6
lines changed

api/v1beta1/grafanafolder_types.go

+11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ type GrafanaFolderSpec struct {
4444
// +optional
4545
AllowCrossNamespaceImport *bool `json:"allowCrossNamespaceImport,omitempty"`
4646

47+
// UID of the folder in which the current folder should be created
48+
// +optional
49+
ParentFolderUID string `json:"parentFolderUID,omitempty"`
50+
4751
// how often the folder is synced, defaults to 5m if not set
4852
// +optional
4953
// +kubebuilder:validation:Type=string
@@ -62,6 +66,9 @@ type GrafanaFolderStatus struct {
6266
NoMatchingInstances bool `json:"NoMatchingInstances,omitempty"`
6367
// Last time the folder was resynced
6468
LastResync metav1.Time `json:"lastResync,omitempty"`
69+
// UID of the parent folder where the folder is created.
70+
// Will be empty if the folder is deployed at the root level
71+
ParentFolderUID string `json:"parentFolderUID,omitempty"`
6572
}
6673

6774
//+kubebuilder:object:root=true
@@ -111,6 +118,10 @@ func (in *GrafanaFolder) Unchanged() bool {
111118
return in.Hash() == in.Status.Hash
112119
}
113120

121+
func (in *GrafanaFolder) Moved() bool {
122+
return in.Spec.ParentFolderUID != in.Status.ParentFolderUID
123+
}
124+
114125
func (in *GrafanaFolder) IsAllowCrossNamespaceImport() bool {
115126
if in.Spec.AllowCrossNamespaceImport != nil {
116127
return *in.Spec.AllowCrossNamespaceImport

config/crd/bases/grafana.integreatly.org_grafanafolders.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ spec:
9999
x-kubernetes-validations:
100100
- message: Value is immutable
101101
rule: self == oldSelf
102+
parentFolderUID:
103+
description: UID of the folder in which the current folder should
104+
be created
105+
type: string
102106
permissions:
103107
description: raw json with folder permissions
104108
type: string
@@ -130,6 +134,11 @@ spec:
130134
description: Last time the folder was resynced
131135
format: date-time
132136
type: string
137+
parentFolderUID:
138+
description: |-
139+
UID of the parent folder where the folder is created.
140+
Will be empty if the folder is deployed at the root level
141+
type: string
133142
type: object
134143
type: object
135144
served: true

controllers/grafanafolder_controller.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
301301
}
302302

303303
// always update after resync period has elapsed even if cr is unchanged.
304-
if exists && cr.Unchanged() && !cr.ResyncPeriodHasElapsed() {
304+
if exists && cr.Unchanged() && !cr.ResyncPeriodHasElapsed() && !cr.Moved() {
305305
return nil
306306
}
307307

@@ -327,11 +327,23 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
327327
return err
328328
}
329329
}
330+
331+
if cr.Moved() {
332+
_, err = grafanaClient.Folders.MoveFolder(remoteUID, &models.MoveFolderCommand{ //nolint
333+
ParentUID: cr.Spec.ParentFolderUID,
334+
})
335+
if err != nil {
336+
return err
337+
}
338+
}
330339
} else {
331-
folderResp, err := grafanaClient.Folders.CreateFolder(&models.CreateFolderCommand{
332-
Title: title,
333-
UID: uid,
334-
})
340+
body := &models.CreateFolderCommand{
341+
Title: title,
342+
UID: uid,
343+
ParentUID: cr.Spec.ParentFolderUID,
344+
}
345+
346+
folderResp, err := grafanaClient.Folders.CreateFolder(body)
335347
if err != nil {
336348
return err
337349
}
@@ -362,6 +374,7 @@ func (r *GrafanaFolderReconciler) onFolderCreated(ctx context.Context, grafana *
362374

363375
func (r *GrafanaFolderReconciler) UpdateStatus(ctx context.Context, cr *grafanav1beta1.GrafanaFolder) error {
364376
cr.Status.Hash = cr.Hash()
377+
cr.Status.ParentFolderUID = cr.Spec.ParentFolderUID
365378
return r.Client.Status().Update(ctx, cr)
366379
}
367380

@@ -372,7 +385,8 @@ func (r *GrafanaFolderReconciler) Exists(client *genapi.GrafanaHTTPAPI, cr *graf
372385
page := int64(1)
373386
limit := int64(10000)
374387
for {
375-
params := folders.NewGetFoldersParams().WithPage(&page).WithLimit(&limit)
388+
params := folders.NewGetFoldersParams().WithPage(&page).WithLimit(&limit).WithParentUID(&cr.Status.ParentFolderUID)
389+
376390
foldersResp, err := client.Folders.GetFolders(params)
377391
if err != nil {
378392
return false, "", err

deploy/helm/grafana-operator/crds/grafana.integreatly.org_grafanafolders.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ spec:
9999
x-kubernetes-validations:
100100
- message: Value is immutable
101101
rule: self == oldSelf
102+
parentFolderUID:
103+
description: UID of the folder in which the current folder should
104+
be created
105+
type: string
102106
permissions:
103107
description: raw json with folder permissions
104108
type: string
@@ -130,6 +134,11 @@ spec:
130134
description: Last time the folder was resynced
131135
format: date-time
132136
type: string
137+
parentFolderUID:
138+
description: |-
139+
UID of the parent folder where the folder is created.
140+
Will be empty if the folder is deployed at the root level
141+
type: string
133142
type: object
134143
type: object
135144
served: true

deploy/kustomize/base/crds.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -1244,6 +1244,10 @@ spec:
12441244
x-kubernetes-validations:
12451245
- message: Value is immutable
12461246
rule: self == oldSelf
1247+
parentFolderUID:
1248+
description: UID of the folder in which the current folder should
1249+
be created
1250+
type: string
12471251
permissions:
12481252
description: raw json with folder permissions
12491253
type: string
@@ -1275,6 +1279,11 @@ spec:
12751279
description: Last time the folder was resynced
12761280
format: date-time
12771281
type: string
1282+
parentFolderUID:
1283+
description: |-
1284+
UID of the parent folder where the folder is created.
1285+
Will be empty if the folder is deployed at the root level
1286+
type: string
12781287
type: object
12791288
type: object
12801289
served: true

docs/docs/api.md

+15
Original file line numberDiff line numberDiff line change
@@ -2472,6 +2472,13 @@ GrafanaFolderSpec defines the desired state of GrafanaFolder
24722472
allow to import this resources from an operator in a different namespace<br/>
24732473
</td>
24742474
<td>false</td>
2475+
</tr><tr>
2476+
<td><b>parentFolderUID</b></td>
2477+
<td>string</td>
2478+
<td>
2479+
UID of the folder in which the current folder should be created<br/>
2480+
</td>
2481+
<td>false</td>
24752482
</tr><tr>
24762483
<td><b>permissions</b></td>
24772484
<td>string</td>
@@ -2622,6 +2629,14 @@ Important: Run "make" to regenerate code after modifying this file<br/>
26222629
<i>Format</i>: date-time<br/>
26232630
</td>
26242631
<td>false</td>
2632+
</tr><tr>
2633+
<td><b>parentFolderUID</b></td>
2634+
<td>string</td>
2635+
<td>
2636+
UID of the parent folder where the folder is created.
2637+
Will be empty if the folder is deployed at the root level<br/>
2638+
</td>
2639+
<td>false</td>
26252640
</tr></tbody>
26262641
</table>
26272642

hack/kind/resources/default/grafana.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ spec:
1010
config:
1111
log:
1212
mode: "console"
13+
feature_toggles:
14+
enable: nestedFolders
1315
auth:
1416
disable_login_form: "false"
1517
security:

0 commit comments

Comments
 (0)