grafana/pkg/tsdb/cloudwatch/cloudwatch_test.go

251 lines
9.1 KiB
Go

package cloudwatch
import (
"context"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
awsrequest "github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/google/go-cmp/cmp"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana/pkg/infra/httpclient"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewInstanceSettings(t *testing.T) {
tests := []struct {
name string
settings backend.DataSourceInstanceSettings
expectedDS datasourceInfo
Err require.ErrorAssertionFunc
}{
{
name: "creates a request",
settings: backend.DataSourceInstanceSettings{
JSONData: []byte(`{
"profile": "foo",
"defaultRegion": "us-east2",
"assumeRoleArn": "role",
"externalId": "id",
"endpoint": "bar",
"customMetricsNamespaces": "ns",
"authType": "keys"
}`),
DecryptedSecureJSONData: map[string]string{
"accessKey": "A123",
"secretKey": "secret",
},
},
expectedDS: datasourceInfo{
profile: "foo",
region: "us-east2",
assumeRoleARN: "role",
externalID: "id",
endpoint: "bar",
namespace: "ns",
authType: awsds.AuthTypeKeys,
accessKey: "A123",
secretKey: "secret",
},
Err: require.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NewInstanceSettings(httpclient.NewProvider())
model, err := f(tt.settings)
tt.Err(t, err)
datasourceComparer := cmp.Comparer(func(d1 datasourceInfo, d2 datasourceInfo) bool {
return d1.profile == d2.profile &&
d1.region == d2.region &&
d1.authType == d2.authType &&
d1.assumeRoleARN == d2.assumeRoleARN &&
d1.externalID == d2.externalID &&
d1.namespace == d2.namespace &&
d1.endpoint == d2.endpoint &&
d1.accessKey == d2.accessKey &&
d1.secretKey == d2.secretKey &&
d1.datasourceID == d2.datasourceID
})
if !cmp.Equal(model.(datasourceInfo), tt.expectedDS, datasourceComparer) {
t.Errorf("Unexpected result. Expecting\n%v \nGot:\n%v", model, tt.expectedDS)
}
})
}
}
func Test_CheckHealth(t *testing.T) {
origNewCWClient := NewCWClient
origNewCWLogsClient := NewCWLogsClient
t.Cleanup(func() {
NewCWClient = origNewCWClient
NewCWLogsClient = origNewCWLogsClient
})
var client fakeCheckHealthClient
NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
return client
}
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
return client
}
t.Run("successfully query metrics and logs", func(t *testing.T) {
client = fakeCheckHealthClient{}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{}, nil
})
executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
Status: backend.HealthStatusOk,
Message: "1. Successfully queried the CloudWatch metrics API.\n2. Successfully queried the CloudWatch logs API.",
}, resp)
})
t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) {
client = fakeCheckHealthClient{
describeLogGroupsWithContext: func(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput,
options ...awsrequest.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
return nil, fmt.Errorf("some logs query error")
}}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{}, nil
})
executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: "1. Successfully queried the CloudWatch metrics API.\n2. CloudWatch logs query failed: some logs query error",
}, resp)
})
t.Run("successfully queries logs, fails during metrics query", func(t *testing.T) {
client = fakeCheckHealthClient{
listMetricsPages: func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error {
return fmt.Errorf("some list metrics error")
}}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{}, nil
})
executor := newExecutor(im, newTestConfig(), &fakeSessionCache{}, featuremgmt.WithFeatures())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: "1. CloudWatch metrics query failed: some list metrics error\n2. Successfully queried the CloudWatch logs API.",
}, resp)
})
t.Run("fail to get clients", func(t *testing.T) {
client = fakeCheckHealthClient{}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{}, nil
})
executor := newExecutor(im, newTestConfig(), &fakeSessionCache{getSession: func(c awsds.SessionConfig) (*session.Session, error) {
return nil, fmt.Errorf("some sessions error")
}}, featuremgmt.WithFeatures())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: "1. CloudWatch metrics query failed: some sessions error\n2. CloudWatch logs query failed: some sessions error",
}, resp)
})
}
func Test_executeLogAlertQuery(t *testing.T) {
origNewCWClient := NewCWClient
t.Cleanup(func() {
NewCWClient = origNewCWClient
})
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
return &cli
}
t.Run("getCWLogsClient is called with region from input JSON", func(t *testing.T) {
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{}, nil
})
sess := fakeSessionCache{}
executor := newExecutor(im, newTestConfig(), &sess, featuremgmt.WithFeatures())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{"FromAlert": "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
TimeRange: backend.TimeRange{From: time.Unix(0, 0), To: time.Unix(1, 0)},
JSON: json.RawMessage(`{
"queryMode": "Logs",
"region": "some region"
}`),
},
},
})
assert.NoError(t, err)
assert.Equal(t, []string{"some region"}, sess.calledRegions)
})
t.Run("getCWLogsClient is called with region from instance manager when region is default", func(t *testing.T) {
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
im := datasource.NewInstanceManager(func(s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return datasourceInfo{region: "instance manager's region"}, nil
})
sess := fakeSessionCache{}
executor := newExecutor(im, newTestConfig(), &sess, featuremgmt.WithFeatures())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{"FromAlert": "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
TimeRange: backend.TimeRange{From: time.Unix(0, 0), To: time.Unix(1, 0)},
JSON: json.RawMessage(`{
"queryMode": "Logs",
"region": "default"
}`),
},
},
})
assert.NoError(t, err)
assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions)
})
}