Unistore: Skip label for getting full path (#102928)

This commit is contained in:
Stephanie Hingtgen
2025-03-26 15:50:53 -05:00
committed by GitHub
parent 558773ed7f
commit 0087d7bd58
7 changed files with 235 additions and 7 deletions
+3
View File
@@ -58,6 +58,9 @@ 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"
+1 -1
View File
@@ -108,7 +108,7 @@ func (s *legacyStorage) List(ctx context.Context, options *internalversion.ListO
}
// 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"}) {
if user.GetOrgRole() == org.RoleAdmin && options.LabelSelector != nil && options.LabelSelector.Matches(labels.Set{utils.LabelGetFullpath: "true"}) {
query.WithFullpath = true
query.WithFullpathUIDs = true
}
@@ -158,7 +158,7 @@ func TestLegacyStorage_List_LabelSelector(t *testing.T) {
})
t.Run("should set fullpath query parameters when label selector matches", func(t *testing.T) {
selector, err := labels.Parse(utils.AnnoKeyFullpath + "=true")
selector, err := labels.Parse(utils.LabelGetFullpath + "=true")
require.NoError(t, err)
options := &metainternalversion.ListOptions{
LabelSelector: selector,
@@ -312,7 +312,7 @@ func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFo
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"
opts.LabelSelector = utils.LabelGetFullpath + "=true"
}
out, err := ss.k8sclient.List(ctx, q.OrgID, opts)
+10 -3
View File
@@ -60,11 +60,18 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
v := r.Key()
// Parse the history request from labels
if v == utils.LabelKeyGetHistory || v == utils.LabelKeyGetTrash {
// 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 {
if len(requirements) != 1 {
return nil, predicate, apierrors.NewBadRequest("single label supported with: " + v)
}
if !opts.Predicate.Field.Empty() {
if opts.Predicate.Field != nil && !opts.Predicate.Field.Empty() {
return nil, predicate, apierrors.NewBadRequest("field selector not supported with: " + v)
}
if r.Operator() != selection.Equals {
@@ -81,7 +88,7 @@ func toListRequest(k *resource.ResourceKey, opts storage.ListOptions) (*resource
if vals[0] != "true" {
return nil, predicate, apierrors.NewBadRequest("expecting true for: " + v)
}
} else {
} else if v == utils.LabelKeyGetHistory {
req.Source = resource.ListRequest_HISTORY
req.Options.Key.Name = vals[0]
}
+218
View File
@@ -0,0 +1,218 @@
package apistore
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apiserver/pkg/storage"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"github.com/grafana/grafana/pkg/storage/unified/resource"
)
func TestToListRequest(t *testing.T) {
tests := []struct {
name string
key *resource.ResourceKey
opts storage.ListOptions
want *resource.ListRequest
wantPredicate storage.SelectionPredicate
wantErr error
}{
{
name: "basic list request",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{},
want: &resource.ListRequest{
VersionMatchV2: 1,
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
},
},
wantPredicate: storage.SelectionPredicate{},
wantErr: nil,
},
{
name: "with resource version",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
ResourceVersion: "123",
},
want: &resource.ListRequest{
VersionMatchV2: 1,
ResourceVersion: 123,
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
},
},
wantPredicate: storage.SelectionPredicate{},
wantErr: nil,
},
{
name: "invalid resource version",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
ResourceVersion: "invalid",
},
want: nil,
wantPredicate: storage.SelectionPredicate{},
wantErr: apierrors.NewBadRequest("invalid resource version: invalid"),
},
{
name: "with label selector",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
Predicate: storage.SelectionPredicate{
Label: labels.SelectorFromSet(labels.Set{"key": "value"}),
},
},
want: &resource.ListRequest{
VersionMatchV2: 1,
Options: &resource.ListOptions{
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
Labels: []*resource.Requirement{
{
Key: "key",
Operator: string(selection.Equals),
Values: []string{"value"},
},
},
},
},
wantPredicate: storage.SelectionPredicate{
Label: labels.SelectorFromSet(labels.Set{"key": "value"}),
},
wantErr: nil,
},
{
name: "with trash label",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
Predicate: storage.SelectionPredicate{
Label: labels.SelectorFromSet(labels.Set{utils.LabelKeyGetTrash: "true"}),
},
},
want: &resource.ListRequest{
VersionMatchV2: 1,
Source: resource.ListRequest_TRASH,
Options: &resource.ListOptions{
Labels: nil,
Fields: nil,
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
},
},
wantPredicate: storage.Everything,
wantErr: nil,
},
{
name: "with history label",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
Predicate: storage.SelectionPredicate{
Label: labels.SelectorFromSet(labels.Set{utils.LabelKeyGetHistory: "test-name"}),
},
},
want: &resource.ListRequest{
VersionMatchV2: 1,
Source: resource.ListRequest_HISTORY,
Options: &resource.ListOptions{
Labels: nil,
Fields: nil,
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
Name: "test-name",
},
},
},
wantPredicate: storage.Everything,
wantErr: nil,
},
{
name: "with fullpath label",
key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
opts: storage.ListOptions{
Predicate: storage.SelectionPredicate{
Label: labels.SelectorFromSet(labels.Set{utils.LabelGetFullpath: "true"}),
},
},
want: &resource.ListRequest{
VersionMatchV2: 1,
Options: &resource.ListOptions{
Labels: nil,
Fields: nil,
Key: &resource.ResourceKey{
Group: "test",
Resource: "test",
Namespace: "default",
},
},
},
wantPredicate: storage.Everything,
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, gotPredicate, err := toListRequest(tt.key, tt.opts)
if tt.wantErr != nil {
assert.Error(t, err)
assert.Equal(t, tt.wantErr.Error(), err.Error())
return
}
require.NoError(t, err)
assert.Equal(t, tt.want, got)
assert.Equal(t, tt.wantPredicate, gotPredicate)
})
}
}
+1 -1
View File
@@ -371,7 +371,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 {
if k == utils.LabelKeyGetHistory || k == utils.LabelKeyGetTrash || k == utils.LabelGetFullpath {
return nil, NewBadRequestError("can not save label: " + k)
}
}