PublicDashboards: Adds command to generate map of supported datasources (#57841)

generates map of supported datasources for pubdash
This commit is contained in:
owensmallwood
2022-11-02 13:35:57 -06:00
committed by GitHub
parent 7a12132237
commit 7de093738d
6 changed files with 249 additions and 0 deletions

View File

@@ -0,0 +1,106 @@
package generate_datasources
import (
"encoding/json"
"fmt"
"net/http"
"sort"
"sync"
)
type listPluginResponse struct {
Items []struct {
Slug string `json:"slug"`
} `json:"items"`
}
type datasourceDetails struct {
Slug string `json:"slug"`
Backend bool `json:"backend"`
Alerting bool `json:"alerting"`
}
type datasource struct {
Json datasourceDetails `json:"json"`
}
// Get the slugs of all datasource plugins that are compatible with public dashboards
func GetCompatibleDatasources(baseUrl string) ([]string, error) {
slugs, err := getDatasourcePluginSlugs(baseUrl)
if err != nil {
return nil, err
}
datasources := getDatasourceDetails(slugs, baseUrl)
// we only consider a datasource to be supported when alerting and backend are both true
var supported []string
for _, datasource := range datasources {
if datasource.Alerting && datasource.Backend {
supported = append(supported, datasource.Slug)
}
}
sort.Strings(supported)
return supported, nil
}
// Get a list of all the datasource plugin slugs
func getDatasourcePluginSlugs(baseUrl string) ([]string, error) {
resp, err := http.Get(baseUrl + allPluginsEndpoint())
if err != nil {
return nil, err
}
res := &listPluginResponse{}
err = json.NewDecoder(resp.Body).Decode(res)
if err != nil {
return nil, err
}
var slugs []string
for _, meta := range res.Items {
slugs = append(slugs, meta.Slug)
}
_ = resp.Body.Close()
return slugs, nil
}
// Get the details for each datasource plugin by its slug
func getDatasourceDetails(slugs []string, baseUrl string) []datasourceDetails {
datasources := make([]datasourceDetails, len(slugs))
var wg sync.WaitGroup
for i, slug := range slugs {
wg.Add(1)
// create new goroutine for each outgoing request
go func(i int, slug string) {
defer wg.Done()
url := baseUrl + pluginEndpoint(slug)
// url can not be constant since it gets generated based on slug
//nolint:gosec
r, err := http.Get(url)
if err != nil {
panic(err)
}
datasource := &datasource{}
err = json.NewDecoder(r.Body).Decode(datasource)
if err != nil {
panic(err)
}
datasource.Json.Slug = slug
datasources[i] = datasource.Json
_ = r.Body.Close()
}(i, slug)
}
wg.Wait()
return datasources
}
func pluginEndpoint(slug string) string {
return fmt.Sprintf("/api/plugins/%s?version=latest", slug)
}
func allPluginsEndpoint() string {
return "/api/plugins?orderBy=weight&direction=asc&typeCodeIn[]=datasource"
}

View File

@@ -0,0 +1,40 @@
//go:build ignore
package main
import (
"fmt"
"os"
"text/template"
"github.com/grafana/grafana/pkg/services/publicdashboards/commands/generate_datasources"
)
var goTemplate = `
// Code generated by go generate; DO NOT EDIT.
package publicdashboards
var SupportedDatasources = map[string]bool{
{{- range . }}
"{{ printf "%s" . }}": true,
{{- end }}
}
`
func main() {
baseUrl := "https://grafana.com"
slugs, err := generate_datasources.GetCompatibleDatasources(baseUrl)
myTemplate := template.Must(template.New("").Parse(goTemplate))
file, err := os.Create("supported_datasources_gen.go")
if err != nil {
fmt.Println(err)
}
err = myTemplate.Execute(file, slugs)
if err != nil {
fmt.Println(err)
}
}

View File

@@ -0,0 +1,36 @@
package generate_datasources
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestCanGetCompatibleDatasources(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var err error
w.WriteHeader(http.StatusOK)
// response for getting metadata for all datasources
if r.URL.String() == allPluginsEndpoint() {
_, err = w.Write([]byte(`{"items":[{"slug":"postgres"},{"slug":"frontendDatasource"}]}`))
}
// responses for specific datasource plugins
if r.URL.String() == pluginEndpoint("postgres") {
_, err = w.Write([]byte(`{"json":{"alerting":true,"backend":true}}`))
}
if r.URL.String() == pluginEndpoint("frontendDatasource") {
_, err = w.Write([]byte(`{"json":{}}`))
}
require.NoError(t, err)
}))
defer server.Close()
datasources, err := GetCompatibleDatasources(server.URL)
require.NoError(t, err)
assert.Len(t, datasources, 1)
assert.Equal(t, "postgres", datasources[0])
}