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  }