Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a clear_output to remove previous backup version #335

Merged
merged 2 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cli/backup/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@
"continue (y/n) ", strings.Join(config.Config().GetDefaultGrafanaConfig().GetMonitoredFolders(), ", "),
), "", true)
}
rootCmd.GrafanaSvc().UploadDashboards(filter)
err := rootCmd.GrafanaSvc().UploadDashboards(filter)
if err != nil {
return err
}

Check warning on line 114 in cli/backup/dashboard.go

View check run for this annotation

Codecov / codecov/patch

cli/backup/dashboard.go#L111-L114

Added lines #L111 - L114 were not covered by tests

rootCmd.TableObj.AppendHeader(table.Row{"Title", "id", "folder", "UID"})
boards := rootCmd.GrafanaSvc().ListDashboards(filter)
Expand Down
1 change: 1 addition & 0 deletions config/importer-example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ contexts:
global:
debug: true
api_debug: false
clear_output: true
ignore_ssl_errors: false ##when set to true will ignore invalid SSL errors
retry_count: 3 ## Will retry any failed API request up to 3 times.
retry_delay: 5s ## will wait for specified duration before trying again.
Expand Down
1 change: 1 addition & 0 deletions internal/config/globals.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type AppGlobals struct {
IgnoreSSLErrors bool `mapstructure:"ignore_ssl_errors" yaml:"ignore_ssl_errors"`
RetryCount int `mapstructure:"retry_count" yaml:"retry_count"`
RetryDelay string `mapstructure:"retry_delay" yaml:"retry_delay"`
ClearOutput bool `mapstructure:"clear_output" yaml:"clear_output"`
retryTimeout *time.Duration `mapstructure:"-" yaml:"-"`
}

Expand Down
8 changes: 5 additions & 3 deletions internal/service/alerting.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"log"
"log/slog"

"github.com/esnet/gdg/internal/storage"

"github.com/samber/lo"

"github.com/esnet/gdg/internal/tools/ptr"
Expand Down Expand Up @@ -51,7 +53,7 @@ func (s *DashNGoImpl) DownloadContactPoints() (string, error) {
return item.Name != emailReceiver
})

dsPath := buildResourcePath("contacts", config.AlertingResource, s.isLocal())
dsPath := buildResourcePath("contacts", config.AlertingResource, s.isLocal(), s.globalConf.ClearOutput)
if dsPacked, err = json.MarshalIndent(payload.ContactPoints, "", " "); err != nil {
return "", fmt.Errorf("unable to serialize data to JSON. %w", err)
}
Expand All @@ -63,7 +65,7 @@ func (s *DashNGoImpl) DownloadContactPoints() (string, error) {
}

func (s *DashNGoImpl) isLocal() bool {
return s.storage.Name() == "LocalStorage"
return s.storage.Name() == storage.LocalStorageType.String()
}

