mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 15:08:03 +08:00
Cloudwatch: use CheckHealth for testing datasource (#45974)
Co-authored-by: Shirley Leu <4163034+fridgepoet@users.noreply.github.com>
This commit is contained in:
@ -169,6 +169,49 @@ func (e *cloudWatchExecutor) CallResource(ctx context.Context, req *backend.Call
|
||||
return e.resourceHandler.CallResource(ctx, req, sender)
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) checkHealthMetrics(pluginCtx backend.PluginContext) error {
|
||||
namespace := "AWS/Billing"
|
||||
metric := "EstimatedCharges"
|
||||
params := &cloudwatch.ListMetricsInput{
|
||||
Namespace: &namespace,
|
||||
MetricName: &metric,
|
||||
}
|
||||
_, err := e.listMetrics(pluginCtx, defaultRegion, params)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error {
|
||||
logsClient, err := e.getCWLogsClient(pluginCtx, defaultRegion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = e.handleDescribeLogGroups(ctx, logsClient, simplejson.NewFromAny(map[string]interface{}{"limit": "1"}))
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
|
||||
status := backend.HealthStatusOk
|
||||
metricsTest := "Successfully queried the CloudWatch metrics API."
|
||||
logsTest := "Successfully queried the CloudWatch logs API."
|
||||
|
||||
err := e.checkHealthMetrics(req.PluginContext)
|
||||
if err != nil {
|
||||
status = backend.HealthStatusError
|
||||
metricsTest = fmt.Sprintf("CloudWatch metrics query failed: %s", err.Error())
|
||||
}
|
||||
|
||||
err = e.checkHealthLogs(ctx, req.PluginContext)
|
||||
if err != nil {
|
||||
status = backend.HealthStatusError
|
||||
logsTest = fmt.Sprintf("CloudWatch logs query failed: %s", err.Error())
|
||||
}
|
||||
|
||||
return &backend.CheckHealthResult{
|
||||
Status: status,
|
||||
Message: fmt.Sprintf("1. %s\n2. %s", metricsTest, logsTest),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (e *cloudWatchExecutor) newSession(pluginCtx backend.PluginContext, region string) (*session.Session, error) {
|
||||
dsInfo, err := e.getDSInfo(pluginCtx)
|
||||
if err != nil {
|
||||
|
@ -1,12 +1,24 @@
|
||||
package cloudwatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
@ -72,3 +84,101 @@ func TestNewInstanceSettings(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
@ -172,6 +172,29 @@ func (c fakeRGTAClient) GetResourcesPages(in *resourcegroupstaggingapi.GetResour
|
||||
return nil
|
||||
}
|
||||
|
||||
type fakeCheckHealthClient struct {
|
||||
cloudwatchiface.CloudWatchAPI
|
||||
cloudwatchlogsiface.CloudWatchLogsAPI
|
||||
|
||||
listMetricsPages func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error
|
||||
describeLogGroupsWithContext func(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput,
|
||||
options ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error)
|
||||
}
|
||||
|
||||
func (c fakeCheckHealthClient) ListMetricsPages(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error {
|
||||
if c.listMetricsPages != nil {
|
||||
return c.listMetricsPages(input, fn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c fakeCheckHealthClient) DescribeLogGroupsWithContext(ctx aws.Context, input *cloudwatchlogs.DescribeLogGroupsInput, options ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
|
||||
if c.describeLogGroupsWithContext != nil {
|
||||
return c.describeLogGroupsWithContext(ctx, input, options...)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func chunkSlice(slice []*cloudwatch.Metric, chunkSize int) [][]*cloudwatch.Metric {
|
||||
var chunks [][]*cloudwatch.Metric
|
||||
for {
|
||||
@ -194,9 +217,13 @@ func newTestConfig() *setting.Cfg {
|
||||
}
|
||||
|
||||
type fakeSessionCache struct {
|
||||
getSession func(c awsds.SessionConfig) (*session.Session, error)
|
||||
}
|
||||
|
||||
func (s fakeSessionCache) GetSession(c awsds.SessionConfig) (*session.Session, error) {
|
||||
if s.getSession != nil {
|
||||
return s.getSession(c)
|
||||
}
|
||||
return &session.Session{
|
||||
Config: &aws.Config{},
|
||||
}, nil
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
toLegacyResponseData,
|
||||
} from '@grafana/data';
|
||||
import { DataSourceWithBackend, FetchError, getBackendSrv, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { toTestingStatus } from '@grafana/runtime/src/utils/queryResponse';
|
||||
import { RowContextOptions } from '@grafana/ui/src/components/Logs/LogRowContextProvider';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
@ -870,24 +869,6 @@ export class CloudWatchDatasource
|
||||
);
|
||||
}
|
||||
|
||||
async testDatasource() {
|
||||
// use billing metrics for test
|
||||
const region = this.defaultRegion;
|
||||
const namespace = 'AWS/Billing';
|
||||
const metricName = 'EstimatedCharges';
|
||||
const dimensions = {};
|
||||
|
||||
try {
|
||||
await this.getDimensionValues(region ?? '', namespace, metricName, 'ServiceName', dimensions);
|
||||
return {
|
||||
status: 'success',
|
||||
message: 'Data source is working',
|
||||
};
|
||||
} catch (error) {
|
||||
return toTestingStatus(error);
|
||||
}
|
||||
}
|
||||
|
||||
awsRequest(url: string, data: MetricRequest, headers: Record<string, any> = {}): Observable<TSDBResponse> {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
|
Reference in New Issue
Block a user