Skip to content

Commit

Permalink
GDG Org refactoring
Browse files Browse the repository at this point in the history
Fixes #254

ChangeLog:
  gdg:
    - naming convention has shifted from org_<id> to org<name|slug>
    - organization_name replaces organization_id
  gdg-generate:
    - org_id deprecated in favor of organization_name
  • Loading branch information
safaci2000 committed Mar 4, 2024
1 parent e9f7d3b commit 2677db7
Show file tree
Hide file tree
Showing 57 changed files with 340 additions and 526 deletions.
19 changes: 13 additions & 6 deletions cli/backup/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ func newDashboardCommand() simplecobra.Commander {
Long: description,
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"dash", "dashboard"}
dashboard := cmd
dashboard.PersistentFlags().BoolVarP(&skipConfirmAction, "skip-confirmation", "", false, "when set to true, bypass confirmation prompts")
dashboard.PersistentFlags().StringP("dashboard", "d", "", "filter by dashboard slug")
dashboard.PersistentFlags().StringP("folder", "f", "", "Filter by Folder Name (Quotes in names not supported)")
dashboard.PersistentFlags().StringArrayP("tags", "t", []string{}, "Filter by list of comma delimited tags")
cmd.PersistentFlags().BoolVarP(&skipConfirmAction, "skip-confirmation", "", false, "when set to true, bypass confirmation prompts")
cmd.PersistentFlags().StringP("dashboard", "d", "", "filter by dashboard slug")
cmd.PersistentFlags().StringP("folder", "f", "", "Filter by Folder Name (Quotes in names not supported)")
cmd.PersistentFlags().StringArrayP("tags", "t", []string{}, "Filter by list of comma delimited tags")
},
CommandsList: []simplecobra.Commander{
newListDashboardsCmd(),
Expand Down Expand Up @@ -164,8 +163,16 @@ func newListDashboardsCmd() simplecobra.Commander {

filters := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...)
boards := rootCmd.GrafanaSvc().ListDashboards(filters)
orgInfo := rootCmd.GrafanaSvc().GetUserOrganization()
orgName := "unknown"
if orgInfo != nil {
orgName = orgInfo.Name
}

slog.Info("Listing dashboards for context", slog.String("context", config.Config().GetGDGConfig().GetContext()), slog.Any("count", len(boards)))
slog.Info("Listing dashboards for context",
slog.String("context", config.Config().GetGDGConfig().GetContext()),
slog.String("orgName", orgName),
slog.Any("count", len(boards)))
for _, link := range boards {
base, err := url.Parse(config.Config().GetDefaultGrafanaConfig().URL)
var baseHost string
Expand Down
11 changes: 8 additions & 3 deletions cli/backup/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/bep/simplecobra"
"github.com/esnet/gdg/cli/support"
"github.com/esnet/gdg/internal/config"
"github.com/gosimple/slug"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/spf13/cobra"
"log/slog"
Expand All @@ -20,6 +21,10 @@ func newOrganizationsCommand() simplecobra.Commander {
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.Aliases = []string{"org", "orgs"}
},
InitCFunc: func(cd *simplecobra.Commandeer, r *support.RootCommand) error {
r.GrafanaSvc().InitOrganizations()
return nil
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
return cd.CobraCommand.Help()
},
Expand All @@ -43,7 +48,7 @@ func newOrganizationsListCmd() simplecobra.Commander {
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
slog.Info("Listing organizations for context", "context", config.Config().GetGDGConfig().GetContext())
rootCmd.TableObj.AppendHeader(table.Row{"id", "org"})
rootCmd.TableObj.AppendHeader(table.Row{"id", "organization Name", "org slug ID"})
listOrganizations := rootCmd.GrafanaSvc().ListOrganizations()
sort.Slice(listOrganizations, func(a, b int) bool {
return listOrganizations[a].ID < listOrganizations[b].ID
Expand All @@ -52,7 +57,7 @@ func newOrganizationsListCmd() simplecobra.Commander {
slog.Info("No organizations found")
} else {
for _, org := range listOrganizations {
rootCmd.TableObj.AppendRow(table.Row{org.ID, org.Name})
rootCmd.TableObj.AppendRow(table.Row{org.ID, org.Name, slug.Make(org.Name)})
}
rootCmd.TableObj.Render()
}
Expand Down Expand Up @@ -97,7 +102,7 @@ func newOrganizationsUploadCmd() simplecobra.Commander {
cmd.Aliases = []string{"u"}
},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
slog.Info("Uploading Folders for context: '%s'", "context", config.Config().GetGDGConfig().GetContext())
slog.Info("Uploading Folders for context: ", "context", config.Config().GetGDGConfig().GetContext())
rootCmd.TableObj.AppendHeader(table.Row{"file"})
folders := rootCmd.GrafanaSvc().UploadOrganizations()
if len(folders) == 0 {
Expand Down
38 changes: 24 additions & 14 deletions cli/tools/organizations.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,22 +43,32 @@ func newOrgCommand() simplecobra.Commander {
func newSetOrgCmd() simplecobra.Commander {
return &support.SimpleCommand{
NameP: "set",
Short: "Set <OrgId>, 0 removes filter",
Long: "Set <OrgId>, 0 removes filter",
Short: "Set --orgId --orgName to set user Org",
Long: "Set --orgId --orgName to set user Org",
WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) {
cmd.PersistentFlags().StringP("orgName", "o", "", "Set user Org by Name (not slug)")
cmd.PersistentFlags().StringP("orgSlugName", "", "", "Set user Org by slug name")

},
RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error {
if len(args) < 1 {
return errors.New("requires an Org ID and name")
}
OrgId := args[0]
orgId, err := strconv.ParseInt(OrgId, 10, 64)
if err != nil {
log.Fatal("invalid Org ID, could not parse value to a numeric value")
}
err = rootCmd.GrafanaSvc().SetOrganization(orgId)
if err != nil {
log.Fatal("unable to set Org ID", "err", err)
orgName, _ := cd.CobraCommand.Flags().GetString("orgName")
slugName, _ := cd.CobraCommand.Flags().GetString("orgSlugName")
if orgName != "" || slugName != "" {
var useSlug = false
if slugName != "" {
useSlug = true
orgName = slugName
}
err := rootCmd.GrafanaSvc().SetOrganizationByName(orgName, useSlug)
if err != nil {
log.Fatal("unable to set Org ID, ", err.Error())
}
}
slog.Info("Successfully set Org ID for context", "context", config.Config().GetGDGConfig().GetContext())

rootCmd.GrafanaSvc().InitOrganizations()
userOrg := rootCmd.GrafanaSvc().GetUserOrganization()
slog.Info("New Org is now set to", slog.String("orgName", userOrg.Name))

return nil

},
Expand Down
4 changes: 1 addition & 3 deletions config/importer-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ contexts:
url: https://grafana.com
user_name: admin
password: admin
organization_id: 1
filter_override:
ignore_dashboard_filters: false # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on
watched:
Expand All @@ -95,14 +94,13 @@ contexts:
regex: ".*"
secure_data: "default.json"
url: https://staging.grafana.com
organization_id: 1
filter_override:
ignore_dashboard_filters: false # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on
watched:
- Folder1
- Folder2
watched_folders_override:
- organization_id: 0
- organization_name: "Some Other Org"
folders:
- General
- SpecialFolder
Expand Down
4 changes: 2 additions & 2 deletions config/templates-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ entities:
- template_name: template_example
output:
- folder: "General"
org_id: 2
organization_name: "Main Org."
dashboard_name: "Testing Foobar"
template_data:
Title: Bob Loves Candy
Expand All @@ -14,7 +14,7 @@ entities:
- lightbulb
- office lights
- folder: "Testing"
org_id: 3
organization_name: Some Other Org
dashboard_name: ""
template_data:
Title: Uncle McDonalds
Expand Down
4 changes: 2 additions & 2 deletions config/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contexts:
url: https://grafana.com
user_name: admin
password: admin
organization: your-org
organization_name: Your Org
ignore_filters: False # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on
watched:
- General
Expand All @@ -63,7 +63,7 @@ contexts:
regex: ".*"
secure_data: "default.json"
url: https://staging.grafana.com
organization: your-org
organization_name: Your Org
ignore_filters: False # When set to true all Watched filtered folders will be ignored and ALL folders will be acted on
watched:
- Folder1
Expand Down
1 change: 0 additions & 1 deletion internal/api/extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ func NewExtendedApi() *ExtendedApi {
o := ExtendedApi{
grafanaCfg: cfg,
}

return &o
}

Expand Down
20 changes: 20 additions & 0 deletions internal/api/orgs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package api

import (
"context"
"github.com/grafana/grafana-openapi-client-go/models"
"net/http"
)

func (extended *ExtendedApi) UserOrg() (int64, error) {
result := new(models.OrgDetailsDTO)
err := extended.getRequestBuilder().
Path("api/org").
ToJSON(result).
Method(http.MethodGet).Fetch(context.Background())
if err != nil {
return 0, err
}
return result.ID, err

}
13 changes: 6 additions & 7 deletions internal/config/config_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/gosimple/slug"
"github.com/grafana/grafana-openapi-client-go/models"
"github.com/tidwall/gjson"
"log/slog"
Expand All @@ -14,7 +15,6 @@ import (
type ResourceType string

const (
AlertNotificationResource = "alertnotifications"
ConnectionPermissionResource = "connections-permissions"
ConnectionResource = "connections"
DashboardResource = "dashboards"
Expand All @@ -30,7 +30,6 @@ const (
)

var orgNamespacedResource = map[ResourceType]bool{
AlertNotificationResource: true,
ConnectionPermissionResource: true,
ConnectionResource: true,
DashboardResource: true,
Expand All @@ -53,8 +52,8 @@ func (s *ResourceType) String() string {
// GetPath returns the path of the resource type, if Namespaced, will delimit the path by org Id
func (s *ResourceType) GetPath(basePath string) string {
if s.isNamespaced() {
orgId := Config().GetDefaultGrafanaConfig().GetOrganizationId()
return path.Join(basePath, fmt.Sprintf("%s_%d", OrganizationMetaResource, orgId), s.String())
orgName := slug.Make(Config().GetDefaultGrafanaConfig().GetOrganizationName())
return path.Join(basePath, fmt.Sprintf("%s_%s", OrganizationMetaResource, orgName), s.String())

}
return path.Join(basePath, s.String())
Expand Down Expand Up @@ -164,9 +163,9 @@ func (s *GrafanaConfig) GetPath(r ResourceType) string {
}

// GetOrgMonitoredFolders return the OrganizationMonitoredFolders that override a given Org
func (s *GrafanaConfig) GetOrgMonitoredFolders(orgId int64) []string {
func (s *GrafanaConfig) GetOrgMonitoredFolders(orgName string) []string {
for _, item := range s.MonitoredFoldersOverride {
if item.OrganizationId == orgId && len(item.Folders) > 0 {
if item.OrganizationName == orgName && len(item.Folders) > 0 {
return item.Folders
}
}
Expand All @@ -176,7 +175,7 @@ func (s *GrafanaConfig) GetOrgMonitoredFolders(orgId int64) []string {

// GetMonitoredFolders return a list of the monitored folders alternatively returns the "General" folder.
func (s *GrafanaConfig) GetMonitoredFolders() []string {
orgFolders := s.GetOrgMonitoredFolders(s.OrganizationId)
orgFolders := s.GetOrgMonitoredFolders(s.GetOrganizationName())
if len(orgFolders) > 0 {
return orgFolders
}
Expand Down
12 changes: 6 additions & 6 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,16 @@ func TestWatchedFoldersConfig(t *testing.T) {
grafanaConf := config.Config().GetDefaultGrafanaConfig()
assert.NotNil(t, grafanaConf)
grafanaConf.MonitoredFoldersOverride = []config.MonitoredOrgFolders{{
OrganizationId: 0,
Folders: []string{"General", "SpecialFolder"},
OrganizationName: "Your Org",
Folders: []string{"General", "SpecialFolder"},
}}
folders := grafanaConf.GetMonitoredFolders()
assert.True(t, slices.Contains(folders, "SpecialFolder"))
grafanaConf.OrganizationId = 2
grafanaConf.OrganizationName = "DumbDumb"
folders = grafanaConf.GetMonitoredFolders()
assert.False(t, slices.Contains(folders, "SpecialFolder"))
assert.True(t, slices.Contains(folders, "Folder2"))
grafanaConf.OrganizationId = 0
grafanaConf.OrganizationName = "Main Org."
grafanaConf.MonitoredFoldersOverride = nil
folders = grafanaConf.GetMonitoredFolders()
assert.False(t, slices.Contains(folders, "SpecialFolder"))
Expand Down Expand Up @@ -140,8 +140,8 @@ func validateGrafanaQA(t *testing.T, grafana *config.GrafanaConfig) {
folders := grafana.GetMonitoredFolders()
assert.True(t, funk.Contains(folders, "Folder1"))
assert.True(t, funk.Contains(folders, "Folder2"))
assert.Equal(t, "test/data/org_1/connections", grafana.GetPath(config.ConnectionResource))
assert.Equal(t, "test/data/org_1/dashboards", grafana.GetPath(config.DashboardResource))
assert.Equal(t, "test/data/org_your-org/connections", grafana.GetPath(config.ConnectionResource))
assert.Equal(t, "test/data/org_your-org/dashboards", grafana.GetPath(config.DashboardResource))
dsSettings := grafana.ConnectionSettings
request := models.AddDataSourceCommand{}
assert.Equal(t, len(grafana.ConnectionSettings.MatchingRules), 3)
Expand Down
30 changes: 16 additions & 14 deletions internal/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ import (
)

const (
ViperGdgConfig = "gdg"
ViperTemplateConfig = "template"
ViperGdgConfig = "gdg"
ViperTemplateConfig = "template"
DefaultOrganizationName = "Main Org."
DefaultOrganizationId = 1
)

type Configuration struct {
Expand All @@ -34,10 +36,10 @@ type TemplateDashboards struct {
}

type TemplateDashboardEntity struct {
Folder string `mapstructure:"folder"`
OrgId int64 `mapstructure:"org_id"`
DashboardName string `mapstructure:"dashboard_name"`
TemplateData map[string]interface{} `mapstructure:"template_data"`
Folder string `mapstructure:"folder"`
OrganizationName string `mapstructure:"organization_name"`
DashboardName string `mapstructure:"dashboard_name"`
TemplateData map[string]interface{} `mapstructure:"template_data"`
}

// AppGlobals is the global configuration for the application
Expand All @@ -63,7 +65,7 @@ type GrafanaConfig struct {
APIToken string `mapstructure:"token" yaml:"token"`
UserName string `mapstructure:"user_name" yaml:"user_name"`
Password string `mapstructure:"password" yaml:"password"`
OrganizationId int64 `mapstructure:"organization_id" yaml:"organization_id"`
OrganizationName string `mapstructure:"organization_name" yaml:"organization_name"`
MonitoredFoldersOverride []MonitoredOrgFolders `mapstructure:"watched_folders_override" yaml:"watched_folders_override"`
MonitoredFolders []string `mapstructure:"watched" yaml:"watched"`
ConnectionSettings *ConnectionSettings `mapstructure:"connections" yaml:"connections"`
Expand All @@ -72,16 +74,16 @@ type GrafanaConfig struct {
}

type MonitoredOrgFolders struct {
OrganizationId int64 `json:"organization_id" yaml:"organization_id"`
Folders []string `json:"folders" yaml:"folders"`
OrganizationName string `json:"organization_name" yaml:"organization_name"`
Folders []string `json:"folders" yaml:"folders"`
}

// GetOrganizationId returns the id of the organization (defaults to 1 if unset)
func (s *GrafanaConfig) GetOrganizationId() int64 {
if s.OrganizationId > 1 {
return s.OrganizationId
// GetOrganizationName returns the id of the organization (defaults to 1 if unset)
func (s *GrafanaConfig) GetOrganizationName() string {
if s.OrganizationName != "" {
return s.OrganizationName
}
return 1
return DefaultOrganizationName
}

// SetAdmin sets true if user has admin permissions
Expand Down
11 changes: 3 additions & 8 deletions internal/service/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,16 @@ func TestUserPath(t *testing.T) {
}
func TestBuildDashboardPath(t *testing.T) {
result := BuildResourceFolder("General", config.DashboardResource)
assert.Equal(t, "test/data/org_1/dashboards/General", result)
assert.Equal(t, "test/data/org_your-org/dashboards/General", result)
}

func TestBuildFolderSourcePath(t *testing.T) {
result := buildResourcePath(slug.Make("Some Folder"), config.FolderResource)
assert.Equal(t, "test/data/org_1/folders/some-folder.json", result)
assert.Equal(t, "test/data/org_your-org/folders/some-folder.json", result)

}

func TestBuildDataSourcePath(t *testing.T) {
result := buildResourcePath(slug.Make("My DS"), config.ConnectionResource)
assert.Equal(t, "test/data/org_1/connections/my-ds.json", result)
}

func TestBuildAlertNotificationPath(t *testing.T) {
result := buildResourcePath("SomeNotification", config.AlertNotificationResource)
assert.Equal(t, "test/data/org_1/alertnotifications/SomeNotification.json", result)
assert.Equal(t, "test/data/org_your-org/connections/my-ds.json", result)
}

0 comments on commit 2677db7

Please sign in to comment.