The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/tsdb/cloudwatch/cloudwatch_test.go

249 lines
8.8 KiB

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/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{})
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{})
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{})
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")
}})
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)
_, 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)
_, 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)
})
}