From 9bfb7e1f0b64dc52ea3732a718200de466f457ae Mon Sep 17 00:00:00 2001 From: Sergey Kostrukov Date: Thu, 8 Feb 2024 04:42:20 -0600 Subject: [PATCH] Azure Monitor: Azure routes from Grafana Azure SDK (#82043) Prometheus: Azure routes from Grafana Azure SDK --- .../azuremonitor-resource-handler_test.go | 3 +- pkg/tsdb/azuremonitor/azuremonitor.go | 34 +--- pkg/tsdb/azuremonitor/azuremonitor_test.go | 83 ++++++---- .../azure-log-analytics-datasource.go | 9 +- pkg/tsdb/azuremonitor/loganalytics/utils.go | 16 -- .../metrics/azuremonitor-datasource.go | 7 +- .../azure-resource-graph-datasource.go | 7 +- .../azure-resource-graph-datasource_test.go | 18 -- pkg/tsdb/azuremonitor/routes.go | 156 +++++++++++------- pkg/tsdb/azuremonitor/types/types.go | 1 - 10 files changed, 156 insertions(+), 178 deletions(-) diff --git a/pkg/tsdb/azuremonitor/azuremonitor-resource-handler_test.go b/pkg/tsdb/azuremonitor/azuremonitor-resource-handler_test.go index 72794dd8248..487bbc9d358 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor-resource-handler_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor-resource-handler_test.go @@ -6,7 +6,6 @@ import ( "net/http/httptest" "testing" - "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/metrics" @@ -105,7 +104,7 @@ func Test_handleResourceReq(t *testing.T) { im: &fakeInstance{ services: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[azsettings.AzurePublic][azureMonitor].URL, + URL: "https://management.azure.com", HTTPClient: &http.Client{}, Logger: log.DefaultLogger, }, diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go index 50a2ee276d7..0fbb2d65391 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor.go +++ b/pkg/tsdb/azuremonitor/azuremonitor.go @@ -10,7 +10,6 @@ import ( "net/http" "strconv" - "github.com/grafana/grafana-azure-sdk-go/azcredentials" "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" @@ -109,18 +108,12 @@ func NewInstanceSettings(clientProvider *httpclient.Provider, executors map[stri credentials = azmoncredentials.GetDefaultCredentials(azureSettings) } - cloud, err := azcredentials.GetAzureCloud(azureSettings, credentials) - if err != nil { - return nil, fmt.Errorf("error getting credentials: %w", err) - } - - routesForModel, err := getAzureRoutes(cloud, settings.JSONData) + routesForModel, err := getAzureMonitorRoutes(azureSettings, credentials, settings.JSONData) if err != nil { return nil, err } model := types.DatasourceInfo{ - Cloud: cloud, Credentials: credentials, Settings: azMonitorSettings, JSONData: jsonData, @@ -142,31 +135,6 @@ func NewInstanceSettings(clientProvider *httpclient.Provider, executors map[stri } } -func getCustomizedCloudSettings(cloud string, jsonData json.RawMessage) (types.AzureMonitorCustomizedCloudSettings, error) { - customizedCloudSettings := types.AzureMonitorCustomizedCloudSettings{} - err := json.Unmarshal(jsonData, &customizedCloudSettings) - if err != nil { - return types.AzureMonitorCustomizedCloudSettings{}, fmt.Errorf("error getting customized cloud settings: %w", err) - } - return customizedCloudSettings, nil -} - -func getAzureRoutes(cloud string, jsonData json.RawMessage) (map[string]types.AzRoute, error) { - if cloud == azsettings.AzureCustomized { - customizedCloudSettings, err := getCustomizedCloudSettings(cloud, jsonData) - if err != nil { - return nil, err - } - if customizedCloudSettings.CustomizedRoutes == nil { - return nil, fmt.Errorf("unable to instantiate routes, customizedRoutes must be set") - } - azureRoutes := customizedCloudSettings.CustomizedRoutes - return azureRoutes, nil - } else { - return routes[cloud], nil - } -} - type azDatasourceExecutor interface { ExecuteTimeSeriesQuery(ctx context.Context, originalQueries []backend.DataQuery, dsInfo types.DatasourceInfo, client *http.Client, url string) (*backend.QueryDataResponse, error) ResourceRequest(rw http.ResponseWriter, req *http.Request, cli *http.Client) (http.ResponseWriter, error) diff --git a/pkg/tsdb/azuremonitor/azuremonitor_test.go b/pkg/tsdb/azuremonitor/azuremonitor_test.go index 88b8c9bfdd1..25ed3b60cd3 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor_test.go @@ -12,7 +12,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-azure-sdk-go/azcredentials" - "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" @@ -24,6 +23,32 @@ import ( "github.com/stretchr/testify/require" ) +var testRoutes = map[string]types.AzRoute{ + azureMonitor: { + URL: "https://management.azure.com", + Scopes: []string{"https://management.azure.com/.default"}, + Headers: map[string]string{"x-ms-app": "Grafana"}, + }, + azureLogAnalytics: { + URL: "https://api.loganalytics.io", + Scopes: []string{"https://api.loganalytics.io/.default"}, + Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, + }, + azureResourceGraph: { + URL: "https://management.azure.com", + Scopes: []string{"https://management.azure.com/.default"}, + Headers: map[string]string{"x-ms-app": "Grafana"}, + }, + azureTraces: { + URL: "https://api.loganalytics.io", + Scopes: []string{"https://api.loganalytics.io/.default"}, + Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, + }, + azurePortal: { + URL: "https://portal.azure.com", + }, +} + func TestNewInstanceSettings(t *testing.T) { tests := []struct { name string @@ -39,10 +64,9 @@ func TestNewInstanceSettings(t *testing.T) { ID: 40, }, expectedModel: types.DatasourceInfo{ - Cloud: azsettings.AzurePublic, Credentials: &azcredentials.AzureManagedIdentityCredentials{}, Settings: types.AzureMonitorSettings{}, - Routes: routes[azsettings.AzurePublic], + Routes: testRoutes, JSONData: map[string]any{"azureAuthType": "msi"}, DatasourceID: 40, DecryptedSecureJSONData: map[string]string{"key": "value"}, @@ -58,7 +82,6 @@ func TestNewInstanceSettings(t *testing.T) { ID: 50, }, expectedModel: types.DatasourceInfo{ - Cloud: "AzureCustomizedCloud", Credentials: &azcredentials.AzureClientSecretCredentials{ AzureCloud: "AzureCustomizedCloud", ClientSecret: "secret", @@ -99,7 +122,6 @@ func TestNewInstanceSettings(t *testing.T) { } type fakeInstance struct { - cloud string routes map[string]types.AzRoute services map[string]types.DatasourceService settings types.AzureMonitorSettings @@ -107,7 +129,6 @@ type fakeInstance struct { func (f *fakeInstance) Get(_ context.Context, _ backend.PluginContext) (instancemgmt.Instance, error) { return types.DatasourceInfo{ - Cloud: f.cloud, Routes: f.routes, Services: f.services, Settings: f.settings, @@ -149,19 +170,19 @@ func Test_newMux(t *testing.T) { { name: "creates an Azure Monitor executor", queryType: azureMonitor, - expectedURL: routes[azsettings.AzurePublic][azureMonitor].URL, + expectedURL: testRoutes[azureMonitor].URL, Err: require.NoError, }, { name: "creates an Azure Log Analytics executor", queryType: azureLogAnalytics, - expectedURL: routes[azsettings.AzurePublic][azureLogAnalytics].URL, + expectedURL: testRoutes[azureLogAnalytics].URL, Err: require.NoError, }, { name: "creates an Azure Traces executor", queryType: azureTraces, - expectedURL: routes[azsettings.AzurePublic][azureLogAnalytics].URL, + expectedURL: testRoutes[azureLogAnalytics].URL, Err: require.NoError, }, } @@ -170,10 +191,10 @@ func Test_newMux(t *testing.T) { t.Run(tt.name, func(t *testing.T) { s := &Service{ im: &fakeInstance{ - routes: routes[azsettings.AzurePublic], + routes: testRoutes, services: map[string]types.DatasourceService{ tt.queryType: { - URL: routes[azsettings.AzurePublic][tt.queryType].URL, + URL: testRoutes[tt.queryType].URL, HTTPClient: &http.Client{}, }, }, @@ -302,7 +323,6 @@ func TestCheckHealth(t *testing.T) { }) } - cloud := "AzureCloud" tests := []struct { name string errorExpected bool @@ -318,15 +338,15 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: azureMonitorClient(false, false), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: okClient, }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: okClient, }}, }, @@ -341,15 +361,15 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: azureMonitorClient(false, true), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: okClient, }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: okClient, }}, }, @@ -364,15 +384,15 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: azureMonitorClient(false, false), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: failClient(false), }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: okClient, }}, }, @@ -387,15 +407,15 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: azureMonitorClient(false, false), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: okClient, }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: failClient(false), }}, }, @@ -410,15 +430,15 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: azureMonitorClient(true, false), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: okClient, }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: okClient, }}, }, @@ -433,23 +453,22 @@ func TestCheckHealth(t *testing.T) { }, customServices: map[string]types.DatasourceService{ azureMonitor: { - URL: routes[cloud]["Azure Monitor"].URL, + URL: testRoutes["Azure Monitor"].URL, HTTPClient: failClient(true), }, azureLogAnalytics: { - URL: routes[cloud]["Azure Log Analytics"].URL, + URL: testRoutes["Azure Log Analytics"].URL, HTTPClient: failClient(true), }, azureResourceGraph: { - URL: routes[cloud]["Azure Resource Graph"].URL, + URL: testRoutes["Azure Resource Graph"].URL, HTTPClient: failClient(true), }}, }, } instance := &fakeInstance{ - cloud: cloud, - routes: routes[cloud], + routes: testRoutes, services: map[string]types.DatasourceService{}, settings: types.AzureMonitorSettings{ LogAnalyticsDefaultWorkspace: "workspace-id", diff --git a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go index 94c76e46518..35640d45d82 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go +++ b/pkg/tsdb/azuremonitor/loganalytics/azure-log-analytics-datasource.go @@ -326,12 +326,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A return &dataResponse, nil } - azurePortalBaseUrl, err := GetAzurePortalUrl(dsInfo.Cloud) - if err != nil { - return nil, err - } - - queryUrl, err := getQueryUrl(query.Query, query.Resources, azurePortalBaseUrl, query.TimeRange) + queryUrl, err := getQueryUrl(query.Query, query.Resources, dsInfo.Routes["Azure Portal"].URL, query.TimeRange) if err != nil { return nil, err } @@ -365,7 +360,7 @@ func (e *AzureLogAnalyticsDatasource) executeQuery(ctx context.Context, query *A } // Use the parent span query for the parent span data link - err = addDataLinksToFields(query, azurePortalBaseUrl, frame, dsInfo, queryUrl) + err = addDataLinksToFields(query, dsInfo.Routes["Azure Portal"].URL, frame, dsInfo, queryUrl) if err != nil { return nil, err } diff --git a/pkg/tsdb/azuremonitor/loganalytics/utils.go b/pkg/tsdb/azuremonitor/loganalytics/utils.go index 2a3f36e64c9..402acc4eaa5 100644 --- a/pkg/tsdb/azuremonitor/loganalytics/utils.go +++ b/pkg/tsdb/azuremonitor/loganalytics/utils.go @@ -1,9 +1,6 @@ package loganalytics import ( - "fmt" - - "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/data" ) @@ -34,16 +31,3 @@ func AddConfigLinks(frame data.Frame, dl string, title *string) data.Frame { return frame } - -func GetAzurePortalUrl(azureCloud string) (string, error) { - switch azureCloud { - case azsettings.AzurePublic: - return "https://portal.azure.com", nil - case azsettings.AzureChina: - return "https://portal.azure.cn", nil - case azsettings.AzureUSGovernment: - return "https://portal.azure.us", nil - default: - return "", fmt.Errorf("the cloud is not supported") - } -} diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index 77bd4a513f5..ad55bbb4a7a 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -339,17 +339,12 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, query *types. return nil, err } - azurePortalUrl, err := loganalytics.GetAzurePortalUrl(dsInfo.Cloud) - if err != nil { - return nil, err - } - subscription, err := e.retrieveSubscriptionDetails(cli, ctx, query.Subscription, dsInfo.Routes["Azure Monitor"].URL, dsInfo.DatasourceID, dsInfo.OrgID) if err != nil { return nil, err } - frames, err := e.parseResponse(data, query, azurePortalUrl, subscription) + frames, err := e.parseResponse(data, query, dsInfo.Routes["Azure Portal"].URL, subscription) if err != nil { return nil, err } diff --git a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go index 518ef43ad0f..b9616ae8cc1 100644 --- a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go +++ b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource.go @@ -190,12 +190,7 @@ func (e *AzureResourceGraphDatasource) executeQuery(ctx context.Context, query * return &dataResponse, nil } - azurePortalUrl, err := loganalytics.GetAzurePortalUrl(dsInfo.Cloud) - if err != nil { - return nil, err - } - - url := azurePortalUrl + "/#blade/HubsExtension/ArgQueryBlade/query/" + url.PathEscape(query.InterpolatedQuery) + url := dsInfo.Routes["Azure Portal"].URL + "/#blade/HubsExtension/ArgQueryBlade/query/" + url.PathEscape(query.InterpolatedQuery) frameWithLink := loganalytics.AddConfigLinks(*frame, url, nil) if frameWithLink.Meta == nil { frameWithLink.Meta = &data.FrameMeta{} diff --git a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go index 751e0757508..f1f7f0ca8ae 100644 --- a/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go +++ b/pkg/tsdb/azuremonitor/resourcegraph/azure-resource-graph-datasource_test.go @@ -10,7 +10,6 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" - "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" @@ -136,23 +135,6 @@ func TestAddConfigData(t *testing.T) { } } -func TestGetAzurePortalUrl(t *testing.T) { - clouds := []string{azsettings.AzurePublic, azsettings.AzureChina, azsettings.AzureUSGovernment} - expectedAzurePortalUrl := map[string]any{ - azsettings.AzurePublic: "https://portal.azure.com", - azsettings.AzureChina: "https://portal.azure.cn", - azsettings.AzureUSGovernment: "https://portal.azure.us", - } - - for _, cloud := range clouds { - azurePortalUrl, err := loganalytics.GetAzurePortalUrl(cloud) - if err != nil { - t.Errorf("The cloud not supported") - } - assert.Equal(t, expectedAzurePortalUrl[cloud], azurePortalUrl) - } -} - func TestUnmarshalResponse400(t *testing.T) { datasource := &AzureResourceGraphDatasource{} res, err := datasource.unmarshalResponse(&http.Response{ diff --git a/pkg/tsdb/azuremonitor/routes.go b/pkg/tsdb/azuremonitor/routes.go index 430c1523add..a7ca85e40ba 100644 --- a/pkg/tsdb/azuremonitor/routes.go +++ b/pkg/tsdb/azuremonitor/routes.go @@ -1,6 +1,12 @@ package azuremonitor import ( + "encoding/json" + "fmt" + "net/url" + "path" + + "github.com/grafana/grafana-azure-sdk-go/azcredentials" "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" @@ -12,63 +18,99 @@ const ( azureLogAnalytics = "Azure Log Analytics" azureResourceGraph = "Azure Resource Graph" azureTraces = "Azure Traces" + azurePortal = "Azure Portal" ) -var azManagement = types.AzRoute{ - URL: "https://management.azure.com", - Scopes: []string{"https://management.azure.com/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana"}, -} - -var azUSGovManagement = types.AzRoute{ - URL: "https://management.usgovcloudapi.net", - Scopes: []string{"https://management.usgovcloudapi.net/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana"}, -} - -var azChinaManagement = types.AzRoute{ - URL: "https://management.chinacloudapi.cn", - Scopes: []string{"https://management.chinacloudapi.cn/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana"}, -} - -var azLogAnalytics = types.AzRoute{ - URL: "https://api.loganalytics.io", - Scopes: []string{"https://api.loganalytics.io/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, -} - -var azChinaLogAnalytics = types.AzRoute{ - URL: "https://api.loganalytics.azure.cn", - Scopes: []string{"https://api.loganalytics.azure.cn/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, -} - -var azUSGovLogAnalytics = types.AzRoute{ - URL: "https://api.loganalytics.us", - Scopes: []string{"https://api.loganalytics.us/.default"}, - Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, -} - -var ( - // The different Azure routes are identified by its cloud (e.g. public or gov) - // and the service to query (e.g. Azure Monitor or Azure Log Analytics) - routes = map[string]map[string]types.AzRoute{ - azsettings.AzurePublic: { - azureMonitor: azManagement, - azureLogAnalytics: azLogAnalytics, - azureResourceGraph: azManagement, - azureTraces: azLogAnalytics, - }, - azsettings.AzureUSGovernment: { - azureMonitor: azUSGovManagement, - azureLogAnalytics: azUSGovLogAnalytics, - azureResourceGraph: azUSGovManagement, - }, - azsettings.AzureChina: { - azureMonitor: azChinaManagement, - azureLogAnalytics: azChinaLogAnalytics, - azureResourceGraph: azChinaManagement, - }, +func getAzureMonitorRoutes(settings *azsettings.AzureSettings, credentials azcredentials.AzureCredentials, jsonData json.RawMessage) (map[string]types.AzRoute, error) { + azureCloud, err := azcredentials.GetAzureCloud(settings, credentials) + if err != nil { + return nil, err } -) + + if azureCloud == azsettings.AzureCustomized { + routes, err := getCustomizedCloudRoutes(jsonData) + if err != nil { + return nil, err + } + return routes, nil + } + + cloudSettings, err := settings.GetCloud(azureCloud) + if err != nil { + return nil, err + } + + resourceManagerUrl, ok := cloudSettings.Properties["resourceManager"] + if !ok { + err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Resource Manager", azureCloud) + return nil, err + } + resourceManagerScopes, err := audienceToScopes(resourceManagerUrl) + if err != nil { + return nil, err + } + resourceManagerRoute := types.AzRoute{ + URL: resourceManagerUrl, + Scopes: resourceManagerScopes, + Headers: map[string]string{"x-ms-app": "Grafana"}, + } + logAnalyticsUrl, ok := cloudSettings.Properties["logAnalytics"] + if !ok { + err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Log Analytics", azureCloud) + return nil, err + } + logAnalyticsScopes, err := audienceToScopes(logAnalyticsUrl) + if err != nil { + return nil, err + } + logAnalyticsRoute := types.AzRoute{ + URL: logAnalyticsUrl, + Scopes: logAnalyticsScopes, + Headers: map[string]string{"x-ms-app": "Grafana", "Cache-Control": "public, max-age=60"}, + } + portalUrl, ok := cloudSettings.Properties["portal"] + if !ok { + err := fmt.Errorf("the Azure cloud '%s' doesn't have configuration for Azure Portal", azureCloud) + return nil, err + } + portalRoute := types.AzRoute{ + URL: portalUrl, + } + + routes := map[string]types.AzRoute{ + azureMonitor: resourceManagerRoute, + azureLogAnalytics: logAnalyticsRoute, + azureResourceGraph: resourceManagerRoute, + azureTraces: logAnalyticsRoute, + azurePortal: portalRoute, + } + + return routes, nil +} + +func getCustomizedCloudRoutes(jsonData json.RawMessage) (map[string]types.AzRoute, error) { + customizedCloudSettings := types.AzureMonitorCustomizedCloudSettings{} + err := json.Unmarshal(jsonData, &customizedCloudSettings) + if err != nil { + return nil, fmt.Errorf("error getting customized cloud settings: %w", err) + } + + if customizedCloudSettings.CustomizedRoutes == nil { + return nil, fmt.Errorf("unable to instantiate routes, customizedRoutes must be set") + } + + azureRoutes := customizedCloudSettings.CustomizedRoutes + return azureRoutes, nil +} + +func audienceToScopes(audience string) ([]string, error) { + resourceId, err := url.Parse(audience) + if err != nil || resourceId.Scheme == "" || resourceId.Host == "" { + err = fmt.Errorf("endpoint resource ID (audience) '%s' invalid", audience) + return nil, err + } + + resourceId.Path = path.Join(resourceId.Path, ".default") + scopes := []string{resourceId.String()} + return scopes, nil +} diff --git a/pkg/tsdb/azuremonitor/types/types.go b/pkg/tsdb/azuremonitor/types/types.go index 8b11c4e41c3..f2e15680e1b 100644 --- a/pkg/tsdb/azuremonitor/types/types.go +++ b/pkg/tsdb/azuremonitor/types/types.go @@ -49,7 +49,6 @@ type DatasourceService struct { } type DatasourceInfo struct { - Cloud string Credentials azcredentials.AzureCredentials Settings AzureMonitorSettings Routes map[string]AzRoute