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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 249 additions and 0 deletions

1
.gitignore vendored
View File

@ -177,6 +177,7 @@ compilation-stats.json
!pkg/framework/**/*_gen.go
!pkg/plugins/pfs/**/*_gen.go
!public/app/plugins/**/*_gen.go
!pkg/services/publicdashboards/*_gen.go
# Auto-generated internationalization files
public/locales/_build/

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])
}

View File

@ -12,6 +12,7 @@ import (
// These are the api contracts. The API should match the underlying service and store
//go:generate go run ./commands/generate_datasources/main.go
//go:generate mockery --name Service --structname FakePublicDashboardService --inpackage --filename public_dashboard_service_mock.go
type Service interface {
FindPublicDashboardAndDashboardByAccessToken(ctx context.Context, accessToken string) (*PublicDashboard, *models.Dashboard, error)

View File

@ -0,0 +1,65 @@
// Code generated by go generate; DO NOT EDIT.
package publicdashboards
var SupportedDatasources = map[string]bool{
"aquaqanalytics-kdbbackend-datasource": true,
"cloudwatch": true,
"dlopes7-appdynamics-datasource": true,
"doitintl-bigquery-datasource": true,
"elasticsearch": true,
"frser-sqlite-datasource": true,
"grafadruid-druid-datasource": true,
"grafana-athena-datasource": true,
"grafana-azure-data-explorer-datasource": true,
"grafana-azure-monitor-datasource": true,
"grafana-bigquery-datasource": true,
"grafana-clickhouse-datasource": true,
"grafana-databricks-datasource": true,
"grafana-datadog-datasource": true,
"grafana-db2-datasource": true,
"grafana-dynatrace-datasource": true,
"grafana-es-open-distro-datasource": true,
"grafana-github-datasource": true,
"grafana-honeycomb-datasource": true,
"grafana-iot-sitewise-datasource": true,
"grafana-jira-datasource": true,
"grafana-mock-datasource": true,
"grafana-mongodb-datasource": true,
"grafana-newrelic-datasource": true,
"grafana-odbc-datasource": true,
"grafana-opcua-datasource": true,
"grafana-opensearch-datasource": true,
"grafana-oracle-datasource": true,
"grafana-orbit-datasource": true,
"grafana-redshift-datasource": true,
"grafana-salesforce-datasource": true,
"grafana-saphana-datasource": true,
"grafana-sentry-datasource": true,
"grafana-servicenow-datasource": true,
"grafana-snowflake-datasource": true,
"grafana-splunk-datasource": true,
"grafana-splunk-monitoring-datasource": true,
"grafana-timestream-datasource": true,
"grafana-wavefront-datasource": true,
"grafana-x-ray-datasource": true,
"graphite": true,
"hadesarchitect-cassandra-datasource": true,
"influxdb": true,
"innius-grpc-datasource": true,
"kniepdennis-neo4j-datasource": true,
"loki": true,
"marcusolsson-csv-datasource": true,
"marcusolsson-ynab-datasource": true,
"mssql": true,
"mysql": true,
"opentsdb": true,
"postgres": true,
"prometheus": true,
"redis-datasource": true,
"sentinelone-dataset-datasource": true,
"tdengine-datasource": true,
"vertamedia-clickhouse-datasource": true,
"vertica-grafana-datasource": true,
"yesoreyeram-infinity-datasource": true,
}