mirror of
https://github.com/grafana/grafana.git
synced 2026-03-13 15:29:48 +08:00
Folders: Remove with full path annotation from k8s api (#119998)
This commit is contained in:
committed by
GitHub
parent
761cd5cdfc
commit
d0ff3be451
@@ -65,12 +65,6 @@ const AnnoKeySourcePath = "grafana.app/sourcePath"
|
||||
const AnnoKeySourceChecksum = "grafana.app/sourceChecksum"
|
||||
const AnnoKeySourceTimestamp = "grafana.app/sourceTimestamp"
|
||||
|
||||
// Only used in modes 0-2 (legacy db) for returning the folder fullpath
|
||||
|
||||
const LabelGetFullpath = "grafana.app/fullpath"
|
||||
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"
|
||||
|
||||
@@ -81,14 +81,6 @@ 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.SetAnnotation(utils.AnnoKeyFullpath, v.Fullpath)
|
||||
}
|
||||
|
||||
if v.FullpathUIDs != "" {
|
||||
meta.SetAnnotation(utils.AnnoKeyFullpathUIDs, v.FullpathUIDs)
|
||||
}
|
||||
|
||||
if v.CreatedBy != 0 {
|
||||
meta.SetCreatedBy(claims.NewTypeID(claims.TypeUser, strconv.FormatInt(v.CreatedBy, 10)))
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ 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"
|
||||
|
||||
@@ -94,11 +93,6 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
|
||||
query.Limit = paging.limit
|
||||
query.Page = paging.page
|
||||
|
||||
if options.LabelSelector != nil && options.LabelSelector.Matches(labels.Set{utils.LabelGetFullpath: "true"}) {
|
||||
query.WithFullpath = true
|
||||
query.WithFullpathUIDs = true
|
||||
}
|
||||
|
||||
hits, err := s.service.GetFoldersLegacy(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -9,11 +9,9 @@ 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"
|
||||
|
||||
folderv1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/folder"
|
||||
"github.com/grafana/grafana/pkg/services/folder/foldertest"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
@@ -180,41 +178,4 @@ func TestLegacyStorage_List_LabelSelector(t *testing.T) {
|
||||
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.LabelGetFullpath + "=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.(*folderv1.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.GetAnnotation(utils.AnnoKeyFullpath))
|
||||
require.Equal(t, "/folder-1", meta.GetAnnotation(utils.AnnoKeyFullpathUIDs))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,8 +72,9 @@ func convertUnstructuredToFolder(item *unstructured.Unstructured, identifiers ma
|
||||
Version: int(meta.GetGeneration()),
|
||||
ManagedBy: manager.Kind,
|
||||
|
||||
Fullpath: meta.GetAnnotation(utils.AnnoKeyFullpath),
|
||||
FullpathUIDs: meta.GetAnnotation(utils.AnnoKeyFullpathUIDs),
|
||||
// Fullpath/FullpathUIDs are not stored on objects; callers use buildFolderFullPaths when needed
|
||||
Fullpath: "",
|
||||
FullpathUIDs: "",
|
||||
URL: url,
|
||||
Created: created,
|
||||
Updated: *updated,
|
||||
|
||||
@@ -103,9 +103,7 @@ func TestFolderListConversions(t *testing.T) {
|
||||
"creationTimestamp": "2022-12-02T02:02:02Z",
|
||||
"generation": 1,
|
||||
"labels": {
|
||||
"grafana.app/deprecatedInternalID": "4",
|
||||
"grafana.app/fullpath": "somefullpath",
|
||||
"grafana.app/fullpathUIDs": "somefullpathuids"
|
||||
"grafana.app/deprecatedInternalID": "4"
|
||||
},
|
||||
"name": "foldername1",
|
||||
"namespace": "default",
|
||||
|
||||
@@ -353,14 +353,7 @@ func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFo
|
||||
ctx, span := ss.tracer.Start(ctx, tracePrefix+"GetFolders")
|
||||
defer span.End()
|
||||
|
||||
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.LabelGetFullpath + "=true"
|
||||
}
|
||||
|
||||
out, err := ss.list(ctx, q.OrgID, opts)
|
||||
out, err := ss.list(ctx, q.OrgID, v1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -583,142 +583,6 @@ func TestGetFolders(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return all folders from k8s with fullpath enabled",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
q: folder.GetFoldersFromStoreQuery{
|
||||
GetFoldersQuery: folder.GetFoldersQuery{
|
||||
OrgID: orgID,
|
||||
WithFullpath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
mock: func(mockCli *client.MockK8sHandler) {
|
||||
mockCli.On("List", mock.Anything, orgID, metav1.ListOptions{
|
||||
Limit: folderListLimit,
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
LabelSelector: "grafana.app/fullpath=true",
|
||||
}).Return(&unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "root1",
|
||||
"uid": "root1",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"title": "root1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "root2",
|
||||
"uid": "root2",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"title": "root2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
},
|
||||
want: []*folder.Folder{
|
||||
{
|
||||
UID: "root1",
|
||||
Title: "root1",
|
||||
OrgID: orgID,
|
||||
Fullpath: "root1",
|
||||
FullpathUIDs: "root1",
|
||||
},
|
||||
{
|
||||
UID: "root2",
|
||||
Title: "root2",
|
||||
OrgID: orgID,
|
||||
Fullpath: "root2",
|
||||
FullpathUIDs: "root2",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return folders from k8s by uid with fullpath enabled",
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
q: folder.GetFoldersFromStoreQuery{
|
||||
GetFoldersQuery: folder.GetFoldersQuery{
|
||||
OrgID: orgID,
|
||||
UIDs: []string{"folder1", "folder2"},
|
||||
WithFullpath: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
mock: func(mockCli *client.MockK8sHandler) {
|
||||
mockCli.On("List", mock.Anything, orgID, metav1.ListOptions{
|
||||
Limit: folderListLimit,
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
LabelSelector: "grafana.app/fullpath=true",
|
||||
}).Return(&unstructured.UnstructuredList{
|
||||
Items: []unstructured.Unstructured{
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "parentcommon",
|
||||
"uid": "parentcommon",
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"title": "parentcommon",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "folder1",
|
||||
"uid": "folder1",
|
||||
"annotations": map[string]interface{}{"grafana.app/folder": "parentcommon"},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"title": "folder1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Object: map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "folder2",
|
||||
"uid": "folder2",
|
||||
"annotations": map[string]interface{}{"grafana.app/folder": "parentcommon"},
|
||||
},
|
||||
"spec": map[string]interface{}{
|
||||
"title": "folder2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil).Once()
|
||||
},
|
||||
want: []*folder.Folder{
|
||||
{
|
||||
UID: "folder1",
|
||||
Title: "folder1",
|
||||
OrgID: orgID,
|
||||
Fullpath: "parentcommon/folder1",
|
||||
FullpathUIDs: "parentcommon/folder1",
|
||||
},
|
||||
{
|
||||
UID: "folder2",
|
||||
Title: "folder2",
|
||||
OrgID: orgID,
|
||||
Fullpath: "parentcommon/folder2",
|
||||
FullpathUIDs: "parentcommon/folder2",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return error if k8s returns error",
|
||||
args: args{
|
||||
|
||||
@@ -58,15 +58,8 @@ func toListRequest(k *resourcepb.ResourceKey, opts storage.ListOptions) (*resour
|
||||
for _, r := range requirements {
|
||||
v := r.Key()
|
||||
|
||||
// Parse the history request from labels
|
||||
// TODO: for LabelGetFullpath, we just skip this for unistore. We need a better solution for
|
||||
// getting the full path for folders in unistore, without making a request for each parent folder.
|
||||
// In modes 0-2 we added this label to indicate that the sql query should return that data as
|
||||
// an annotation on the folder. However, this annotation cannot be saved to unified storage, otherwise
|
||||
// we will have to recompute annotations for all descendants of a folder during a folder move.
|
||||
// While we look for a better solution, unified storage will continue to return all folders & the folder
|
||||
// service will get the full path by retrieving each parent folder.
|
||||
if v == utils.LabelKeyGetHistory || v == utils.LabelKeyGetTrash || v == utils.LabelGetFullpath {
|
||||
// Parse the history/trash request from labels
|
||||
if v == utils.LabelKeyGetHistory || v == utils.LabelKeyGetTrash {
|
||||
if len(requirements) != 1 {
|
||||
return nil, predicate, apierrors.NewBadRequest("single label supported with: " + v)
|
||||
}
|
||||
|
||||
@@ -218,33 +218,6 @@ func TestToListRequest(t *testing.T) {
|
||||
wantPredicate: storage.Everything,
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "with fullpath label",
|
||||
key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
},
|
||||
opts: storage.ListOptions{
|
||||
Predicate: storage.SelectionPredicate{
|
||||
Label: labels.SelectorFromSet(labels.Set{utils.LabelGetFullpath: "true"}),
|
||||
},
|
||||
},
|
||||
want: &resourcepb.ListRequest{
|
||||
VersionMatchV2: 1,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Labels: nil,
|
||||
Fields: nil,
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Group: "test",
|
||||
Resource: "test",
|
||||
Namespace: "default",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantPredicate: storage.Everything,
|
||||
wantErr: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -559,7 +559,7 @@ func (s *server) newEvent(ctx context.Context, user claims.AuthInfo, key *resour
|
||||
|
||||
// Make sure the command labels are not saved
|
||||
for k := range obj.GetLabels() {
|
||||
if k == utils.LabelKeyGetHistory || k == utils.LabelKeyGetTrash || k == utils.LabelGetFullpath {
|
||||
if k == utils.LabelKeyGetHistory || k == utils.LabelKeyGetTrash {
|
||||
return nil, NewBadRequestError("can not save label: " + k)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user