K8s: Reduce folder get calls in modes 0-2 (#102831)
This commit is contained in:
@@ -58,6 +58,9 @@ const AnnoKeySourcePath = "grafana.app/sourcePath"
|
||||
const AnnoKeySourceChecksum = "grafana.app/sourceChecksum"
|
||||
const AnnoKeySourceTimestamp = "grafana.app/sourceTimestamp"
|
||||
|
||||
const AnnoKeyFullpath = "grafana.app/fullpath"
|
||||
const AnnoKeyFullpathUIDs = "grafana.app/fullpathUIDs"
|
||||
|
||||
// LabelKeyDeprecatedInternalID gives the deprecated internal ID of a resource
|
||||
// Deprecated: will be removed in grafana 13
|
||||
const LabelKeyDeprecatedInternalID = "grafana.app/deprecatedInternalID"
|
||||
@@ -97,6 +100,11 @@ type GrafanaMetaAccessor interface {
|
||||
// Deprecated: This will be removed in Grafana 13
|
||||
SetDeprecatedInternalID(id int64)
|
||||
|
||||
GetFullpath() string
|
||||
SetFullpath(path string)
|
||||
GetFullpathUIDs() string
|
||||
SetFullpathUIDs(uids string)
|
||||
|
||||
GetSpec() (any, error)
|
||||
SetSpec(any) error
|
||||
|
||||
@@ -317,6 +325,22 @@ func (m *grafanaMetaAccessor) SetDeprecatedInternalID(id int64) {
|
||||
m.obj.SetLabels(labels)
|
||||
}
|
||||
|
||||
func (m *grafanaMetaAccessor) GetFullpath() string {
|
||||
return m.get(AnnoKeyFullpath)
|
||||
}
|
||||
|
||||
func (m *grafanaMetaAccessor) SetFullpath(path string) {
|
||||
m.SetAnnotation(AnnoKeyFullpath, path)
|
||||
}
|
||||
|
||||
func (m *grafanaMetaAccessor) GetFullpathUIDs() string {
|
||||
return m.get(AnnoKeyFullpathUIDs)
|
||||
}
|
||||
|
||||
func (m *grafanaMetaAccessor) SetFullpathUIDs(uids string) {
|
||||
m.SetAnnotation(AnnoKeyFullpathUIDs, uids)
|
||||
}
|
||||
|
||||
// GetAnnotations implements GrafanaMetaAccessor.
|
||||
func (m *grafanaMetaAccessor) GetAnnotations() map[string]string {
|
||||
return m.obj.GetAnnotations()
|
||||
|
||||
@@ -72,6 +72,14 @@ func convertToK8sResource(v *folder.Folder, namespacer request.NamespaceMapper)
|
||||
// We're going to have to align with that. For now we do need the user ID because the folder type stores it
|
||||
// as the only user identifier
|
||||
|
||||
if v.Fullpath != "" {
|
||||
meta.SetFullpath(v.Fullpath)
|
||||
}
|
||||
|
||||
if v.FullpathUIDs != "" {
|
||||
meta.SetFullpathUIDs(v.FullpathUIDs)
|
||||
}
|
||||
|
||||
if v.CreatedBy != 0 {
|
||||
meta.SetCreatedBy(claims.NewTypeID(claims.TypeUser, strconv.FormatInt(v.CreatedBy, 10)))
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
@@ -105,6 +107,12 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
|
||||
paging.page = 1
|
||||
}
|
||||
|
||||
// only admins can add this to the query, otherwise we may return parent folder names that are not visible to the user
|
||||
if user.GetOrgRole() == org.RoleAdmin && options.LabelSelector != nil && options.LabelSelector.Matches(labels.Set{utils.AnnoKeyFullpath: "true"}) {
|
||||
query.WithFullpath = true
|
||||
query.WithFullpathUIDs = true
|
||||
}
|
||||
|
||||
hits, err := s.service.GetFoldersLegacy(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -8,13 +8,16 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
)
|
||||
|
||||
@@ -119,3 +122,75 @@ func TestLegacyStorage_List_Pagination(t *testing.T) {
|
||||
require.Equal(t, int64(1), folderService.LastQuery.Page)
|
||||
})
|
||||
}
|
||||
|
||||
func TestLegacyStorage_List_LabelSelector(t *testing.T) {
|
||||
usr := &user.SignedInUser{UserID: 1, OrgRole: org.RoleAdmin}
|
||||
ctx := identity.WithRequester(context.Background(), usr)
|
||||
folderService := &foldertest.FakeService{}
|
||||
storage := legacyStorage{
|
||||
service: folderService,
|
||||
namespacer: func(_ int64) string { return "1" },
|
||||
}
|
||||
|
||||
t.Run("should handle nil label selector", func(t *testing.T) {
|
||||
options := &metainternalversion.ListOptions{
|
||||
LabelSelector: nil,
|
||||
}
|
||||
|
||||
folders := []*folder.Folder{
|
||||
{
|
||||
UID: "folder-1",
|
||||
Title: "Folder 1",
|
||||
},
|
||||
}
|
||||
folderService.ExpectedFolders = folders
|
||||
|
||||
result, err := storage.List(ctx, options)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify we queried the service correctly
|
||||
require.False(t, folderService.LastQuery.WithFullpath)
|
||||
require.False(t, folderService.LastQuery.WithFullpathUIDs)
|
||||
|
||||
list, ok := result.(*v0alpha1.FolderList)
|
||||
require.True(t, ok)
|
||||
require.Len(t, list.Items, 1)
|
||||
})
|
||||
|
||||
t.Run("should set fullpath query parameters when label selector matches", func(t *testing.T) {
|
||||
selector, err := labels.Parse(utils.AnnoKeyFullpath + "=true")
|
||||
require.NoError(t, err)
|
||||
options := &metainternalversion.ListOptions{
|
||||
LabelSelector: selector,
|
||||
}
|
||||
|
||||
folders := []*folder.Folder{
|
||||
{
|
||||
UID: "folder-1",
|
||||
Title: "Folder 1",
|
||||
Fullpath: "/Folder 1",
|
||||
FullpathUIDs: "/folder-1",
|
||||
},
|
||||
}
|
||||
folderService.ExpectedFolders = folders
|
||||
|
||||
result, err := storage.List(ctx, options)
|
||||
require.NoError(t, err)
|
||||
|
||||
// verify we queried the service correctly
|
||||
require.True(t, folderService.LastQuery.WithFullpath)
|
||||
require.True(t, folderService.LastQuery.WithFullpathUIDs)
|
||||
|
||||
list, ok := result.(*v0alpha1.FolderList)
|
||||
require.True(t, ok)
|
||||
require.Len(t, list.Items, 1)
|
||||
|
||||
folder := list.Items[0]
|
||||
meta, err := utils.MetaAccessor(&folder)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make sure the annotations are set
|
||||
require.Equal(t, "/Folder 1", meta.GetFullpath())
|
||||
require.Equal(t, "/folder-1", meta.GetFullpathUIDs())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context
|
||||
if updater.UID == "" {
|
||||
updater = creator
|
||||
}
|
||||
|
||||
manager, _ := meta.GetManagerProperties()
|
||||
return &folder.Folder{
|
||||
UID: uid,
|
||||
@@ -68,12 +69,14 @@ func (ss *FolderUnifiedStoreImpl) UnstructuredToLegacyFolder(ctx context.Context
|
||||
Version: int(meta.GetGeneration()),
|
||||
ManagedBy: manager.Kind,
|
||||
|
||||
URL: url,
|
||||
Created: created,
|
||||
Updated: *updated,
|
||||
OrgID: info.OrgID,
|
||||
CreatedBy: creator.ID,
|
||||
UpdatedBy: updater.ID,
|
||||
Fullpath: meta.GetFullpath(),
|
||||
FullpathUIDs: meta.GetFullpathUIDs(),
|
||||
URL: url,
|
||||
Created: created,
|
||||
Updated: *updated,
|
||||
OrgID: info.OrgID,
|
||||
CreatedBy: creator.ID,
|
||||
UpdatedBy: updater.ID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -308,7 +308,14 @@ func (ss *FolderUnifiedStoreImpl) GetHeight(ctx context.Context, foldrUID string
|
||||
// The full path UIDs of B is "uid1/uid2".
|
||||
// The full path UIDs of A is "uid1".
|
||||
func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFoldersFromStoreQuery) ([]*folder.Folder, error) {
|
||||
out, err := ss.k8sclient.List(ctx, q.OrgID, v1.ListOptions{})
|
||||
opts := v1.ListOptions{}
|
||||
if q.WithFullpath || q.WithFullpathUIDs {
|
||||
// only supported in modes 0-2, to keep the alerting queries from causing tons of get folder requests
|
||||
// to retrieve the parent for all folders in grafana
|
||||
opts.LabelSelector = utils.AnnoKeyFullpath + "=true"
|
||||
}
|
||||
|
||||
out, err := ss.k8sclient.List(ctx, q.OrgID, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -320,7 +327,8 @@ func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFo
|
||||
if f == nil {
|
||||
return nil, fmt.Errorf("unable to convert unstructured item to legacy folder %w", err)
|
||||
}
|
||||
if q.WithFullpath || q.WithFullpathUIDs {
|
||||
|
||||
if (q.WithFullpath || q.WithFullpathUIDs) && f.Fullpath == "" {
|
||||
parents, err := ss.GetParents(ctx, folder.GetParentsQuery{UID: f.UID, OrgID: q.OrgID})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get parents for folder %s: %w", f.UID, err)
|
||||
|
||||
Reference in New Issue
Block a user