func (s *DashNGoImpl) UploadContactPoints() ([]string, error) {
Expand All @@ -82,7 +84,7 @@ func (s *DashNGoImpl) UploadContactPoints() ([]string, error) {
m[i.UID] = currentContacts[ndx]
}

fileLocation := buildResourcePath("contacts", config.AlertingResource, s.isLocal())
fileLocation := buildResourcePath("contacts", config.AlertingResource, s.isLocal(), false)
if rawDS, err = s.storage.ReadFile(fileLocation); err != nil {
return nil, fmt.Errorf("failed to read file. file: %s, err: %w", fileLocation, err)
}
Expand Down
8 changes: 4 additions & 4 deletions internal/service/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,22 +58,22 @@ func getFolderFromResourcePath(filePath string, resourceType config.ResourceType
return "", errors.New("unable to parse resource to retrieve folder name")
}

func BuildResourceFolder(folderName string, resourceType config.ResourceType, createDestination bool) string {
func BuildResourceFolder(folderName string, resourceType config.ResourceType, createDestination bool, clearOutput bool) string {
if resourceType == config.DashboardResource && folderName == "" {
folderName = DefaultFolderName
}

v := fmt.Sprintf("%s/%s", config.Config().GetDefaultGrafanaConfig().GetPath(resourceType), folderName)
if createDestination {
tools.CreateDestinationPath(v)
tools.CreateDestinationPath(config.Config().GetDefaultGrafanaConfig().GetPath(resourceType), clearOutput, v)
}
return v
}

func buildResourcePath(folderName string, resourceType config.ResourceType, createDestination bool) string {
func buildResourcePath(folderName string, resourceType config.ResourceType, createDestination bool, clearOutput bool) string {
v := fmt.Sprintf("%s/%s.json", config.Config().GetDefaultGrafanaConfig().GetPath(resourceType), folderName)
if createDestination {
tools.CreateDestinationPath(filepath.Dir(v))
tools.CreateDestinationPath(config.Config().GetDefaultGrafanaConfig().GetPath(resourceType), clearOutput, filepath.Dir(v))
}
return v
}
8 changes: 4 additions & 4 deletions internal/service/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,25 @@ func TestSlug(t *testing.T) {

func TestUserPath(t *testing.T) {
fixEnvironment(t)
userPath := BuildResourceFolder("", config.UserResource, false)
userPath := BuildResourceFolder("", config.UserResource, false, false)
assert.Equal(t, "test/data/users/", userPath)
}

func TestBuildDashboardPath(t *testing.T) {
fixEnvironment(t)
result := BuildResourceFolder("General", config.DashboardResource, false)
result := BuildResourceFolder("General", config.DashboardResource, false, false)
assert.Equal(t, "test/data/org_your-org/dashboards/General", result)
}

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

func TestBuildDataSourcePath(t *testing.T) {
fixEnvironment(t)

result := buildResourcePath(slug.Make("My DS"), config.ConnectionResource, false)
result := buildResourcePath(slug.Make("My DS"), config.ConnectionResource, false, false)
assert.Equal(t, "test/data/org_your-org/connections/my-ds.json", result)
}
2 changes: 1 addition & 1 deletion internal/service/connection_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
slog.Error("unable to marshall json ", "err", err.Error(), "connectionName", connection.Connection.Name)
continue
}
dsPath := buildResourcePath(slug.Make(connection.Connection.Name), config.ConnectionPermissionResource, s.isLocal())
dsPath := buildResourcePath(slug.Make(connection.Connection.Name), config.ConnectionPermissionResource, s.isLocal(), s.globalConf.ClearOutput)

Check warning on line 80 in internal/service/connection_permissions.go

View check run for this annotation

Codecov / codecov/patch

internal/service/connection_permissions.go#L80

Added line #L80 was not covered by tests
if err = s.storage.WriteFile(dsPath, dsPacked); err != nil {
slog.Error("unable to write file. ", "filename", slug.Make(connection.Connection.Name), "error", err.Error())
} else {
Expand Down
2 changes: 1 addition & 1 deletion internal/service/connections.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (s *DashNGoImpl) DownloadConnections(filter filters.Filter) []string {
continue
}

dsPath := buildResourcePath(slug.Make(ds.Name), config.ConnectionResource, s.isLocal())
dsPath := buildResourcePath(slug.Make(ds.Name), config.ConnectionResource, s.isLocal(), s.globalConf.ClearOutput)

if err = s.storage.WriteFile(dsPath, dsPacked); err != nil {
slog.Error("Unable to write file", "filename", slug.Make(ds.Name), "err", err)
Expand Down
2 changes: 1 addition & 1 deletion internal/service/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type ConnectionPermissions interface {
type DashboardsApi interface {
ListDashboards(filter filters.Filter) []*models.Hit
DownloadDashboards(filter filters.Filter) []string
UploadDashboards(filter filters.Filter)
UploadDashboards(filter filters.Filter) error
DeleteAllDashboards(filter filters.Filter) []string
LintDashboards(req types.LintRequest) []string
}
Expand Down
2 changes: 1 addition & 1 deletion internal/service/dashboard_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
continue
}

dsPath := fmt.Sprintf("%s/%s.json", BuildResourceFolder(link.Dashboard.FolderTitle, config.DashboardPermissionsResource, s.isLocal()), slug.Make(link.Dashboard.Title))
dsPath := fmt.Sprintf("%s/%s.json", BuildResourceFolder(link.Dashboard.FolderTitle, config.DashboardPermissionsResource, s.isLocal(), s.globalConf.ClearOutput), slug.Make(link.Dashboard.Title))

Check warning on line 62 in internal/service/dashboard_permissions.go

View check run for this annotation

Codecov / codecov/patch

internal/service/dashboard_permissions.go#L62

Added line #L62 was not covered by tests
if err = s.storage.WriteFile(dsPath, dsPacked); err != nil {
slog.Error("unable to write file. ", "filename", slug.Make(link.Dashboard.Title), "error", err.Error())
} else {
Expand Down
17 changes: 9 additions & 8 deletions internal/service/dashboards.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@
continue
}

fileName := buildDashboardFileName(link, metaData.GetPayload().Meta.Slug, folderUidMap, useNestedFolders, s.grafanaConf.Storage == "")
fileName := buildDashboardFileName(link, metaData.GetPayload().Meta.Slug, folderUidMap, useNestedFolders, s.isLocal(), s.globalConf.ClearOutput)
if err = s.storage.WriteFile(fileName, pretty.Pretty(rawBoard)); err != nil {
slog.Error("Unable to save dashboard to file\n", "err", err, "dashboard", metaData.GetPayload().Meta.Slug)
} else {
Expand Down Expand Up @@ -406,14 +406,14 @@
}

// buildDashboardFileName for a given dashboard, a full nested folder path is constructed
func buildDashboardFileName(db *models.Hit, dbSlug string, folderUidMap map[string]*customTypes.FolderDetails, nested, createDestination bool) string {
func buildDashboardFileName(db *models.Hit, dbSlug string, folderUidMap map[string]*customTypes.FolderDetails, nested, createDestination, clearOutput bool) string {
var folderPath string
if nested {
folderPath = getNestedFolder(db.FolderTitle, db.FolderUID, folderUidMap)
} else {
folderPath = db.FolderTitle
}
fileName := fmt.Sprintf("%s/%s.json", BuildResourceFolder(folderPath, config.DashboardResource, createDestination), dbSlug)
fileName := fmt.Sprintf("%s/%s.json", BuildResourceFolder(folderPath, config.DashboardResource, createDestination, clearOutput), dbSlug)
return fileName
}

Expand Down Expand Up @@ -482,7 +482,7 @@

// UploadDashboards finds all the dashboards in the configured location and exports them to grafana.
// if the folder doesn't exist, it'll be created.
func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) {
func (s *DashNGoImpl) UploadDashboards(filterReq filters.Filter) error {
var (
rawBoard []byte
folderName string
Expand All @@ -491,13 +491,13 @@
dashboardPath := config.Config().GetDefaultGrafanaConfig().GetPath(config.DashboardResource)
filesInDir, err := s.storage.FindAllFiles(dashboardPath, true)
if err != nil {
log.Fatalf("unable to find any files to export from storage engine, err: %v", err)
return fmt.Errorf("unable to find any files to export from storage engine, err: %w", err)

Check warning on line 494 in internal/service/dashboards.go

View check run for this annotation

Codecov / codecov/patch

internal/service/dashboards.go#L494

Added line #L494 was not covered by tests
}

if !s.grafanaConf.GetDashboardSettings().NestedFolders {
p, regexErr := regexp.Compile(nestedDashboardRegexFilter)
if regexErr != nil {
log.Fatal("unable to compile nested folder validation regex patter")
return fmt.Errorf("unable to compile nested folder validation regex patter, err: %w", regexErr)

Check warning on line 500 in internal/service/dashboards.go

View check run for this annotation

Codecov / codecov/patch

internal/service/dashboards.go#L500

Added line #L500 was not covered by tests
}

invalidCount := lo.FilterMap(filesInDir, func(file string, index int) (string, bool) {
Expand All @@ -521,7 +521,7 @@
return file, false
})
if len(invalidCount) > 0 {
log.Fatal("nested folder feature is disabled in GDG but import dashboardPath contains a nested folder. Please fix the import or configuration. ", slog.String("files", strings.Join(invalidCount, ", ")))
return fmt.Errorf("nested folder feature is disabled in GDG but import dashboardPath contains a nested folder. Please fix the import or configuration. Files: %s", strings.Join(invalidCount, ", "))

Check warning on line 524 in internal/service/dashboards.go

View check run for this annotation

Codecov / codecov/patch

internal/service/dashboards.go#L524

Added line #L524 was not covered by tests
}

}
Expand Down Expand Up @@ -584,7 +584,7 @@

}
if _, ok := alreadyProcessed[board["uid"]]; ok {
log.Fatalf("Board with same UID was already processed. Please check your backup folder. This may occur if you pulled the data multiple times with configuration of: nested folder enabled and disabled, uid: %v, title: %v", board["uid"], slug.Make((board["title"]).(string)))
return fmt.Errorf("Board with same UID was already processed. Please check your backup folder. This may occur if you pulled the data multiple times with configuration of: nested folder enabled and disabled, uid: %v, title: %v", board["uid"], slug.Make((board["title"]).(string)))

Check warning on line 587 in internal/service/dashboards.go

View check run for this annotation

Codecov / codecov/patch

internal/service/dashboards.go#L587

Added line #L587 was not covered by tests
} else {
alreadyProcessed[board["uid"]] = true
}
Expand Down Expand Up @@ -641,6 +641,7 @@
}

}
return nil
}

// DeleteAllDashboards clears all current dashboards being monitored. Any folder not white listed
Expand Down
8 changes: 4 additions & 4 deletions internal/service/folders.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
if fileName == "" {
fileName = folder.Title
}
dsPath := buildResourcePath(slug.Make(fileName), config.FolderPermissionResource, s.isLocal())
dsPath := buildResourcePath(slug.Make(fileName), config.FolderPermissionResource, s.isLocal(), s.globalConf.ClearOutput)
if err = s.storage.WriteFile(dsPath, dsPacked); err != nil {
slog.Error("Unable to write file", "err", err.Error(), "filename", slug.Make(folder.Title))
} else {
Expand Down Expand Up @@ -240,7 +240,7 @@
slog.Error("Unable to serialize data to JSON", "err", err, "folderName", folder.Title)
continue
}
dsPath := buildResourcePath(folder.Title, config.FolderResource, s.isLocal())
dsPath := buildResourcePath(folder.Title, config.FolderResource, s.isLocal(), s.globalConf.ClearOutput)

if !s.checkFolderName(folder.Title) {
slog.Warn("Folder has an invalid character and is not supported, skipping folder", "folderName", folder.Title)
Expand All @@ -252,8 +252,8 @@
if slugFolder != folder.NestedPath {
dsPath = strings.Replace(dsPath, slugFolder, folder.NestedPath, 1)
baseFolder := filepath.Dir(dsPath)
if s.isLocal() {
tools.CreateDestinationPath(baseFolder)
if s.isLocal() { //&& baseFolder != "" {
tools.CreateDestinationPath("", false, baseFolder)

Check warning on line 256 in internal/service/folders.go

View check run for this annotation

Codecov / codecov/patch

internal/service/folders.go#L255-L256

Added lines #L255 - L256 were not covered by tests
}
}
}
Expand Down
14 changes: 4 additions & 10 deletions internal/service/gdg_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/esnet/gdg/internal/api"
"github.com/esnet/gdg/internal/config"
"github.com/esnet/gdg/internal/storage"
"github.com/spf13/viper"
)

var (
Expand All @@ -20,12 +19,9 @@ var (
)

type DashNGoImpl struct {
extended *api.ExtendedApi

extended *api.ExtendedApi
grafanaConf *config.GrafanaConfig
configRef *viper.Viper
debug bool
apiDebug bool
globalConf *config.AppGlobals
storage storage.Storage
}

Expand All @@ -35,16 +31,14 @@ var DefaultConfigProvider config.Provider = func() *config.Configuration {

func setupConfigData(cfg *config.Configuration, obj *DashNGoImpl) {
obj.grafanaConf = cfg.GetDefaultGrafanaConfig()
obj.configRef = cfg.GetViperConfig()
obj.debug = cfg.IsDebug()
obj.apiDebug = cfg.IsApiDebug()
obj.globalConf = cfg.GetGDGConfig().GetAppGlobals()
}

func newInstance() *DashNGoImpl {
obj := &DashNGoImpl{}
setupConfigData(config.Config(), obj)

if obj.apiDebug {
if obj.globalConf.ApiDebug {
err := os.Setenv("DEBUG", "1")
if err != nil {
slog.Debug("unable to set debug env value", slog.Any("err", err))
Expand Down
2 changes: 1 addition & 1 deletion internal/service/libraryelements.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (s *DashNGoImpl) DownloadLibraryElements(filter filters.Filter) []string {
folderName = val
}

libraryPath := fmt.Sprintf("%s/%s.json", BuildResourceFolder(folderName, config.LibraryElementResource, s.isLocal()), slug.Make(item.Name))
libraryPath := fmt.Sprintf("%s/%s.json", BuildResourceFolder(folderName, config.LibraryElementResource, s.isLocal(), s.globalConf.ClearOutput), slug.Make(item.Name))

if err = s.storage.WriteFile(libraryPath, dsPacked); err != nil {
slog.Error("Unable to write file", "err", err, "library-element", slug.Make(item.Name))
Expand Down
6 changes: 3 additions & 3 deletions internal/service/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
Schemes: []string{u.Scheme},
NumRetries: config.Config().GetGDGConfig().GetAppGlobals().RetryCount,
RetryTimeout: config.Config().GetGDGConfig().GetAppGlobals().GetRetryTimeout(),
Debug: s.apiDebug,
Debug: s.globalConf.ApiDebug,
}

// If more than one opts is passed, depend on the caller to setup his required configuration
Expand All @@ -97,7 +97,7 @@
if s.grafanaConf.APIToken != "" {
grafanaClient, _ := s.getNewClient(func(clientCfg *client.TransportConfig) {
clientCfg.APIKey = s.grafanaConf.APIToken
clientCfg.Debug = s.apiDebug
clientCfg.Debug = s.globalConf.ApiDebug

Check warning on line 100 in internal/service/login.go

View check run for this annotation

Codecov / codecov/patch

internal/service/login.go#L100

Added line #L100 was not covered by tests
})
return grafanaClient
} else {
Expand All @@ -123,7 +123,7 @@
func (s *DashNGoImpl) getDefaultBasicOpts() []NewClientOpts {
return []NewClientOpts{func(clientCfg *client.TransportConfig) {
clientCfg.BasicAuth = url.UserPassword(s.grafanaConf.UserName, s.grafanaConf.Password)
clientCfg.Debug = s.apiDebug
clientCfg.Debug = s.globalConf.ApiDebug
}}
}

Expand Down
23 changes: 18 additions & 5 deletions internal/service/mocks/DashboardsApi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading