azuremonitor: fix auto interval calculation on backend

Not needed for alerting (as the query intervalms will always be 0) but needed
later when being called from the frontend)
This commit is contained in:
Daniel Lee 2019-02-11 13:27:08 +01:00
parent a54484638d
commit 0b74860f55
7 changed files with 45 additions and 35 deletions

View File

@ -89,7 +89,7 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
urlComponents["metricDefinition"] = fmt.Sprintf("%v", azureMonitorTarget["metricDefinition"])
urlComponents["resourceName"] = fmt.Sprintf("%v", azureMonitorTarget["resourceName"])
ub := URLBuilder{
ub := urlBuilder{
ResourceGroup: urlComponents["resourceGroup"],
MetricDefinition: urlComponents["metricDefinition"],
ResourceName: urlComponents["resourceName"],
@ -100,9 +100,9 @@ func (e *AzureMonitorDatasource) buildQueries(queries []*tsdb.Query, timeRange *
timeGrain := fmt.Sprintf("%v", azureMonitorTarget["timeGrain"])
if timeGrain == "auto" {
autoInSeconds := e.findClosestAllowedIntervalMs(query.IntervalMs) / 1000
autoInterval := e.findClosestAllowedIntervalMS(query.IntervalMs)
tg := &TimeGrain{}
timeGrain, err = tg.createISO8601DurationFromInterval(fmt.Sprintf("%vs", autoInSeconds))
timeGrain, err = tg.createISO8601DurationFromIntervalMS(autoInterval)
if err != nil {
return nil, err
}
@ -288,7 +288,7 @@ func (e *AzureMonitorDatasource) parseResponse(queryRes *tsdb.QueryResult, data
// findClosestAllowedIntervalMs is used for the auto time grain setting.
// It finds the closest time grain from the list of allowed time grains for Azure Monitor
// using the Grafana interval in milliseconds
func (e *AzureMonitorDatasource) findClosestAllowedIntervalMs(intervalMs int64) int64 {
func (e *AzureMonitorDatasource) findClosestAllowedIntervalMS(intervalMs int64) int64 {
closest := allowedIntervalsMS[0]
for i, allowed := range allowedIntervalsMS {

View File

@ -240,13 +240,13 @@ func TestAzureMonitorDatasource(t *testing.T) {
"2d": 172800000,
}
closest := datasource.findClosestAllowedIntervalMs(intervals["3m"])
closest := datasource.findClosestAllowedIntervalMS(intervals["3m"])
So(closest, ShouldEqual, intervals["5m"])
closest = datasource.findClosestAllowedIntervalMs(intervals["10m"])
closest = datasource.findClosestAllowedIntervalMS(intervals["10m"])
So(closest, ShouldEqual, intervals["15m"])
closest = datasource.findClosestAllowedIntervalMs(intervals["2d"])
closest = datasource.findClosestAllowedIntervalMS(intervals["2d"])
So(closest, ShouldEqual, intervals["1d"])
})
})

View File

@ -4,6 +4,9 @@ import (
"fmt"
"strconv"
"strings"
"time"
"github.com/grafana/grafana/pkg/tsdb"
)
// TimeGrain handles convertions between
@ -15,28 +18,24 @@ var (
smallTimeUnits = []string{"hour", "minute", "h", "m"}
)
func (tg *TimeGrain) createISO8601DurationFromInterval(interval string) (string, error) {
if strings.Contains(interval, "ms") {
func (tg *TimeGrain) createISO8601DurationFromIntervalMS(interval int64) (string, error) {
formatted := tsdb.FormatDuration(time.Duration(interval) * time.Millisecond)
if strings.Contains(formatted, "ms") {
return "PT1M", nil
}
timeValueString := interval[0 : len(interval)-1]
timeValueString := formatted[0 : len(formatted)-1]
timeValue, err := strconv.Atoi(timeValueString)
if err != nil {
return "", fmt.Errorf("Could not parse interval %v to an ISO 8061 duration", interval)
}
unit := interval[len(interval)-1:]
if unit == "s" {
toMinutes := (timeValue * 60) % 60
unit := formatted[len(formatted)-1:]
if unit == "s" && timeValue < 60 {
// mimumum interval is 1m for Azure Monitor
if toMinutes < 1 {
toMinutes = 1
}
return tg.createISO8601Duration(toMinutes, "m"), nil
return "PT1M", nil
}
return tg.createISO8601Duration(timeValue, unit), nil

View File

@ -37,10 +37,14 @@ func TestTimeGrain(t *testing.T) {
})
})
Convey("create ISO 8601 Duration from Grafana interval", func() {
Convey("create ISO 8601 Duration from Grafana interval in milliseconds", func() {
Convey("and interval is less than a minute", func() {
durationMS, _ := tgc.createISO8601DurationFromInterval("100ms")
durationS, _ := tgc.createISO8601DurationFromInterval("59s")
durationMS, err := tgc.createISO8601DurationFromIntervalMS(100)
So(err, ShouldBeNil)
durationS, err := tgc.createISO8601DurationFromIntervalMS(59999)
So(err, ShouldBeNil)
Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() {
So(durationMS, ShouldEqual, "PT1M")
So(durationS, ShouldEqual, "PT1M")
@ -48,8 +52,15 @@ func TestTimeGrain(t *testing.T) {
})
Convey("and interval is more than a minute", func() {
durationM, _ := tgc.createISO8601DurationFromInterval("10m")
durationD, _ := tgc.createISO8601DurationFromInterval("2d")
intervals := map[string]int64{
"10m": 600000,
"2d": 172800000,
}
durationM, err := tgc.createISO8601DurationFromIntervalMS(intervals["10m"])
So(err, ShouldBeNil)
durationD, err := tgc.createISO8601DurationFromIntervalMS(intervals["2d"])
So(err, ShouldBeNil)
Convey("should be rounded up to a minute as is the minimum interval for Azure Monitor", func() {
So(durationM, ShouldEqual, "PT10M")
So(durationD, ShouldEqual, "P2D")

View File

@ -5,8 +5,8 @@ import (
"strings"
)
// URLBuilder builds the URL for calling the Azure Monitor API
type URLBuilder struct {
// urlBuilder builds the URL for calling the Azure Monitor API
type urlBuilder struct {
ResourceGroup string
MetricDefinition string
ResourceName string
@ -14,7 +14,7 @@ type URLBuilder struct {
// Build checks the metric definition property to see which form of the url
// should be returned
func (ub *URLBuilder) Build() string {
func (ub *urlBuilder) Build() string {
if strings.Count(ub.MetricDefinition, "/") > 1 {
rn := strings.Split(ub.ResourceName, "/")

View File

@ -10,7 +10,7 @@ func TestURLBuilder(t *testing.T) {
Convey("AzureMonitor URL Builder", t, func() {
Convey("when metric definition is in the short form", func() {
ub := &URLBuilder{
ub := &urlBuilder{
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Compute/virtualMachines",
ResourceName: "rn",
@ -21,7 +21,7 @@ func TestURLBuilder(t *testing.T) {
})
Convey("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func() {
ub := &URLBuilder{
ub := &urlBuilder{
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/blobServices",
ResourceName: "rn1/default",
@ -32,7 +32,7 @@ func TestURLBuilder(t *testing.T) {
})
Convey("when metric definition is Microsoft.Storage/storageAccounts/fileServices", func() {
ub := &URLBuilder{
ub := &urlBuilder{
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/fileServices",
ResourceName: "rn1/default",

View File

@ -51,11 +51,11 @@ func TestInterval(t *testing.T) {
})
Convey("Format value", func() {
So(formatDuration(time.Second*61), ShouldEqual, "1m")
So(formatDuration(time.Millisecond*30), ShouldEqual, "30ms")
So(formatDuration(time.Hour*23), ShouldEqual, "23h")
So(formatDuration(time.Hour*24), ShouldEqual, "1d")
So(formatDuration(time.Hour*24*367), ShouldEqual, "1y")
So(FormatDuration(time.Second*61), ShouldEqual, "1m")
So(FormatDuration(time.Millisecond*30), ShouldEqual, "30ms")
So(FormatDuration(time.Hour*23), ShouldEqual, "23h")
So(FormatDuration(time.Hour*24), ShouldEqual, "1d")
So(FormatDuration(time.Hour*24*367), ShouldEqual, "1y")
})
})
}