Filewalkwithme/unistore refactor folder service to hit folder apiserver (#98409)

Refactor folder service to use Unified Storage

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

---------

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
Co-authored-by: Stephanie Hingtgen <stephanie.hingtgen@grafana.com>
This commit is contained in:
maicon
2025-01-13 18:15:35 -03:00
committed by GitHub
co-authored by Stephanie Hingtgen
parent 009d7f42b3
commit 766d645d82
38 changed files with 1900 additions and 73 deletions
+128 -19
View File
@@ -18,12 +18,14 @@ import (
"golang.org/x/exp/slices"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/events"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/dashboards/dashboardaccess"
"github.com/grafana/grafana/pkg/services/featuremgmt"
@@ -34,6 +36,7 @@ import (
"github.com/grafana/grafana/pkg/services/store/entity"
"github.com/grafana/grafana/pkg/services/supportbundles"
"github.com/grafana/grafana/pkg/services/user"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -41,12 +44,14 @@ const FULLPATH_SEPARATOR = "/"
type Service struct {
store folder.Store
unifiedStore folder.Store
db db.DB
log *slog.Logger
dashboardStore dashboards.Store
dashboardFolderStore folder.FolderStore
features featuremgmt.FeatureToggles
accessControl accesscontrol.AccessControl
k8sclient folderK8sHandler
// bus is currently used to publish event in case of folder full path change.
// For example when a folder is moved to another folder or when a folder is renamed.
bus bus.Bus
@@ -66,14 +71,24 @@ func ProvideService(
db db.DB, // DB for the (new) nested folder store
features featuremgmt.FeatureToggles,
supportBundles supportbundles.Service,
cfg *setting.Cfg,
r prometheus.Registerer,
tracer tracing.Tracer,
) *Service {
k8sHandler := &foldk8sHandler{
gvr: v0alpha1.FolderResourceInfo.GroupVersionResource(),
namespacer: request.GetNamespaceMapper(cfg),
cfg: cfg,
}
unifiedStore := ProvideUnifiedStore(cfg)
srv := &Service{
log: slog.Default().With("logger", "folder-service"),
dashboardStore: dashboardStore,
dashboardFolderStore: folderStore,
store: store,
unifiedStore: unifiedStore,
features: features,
accessControl: ac,
bus: bus,
@@ -81,6 +96,7 @@ func ProvideService(
registry: make(map[string]folder.RegistryService),
metrics: newFoldersMetrics(r),
tracer: tracer,
k8sclient: k8sHandler,
}
srv.DBMigration(db)
@@ -138,6 +154,13 @@ func (s *Service) DBMigration(db db.DB) {
}
func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.getFoldersFromApiServer(ctx, q)
}
return s.GetFoldersLegacy(ctx, q)
}
func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery) ([]*folder.Folder, error) {
if q.SignedInUser == nil {
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
}
@@ -190,6 +213,13 @@ func (s *Service) GetFolders(ctx context.Context, q folder.GetFoldersQuery) ([]*
}
func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.getFromApiServer(ctx, q)
}
return s.GetLegacy(ctx, q)
}
func (s *Service) GetLegacy(ctx context.Context, q *folder.GetFolderQuery) (*folder.Folder, error) {
if q.SignedInUser == nil {
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
}
@@ -278,13 +308,13 @@ func (s *Service) Get(ctx context.Context, q *folder.GetFolderQuery) (*folder.Fo
}
if s.features.IsEnabled(ctx, featuremgmt.FlagKubernetesFolders) {
f, err = s.setFullpath(ctx, f, q.SignedInUser)
f, err = s.setFullpath(ctx, f, q.SignedInUser, true)
}
return f, err
}
func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identity.Requester) (*folder.Folder, error) {
func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identity.Requester, forceLegacy bool) (*folder.Folder, error) {
// #TODO is some kind of intermediate conversion required as is the case with user id where
// it gets parsed using UserIdentifier(). Also is there some kind of validation taking place as
// part of the parsing?
@@ -297,10 +327,19 @@ func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identi
// Fetch the parent since the permissions for fetching the newly created folder
// are not yet present for the user--this requires a call to ClearUserPermissionCache
parents, err := s.GetParents(ctx, folder.GetParentsQuery{
UID: f.UID,
OrgID: f.OrgID,
})
var parents []*folder.Folder
var err error
if forceLegacy {
parents, err = s.GetParentsLegacy(ctx, folder.GetParentsQuery{
UID: f.UID,
OrgID: f.OrgID,
})
} else {
parents, err = s.GetParents(ctx, folder.GetParentsQuery{
UID: f.UID,
OrgID: f.OrgID,
})
}
if err != nil {
return nil, err
}
@@ -319,6 +358,13 @@ func (s *Service) setFullpath(ctx context.Context, f *folder.Folder, user identi
}
func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.getChildrenFromApiServer(ctx, q)
}
return s.GetChildrenLegacy(ctx, q)
}
func (s *Service) GetChildrenLegacy(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
defer func(t time.Time) {
parent := q.UID
if q.UID != folder.SharedWithMeFolderUID {
@@ -332,7 +378,7 @@ func (s *Service) GetChildren(ctx context.Context, q *folder.GetChildrenQuery) (
}
if s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) && q.UID == folder.SharedWithMeFolderUID {
return s.GetSharedWithMe(ctx, q)
return s.GetSharedWithMe(ctx, q, true)
}
if q.UID == "" {
@@ -460,14 +506,19 @@ func (s *Service) getRootFolders(ctx context.Context, q *folder.GetChildrenQuery
}
// GetSharedWithMe returns folders available to user, which cannot be accessed from the root folders
func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.Folder, error) {
start := time.Now()
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, q)
availableNonRootFolders, err := s.getAvailableNonRootFolders(ctx, q, forceLegacy)
if err != nil {
s.metrics.sharedWithMeFetchFoldersRequestsDuration.WithLabelValues("failure").Observe(time.Since(start).Seconds())
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders to which the user has explicit access: %w", err)
}
rootFolders, err := s.GetChildren(ctx, &folder.GetChildrenQuery{UID: "", OrgID: q.OrgID, SignedInUser: q.SignedInUser, Permission: q.Permission})
var rootFolders []*folder.Folder
if forceLegacy {
rootFolders, err = s.GetChildrenLegacy(ctx, &folder.GetChildrenQuery{UID: "", OrgID: q.OrgID, SignedInUser: q.SignedInUser, Permission: q.Permission})
} else {
rootFolders, err = s.GetChildren(ctx, &folder.GetChildrenQuery{UID: "", OrgID: q.OrgID, SignedInUser: q.SignedInUser, Permission: q.Permission})
}
if err != nil {
s.metrics.sharedWithMeFetchFoldersRequestsDuration.WithLabelValues("failure").Observe(time.Since(start).Seconds())
return nil, folder.ErrInternal.Errorf("failed to fetch root folders to which the user has access: %w", err)
@@ -478,7 +529,7 @@ func (s *Service) GetSharedWithMe(ctx context.Context, q *folder.GetChildrenQuer
return availableNonRootFolders, nil
}
func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetChildrenQuery) ([]*folder.Folder, error) {
func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetChildrenQuery, forceLegacy bool) ([]*folder.Folder, error) {
permissions := q.SignedInUser.GetPermissions()
var folderPermissions []string
if q.Permission == dashboardaccess.PERMISSION_EDIT {
@@ -507,13 +558,25 @@ func (s *Service) getAvailableNonRootFolders(ctx context.Context, q *folder.GetC
return nonRootFolders, nil
}
dashFolders, err := s.GetFolders(ctx, folder.GetFoldersQuery{
UIDs: folderUids,
OrgID: q.OrgID,
SignedInUser: q.SignedInUser,
OrderByTitle: true,
WithFullpathUIDs: true,
})
var dashFolders []*folder.Folder
var err error
if forceLegacy {
dashFolders, err = s.GetFoldersLegacy(ctx, folder.GetFoldersQuery{
UIDs: folderUids,
OrgID: q.OrgID,
SignedInUser: q.SignedInUser,
OrderByTitle: true,
WithFullpathUIDs: true,
})
} else {
dashFolders, err = s.GetFolders(ctx, folder.GetFoldersQuery{
UIDs: folderUids,
OrgID: q.OrgID,
SignedInUser: q.SignedInUser,
OrderByTitle: true,
WithFullpathUIDs: true,
})
}
if err != nil {
return nil, folder.ErrInternal.Errorf("failed to fetch subfolders: %w", err)
}
@@ -565,6 +628,13 @@ func (s *Service) deduplicateAvailableFolders(ctx context.Context, folders []*fo
}
func (s *Service) GetParents(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.getParentsFromApiServer(ctx, q)
}
return s.GetParentsLegacy(ctx, q)
}
func (s *Service) GetParentsLegacy(ctx context.Context, q folder.GetParentsQuery) ([]*folder.Folder, error) {
if !s.features.IsEnabled(ctx, featuremgmt.FlagNestedFolders) || q.UID == accesscontrol.GeneralFolderUID {
return nil, nil
}
@@ -591,6 +661,13 @@ func (s *Service) getFolderByTitle(ctx context.Context, orgID int64, title strin
}
func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.createOnApiServer(ctx, cmd)
}
return s.CreateLegacy(ctx, cmd)
}
func (s *Service) CreateLegacy(ctx context.Context, cmd *folder.CreateFolderCommand) (*folder.Folder, error) {
if cmd.SignedInUser == nil || cmd.SignedInUser.IsNil() {
return nil, folder.ErrBadRequest.Errorf("missing signed in user")
}
@@ -700,13 +777,23 @@ func (s *Service) Create(ctx context.Context, cmd *folder.CreateFolderCommand) (
}
if s.features.IsEnabled(ctx, featuremgmt.FlagKubernetesFolders) {
f, err = s.setFullpath(ctx, f, user)
f, err = s.setFullpath(ctx, f, user, true)
if err != nil {
return nil, err
}
}
return f, nil
}
func (s *Service) Update(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.updateOnApiServer(ctx, cmd)
}
return s.UpdateLegacy(ctx, cmd)
}
func (s *Service) UpdateLegacy(ctx context.Context, cmd *folder.UpdateFolderCommand) (*folder.Folder, error) {
ctx, span := s.tracer.Start(ctx, "folder.Update")
defer span.End()
@@ -839,6 +926,13 @@ func prepareForUpdate(dashFolder *dashboards.Dashboard, orgId int64, userId int6
}
func (s *Service) Delete(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.deleteFromApiServer(ctx, cmd)
}
return s.DeleteLegacy(ctx, cmd)
}
func (s *Service) DeleteLegacy(ctx context.Context, cmd *folder.DeleteFolderCommand) error {
if cmd.SignedInUser == nil {
return folder.ErrBadRequest.Errorf("missing signed in user")
}
@@ -922,6 +1016,13 @@ func (s *Service) legacyDelete(ctx context.Context, cmd *folder.DeleteFolderComm
}
func (s *Service) Move(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.moveOnApiServer(ctx, cmd)
}
return s.MoveLegacy(ctx, cmd)
}
func (s *Service) MoveLegacy(ctx context.Context, cmd *folder.MoveFolderCommand) (*folder.Folder, error) {
ctx, span := s.tracer.Start(ctx, "folder.Move")
defer span.End()
@@ -1142,6 +1243,14 @@ func (s *Service) nestedFolderDelete(ctx context.Context, cmd *folder.DeleteFold
}
func (s *Service) GetDescendantCounts(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
if s.features.IsEnabledGlobally(featuremgmt.FlagKubernetesFoldersServiceV2) {
return s.getDescendantCountsFromApiServer(ctx, q)
}
return s.GetDescendantCountsLegacy(ctx, q)
}
func (s *Service) GetDescendantCountsLegacy(ctx context.Context, q *folder.GetDescendantCountsQuery) (folder.DescendantCounts, error) {
if q.SignedInUser == nil {
return nil, folder.ErrBadRequest.Errorf("missing signed-in user")
}