mirror of
https://github.com/grafana/grafana.git
synced 2025-02-16 10:24:54 -06:00
Cloudwatch: Set time zone offset in GMD request (#48772)
* set timezone offset in case feature is enabled * add unit tests * add unit tests * remove unused import
This commit is contained in:
parent
fa37c6c9d3
commit
3106af9eec
@ -9,23 +9,24 @@ import (
|
||||
)
|
||||
|
||||
type cloudWatchQuery struct {
|
||||
RefId string
|
||||
Region string
|
||||
Id string
|
||||
Namespace string
|
||||
MetricName string
|
||||
Statistic string
|
||||
Expression string
|
||||
SqlExpression string
|
||||
ReturnData bool
|
||||
Dimensions map[string][]string
|
||||
Period int
|
||||
Alias string
|
||||
Label string
|
||||
MatchExact bool
|
||||
UsedExpression string
|
||||
MetricQueryType metricQueryType
|
||||
MetricEditorMode metricEditorMode
|
||||
RefId string
|
||||
Region string
|
||||
Id string
|
||||
Namespace string
|
||||
MetricName string
|
||||
Statistic string
|
||||
Expression string
|
||||
SqlExpression string
|
||||
ReturnData bool
|
||||
Dimensions map[string][]string
|
||||
Period int
|
||||
Alias string
|
||||
Label string
|
||||
MatchExact bool
|
||||
UsedExpression string
|
||||
TimezoneUTCOffset string
|
||||
MetricQueryType metricQueryType
|
||||
MetricEditorMode metricEditorMode
|
||||
}
|
||||
|
||||
func (q *cloudWatchQuery) getGMDAPIMode() gmdApiMode {
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
)
|
||||
|
||||
func (e *cloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime time.Time,
|
||||
@ -14,6 +15,15 @@ func (e *cloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime t
|
||||
EndTime: aws.Time(endTime),
|
||||
ScanBy: aws.String("TimestampAscending"),
|
||||
}
|
||||
|
||||
shouldSetLabelOptions := e.features.IsEnabled(featuremgmt.FlagCloudWatchDynamicLabels) && len(queries) > 0 && len(queries[0].TimezoneUTCOffset) > 0
|
||||
|
||||
if shouldSetLabelOptions {
|
||||
metricDataInput.LabelOptions = &cloudwatch.LabelOptions{
|
||||
Timezone: aws.String(queries[0].TimezoneUTCOffset),
|
||||
}
|
||||
}
|
||||
|
||||
for _, query := range queries {
|
||||
metricDataQuery, err := e.buildMetricDataQuery(query)
|
||||
if err != nil {
|
||||
|
44
pkg/tsdb/cloudwatch/metric_data_input_builder_test.go
Normal file
44
pkg/tsdb/cloudwatch/metric_data_input_builder_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
package cloudwatch
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/service/cloudwatch"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMetricDataInputBuilder(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
timezoneUTCOffset string
|
||||
expectedLabelOptions *cloudwatch.LabelOptions
|
||||
featureEnabled bool
|
||||
}{
|
||||
{name: "when timezoneUTCOffset is provided and feature is enabled", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatch.LabelOptions{Timezone: aws.String("+1234")}, featureEnabled: true},
|
||||
{name: "when timezoneUTCOffset is not provided and feature is enabled", timezoneUTCOffset: "", expectedLabelOptions: nil, featureEnabled: true},
|
||||
{name: "when timezoneUTCOffset is provided and feature is disabled", timezoneUTCOffset: "+1234", expectedLabelOptions: nil, featureEnabled: false},
|
||||
{name: "when timezoneUTCOffset is not provided and feature is disabled", timezoneUTCOffset: "", expectedLabelOptions: nil, featureEnabled: false},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
executor := newExecutor(nil, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures(featuremgmt.FlagCloudWatchDynamicLabels, tc.featureEnabled))
|
||||
query := getBaseQuery()
|
||||
query.TimezoneUTCOffset = tc.timezoneUTCOffset
|
||||
|
||||
from := now.Add(time.Hour * -2)
|
||||
to := now.Add(time.Hour * -1)
|
||||
mdi, err := executor.buildMetricDataInput(from, to, []*cloudWatchQuery{query})
|
||||
|
||||
assert.NoError(t, err)
|
||||
require.NotNil(t, mdi)
|
||||
assert.Equal(t, tc.expectedLabelOptions, mdi.LabelOptions)
|
||||
})
|
||||
}
|
||||
}
|
@ -201,6 +201,8 @@ func parseRequestQuery(model *simplejson.Json, refId string, startTime time.Time
|
||||
label := model.Get("label").MustString()
|
||||
returnData := !model.Get("hide").MustBool(false)
|
||||
queryType := model.Get("type").MustString()
|
||||
timezoneUTCOffset := model.Get("timezoneUTCOffset").MustString("")
|
||||
|
||||
if queryType == "" {
|
||||
// If no type is provided we assume we are called by alerting service, which requires to return data!
|
||||
// Note, this is sort of a hack, but the official Grafana interfaces do not carry the information
|
||||
@ -221,23 +223,24 @@ func parseRequestQuery(model *simplejson.Json, refId string, startTime time.Time
|
||||
}
|
||||
|
||||
return &cloudWatchQuery{
|
||||
RefId: refId,
|
||||
Region: region,
|
||||
Id: id,
|
||||
Namespace: namespace,
|
||||
MetricName: metricName,
|
||||
Statistic: statistic,
|
||||
Expression: expression,
|
||||
ReturnData: returnData,
|
||||
Dimensions: dimensions,
|
||||
Period: period,
|
||||
Alias: alias,
|
||||
Label: label,
|
||||
MatchExact: matchExact,
|
||||
UsedExpression: "",
|
||||
MetricQueryType: metricQueryType,
|
||||
MetricEditorMode: metricEditorModeValue,
|
||||
SqlExpression: sqlExpression,
|
||||
RefId: refId,
|
||||
Region: region,
|
||||
Id: id,
|
||||
Namespace: namespace,
|
||||
MetricName: metricName,
|
||||
Statistic: statistic,
|
||||
Expression: expression,
|
||||
ReturnData: returnData,
|
||||
Dimensions: dimensions,
|
||||
Period: period,
|
||||
Alias: alias,
|
||||
Label: label,
|
||||
MatchExact: matchExact,
|
||||
UsedExpression: "",
|
||||
MetricQueryType: metricQueryType,
|
||||
MetricEditorMode: metricEditorModeValue,
|
||||
SqlExpression: sqlExpression,
|
||||
TimezoneUTCOffset: timezoneUTCOffset,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -399,6 +399,50 @@ describe('datasource', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('timezoneUTCOffset', () => {
|
||||
const testQuery = {
|
||||
id: '',
|
||||
refId: 'a',
|
||||
region: 'us-east-2',
|
||||
namespace: '',
|
||||
period: '',
|
||||
label: '${MAX_TIME_RELATIVE}',
|
||||
metricName: '',
|
||||
dimensions: {},
|
||||
matchExact: true,
|
||||
statistic: '',
|
||||
expression: '',
|
||||
metricQueryType: MetricQueryType.Query,
|
||||
metricEditorMode: MetricEditorMode.Code,
|
||||
sqlExpression: 'SELECT SUM($metric) FROM "$namespace" GROUP BY ${labels:raw} LIMIT $limit',
|
||||
};
|
||||
const testTable = [
|
||||
['Europe/Stockholm', '+0200'],
|
||||
['America/New_York', '-0400'],
|
||||
['Asia/Tokyo', '+0900'],
|
||||
['UTC', '+0000'],
|
||||
];
|
||||
describe.each(testTable)('should use the right time zone offset', (ianaTimezone, expectedOffset) => {
|
||||
const { datasource, fetchMock } = setupMockedDataSource();
|
||||
datasource.handleMetricQueries([testQuery], {
|
||||
range: { from: dateTime(), to: dateTime() },
|
||||
timezone: ianaTimezone,
|
||||
} as any);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
queries: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
timezoneUTCOffset: expectedOffset,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertMultiFiltersFormat', () => {
|
||||
const ds = setupMockedDataSource({ variables: [labelsVariable, dimensionVariable], mockGetVariableName: false });
|
||||
it('converts keys and values correctly', () => {
|
||||
|
@ -12,6 +12,7 @@ import {
|
||||
DataSourceInstanceSettings,
|
||||
DataSourceWithLogsContextSupport,
|
||||
dateMath,
|
||||
dateTimeFormat,
|
||||
FieldType,
|
||||
LoadingState,
|
||||
LogRowModel,
|
||||
@ -22,6 +23,7 @@ import {
|
||||
import { DataSourceWithBackend, FetchError, getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { config } from 'app/core/config';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
|
||||
@ -29,8 +31,6 @@ import { VariableWithMultiSupport } from 'app/features/variables/types';
|
||||
import { store } from 'app/store/store';
|
||||
import { AppNotificationTimeout } from 'app/types';
|
||||
|
||||
import config from '../../../core/config';
|
||||
|
||||
import { CloudWatchAnnotationSupport } from './annotationSupport';
|
||||
import { SQLCompletionItemProvider } from './cloudwatch-sql/completion/CompletionItemProvider';
|
||||
import { ThrottlingErrorMessage } from './components/ThrottlingErrorMessage';
|
||||
@ -290,11 +290,17 @@ export class CloudWatchDatasource
|
||||
metricQueries: CloudWatchMetricsQuery[],
|
||||
options: DataQueryRequest<CloudWatchQuery>
|
||||
): Observable<DataQueryResponse> => {
|
||||
const timezoneUTCOffset = dateTimeFormat(Date.now(), {
|
||||
timeZone: options.timezone,
|
||||
format: 'Z',
|
||||
}).replace(':', '');
|
||||
|
||||
const validMetricsQueries = metricQueries.filter(this.filterQuery).map((q: CloudWatchMetricsQuery): MetricQuery => {
|
||||
const migratedQuery = migrateMetricQuery(q);
|
||||
const migratedAndIterpolatedQuery = this.replaceMetricQueryVars(migratedQuery, options);
|
||||
|
||||
return {
|
||||
timezoneUTCOffset,
|
||||
intervalMs: options.intervalMs,
|
||||
maxDataPoints: options.maxDataPoints,
|
||||
...migratedAndIterpolatedQuery,
|
||||
|
Loading…
Reference in New Issue
Block a user