From 8567ec92b0c65126d9ca7dcc826c74d8b14018a6 Mon Sep 17 00:00:00 2001 From: Sven Grossmann Date: Fri, 14 Apr 2023 16:21:16 +0200 Subject: [PATCH] Cloudwatch: Change type of `ts` field in log responses to `time` (#66525) * make timestamp field time type * Add test for type on handleLogEvents data frame field response --------- Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com> --- pkg/tsdb/cloudwatch/log_actions.go | 5 ++- pkg/tsdb/cloudwatch/log_actions_test.go | 57 ++++++++++++++++++++++++- pkg/tsdb/cloudwatch/mocks/logs.go | 15 +++++++ 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/pkg/tsdb/cloudwatch/log_actions.go b/pkg/tsdb/cloudwatch/log_actions.go index 8866f95e265..306ea4d6cd8 100644 --- a/pkg/tsdb/cloudwatch/log_actions.go +++ b/pkg/tsdb/cloudwatch/log_actions.go @@ -8,6 +8,7 @@ import ( "math" "sort" "strings" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" @@ -164,7 +165,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient } messages := make([]*string, 0) - timestamps := make([]*int64, 0) + timestamps := make([]time.Time, 0) sort.Slice(logEvents.Events, func(i, j int) bool { return *(logEvents.Events[i].Timestamp) > *(logEvents.Events[j].Timestamp) @@ -172,7 +173,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient for _, event := range logEvents.Events { messages = append(messages, event.Message) - timestamps = append(timestamps, event.Timestamp) + timestamps = append(timestamps, time.UnixMilli(*event.Timestamp).UTC()) } timestampField := data.NewField("ts", nil, timestamps) diff --git a/pkg/tsdb/cloudwatch/log_actions_test.go b/pkg/tsdb/cloudwatch/log_actions_test.go index 51573eb205c..cf97feb80d9 100644 --- a/pkg/tsdb/cloudwatch/log_actions_test.go +++ b/pkg/tsdb/cloudwatch/log_actions_test.go @@ -15,13 +15,16 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/data" "github.com/grafana/grafana/pkg/services/featuremgmt" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestQuery_GetLogEvents(t *testing.T) { +func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { NewCWLogsClient = origNewCWLogsClient @@ -107,6 +110,58 @@ func TestQuery_GetLogEvents(t *testing.T) { } } +func TestQuery_GetLogEvents_returns_response_from_GetLogEvents_to_data_frame_field(t *testing.T) { + origNewCWLogsClient := NewCWLogsClient + t.Cleanup(func() { + NewCWLogsClient = origNewCWLogsClient + }) + var cli *mocks.MockLogEvents + NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI { + return cli + } + im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return DataSource{Settings: models.CloudWatchSettings{}}, nil + }) + executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures()) + + cli = &mocks.MockLogEvents{} + cli.On("GetLogEventsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{ + Events: []*cloudwatchlogs.OutputLogEvent{{ + Message: utils.Pointer("some message"), + Timestamp: utils.Pointer(int64(15)), + }}}, nil) + + resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{ + PluginContext: backend.PluginContext{ + DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}, + }, + Queries: []backend.DataQuery{ + { + RefID: "A", + TimeRange: backend.TimeRange{From: time.Unix(0, 0), To: time.Unix(1, 0)}, + JSON: json.RawMessage(`{ + "type": "logAction", + "subtype": "GetLogEvents", + "logGroupName": "foo", + "logStreamName": "bar", + "endTime": 1, + "startFromHead": false + }`), + }, + }, + }) + + require.NoError(t, err) + + respA, ok := resp.Responses["A"] + assert.True(t, ok) + + expectedTsField := data.NewField("ts", nil, []time.Time{time.UnixMilli(15).UTC()}) + expectedMessageField := data.NewField("line", nil, []*string{utils.Pointer("some message")}) + expectedTsField.SetConfig(&data.FieldConfig{DisplayName: "Time"}) + assert.Equal(t, []*data.Field{expectedTsField, expectedMessageField}, respA.Frames[0].Fields) +} + func TestQuery_StartQuery(t *testing.T) { origNewCWLogsClient := NewCWLogsClient t.Cleanup(func() { diff --git a/pkg/tsdb/cloudwatch/mocks/logs.go b/pkg/tsdb/cloudwatch/mocks/logs.go index f080a961f42..54ac6737a16 100644 --- a/pkg/tsdb/cloudwatch/mocks/logs.go +++ b/pkg/tsdb/cloudwatch/mocks/logs.go @@ -1,7 +1,10 @@ package mocks import ( + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/cloudwatchlogs" + "github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources" "github.com/stretchr/testify/mock" ) @@ -47,3 +50,15 @@ func (f *MockFeatures) IsEnabled(feature string) bool { return args.Bool(0) } + +type MockLogEvents struct { + cloudwatchlogsiface.CloudWatchLogsAPI + + mock.Mock +} + +func (m *MockLogEvents) GetLogEventsWithContext(ctx aws.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) { + args := m.Called(ctx, input, option) + + return args.Get(0).(*cloudwatchlogs.GetLogEventsOutput), args.Error(1) +}