fix: apply appropriate labels and annotations when creating provisioned folders (#114669)
fix: labels and anotations when creating provisioned folders
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user