From 4832f743af04e87212b7f460d4784bf53382b6ba Mon Sep 17 00:00:00 2001 From: Chris Grindstaff Date: Wed, 8 Jan 2025 13:34:31 -0500 Subject: [PATCH 1/2] feat: Harvest grafana import should support nested Grafana folders Thanks to @IvanZenger for reporting Fixes: #3412 --- cmd/tools/grafana/grafana.go | 62 ++++++++++++++++++++++++++++++------ integration/go.mod | 6 ++-- integration/go.sum | 16 +++++----- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/cmd/tools/grafana/grafana.go b/cmd/tools/grafana/grafana.go index 371270401..7d2d11e09 100644 --- a/cmd/tools/grafana/grafana.go +++ b/cmd/tools/grafana/grafana.go @@ -38,6 +38,7 @@ const ( var ( grafanaMinVers = "7.1.0" // lowest grafana version we require homePath string + grafanaVersion *goversion.Version ) type options struct { @@ -68,9 +69,10 @@ type options struct { } type Folder struct { - name string // Grafana folder where to upload from where to download dashboards - id int64 - uid string + name string // Grafana folder where to upload from where to download dashboards + id int64 + uid string + parentUID string // If nested folders are enabled, and the folder is nested, this is the parent folder's uid } func adjustOptions() { @@ -472,12 +474,13 @@ func checkAndCreateServerFolder(folder *Folder) error { os.Exit(1) } + folderName := folder.name if folder.uid != "" && folder.id != 0 { fmt.Printf("folder [%s] exists in Grafana - OK\n", folder.name) - } else if err := createServerFolder(folder); err != nil { + } else if err := createServerFolders(folder); err != nil { return err } else { - fmt.Printf("created Grafana folder [%s] - OK\n", folder.name) + fmt.Printf("created Grafana folder [%s] - OK\n", folderName) } return nil } @@ -1123,20 +1126,21 @@ func isValidDatasource(result map[string]any) bool { } func checkVersion(inputVersion string) bool { - v1, err := goversion.NewVersion(inputVersion) + var err error + + grafanaVersion, err = goversion.NewVersion(inputVersion) if err != nil { fmt.Println(err) return false } - minV, _ := goversion.NewVersion(grafanaMinVers) // Not using a constraint check since a pre-release version (e.g. 8.4.0-beta1) never matches // a constraint specified without a pre-release https://github.com/hashicorp/go-version/pull/35 - satisfies := v1.GreaterThanOrEqual(minV) + satisfies := grafanaVersion.GreaterThanOrEqual(minV) if !satisfies { - fmt.Printf("%s is not >= %s", v1, minV) + fmt.Printf("%s is not >= %s", grafanaVersion, minV) } return satisfies } @@ -1174,11 +1178,14 @@ func checkFolder(folder *Folder) error { } func createServerFolder(folder *Folder) error { - request := make(map[string]any) request["title"] = folder.name + if folder.parentUID != "" { + request["parentUid"] = folder.parentUID + } + result, status, code, err := sendRequest(opts, "POST", "/api/folders", request) if err != nil { @@ -1195,6 +1202,41 @@ func createServerFolder(folder *Folder) error { return nil } +func createServerFolders(folder *Folder) error { + + if grafanaVersion == nil || grafanaVersion.LessThan(goversion.Must(goversion.NewVersion("11.0.0"))) { + return createServerFolder(folder) + } + + // handle nested folders + folders := strings.Split(folder.name, "/") + var parentUID string + + for _, f := range folders { + curFolder := &Folder{name: f} + if parentUID != "" { + curFolder.parentUID = parentUID + } + + if err := checkFolder(curFolder); err != nil { + return err + } + + if curFolder.id == 0 { + curFolder.name = f + if err := createServerFolder(curFolder); err != nil { + return err + } + folder.name = f + folder.id = curFolder.id + } + + parentUID = curFolder.uid + } + + return nil +} + func sendRequest(opts *options, method, url string, query map[string]any) (map[string]any, string, int, error) { var result map[string]any diff --git a/integration/go.mod b/integration/go.mod index 3deea226b..e1f045b39 100644 --- a/integration/go.mod +++ b/integration/go.mod @@ -25,7 +25,7 @@ require ( github.com/mailru/easyjson v0.7.7 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/shirou/gopsutil/v4 v4.24.11 // indirect + github.com/shirou/gopsutil/v4 v4.24.12 // indirect github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -34,8 +34,8 @@ require ( github.com/tklauser/numcpus v0.9.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect golang.org/x/net v0.33.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/integration/go.sum b/integration/go.sum index 9f9ecd773..a42ef1c65 100644 --- a/integration/go.sum +++ b/integration/go.sum @@ -39,14 +39,14 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v4 v4.24.11 h1:WaU9xqGFKvFfsUv94SXcUPD7rCkU0vr/asVdQOBZNj8= -github.com/shirou/gopsutil/v4 v4.24.11/go.mod h1:s4D/wg+ag4rG0WO7AiTj2BeYCRhym0vM7DHbZRxnIT8= +github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4= +github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= @@ -62,10 +62,10 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 51b2dbf3f42461dddb14363c53d67d08d09884db Mon Sep 17 00:00:00 2001 From: Chris Grindstaff Date: Thu, 9 Jan 2025 08:20:12 -0500 Subject: [PATCH 2/2] feat: Harvest grafana import should support nested Grafana folders Thanks to @IvanZenger for reporting Fixes: #3412 --- cmd/tools/grafana/grafana.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cmd/tools/grafana/grafana.go b/cmd/tools/grafana/grafana.go index 7d2d11e09..aa9eb6aca 100644 --- a/cmd/tools/grafana/grafana.go +++ b/cmd/tools/grafana/grafana.go @@ -1147,7 +1147,13 @@ func checkVersion(inputVersion string) bool { func checkFolder(folder *Folder) error { - result, status, code, err := sendRequestArray(opts, "GET", "/api/folders?limit=1000", nil) + q := "/api/folders?limit=1000" + + if folder.parentUID != "" { + q += "&parentUid=" + folder.parentUID + } + + result, status, code, err := sendRequestArray(opts, "GET", q, nil) if err != nil { return err @@ -1227,11 +1233,11 @@ func createServerFolders(folder *Folder) error { if err := createServerFolder(curFolder); err != nil { return err } - folder.name = f - folder.id = curFolder.id } parentUID = curFolder.uid + folder.name = f + folder.id = curFolder.id } return nil