Skip to content

Commit

Permalink
Replace vendored dashboard with dashboards.Dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobsonMT committed Sep 6, 2023
1 parent 149ec38 commit d023023
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 154 deletions.
108 changes: 0 additions & 108 deletions pkg/services/ngalert/migration/dashboard.go

This file was deleted.

38 changes: 19 additions & 19 deletions pkg/services/ngalert/migration/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ type folderHelper struct {

// getOrCreateGeneralFolder returns the general folder under the specific organisation
// If the general folder does not exist it creates it.
func (m *folderHelper) getOrCreateGeneralFolder(ctx context.Context, orgID int64) (*dashboard, error) {
func (m *folderHelper) getOrCreateGeneralFolder(ctx context.Context, orgID int64) (*dashboards.Dashboard, error) {
// there is a unique constraint on org_id, folder_id, title
// there are no nested folders so the parent folder id is always 0
dashboard := dashboard{OrgId: orgID, FolderId: 0, Title: GENERAL_FOLDER}
dashboard := dashboards.Dashboard{OrgID: orgID, FolderID: 0, Title: GENERAL_FOLDER}
err := m.store.WithDbSession(ctx, func(sess *db.Session) error {
has, err := sess.Get(&dashboard)
if err != nil {
Expand All @@ -77,25 +77,25 @@ func (m *folderHelper) getOrCreateGeneralFolder(ctx context.Context, orgID int64
return &dashboard, nil
}

func (m *folderHelper) createGeneralFolder(ctx context.Context, orgID int64) (*dashboard, error) {
func (m *folderHelper) createGeneralFolder(ctx context.Context, orgID int64) (*dashboards.Dashboard, error) {
return m.createFolder(ctx, orgID, GENERAL_FOLDER)
}

// returns the folder of the given dashboard (if exists)
func (m *folderHelper) getFolder(ctx context.Context, dash dashboard) (dashboard, error) {
func (m *folderHelper) getFolder(ctx context.Context, dash dashboards.Dashboard, da dashAlert) (dashboards.Dashboard, error) {
// get folder if exists
folder := dashboard{}
if dash.FolderId > 0 {
folder := dashboards.Dashboard{}
if dash.FolderID > 0 {
err := m.store.WithDbSession(ctx, func(sess *db.Session) error {
exists, err := sess.Where("id=?", dash.FolderId).Get(&folder)
exists, err := sess.Where("id=?", dash.FolderID).Get(&folder)
if err != nil {
return fmt.Errorf("failed to get folder %d: %w", dash.FolderId, err)
return fmt.Errorf("failed to get folder %d: %w", dash.FolderID, err)
}
if !exists {
return fmt.Errorf("folder with id %v not found", dash.FolderId)
return fmt.Errorf("folder with id %v not found", dash.FolderID)
}
if !folder.IsFolder {
return fmt.Errorf("id %v is a dashboard not a folder", dash.FolderId)
return fmt.Errorf("id %v is a dashboard not a folder", dash.FolderID)
}
return nil
})
Expand All @@ -108,22 +108,22 @@ func (m *folderHelper) getFolder(ctx context.Context, dash dashboard) (dashboard

// based on sqlstore.saveDashboard()
// it should be called from inside a transaction
func (m *folderHelper) createFolder(ctx context.Context, orgID int64, title string) (*dashboard, error) {
var dash *dashboard
func (m *folderHelper) createFolder(ctx context.Context, orgID int64, title string) (*dashboards.Dashboard, error) {
var dash *dashboards.Dashboard
err := m.store.WithDbSession(ctx, func(sess *db.Session) error {
cmd := saveFolderCommand{
OrgId: orgID,
FolderId: 0,
cmd := dashboards.SaveDashboardCommand{
OrgID: orgID,
FolderID: 0,
IsFolder: true,
Dashboard: simplejson.NewFromAny(map[string]any{
"title": title,
}),
}
dash = cmd.getDashboardModel()
dash.setUid(util.GenerateShortUID())
dash = cmd.GetDashboardModel()
dash.SetUID(util.GenerateShortUID())

parentVersion := dash.Version
dash.setVersion(1)
dash.SetVersion(1)
dash.Created = time.Now()
dash.CreatedBy = FOLDER_CREATED_BY
dash.Updated = time.Now()
Expand All @@ -135,7 +135,7 @@ func (m *folderHelper) createFolder(ctx context.Context, orgID int64, title stri
}

dashVersion := &dashver.DashboardVersion{
DashboardID: dash.Id,
DashboardID: dash.ID,
ParentVersion: parentVersion,
RestoredFrom: cmd.RestoredFrom,
Version: dash.Version,
Expand Down
43 changes: 22 additions & 21 deletions pkg/services/ngalert/migration/ualert.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/dashboards"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/grafana/grafana/pkg/services/secrets"
Expand Down Expand Up @@ -107,27 +108,27 @@ func (m *migration) Exec(ctx context.Context) error {
}

// cache for folders created for dashboards that have custom permissions
folderCache := make(map[string]*dashboard)
folderCache := make(map[string]*dashboards.Dashboard)
// cache for the general folders
generalFolderCache := make(map[int64]*dashboard)
generalFolderCache := make(map[int64]*dashboards.Dashboard)

folderHelper := folderHelper{
store: m.store,
dialect: m.dialect,
}

gf := func(dash dashboard, da dashAlert) (*dashboard, error) {
f, ok := generalFolderCache[dash.OrgId]
gf := func(dash dashboards.Dashboard, da dashAlert) (*dashboards.Dashboard, error) {
f, ok := generalFolderCache[dash.OrgID]
if !ok {
// get or create general folder
f, err = folderHelper.getOrCreateGeneralFolder(ctx, dash.OrgId)
f, err = folderHelper.getOrCreateGeneralFolder(ctx, dash.OrgID)
if err != nil {
return nil, MigrationError{
Err: fmt.Errorf("failed to get or create general folder under organisation %d: %w", dash.OrgId, err),
Err: fmt.Errorf("failed to get or create general folder under organisation %d: %w", dash.OrgID, err),
AlertId: da.Id,
}
}
generalFolderCache[dash.OrgId] = f
generalFolderCache[dash.OrgID] = f
}
// No need to assign default permissions to general folder
// because they are included to the query result if it's a folder with no permissions
Expand All @@ -149,7 +150,7 @@ func (m *migration) Exec(ctx context.Context) error {
da.DashboardUID = dashIDMap[[2]int64{da.OrgId, da.DashboardId}]

// get dashboard
dash := dashboard{}
dash := dashboards.Dashboard{}
err = m.store.WithDbSession(ctx, func(sess *db.Session) error {
exists, err := sess.Where("org_id=? AND uid=?", da.OrgId, da.DashboardUID).Get(&dash)
if err != nil {
Expand All @@ -170,44 +171,44 @@ func (m *migration) Exec(ctx context.Context) error {
return err
}

var folder *dashboard
var folder *dashboards.Dashboard
switch {
case dash.HasACL:
folderName := getAlertFolderNameFromDashboard(&dash)
f, ok := folderCache[folderName]
if !ok {
l.Info("create a new folder for alerts that belongs to dashboard because it has custom permissions", "folder", folderName)
// create folder and assign the permissions of the dashboard (included default and inherited)
f, err = folderHelper.createFolder(ctx, dash.OrgId, folderName)
f, err = folderHelper.createFolder(ctx, dash.OrgID, folderName)
if err != nil {
return MigrationError{
Err: fmt.Errorf("failed to create folder: %w", err),
AlertId: da.Id,
}
}
permissions, err := folderHelper.getACL(ctx, dash.OrgId, dash.Id)
permissions, err := folderHelper.getACL(ctx, dash.OrgID, dash.ID)
if err != nil {
return MigrationError{
Err: fmt.Errorf("failed to get dashboard %d under organisation %d permissions: %w", dash.Id, dash.OrgId, err),
Err: fmt.Errorf("failed to get dashboard %d under organisation %d permissions: %w", dash.ID, dash.OrgID, err),
AlertId: da.Id,
}
}
err = folderHelper.setACL(ctx, f.OrgId, f.Id, permissions)
err = folderHelper.setACL(ctx, f.OrgID, f.ID, permissions)
if err != nil {
return MigrationError{
Err: fmt.Errorf("failed to set folder %d under organisation %d permissions: %w", f.Id, f.OrgId, err),
Err: fmt.Errorf("failed to set folder %d under organisation %d permissions: %w", f.ID, f.OrgID, err),
AlertId: da.Id,
}
}
folderCache[folderName] = f
}
folder = f
case dash.FolderId > 0:
case dash.FolderID > 0:
// get folder if exists
f, err := folderHelper.getFolder(ctx, dash)
if err != nil {
// If folder does not exist then the dashboard is an orphan and we migrate the alert to the general folder.
l.Warn("Failed to find folder for dashboard. Migrate rule to the default folder", "rule_name", da.Name, "dashboard_uid", da.DashboardUID, "missing_folder_id", dash.FolderId)
l.Warn("Failed to find folder for dashboard. Migrate rule to the default folder", "rule_name", da.Name, "dashboard_uid", da.DashboardUID, "missing_folder_id", dash.FolderID)
folder, err = gf(dash, da)
if err != nil {
return err
Expand All @@ -222,13 +223,13 @@ func (m *migration) Exec(ctx context.Context) error {
}
}

if folder.Uid == "" {
if folder.UID == "" {
return MigrationError{
Err: fmt.Errorf("empty folder identifier"),
AlertId: da.Id,
}
}
rule, err := m.makeAlertRule(l, *newCond, da, folder.Uid)
rule, err := m.makeAlertRule(l, *newCond, da, folder.UID)
if err != nil {
return fmt.Errorf("failed to migrate alert rule '%s' [ID:%d, DashboardUID:%s, orgID:%d]: %w", da.Name, da.Id, da.DashboardUID, da.OrgId, err)
}
Expand Down Expand Up @@ -347,13 +348,13 @@ func (m *migration) validateAlertmanagerConfig(config *apimodels.PostableUserCon

// getAlertFolderNameFromDashboard generates a folder name for alerts that belong to a dashboard. Formats the string according to DASHBOARD_FOLDER format.
// If the resulting string exceeds the migrations.MaxTitleLength, the dashboard title is stripped to be at the maximum length
func getAlertFolderNameFromDashboard(dash *dashboard) string {
maxLen := MaxFolderName - len(fmt.Sprintf(DASHBOARD_FOLDER, "", dash.Uid))
func getAlertFolderNameFromDashboard(dash *dashboards.Dashboard) string {
maxLen := MaxFolderName - len(fmt.Sprintf(DASHBOARD_FOLDER, "", dash.UID))
title := dash.Title
if len(title) > maxLen {
title = title[:maxLen]
}
return fmt.Sprintf(DASHBOARD_FOLDER, title, dash.Uid) // include UID to the name to avoid collision
return fmt.Sprintf(DASHBOARD_FOLDER, title, dash.UID) // include UID to the name to avoid collision
}

// uidSet is a wrapper around map[string]struct{} and util.GenerateShortUID() which aims help generate uids in quick
Expand Down
13 changes: 7 additions & 6 deletions pkg/services/ngalert/migration/ualert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/stretchr/testify/require"

"github.com/grafana/grafana/pkg/services/dashboards"
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
"github.com/grafana/grafana/pkg/util"
)
Expand Down Expand Up @@ -102,13 +103,13 @@ const invalidUri = "�6�M��)uk譹1(�h`$�o�N>mĕ����cS2�dh

func Test_getAlertFolderNameFromDashboard(t *testing.T) {
t.Run("should include full title", func(t *testing.T) {
dash := &dashboard{
Uid: util.GenerateShortUID(),
dash := &dashboards.Dashboard{
UID: util.GenerateShortUID(),
Title: "TEST",
}
folder := getAlertFolderNameFromDashboard(dash)
require.Contains(t, folder, dash.Title)
require.Contains(t, folder, dash.Uid)
require.Contains(t, folder, dash.UID)
})
t.Run("should cut title to the length", func(t *testing.T) {
title := ""
Expand All @@ -120,13 +121,13 @@ func Test_getAlertFolderNameFromDashboard(t *testing.T) {
}
}

dash := &dashboard{
Uid: util.GenerateShortUID(),
dash := &dashboards.Dashboard{
UID: util.GenerateShortUID(),
Title: title,
}
folder := getAlertFolderNameFromDashboard(dash)
require.Len(t, folder, MaxFolderName)
require.Contains(t, folder, dash.Uid)
require.Contains(t, folder, dash.UID)
})
}

Expand Down

0 comments on commit d023023

Please sign in to comment.