diff --git a/README.MD b/README.MD index bc8f223..08fb1e7 100644 --- a/README.MD +++ b/README.MD @@ -11,8 +11,12 @@ Keep your grafana dashboards in sync. - [Pull](#pull) - [Save all dashboards to directory](#save-all-dashboards-to-directory) - [Save dashboards with specific tags to directory](#save-dashboards-with-specific-tags-to-directory) + - [Save folders configuration to directory](#save-folders-configuration-to-directory) + - [Save notifications configuration to directory](#save-notifications-configuration-to-directory) - [Push](#push) - [Push dashboards to grafana](#push-dashboards-to-grafana) + - [Push folders to grafana](#push-folders-to-grafana) + - [Push notifications to grafana](#push-notifications-to-grafana) - [Global parameters](#global-parameters) - [Contributing](#contributing) - [License](#license) @@ -27,23 +31,47 @@ Download the latest binary from [releases](https://github.com/mpostument/grafana #### Save all dashboards to directory -`grafana-sync pull` +`grafana-sync pull-dashboards` Example: - `grafana-sync pull --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboard" --url http://127.0.0.1:3000` + `grafana-sync pull-dashboards --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboards" --url http://127.0.0.1:3000` #### Save dashboards with specific tags to directory -`grafana-sync pull` +`grafana-sync pull-dashboards` Example: - `grafana-sync pull --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboard" --url http://127.0.0.1:3000 --tag=export` + `grafana-sync pull-dashboards --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboards" --url http://127.0.0.1:3000 --tag=export` + +#### Save folders configuration to directory + +`grafana-sync pull-folders` +Example: + `grafana-sync pull-folders --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="folders" --url http://127.0.0.1:3000` + +#### Save notifications configuration to directory + +`grafana-sync pull-notifications` +Example: + `grafana-sync pull-notifications --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="notifications" --url http://127.0.0.1:3000` ### Push #### Push dashboards to grafana -`grafana-sync push` +`grafana-sync push-dashboards` +Example: + `grafana-sync push-dashboards --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboards" --url http://127.0.0.1:3000` + +#### Push folders to grafana + +`grafana-sync push-folders` +Example: + `grafana-sync push-folders --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="folders" --url http://127.0.0.1:3000` + +#### Push notifications to grafana + +`grafana-sync push-notifications` Example: - `grafana-sync push --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="dashboard" --url http://127.0.0.1:3000` + `grafana-sync push-notifications --apikey="eyJrIjoiOWJYTktGNFlCbFVMOG1LY3d6ekN4Mmw4MFgyYU44a1UiLCJuIjoiY29icmEiLCJpZCI6MX0=" --directory="notifications" --url http://127.0.0.1:3000` ## Global parameters diff --git a/cmd/root.go b/cmd/root.go index 1afd091..f166b57 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -35,9 +35,9 @@ var rootCmd = &cobra.Command{ Long: `Root command for grafana interaction.`, } -var pullCmd = &cobra.Command{ - Use: "pull", - Short: "Pull grafana dashboards in to directory", +var pullDashboardsCmd = &cobra.Command{ + Use: "pull-dashboards", + Short: "Pull grafana dashboards in to the directory", Long: `Save to the directory grafana dashboards. Directory name specified by flag --directory. If flag --tags is used, additional directory will be created with tag name creating structure like directory/tag`, @@ -46,19 +46,81 @@ additional directory will be created with tag name creating structure like direc apiKey := viper.GetString("apikey") directory, _ := cmd.Flags().GetString("directory") tag, _ := cmd.Flags().GetString("tag") - grafana.PullDashboard(url, apiKey, directory, tag) + if err := grafana.PullDashboard(url, apiKey, directory, tag); err != nil { + log.Fatalln("Pull dashboards command failed", err) + } }, } -var pushCmd = &cobra.Command{ - Use: "push", +var pushDashboardsCmd = &cobra.Command{ + Use: "push-dashboards", Short: "Push grafana dashboards from directory", Long: `Read json with dashboards description and publish to grafana.`, Run: func(cmd *cobra.Command, args []string) { url, _ := cmd.Flags().GetString("url") apiKey, _ := cmd.Flags().GetString("apikey") directory, _ := cmd.Flags().GetString("directory") - grafana.PushDashboard(url, apiKey, directory) + if err := grafana.PushDashboard(url, apiKey, directory); err != nil { + log.Fatalln("Push dashboards command failed", err) + } + }, +} + +var pullFoldersCmd = &cobra.Command{ + Use: "pull-folders", + Short: "Pull grafana folders json in to the directory", + Long: `Save to the directory grafana folders json. +Directory name specified by flag --directory.`, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + apiKey := viper.GetString("apikey") + directory, _ := cmd.Flags().GetString("directory") + if err := grafana.PullFolders(url, apiKey, directory); err != nil { + log.Fatalln("Pull folders command failed", err) + } + }, +} + +var pushFoldersCmd = &cobra.Command{ + Use: "push-folders", + Short: "Read json and create grafana folders", + Long: `Read json with folders description and publish to grafana.`, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + apiKey := viper.GetString("apikey") + directory, _ := cmd.Flags().GetString("directory") + if err := grafana.PushFolder(url, apiKey, directory); err != nil { + log.Fatalln("Push folders command failed", err) + } + }, +} + +var pullNotificationsCmd = &cobra.Command{ + Use: "pull-notifications", + Short: "Pull grafana notifications json in to the directory", + Long: `Save to the directory grafana folders json. +Directory name specified by flag --directory.`, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + apiKey := viper.GetString("apikey") + directory, _ := cmd.Flags().GetString("directory") + if err := grafana.PullNotifications(url, apiKey, directory); err != nil { + log.Fatalln("Pull notifications command failed", err) + } + }, +} + +var pushNotificationsCmd = &cobra.Command{ + Use: "push-notifications", + Short: "Read json and create grafana notifications", + Long: `Read json with notifications description and publish to grafana.`, + Run: func(cmd *cobra.Command, args []string) { + url, _ := cmd.Flags().GetString("url") + apiKey := viper.GetString("apikey") + directory, _ := cmd.Flags().GetString("directory") + if err := grafana.PushNotification(url, apiKey, directory); err != nil { + log.Fatalln("Push notifications command failed", err) + } }, } @@ -82,8 +144,12 @@ func init() { log.Println(err) } - rootCmd.AddCommand(pullCmd) - rootCmd.AddCommand(pushCmd) + rootCmd.AddCommand(pullDashboardsCmd) + rootCmd.AddCommand(pushDashboardsCmd) + rootCmd.AddCommand(pullFoldersCmd) + rootCmd.AddCommand(pushFoldersCmd) + rootCmd.AddCommand(pullNotificationsCmd) + rootCmd.AddCommand(pushNotificationsCmd) } // initConfig reads in config file and ENV variables if set. diff --git a/go.mod b/go.mod index 8af378e..ab2d76f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/mpostument/grafana-sync go 1.15 require ( - github.com/grafana-tools/sdk v0.0.0-20201123153837-5fb28a7aa2ef + github.com/grafana-tools/sdk v0.0.0-20210127134634-c831d821bb8a github.com/mitchellh/go-homedir v1.1.0 github.com/spf13/cobra v1.1.1 github.com/spf13/viper v1.7.1 diff --git a/go.sum b/go.sum index 093694c..0395c08 100644 --- a/go.sum +++ b/go.sum @@ -25,6 +25,12 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chromedp/cdproto v0.0.0-20210122124816-7a656c010d57 h1:htpyTFarq7OHx9SpkQ+7x20thTQA6JAsgnuMGoPbH4E= +github.com/chromedp/cdproto v0.0.0-20210122124816-7a656c010d57/go.mod h1:55pim6Ht4LJKdVLlyFJV/g++HsEA1hQxPbB5JyNdZC0= +github.com/chromedp/chromedp v0.6.5 h1:hPaDYBpvD2WFicln0ByzV+XRhSOtLgAgsu39O455iWY= +github.com/chromedp/chromedp v0.6.5/go.mod h1:/Q6h52DkrFuvOgmCuR6O3xT5g0bZYoPqjANKBEvQGEY= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -46,6 +52,12 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.4 h1:5eXU1CZhpQdq5kXbKb+sECH5Ia5KiO6CYzIzdlVx6Bs= +github.com/gobwas/ws v1.0.4/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -71,8 +83,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gosimple/slug v1.1.1 h1:fRu/digW+NMwBIP+RmviTK97Ho/bEj/C9swrCspN3D4= github.com/gosimple/slug v1.1.1/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0= -github.com/grafana-tools/sdk v0.0.0-20201123153837-5fb28a7aa2ef h1:pBtpdcXGO5VLXzmOvlnqfxofkw1Ce8CyoV6mAQUJmm4= -github.com/grafana-tools/sdk v0.0.0-20201123153837-5fb28a7aa2ef/go.mod h1:aqBqJVTJmj0MTX9cP8wuReJPte6HyttMDzSS2u8nJwo= +github.com/grafana-tools/sdk v0.0.0-20210127134634-c831d821bb8a h1:UK4jWytfW64zniHvovsS/ykA923QUr6IYa+j24larnI= +github.com/grafana-tools/sdk v0.0.0-20210127134634-c831d821bb8a/go.mod h1:uby+6hPUCRVNG/iAZKCOlaq5YhyK0oKMRke+FDesZdw= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= @@ -100,6 +112,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -116,6 +130,8 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= @@ -257,6 +273,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210122093101-04d7465088b8 h1:de2yTH1xuxjmGB7i6Z5o2z3RCHVa0XlpSZzjd8Fe6bE= +golang.org/x/sys v0.0.0-20210122093101-04d7465088b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= diff --git a/grafana/common.go b/grafana/common.go new file mode 100644 index 0000000..6bedd31 --- /dev/null +++ b/grafana/common.go @@ -0,0 +1,41 @@ +package grafana + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" +) + +func writeToFile(directory string, content []byte, name string, tag string) error { + var ( + err error + path string + dashboardFile *os.File + fileName string + ) + + path = directory + if tag != "" { + path = filepath.Join(path, tag) + } + + if _, err = os.Stat(path); os.IsNotExist(err) { + if err = os.MkdirAll(path, 0755); err != nil { + return err + } + } + fileName = fmt.Sprintf("%s/%s.json", path, name) + dashboardFile, err = os.Create(fileName) + if err != nil { + return err + } + + defer dashboardFile.Close() + + err = ioutil.WriteFile(dashboardFile.Name(), content, os.FileMode(0755)) + if err != nil { + return err + } + return nil +} diff --git a/grafana/dashboard.go b/grafana/dashboard.go new file mode 100644 index 0000000..67655fc --- /dev/null +++ b/grafana/dashboard.go @@ -0,0 +1,81 @@ +package grafana + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/grafana-tools/sdk" +) + +func PullDashboard(grafanaURL string, apiKey string, directory string, tag string) error { + var ( + boardLinks []sdk.FoundBoard + rawBoard sdk.Board + meta sdk.BoardProperties + err error + ) + ctx := context.Background() + + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + + if boardLinks, err = c.Search(ctx, sdk.SearchTag(tag), sdk.SearchType(sdk.SearchTypeDashboard)); err != nil { + return err + } + + for _, link := range boardLinks { + if rawBoard, meta, err = c.GetDashboardByUID(ctx, link.UID); err != nil { + log.Printf("%s for %s\n", err, link.URI) + continue + } + rawBoard.ID = 0 + b, err := json.Marshal(rawBoard) + if err != nil { + return err + } + if err = writeToFile(directory, b, meta.Slug, tag); err != nil { + return err + } + } + return nil +} + +func PushDashboard(grafanaURL string, apiKey string, directory string) error { + var ( + filesInDir []os.FileInfo + rawBoard []byte + err error + ) + + ctx := context.Background() + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + if filesInDir, err = ioutil.ReadDir(directory); err != nil { + return err + } + for _, file := range filesInDir { + if strings.HasSuffix(file.Name(), ".json") { + if rawBoard, err = ioutil.ReadFile(fmt.Sprintf("%s/%s", directory, file.Name())); err != nil { + log.Println(err) + continue + } + var board sdk.Board + if err = json.Unmarshal(rawBoard, &board); err != nil { + log.Println(err) + continue + } + params := sdk.SetDashboardParams{ + FolderID: sdk.DefaultFolderId, + Overwrite: true, + } + if _, err := c.SetDashboard(ctx, board, params); err != nil { + log.Printf("error on importing dashboard %s", board.Title) + continue + } + } + } + return nil +} diff --git a/grafana/folder.go b/grafana/folder.go new file mode 100644 index 0000000..9a341dc --- /dev/null +++ b/grafana/folder.go @@ -0,0 +1,70 @@ +package grafana + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/grafana-tools/sdk" +) + +func PullFolders(grafanaURL string, apiKey string, directory string) error { + var ( + folders []sdk.Folder + err error + ) + ctx := context.Background() + + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + + if folders, err = c.GetAllFolders(ctx); err != nil { + return err + } + + for _, folder := range folders { + b, err := json.Marshal(folder) + if err != nil { + return err + } + if err = writeToFile(directory, b, folder.Title, ""); err != nil { + return err + } + } + return nil +} + +func PushFolder(grafanaURL string, apiKey string, directory string) error { + var ( + filesInDir []os.FileInfo + rawFolder []byte + err error + ) + + ctx := context.Background() + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + if filesInDir, err = ioutil.ReadDir(directory); err != nil { + return err + } + for _, file := range filesInDir { + if strings.HasSuffix(file.Name(), ".json") { + if rawFolder, err = ioutil.ReadFile(fmt.Sprintf("%s/%s", directory, file.Name())); err != nil { + log.Println(err) + continue + } + var folder sdk.Folder + if err = json.Unmarshal(rawFolder, &folder); err != nil { + log.Println(err) + continue + } + if _, err := c.CreateFolder(ctx, folder); err != nil { + log.Printf("error on importing folder %s", folder.Title) + continue + } + } + } + return nil +} diff --git a/grafana/notification.go b/grafana/notification.go new file mode 100644 index 0000000..4befc67 --- /dev/null +++ b/grafana/notification.go @@ -0,0 +1,70 @@ +package grafana + +import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/grafana-tools/sdk" +) + +func PullNotifications(grafanaURL string, apiKey string, directory string) error { + var ( + notifications []sdk.AlertNotification + err error + ) + ctx := context.Background() + + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + + if notifications, err = c.GetAllAlertNotifications(ctx); err != nil { + return err + } + + for _, notification := range notifications { + b, err := json.Marshal(notification) + if err != nil { + return err + } + if err = writeToFile(directory, b, notification.Name, ""); err != nil { + return err + } + } + return nil +} + +func PushNotification(grafanaURL string, apiKey string, directory string) error { + var ( + filesInDir []os.FileInfo + rawFolder []byte + err error + ) + + ctx := context.Background() + c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) + if filesInDir, err = ioutil.ReadDir(directory); err != nil { + return err + } + for _, file := range filesInDir { + if strings.HasSuffix(file.Name(), ".json") { + if rawFolder, err = ioutil.ReadFile(fmt.Sprintf("%s/%s", directory, file.Name())); err != nil { + log.Println(err) + continue + } + var notification sdk.AlertNotification + if err = json.Unmarshal(rawFolder, ¬ification); err != nil { + log.Println(err) + continue + } + if _, err := c.CreateAlertNotification(ctx, notification); err != nil { + log.Printf("error on importing notification %s", notification.Name) + continue + } + } + } + return nil +} diff --git a/grafana/pullDashboard.go b/grafana/pullDashboard.go deleted file mode 100644 index 6b9dae0..0000000 --- a/grafana/pullDashboard.go +++ /dev/null @@ -1,69 +0,0 @@ -package grafana - -import ( - "context" - "encoding/json" - "fmt" - "log" - "os" - "path/filepath" - - "github.com/grafana-tools/sdk" -) - -func PullDashboard(grafanaURL string, apiKey string, directory string, tag string) { - var ( - boardLinks []sdk.FoundBoard - rawBoard sdk.Board - meta sdk.BoardProperties - err error - ) - ctx := context.Background() - - c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) - - if boardLinks, err = c.Search(ctx, sdk.SearchTag(tag)); err != nil { - log.Fatalln(err) - } - - for _, link := range boardLinks { - if rawBoard, meta, err = c.GetDashboardByUID(ctx, link.UID); err != nil { - log.Printf("%s for %s\n", err, link.URI) - continue - } - rawBoard.ID = 0 - writeDashboardToFile(directory, rawBoard, meta.Slug, tag) - } -} - -func writeDashboardToFile(directory string, dashboard sdk.Board, name string, tag string) { - var ( - err error - path string - dashboardFile *os.File - fileName string - ) - - path = directory - if tag != "" { - path = filepath.Join(path, tag) - } - - if _, err = os.Stat(path); os.IsNotExist(err) { - if err = os.MkdirAll(path, 0755); err != nil { - log.Fatalln("Directory is not created", err) - } - } - fileName = fmt.Sprintf("%s/%s.json", path, name) - dashboardFile, err = os.Create(fileName) - if err != nil { - log.Printf("failed to create file for dashboard %s", fileName) - } - - defer dashboardFile.Close() - - err = json.NewEncoder(dashboardFile).Encode(dashboard) - if err != nil { - log.Printf("failed to encode dashboard json to file %s", fileName) - } -} diff --git a/grafana/pushDashboard.go b/grafana/pushDashboard.go deleted file mode 100644 index 2bab039..0000000 --- a/grafana/pushDashboard.go +++ /dev/null @@ -1,50 +0,0 @@ -package grafana - -import ( - "context" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" - "strings" - - "github.com/grafana-tools/sdk" -) - -func PushDashboard(grafanaURL string, apiKey string, directory string) { - var ( - filesInDir []os.FileInfo - rawBoard []byte - err error - ) - - ctx := context.Background() - c := sdk.NewClient(grafanaURL, apiKey, sdk.DefaultHTTPClient) - filesInDir, err = ioutil.ReadDir(directory) - if err != nil { - log.Fatal(err) - } - for _, file := range filesInDir { - if strings.HasSuffix(file.Name(), ".json") { - if rawBoard, err = ioutil.ReadFile(fmt.Sprintf("%s/%s", directory, file.Name())); err != nil { - log.Println(err) - continue - } - var board sdk.Board - if err = json.Unmarshal(rawBoard, &board); err != nil { - log.Println(err) - continue - } - params := sdk.SetDashboardParams{ - FolderID: sdk.DefaultFolderId, - Overwrite: true, - } - _, err := c.SetDashboard(ctx, board, params) - if err != nil { - log.Printf("error on importing dashboard %s", board.Title) - continue - } - } - } -}