AzureMonitor: Use plugin SDK contracts (#34729)

This commit is contained in:
Andres Martinez Gotor
2021-06-07 14:54:51 +02:00
committed by GitHub
parent e8bc48a796
commit d225323049
15 changed files with 652 additions and 735 deletions

View File

@@ -2,19 +2,27 @@ package azuremonitor
import (
"context"
"encoding/json"
"fmt"
"net/http"
"regexp"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/coreplugin"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
const timeSeries = "time_series"
const (
timeSeries = "time_series"
dsName = "grafana-azure-monitor-datasource"
)
var (
azlog = log.New("tsdb.azuremonitor")
@@ -30,147 +38,111 @@ func init() {
}
type Service struct {
PluginManager plugins.Manager `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
Cfg *setting.Cfg `inject:""`
PluginManager plugins.Manager `inject:""`
HTTPClientProvider httpclient.Provider `inject:""`
Cfg *setting.Cfg `inject:""`
BackendPluginManager backendplugin.Manager `inject:""`
}
type azureMonitorSettings struct {
AppInsightsAppId string `json:"appInsightsAppId"`
AzureLogAnalyticsSameAs bool `json:"azureLogAnalyticsSameAs"`
ClientId string `json:"clientId"`
CloudName string `json:"cloudName"`
LogAnalyticsClientId string `json:"logAnalyticsClientId"`
LogAnalyticsDefaultWorkspace string `json:"logAnalyticsDefaultWorkspace"`
LogAnalyticsSubscriptionId string `json:"logAnalyticsSubscriptionId"`
LogAnalyticsTenantId string `json:"logAnalyticsTenantId"`
SubscriptionId string `json:"subscriptionId"`
TenantId string `json:"tenantId"`
AzureAuthType string `json:"azureAuthType,omitempty"`
}
type datasourceInfo struct {
Settings azureMonitorSettings
HTTPClient *http.Client
URL string
JSONData map[string]interface{}
DecryptedSecureJSONData map[string]string
DatasourceID int64
OrgID int64
}
func NewInstanceSettings(httpClientProvider httpclient.Provider) datasource.InstanceFactoryFunc {
return func(settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
opts, err := settings.HTTPClientOptions()
if err != nil {
return nil, err
}
client, err := httpClientProvider.New(opts)
if err != nil {
return nil, err
}
jsonData := map[string]interface{}{}
err = json.Unmarshal(settings.JSONData, &jsonData)
if err != nil {
return nil, fmt.Errorf("error reading settings: %w", err)
}
azMonitorSettings := azureMonitorSettings{}
err = json.Unmarshal(settings.JSONData, &azMonitorSettings)
if err != nil {
return nil, fmt.Errorf("error reading settings: %w", err)
}
model := datasourceInfo{
Settings: azMonitorSettings,
HTTPClient: client,
URL: settings.URL,
JSONData: jsonData,
DecryptedSecureJSONData: settings.DecryptedSecureJSONData,
DatasourceID: settings.ID,
}
return model, nil
}
}
type azDatasourceExecutor interface {
executeTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo datasourceInfo) (*backend.QueryDataResponse, error)
}
func newExecutor(im instancemgmt.InstanceManager, pm plugins.Manager, httpC httpclient.Provider, cfg *setting.Cfg) *datasource.QueryTypeMux {
mux := datasource.NewQueryTypeMux()
executors := map[string]azDatasourceExecutor{
"Azure Monitor": &AzureMonitorDatasource{pm, cfg},
"Application Insights": &ApplicationInsightsDatasource{pm, cfg},
"Azure Log Analytics": &AzureLogAnalyticsDatasource{pm, cfg},
"Insights Analytics": &InsightsAnalyticsDatasource{pm, cfg},
"Azure Resource Graph": &AzureResourceGraphDatasource{pm, cfg},
}
for dsType := range executors {
// Make a copy of the string to keep the reference after the iterator
dst := dsType
mux.HandleFunc(dsType, func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
i, err := im.Get(req.PluginContext)
if err != nil {
return nil, err
}
dsInfo := i.(datasourceInfo)
dsInfo.OrgID = req.PluginContext.OrgID
ds := executors[dst]
return ds.executeTimeSeriesQuery(ctx, req.Queries, dsInfo)
})
}
return mux
}
func (s *Service) Init() error {
im := datasource.NewInstanceManager(NewInstanceSettings(s.HTTPClientProvider))
factory := coreplugin.New(backend.ServeOpts{
QueryDataHandler: newExecutor(im, s.PluginManager, s.HTTPClientProvider, s.Cfg),
})
if err := s.BackendPluginManager.Register(dsName, factory); err != nil {
azlog.Error("Failed to register plugin", "error", err)
}
return nil
}
// AzureMonitorExecutor executes queries for the Azure Monitor datasource - all four services
type AzureMonitorExecutor struct {
httpClient *http.Client
dsInfo *models.DataSource
pluginManager plugins.Manager
cfg *setting.Cfg
}
// NewAzureMonitorExecutor initializes a http client
//nolint: staticcheck // plugins.DataPlugin deprecated
func (s *Service) NewExecutor(dsInfo *models.DataSource) (plugins.DataPlugin, error) {
httpClient, err := dsInfo.GetHTTPClient(s.HTTPClientProvider)
if err != nil {
return nil, err
}
return &AzureMonitorExecutor{
httpClient: httpClient,
dsInfo: dsInfo,
pluginManager: s.PluginManager,
cfg: s.Cfg,
}, nil
}
// Query takes in the frontend queries, parses them into the query format
// expected by chosen Azure Monitor service (Azure Monitor, App Insights etc.)
// executes the queries against the API and parses the response into
// the right format
//nolint: staticcheck // plugins.DataPlugin deprecated
func (e *AzureMonitorExecutor) DataQuery(ctx context.Context, dsInfo *models.DataSource,
tsdbQuery plugins.DataQuery) (plugins.DataResponse, error) {
var err error
var azureMonitorQueries []plugins.DataSubQuery
var applicationInsightsQueries []plugins.DataSubQuery
var azureLogAnalyticsQueries []plugins.DataSubQuery
var insightsAnalyticsQueries []plugins.DataSubQuery
var azureResourceGraphQueries []plugins.DataSubQuery
for _, query := range tsdbQuery.Queries {
queryType := query.Model.Get("queryType").MustString("")
switch queryType {
case "Azure Monitor":
azureMonitorQueries = append(azureMonitorQueries, query)
case "Application Insights":
applicationInsightsQueries = append(applicationInsightsQueries, query)
case "Azure Log Analytics":
azureLogAnalyticsQueries = append(azureLogAnalyticsQueries, query)
case "Insights Analytics":
insightsAnalyticsQueries = append(insightsAnalyticsQueries, query)
case "Azure Resource Graph":
azureResourceGraphQueries = append(azureResourceGraphQueries, query)
default:
return plugins.DataResponse{}, fmt.Errorf("alerting not supported for %q", queryType)
}
}
azDatasource := &AzureMonitorDatasource{
httpClient: e.httpClient,
dsInfo: e.dsInfo,
pluginManager: e.pluginManager,
cfg: e.cfg,
}
aiDatasource := &ApplicationInsightsDatasource{
httpClient: e.httpClient,
dsInfo: e.dsInfo,
pluginManager: e.pluginManager,
cfg: e.cfg,
}
alaDatasource := &AzureLogAnalyticsDatasource{
httpClient: e.httpClient,
dsInfo: e.dsInfo,
pluginManager: e.pluginManager,
cfg: e.cfg,
}
iaDatasource := &InsightsAnalyticsDatasource{
httpClient: e.httpClient,
dsInfo: e.dsInfo,
pluginManager: e.pluginManager,
cfg: e.cfg,
}
argDatasource := &AzureResourceGraphDatasource{
httpClient: e.httpClient,
dsInfo: e.dsInfo,
pluginManager: e.pluginManager,
}
azResult, err := azDatasource.executeTimeSeriesQuery(ctx, azureMonitorQueries, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
aiResult, err := aiDatasource.executeTimeSeriesQuery(ctx, applicationInsightsQueries, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
alaResult, err := alaDatasource.executeTimeSeriesQuery(ctx, azureLogAnalyticsQueries, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
iaResult, err := iaDatasource.executeTimeSeriesQuery(ctx, insightsAnalyticsQueries, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
argResult, err := argDatasource.executeTimeSeriesQuery(ctx, azureResourceGraphQueries, *tsdbQuery.TimeRange)
if err != nil {
return plugins.DataResponse{}, err
}
for k, v := range aiResult.Results {
azResult.Results[k] = v
}
for k, v := range alaResult.Results {
azResult.Results[k] = v
}
for k, v := range iaResult.Results {
azResult.Results[k] = v
}
for k, v := range argResult.Responses {
azResult.Results[k] = plugins.DataQueryResult{Error: v.Error, Dataframes: plugins.NewDecodedDataFrames(v.Frames)}
}
return azResult, nil
}