github.com/esnet/gdg@v0.6.1-0.20240412190737-6b6eba9c14d8/cli/backup/dashboard.go (about) 1 package backup 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "github.com/grafana/grafana-openapi-client-go/models" 8 "log/slog" 9 "net/url" 10 "strings" 11 12 "github.com/bep/simplecobra" 13 "github.com/esnet/gdg/cli/support" 14 "github.com/esnet/gdg/internal/config" 15 "github.com/esnet/gdg/internal/service" 16 "github.com/esnet/gdg/internal/tools" 17 "github.com/jedib0t/go-pretty/v6/table" 18 "github.com/spf13/cobra" 19 ) 20 21 var skipConfirmAction bool 22 23 func parseDashboardGlobalFlags(command *cobra.Command) []string { 24 folderFilter, _ := command.Flags().GetString("folder") 25 dashboardFilter, _ := command.Flags().GetString("dashboard") 26 tagsFilter, _ := command.Flags().GetStringArray("tags") 27 rawTags, err := json.Marshal(tagsFilter) 28 jsonTags := "" 29 if err == nil { 30 jsonTags = string(rawTags) 31 } 32 33 return []string{folderFilter, dashboardFilter, jsonTags} 34 } 35 36 func newDashboardCommand() simplecobra.Commander { 37 description := "Manage Grafana Dashboards" 38 return &support.SimpleCommand{ 39 NameP: "dashboards", 40 Short: description, 41 Long: description, 42 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 43 cmd.Aliases = []string{"dash", "dashboard"} 44 cmd.PersistentFlags().BoolVarP(&skipConfirmAction, "skip-confirmation", "", false, "when set to true, bypass confirmation prompts") 45 cmd.PersistentFlags().StringP("dashboard", "d", "", "filter by dashboard slug") 46 cmd.PersistentFlags().StringP("folder", "f", "", "Filter by Folder Name (Quotes in names not supported)") 47 cmd.PersistentFlags().StringArrayP("tags", "t", []string{}, "Filter by list of comma delimited tags") 48 }, 49 CommandsList: []simplecobra.Commander{ 50 newListDashboardsCmd(), 51 newDownloadDashboardsCmd(), 52 newUploadDashboardsCmd(), 53 newClearDashboardsCmd(), 54 //Permissions 55 newDashboardPermissionCmd(), 56 }, 57 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 58 return cd.CobraCommand.Help() 59 }, 60 } 61 } 62 63 func newClearDashboardsCmd() simplecobra.Commander { 64 description := "delete all monitored dashboards from grafana" 65 return &support.SimpleCommand{ 66 NameP: "clear", 67 Short: description, 68 Long: description, 69 CommandsList: []simplecobra.Commander{}, 70 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 71 cmd.Aliases = []string{"c"} 72 }, 73 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 74 filter := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) 75 deletedDashboards := rootCmd.GrafanaSvc().DeleteAllDashboards(filter) 76 rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) 77 for _, file := range deletedDashboards { 78 rootCmd.TableObj.AppendRow(table.Row{"dashboard", file}) 79 } 80 if len(deletedDashboards) == 0 { 81 slog.Info("No dashboards were found. 0 dashboards were removed") 82 } else { 83 slog.Info("dashboards were deleted", "count", len(deletedDashboards)) 84 rootCmd.Render(cd.CobraCommand, deletedDashboards) 85 } 86 return nil 87 }, 88 } 89 } 90 91 func newUploadDashboardsCmd() simplecobra.Commander { 92 description := "upload all dashboards to grafana" 93 return &support.SimpleCommand{ 94 NameP: "upload", 95 Short: description, 96 Long: description, 97 CommandsList: []simplecobra.Commander{}, 98 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 99 cmd.Aliases = []string{"u", "up"} 100 }, 101 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 102 filter := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) 103 104 if !skipConfirmAction { 105 tools.GetUserConfirmation(fmt.Sprintf("WARNING: this will delete all dashboards from the monitored folders: '%s' "+ 106 "(or all folders if ignore_dashboard_filters is set to true) and upload your local copy. Do you wish to "+ 107 "continue (y/n) ", strings.Join(config.Config().GetDefaultGrafanaConfig().GetMonitoredFolders(), ", "), 108 ), "", true) 109 } 110 rootCmd.GrafanaSvc().UploadDashboards(filter) 111 112 rootCmd.TableObj.AppendHeader(table.Row{"Title", "id", "folder", "UID"}) 113 boards := rootCmd.GrafanaSvc().ListDashboards(filter) 114 115 slog.Info("dashboards have been uploaded", slog.Any("count", len(boards)), 116 slog.String("context", GetContext()), 117 slog.String("Organization", GetOrganizationName())) 118 for _, link := range boards { 119 rootCmd.TableObj.AppendRow(table.Row{link.Title, link.ID, link.FolderTitle, link.UID}) 120 } 121 if len(boards) > 0 { 122 rootCmd.Render(cd.CobraCommand, boards) 123 } else { 124 slog.Info("No dashboards found") 125 } 126 return nil 127 }, 128 } 129 } 130 131 func newDownloadDashboardsCmd() simplecobra.Commander { 132 description := "download all dashboards from grafana" 133 return &support.SimpleCommand{ 134 NameP: "download", 135 Short: description, 136 Long: description, 137 CommandsList: []simplecobra.Commander{}, 138 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 139 cmd.Aliases = []string{"d"} 140 }, 141 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 142 filter := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) 143 savedFiles := rootCmd.GrafanaSvc().DownloadDashboards(filter) 144 slog.Info("Downloading dashboards for context", 145 slog.String("Organization", GetOrganizationName()), 146 "context", GetContext()) 147 rootCmd.TableObj.AppendHeader(table.Row{"type", "filename"}) 148 for _, file := range savedFiles { 149 rootCmd.TableObj.AppendRow(table.Row{"dashboard", file}) 150 } 151 rootCmd.Render(cd.CobraCommand, savedFiles) 152 return nil 153 }, 154 } 155 } 156 157 func getDashboardUrl(link *models.Hit) string { 158 base, err := url.Parse(config.Config().GetDefaultGrafanaConfig().URL) 159 var baseHost string 160 if err != nil { 161 baseHost = "http://unknown/" 162 slog.Warn("unable to determine grafana base host for dashboard", slog.String("dashboard-uid", link.UID)) 163 } else { 164 base.Path = "" 165 baseHost = base.String() 166 } 167 return fmt.Sprintf("%s%s", baseHost, link.URL) 168 169 } 170 171 func newListDashboardsCmd() simplecobra.Commander { 172 description := "List all dashboards from grafana" 173 return &support.SimpleCommand{ 174 NameP: "list", 175 Short: description, 176 Long: description, 177 CommandsList: []simplecobra.Commander{}, 178 WithCFunc: func(cmd *cobra.Command, r *support.RootCommand) { 179 cmd.Aliases = []string{"l"} 180 }, 181 RunFunc: func(ctx context.Context, cd *simplecobra.Commandeer, rootCmd *support.RootCommand, args []string) error { 182 rootCmd.TableObj.AppendHeader(table.Row{"id", "Title", "Slug", "Folder", "UID", "Tags", "URL"}) 183 184 filters := service.NewDashboardFilter(parseDashboardGlobalFlags(cd.CobraCommand)...) 185 boards := rootCmd.GrafanaSvc().ListDashboards(filters) 186 187 slog.Info("Listing dashboards for context", 188 slog.String("context", GetContext()), 189 slog.String("orgName", GetOrganizationName()), 190 slog.Any("count", len(boards))) 191 for _, link := range boards { 192 urlValue := getDashboardUrl(link) 193 var tagVal string 194 if len(link.Tags) > 0 { 195 tagValByte, err := json.Marshal(link.Tags) 196 if err == nil { 197 tagVal = string(tagValByte) 198 } 199 } 200 201 rootCmd.TableObj.AppendRow(table.Row{ 202 link.ID, link.Title, link.Slug, link.FolderTitle, 203 link.UID, tagVal, urlValue, 204 }) 205 206 } 207 if len(boards) > 0 { 208 rootCmd.Render(cd.CobraCommand, boards) 209 } else { 210 slog.Info("No dashboards found") 211 } 212 return nil 213 }, 214 } 215 }