Skip to content

Commit

Permalink
Unistore: refactor provisioning to work with folder service (grafana#…
Browse files Browse the repository at this point in the history
  • Loading branch information
filewalkwithme authored Jan 29, 2025
1 parent 6908f91 commit 20f02ec
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 77 deletions.
2 changes: 2 additions & 0 deletions pkg/apimachinery/identity/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ var serviceIdentityPermissions = getWildcardPermissions(
"dashboards:write",
"dashboards:create",
"datasources:read",
"alert.provisioning:write",
"alert.provisioning.secrets:read",
)

func IsServiceIdentity(ctx context.Context) bool {
Expand Down
2 changes: 2 additions & 0 deletions pkg/registry/apis/dashboard/legacysearcher/search_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
claims "github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"google.golang.org/grpc"
)
Expand Down Expand Up @@ -48,6 +49,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
Title: req.Query,
Limit: req.Limit,
// FolderUIDs: req.FolderUIDs,
Type: searchstore.TypeDashboard,
SignedInUser: user,
}

Expand Down
8 changes: 6 additions & 2 deletions pkg/registry/apis/dashboard/search_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import (
"google.golang.org/grpc"
)

/* Temporarily disabled search fallback while we add functionality
/*
Search Fallback was returning both Folders and Dashboards which resulted
in issues with rendering the Folder UI. Also, filters are not implemented
yet. For those reasons, we will be disabling Search Fallback for now
func TestSearchFallback(t *testing.T) {
t.Run("should hit legacy search handler on mode 0", func(t *testing.T) {
mockClient := &MockClient{}
Expand Down Expand Up @@ -171,7 +174,8 @@ func TestSearchFallback(t *testing.T) {
t.Fatalf("expected Search NOT to be called, but it was")
}
})
}*/
}
*/

func TestSearchHandlerFields(t *testing.T) {
// Create a mock client
Expand Down
2 changes: 1 addition & 1 deletion pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (s *Server) Init() error {
return err
}

return s.provisioningService.RunInitProvisioners(s.context)
return nil
}

