fix: apply appropriate labels and annotations when creating provisioned folders (#114669)

fix: labels and anotations when creating provisioned folders
This commit is contained in:
Renato Costa
2025-12-03 09:18:18 -05:00
committed by GitHub
parent d04f51f7ed
commit 3cb03b888f
3 changed files with 99 additions and 24 deletions
@@ -429,7 +429,8 @@ func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, servic
return 0, "", dashboards.ErrFolderInvalidUID
}
// When we expect folders in unified storage, they should have a manager indicated
// When we expect folders in unified storage, they should have a manager indicated.
// NOTE: when everything has been running in mode5 for a while, this check can be removed.
if err == nil && result != nil && result.ManagedBy == "" && fr.foldersInUnified {
result, err = service.UpdateFolderWithManagedByAnnotation(ctx, result, fr.Cfg.Name)
if err != nil {
@@ -257,6 +257,19 @@ func (d *dualWriter) Create(ctx context.Context, in runtime.Object, createValida
if permissions != "" {
objCopy.SetAnnotation(utils.AnnoKeyGrantPermissions, permissions)
}
// Propagate annotations and labels to the object saved in
// unified storage, making sure the `deprecatedID` is saved
// as well as provisioning metadata, when present.
for name, val := range accIn.GetAnnotations() {
objCopy.SetAnnotation(name, val)
}
legacyAcc, err := meta.Accessor(createdFromLegacy)
if err != nil {
return nil, err
}
objCopy.SetLabels(legacyAcc.GetLabels())
}
// If unified storage is the primary storage, let's just create it in the foreground and return it.
+84 -23
View File
@@ -25,6 +25,7 @@ import (
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/apimachinery/utils"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/expr"
"github.com/grafana/grafana/pkg/infra/db"
@@ -1983,6 +1984,31 @@ func TestIntegrationDeleteNestedFoldersPostorder(t *testing.T) {
}
}
func setupProvisioningDir(t *testing.T, opts *testinfra.GrafanaOpts) {
opts.Dir, opts.DirPath = testinfra.CreateGrafDir(t, *opts)
// Create provisioning directories
provDashboardsDir := fmt.Sprintf("%s/conf/provisioning/dashboards", opts.Dir)
provDashboardsCfg := fmt.Sprintf("%s/dev.yaml", provDashboardsDir)
blob := []byte(fmt.Sprintf(`
apiVersion: 1
providers:
- name: 'provisioned dashboards'
type: file
orgId: 1
folder: 'GrafanaCloud'
options:
path: %s`, provDashboardsDir))
err := os.WriteFile(provDashboardsCfg, blob, 0o644)
require.NoError(t, err)
input, err := os.ReadFile(filepath.Join("testdata/dashboard.json"))
require.NoError(t, err)
provDashboardFile := filepath.Join(provDashboardsDir, "dashboard.json")
err = os.WriteFile(provDashboardFile, input, 0o644)
require.NoError(t, err)
}
// Test deleting folder with provisioned dashboard has proper handling with forceDeleteRules
func TestIntegrationDeleteFolderWithProvisionedDashboards(t *testing.T) {
testutil.SkipIntegrationTestInShortMode(t)
@@ -2010,29 +2036,8 @@ func TestIntegrationDeleteFolderWithProvisionedDashboards(t *testing.T) {
featuremgmt.FlagUnifiedStorageSearch,
},
}
// Setup Grafana with provisioning
ops.Dir, ops.DirPath = testinfra.CreateGrafDir(t, ops)
// Create provisioning directories
provDashboardsDir := fmt.Sprintf("%s/conf/provisioning/dashboards", ops.Dir)
provDashboardsCfg := fmt.Sprintf("%s/dev.yaml", provDashboardsDir)
blob := []byte(fmt.Sprintf(`
apiVersion: 1
providers:
- name: 'provisioned dashboards'
type: file
orgId: 1
folder: 'GrafanaCloud'
options:
path: %s`, provDashboardsDir))
err := os.WriteFile(provDashboardsCfg, blob, 0o644)
require.NoError(t, err)
input, err := os.ReadFile(filepath.Join("testdata/dashboard.json"))
require.NoError(t, err)
provDashboardFile := filepath.Join(provDashboardsDir, "dashboard.json")
err = os.WriteFile(provDashboardFile, input, 0o644)
require.NoError(t, err)
setupProvisioningDir(t, &ops)
helper := apis.NewK8sTestHelper(t, ops)
client := helper.GetResourceClient(apis.ResourceClientArgs{
@@ -2059,7 +2064,7 @@ providers:
}
}, 10*time.Second, 25*time.Millisecond)
_, err = client.Resource.Get(context.Background(), folderUID, metav1.GetOptions{})
_, err := client.Resource.Get(context.Background(), folderUID, metav1.GetOptions{})
require.NoError(t, err, "folder %s should exist", folderUID)
// Verify dashboards exist
verifyDashboardExists := func(shouldExist bool) {
@@ -2116,3 +2121,59 @@ providers:
})
}
}
// Test that folders created during provisioning using the dual writer have the
// appropriate labels and annotations in unified storage.
func TestIntegrationProvisionedFolderPropagatesLabelsAndAnnotations(t *testing.T) {
mode3 := grafanarest.DualWriterMode(3)
ops := testinfra.GrafanaOpts{
DisableAnonymous: true,
AppModeProduction: true,
APIServerStorageType: "unified",
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
folders.RESOURCEGROUP: {
DualWriterMode: mode3,
},
"dashboards.dashboard.grafana.app": {
DualWriterMode: mode3,
},
},
EnableFeatureToggles: []string{
featuremgmt.FlagUnifiedStorageSearch,
},
}
setupProvisioningDir(t, &ops)
helper := apis.NewK8sTestHelper(t, ops)
client := helper.GetResourceClient(apis.ResourceClientArgs{
User: helper.Org1.Admin,
GVR: gvr,
})
var folderList folders.FolderList
require.EventuallyWithT(t, func(collect *assert.CollectT) {
resp := apis.DoRequest(helper, apis.RequestParams{
User: client.Args.User,
Method: http.MethodGet,
Path: fmt.Sprintf("/apis/folder.grafana.app/v1beta1/namespaces/%s/folders", client.Args.Namespace),
}, &map[string]interface{}{})
require.NotNil(t, resp.Response)
require.Equal(t, http.StatusOK, resp.Response.StatusCode)
require.NoError(t, json.Unmarshal(resp.Body, &folderList))
}, 10*time.Second, 25*time.Millisecond)
require.Len(t, folderList.Items, 1)
accessor, err := utils.MetaAccessor(&folderList.Items[0])
require.NoError(t, err)
expectedLabels := map[string]string{"grafana.app/deprecatedInternalID": "1"}
expectedAnnotations := map[string]string{
"grafana.app/createdBy": "access-policy:service",
"grafana.app/managedBy": "classic-file-provisioning",
"grafana.app/managerId": "provisioned dashboards",
}
require.Equal(t, expectedLabels, accessor.GetLabels())
require.Equal(t, expectedAnnotations, accessor.GetAnnotations())
}