From 9d5659bfba73565c335aae3ac92b13b439a886e4 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 2 Dec 2025 13:20:13 +0300 Subject: [PATCH] ensure folder annotation --- pkg/storage/unified/apistore/prepare.go | 28 +++++++++++++++++++------ pkg/storage/unified/apistore/store.go | 8 +++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/pkg/storage/unified/apistore/prepare.go b/pkg/storage/unified/apistore/prepare.go index 3691cb29bbd..43cfed7115d 100644 --- a/pkg/storage/unified/apistore/prepare.go +++ b/pkg/storage/unified/apistore/prepare.go @@ -21,6 +21,7 @@ import ( common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/apimachinery/utils" secrets "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" ) @@ -76,6 +77,20 @@ func (v *objectForStorage) finish(ctx context.Context, err error, secrets secret return nil } +func (s *Storage) verifyFolder(obj utils.GrafanaMetaAccessor) error { + if s.opts.EnableFolderSupport { + if obj.GetFolder() == "" { + // return apierrors.NewBadRequest("missing folder annotation") + // TODO?: should this be optionally be done in a mutation webhook? + obj.SetFolder(folder.GeneralFolderUID) // always enter something + return nil + } + } else if obj.GetFolder() != "" { + return apierrors.NewBadRequest("folders not supported in this resource") + } + return nil +} + // Called on create func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime.Object) (objectForStorage, error) { v := objectForStorage{} @@ -103,6 +118,9 @@ func (s *Storage) prepareObjectForStorage(ctx context.Context, newObject runtime if s.opts.MaximumNameLength > 0 && len(obj.GetName()) > s.opts.MaximumNameLength { return v, apierrors.NewBadRequest(fmt.Sprintf("name exceeds maximum length (%d)", s.opts.MaximumNameLength)) } + if err = s.verifyFolder(obj); err != nil { + return v, err + } v.grantPermissions = obj.GetAnnotation(utils.AnnoKeyGrantPermissions) if v.grantPermissions != "" { @@ -193,17 +211,15 @@ func (s *Storage) prepareObjectForUpdate(ctx context.Context, updateObject runti obj.SetDeprecatedInternalID(previousInternalID) // nolint:staticcheck } - err = prepareSecureValues(ctx, s.opts.SecureValues, obj, previous, &v) - if err != nil { + if err = prepareSecureValues(ctx, s.opts.SecureValues, obj, previous, &v); err != nil { + return v, err + } + if err = s.verifyFolder(obj); err != nil { return v, err } // Check if we should bump the generation if obj.GetFolder() != previous.GetFolder() { - if !s.opts.EnableFolderSupport { - return v, apierrors.NewBadRequest(fmt.Sprintf("folders are not supported for: %s", s.gr.String())) - } - // TODO: check that we can move the folder? v.hasChanged = true } else if obj.GetDeletionTimestamp() != nil && previous.GetDeletionTimestamp() == nil { v.hasChanged = true // bump generation when deleted diff --git a/pkg/storage/unified/apistore/store.go b/pkg/storage/unified/apistore/store.go index 5adf5e8b958..48f33bb81f4 100644 --- a/pkg/storage/unified/apistore/store.go +++ b/pkg/storage/unified/apistore/store.go @@ -38,6 +38,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/utils" grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" secrets "github.com/grafana/grafana/pkg/registry/apis/secret/contracts" + "github.com/grafana/grafana/pkg/services/folder" "github.com/grafana/grafana/pkg/storage/unified/resource" "github.com/grafana/grafana/pkg/storage/unified/resourcepb" ) @@ -209,6 +210,13 @@ func (s *Storage) convertToObject(ctx context.Context, data []byte, obj runtime. _, span := tracer.Start(ctx, "apistore.Storage.convertToObject") defer span.End() obj, _, err := s.codec.Decode(data, nil, obj) + // Replace empty folder with "general" on read (this was not a requirement early on) + if s.opts.EnableFolderSupport { + m, _ := utils.MetaAccessor(obj) + if m != nil && m.GetFolder() == "" { + m.SetFolder(folder.GeneralFolderUID) + } + } return obj, err }