// Run initializes and starts services. This will block until all services have
Expand Down
4 changes: 2 additions & 2 deletions pkg/services/folder/folderimpl/folder_unifiedstorage.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,9 @@ func (s *Service) getFolderByTitleFromApiServer(ctx context.Context, orgID int64
Key: folderkey,
Fields: []*resource.Requirement{
{
Key: resource.SEARCH_FIELD_TITLE,
Key: resource.SEARCH_FIELD_TITLE_SORT,
Operator: string(selection.In),
Values: []string{title},
Values: []string{strings.ToLower(title)},
},
},
Labels: []*resource.Requirement{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,7 +567,7 @@ func (r resourceClientMock) Search(ctx context.Context, in *resource.ResourceSea
}

if len(in.Options.Fields) > 0 &&
in.Options.Fields[0].Key == resource.SEARCH_FIELD_TITLE &&
in.Options.Fields[0].Key == resource.SEARCH_FIELD_TITLE_SORT &&
in.Options.Fields[0].Operator == "in" &&
len(in.Options.Fields[0].Values) > 0 &&
in.Options.Fields[0].Values[0] == "foo" {
Expand Down
7 changes: 5 additions & 2 deletions pkg/services/provisioning/alerting/rules_provisioner.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (prov *defaultAlertRuleProvisioner) Provision(ctx context.Context,
files []*AlertingFile) error {
for _, file := range files {
for _, group := range file.Groups {
u := provisionerUser(group.OrgID)
ctx, u := identity.WithServiceIdentitiy(ctx, group.OrgID)

folderUID, err := prov.getOrCreateFolderFullpath(ctx, group.FolderFullpath, group.OrgID)
if err != nil {
prov.logger.Error("failed to get or create folder", "folder", group.FolderFullpath, "org", group.OrgID, "err", err)
Expand Down Expand Up @@ -120,11 +121,13 @@ func (prov *defaultAlertRuleProvisioner) getOrCreateFolderFullpath(

func (prov *defaultAlertRuleProvisioner) getOrCreateFolderByTitle(
ctx context.Context, folderName string, orgID int64, parentUID *string) (string, error) {
ctx, user := identity.WithServiceIdentitiy(ctx, orgID)

cmd := &folder.GetFolderQuery{
Title: &folderName,
ParentUID: parentUID,
OrgID: orgID,
SignedInUser: provisionerUser(orgID),
SignedInUser: user,
}

cmdResult, err := prov.folderService.Get(ctx, cmd)
Expand Down
47 changes: 26 additions & 21 deletions pkg/services/provisioning/dashboards/file_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"sync"
"time"

"github.com/grafana/grafana/pkg/apimachinery/identity"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
Expand Down Expand Up @@ -147,6 +148,8 @@ func (fr *FileReader) isDatabaseAccessRestricted() bool {
// storeDashboardsInFolder saves dashboards from the filesystem on disk to the folder from config
func (fr *FileReader) storeDashboardsInFolder(ctx context.Context, filesFoundOnDisk map[string]os.FileInfo,
dashboardRefs map[string]*dashboards.DashboardProvisioning, usageTracker *usageTracker) error {
ctx, _ = identity.WithServiceIdentitiy(ctx, fr.Cfg.OrgID)

folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, fr.Cfg.Folder)
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
return fmt.Errorf("%w with name %q: %w", ErrGetOrCreateFolder, fr.Cfg.Folder, err)
Expand Down Expand Up @@ -177,6 +180,7 @@ func (fr *FileReader) storeDashboardsInFoldersFromFileStructure(ctx context.Cont
folderName = filepath.Base(dashboardsFolder)
}

ctx, _ = identity.WithServiceIdentitiy(ctx, fr.Cfg.OrgID)
folderID, folderUID, err := fr.getOrCreateFolder(ctx, fr.Cfg, fr.dashboardProvisioningService, folderName)
if err != nil && !errors.Is(err, ErrFolderNameMissing) {
return fmt.Errorf("%w with name %q from file system structure: %w", ErrGetOrCreateFolder, folderName, err)
Expand Down Expand Up @@ -342,38 +346,42 @@ func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, servic
return 0, "", ErrFolderNameMissing
}

// TODO use folder service instead
user, err := identity.GetRequester(ctx)
if err != nil {
return 0, "", err
}

metrics.MFolderIDsServiceCount.WithLabelValues(metrics.Provisioning).Inc()
cmd := &dashboards.GetDashboardQuery{
FolderID: util.Pointer(int64(0)), // nolint:staticcheck
OrgID: cfg.OrgID,
cmd := &folder.GetFolderQuery{
OrgID: cfg.OrgID,
SignedInUser: user,
}

if cfg.FolderUID != "" {
cmd.UID = cfg.FolderUID
cmd.UID = &cfg.FolderUID
} else {
// provisioning depends on unique names
//nolint:staticcheck
cmd.Title = &folderName
}

result, err := fr.dashboardStore.GetDashboard(ctx, cmd)

if err != nil && !errors.Is(err, dashboards.ErrDashboardNotFound) {
result, err := fr.folderService.Get(ctx, cmd)
if err != nil && !errors.Is(err, dashboards.ErrFolderNotFound) {
return 0, "", err
}

// dashboard folder not found. create one.
if errors.Is(err, dashboards.ErrDashboardNotFound) {
// set dashboard folderUid if given
if cfg.FolderUID == accesscontrol.GeneralFolderUID {
return 0, "", dashboards.ErrFolderInvalidUID
}
// do not allow the creation of folder with uid "general"
if result != nil && result.UID == accesscontrol.GeneralFolderUID {
return 0, "", dashboards.ErrFolderInvalidUID
}

// dashboard folder not found. create one.
if errors.Is(err, dashboards.ErrFolderNotFound) {
createCmd := &folder.CreateFolderCommand{
OrgID: cfg.OrgID,
UID: cfg.FolderUID,
Title: folderName,
OrgID: cfg.OrgID,
UID: cfg.FolderUID,
Title: folderName,
SignedInUser: user,
}

f, err := service.SaveFolderForProvisionedDashboards(ctx, createCmd)
Expand All @@ -385,10 +393,7 @@ func (fr *FileReader) getOrCreateFolder(ctx context.Context, cfg *config, servic
return f.ID, f.UID, nil
}

if !result.IsFolder {
return 0, "", fmt.Errorf("got invalid response. expected folder, found dashboard")
}

//nolint:staticcheck
return result.ID, result.UID, nil
}

Expand Down
Loading

0 comments on commit 20f02ec

Please sign in to comment.