package azuremonitor import ( "context" "fmt" "net/http" "net/url" "strings" "testing" "time" "github.com/google/go-cmp/cmp" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/stretchr/testify/require" ) func TestBuildingAzureLogAnalyticsQueries(t *testing.T) { datasource := &AzureLogAnalyticsDatasource{} fromStart := time.Date(2018, 3, 15, 13, 0, 0, 0, time.UTC).In(time.Local) timeRange := backend.TimeRange{From: fromStart, To: fromStart.Add(34 * time.Minute)} tests := []struct { name string queryModel []backend.DataQuery azureLogAnalyticsQueries []*AzureLogAnalyticsQuery Err require.ErrorAssertionFunc }{ { name: "Query with macros should be interpolated", queryModel: []backend.DataQuery{ { JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf | where $__timeFilter() | where $__contains(Computer, 'comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, $__interval), Computer", "resultFormat": "%s" } }`, timeSeries)), RefID: "A", TimeRange: timeRange, }, }, azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ { RefID: "A", ResultFormat: timeSeries, URL: "v1/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace/query", JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf | where $__timeFilter() | where $__contains(Computer, 'comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, $__interval), Computer", "resultFormat": "%s" } }`, timeSeries)), Params: url.Values{"query": {"query=Perf | where ['TimeGenerated'] >= datetime('2018-03-15T13:00:00Z') and ['TimeGenerated'] <= datetime('2018-03-15T13:34:00Z') | where ['Computer'] in ('comp1','comp2') | summarize avg(CounterValue) by bin(TimeGenerated, 34000ms), Computer"}}, Target: "query=query%3DPerf+%7C+where+%5B%27TimeGenerated%27%5D+%3E%3D+datetime%28%272018-03-15T13%3A00%3A00Z%27%29+and+%5B%27TimeGenerated%27%5D+%3C%3D+datetime%28%272018-03-15T13%3A34%3A00Z%27%29+%7C+where+%5B%27Computer%27%5D+in+%28%27comp1%27%2C%27comp2%27%29+%7C+summarize+avg%28CounterValue%29+by+bin%28TimeGenerated%2C+34000ms%29%2C+Computer", TimeRange: timeRange, }, }, Err: require.NoError, }, { name: "Legacy queries with a workspace GUID should use workspace-centric url", queryModel: []backend.DataQuery{ { JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "workspace": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), RefID: "A", }, }, azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ { RefID: "A", ResultFormat: timeSeries, URL: "v1/workspaces/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/query", JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "workspace": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), Params: url.Values{"query": {"query=Perf"}}, Target: "query=query%3DPerf", }, }, Err: require.NoError, }, { name: "Legacy workspace queries with a resource URI (from a template variable) should use resource-centric url", queryModel: []backend.DataQuery{ { JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "workspace": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), RefID: "A", }, }, azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ { RefID: "A", ResultFormat: timeSeries, URL: "v1/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace/query", JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "workspace": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), Params: url.Values{"query": {"query=Perf"}}, Target: "query=query%3DPerf", }, }, Err: require.NoError, }, { name: "Queries with a Resource should use resource-centric url", queryModel: []backend.DataQuery{ { JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), RefID: "A", }, }, azureLogAnalyticsQueries: []*AzureLogAnalyticsQuery{ { RefID: "A", ResultFormat: timeSeries, URL: "v1/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace/query", JSON: []byte(fmt.Sprintf(`{ "queryType": "Azure Log Analytics", "azureLogAnalytics": { "resource": "/subscriptions/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee/resourceGroups/cloud-datasources/providers/Microsoft.OperationalInsights/workspaces/AppInsightsTestDataWorkspace", "query": "query=Perf", "resultFormat": "%s" } }`, timeSeries)), Params: url.Values{"query": {"query=Perf"}}, Target: "query=query%3DPerf", }, }, Err: require.NoError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { queries, err := datasource.buildQueries(tt.queryModel, datasourceInfo{}) tt.Err(t, err) if diff := cmp.Diff(tt.azureLogAnalyticsQueries[0], queries[0]); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } }) } } func TestLogAnalyticsCreateRequest(t *testing.T) { ctx := context.Background() url := "http://ds" dsInfo := datasourceInfo{} tests := []struct { name string expectedURL string expectedHeaders http.Header Err require.ErrorAssertionFunc }{ { name: "creates a request", expectedURL: "http://ds/", expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, Err: require.NoError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ds := AzureLogAnalyticsDatasource{} req, err := ds.createRequest(ctx, dsInfo, url) tt.Err(t, err) if req.URL.String() != tt.expectedURL { t.Errorf("Expecting %s, got %s", tt.expectedURL, req.URL.String()) } if !cmp.Equal(req.Header, tt.expectedHeaders) { t.Errorf("Unexpected HTTP headers: %v", cmp.Diff(req.Header, tt.expectedHeaders)) } }) } } func Test_executeQueryErrorWithDifferentLogAnalyticsCreds(t *testing.T) { ds := AzureLogAnalyticsDatasource{} dsInfo := datasourceInfo{ Services: map[string]datasourceService{ azureLogAnalytics: {URL: "http://ds"}, }, JSONData: map[string]interface{}{ "azureLogAnalyticsSameAs": false, }, } ctx := context.TODO() query := &AzureLogAnalyticsQuery{ Params: url.Values{}, TimeRange: backend.TimeRange{}, } res := ds.executeQuery(ctx, query, dsInfo, &http.Client{}, dsInfo.Services[azureLogAnalytics].URL) if res.Error == nil { t.Fatal("expecting an error") } if !strings.Contains(res.Error.Error(), "Log Analytics credentials are no longer supported") { t.Error("expecting the error to inform of bad credentials") } }