346 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			346 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| package cloudwatch
 | |
| 
 | |
| import (
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/aws/aws-sdk-go/aws"
 | |
| 	"github.com/aws/aws-sdk-go/service/cloudwatch"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf/filter"
 | |
| 	"github.com/influxdata/telegraf/internal"
 | |
| 	"github.com/influxdata/telegraf/testutil"
 | |
| )
 | |
| 
 | |
| type mockGatherCloudWatchClient struct{}
 | |
| 
 | |
| func (m *mockGatherCloudWatchClient) ListMetrics(params *cloudwatch.ListMetricsInput) (*cloudwatch.ListMetricsOutput, error) {
 | |
| 	return &cloudwatch.ListMetricsOutput{
 | |
| 		Metrics: []*cloudwatch.Metric{
 | |
| 			{
 | |
| 				Namespace:  params.Namespace,
 | |
| 				MetricName: aws.String("Latency"),
 | |
| 				Dimensions: []*cloudwatch.Dimension{
 | |
| 					{
 | |
| 						Name:  aws.String("LoadBalancerName"),
 | |
| 						Value: aws.String("p-example"),
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func (m *mockGatherCloudWatchClient) GetMetricData(params *cloudwatch.GetMetricDataInput) (*cloudwatch.GetMetricDataOutput, error) {
 | |
| 	return &cloudwatch.GetMetricDataOutput{
 | |
| 		MetricDataResults: []*cloudwatch.MetricDataResult{
 | |
| 			{
 | |
| 				Id:         aws.String("minimum_0_0"),
 | |
| 				Label:      aws.String("latency_minimum"),
 | |
| 				StatusCode: aws.String("completed"),
 | |
| 				Timestamps: []*time.Time{
 | |
| 					params.EndTime,
 | |
| 				},
 | |
| 				Values: []*float64{
 | |
| 					aws.Float64(0.1),
 | |
| 				},
 | |
| 			},
 | |
| 			{
 | |
| 				Id:         aws.String("maximum_0_0"),
 | |
| 				Label:      aws.String("latency_maximum"),
 | |
| 				StatusCode: aws.String("completed"),
 | |
| 				Timestamps: []*time.Time{
 | |
| 					params.EndTime,
 | |
| 				},
 | |
| 				Values: []*float64{
 | |
| 					aws.Float64(0.3),
 | |
| 				},
 | |
| 			},
 | |
| 			{
 | |
| 				Id:         aws.String("average_0_0"),
 | |
| 				Label:      aws.String("latency_average"),
 | |
| 				StatusCode: aws.String("completed"),
 | |
| 				Timestamps: []*time.Time{
 | |
| 					params.EndTime,
 | |
| 				},
 | |
| 				Values: []*float64{
 | |
| 					aws.Float64(0.2),
 | |
| 				},
 | |
| 			},
 | |
| 			{
 | |
| 				Id:         aws.String("sum_0_0"),
 | |
| 				Label:      aws.String("latency_sum"),
 | |
| 				StatusCode: aws.String("completed"),
 | |
| 				Timestamps: []*time.Time{
 | |
| 					params.EndTime,
 | |
| 				},
 | |
| 				Values: []*float64{
 | |
| 					aws.Float64(123),
 | |
| 				},
 | |
| 			},
 | |
| 			{
 | |
| 				Id:         aws.String("sample_count_0_0"),
 | |
| 				Label:      aws.String("latency_sample_count"),
 | |
| 				StatusCode: aws.String("completed"),
 | |
| 				Timestamps: []*time.Time{
 | |
| 					params.EndTime,
 | |
| 				},
 | |
| 				Values: []*float64{
 | |
| 					aws.Float64(100),
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| func TestSnakeCase(t *testing.T) {
 | |
| 	assert.Equal(t, "cluster_name", snakeCase("Cluster Name"))
 | |
| 	assert.Equal(t, "broker_id", snakeCase("Broker ID"))
 | |
| }
 | |
| 
 | |
| func TestGather(t *testing.T) {
 | |
| 	duration, _ := time.ParseDuration("1m")
 | |
| 	internalDuration := internal.Duration{
 | |
| 		Duration: duration,
 | |
| 	}
 | |
| 	c := &CloudWatch{
 | |
| 		Region:    "us-east-1",
 | |
| 		Namespace: "AWS/ELB",
 | |
| 		Delay:     internalDuration,
 | |
| 		Period:    internalDuration,
 | |
| 		RateLimit: 200,
 | |
| 	}
 | |
| 
 | |
| 	var acc testutil.Accumulator
 | |
| 	c.client = &mockGatherCloudWatchClient{}
 | |
| 
 | |
| 	assert.NoError(t, acc.GatherError(c.Gather))
 | |
| 
 | |
| 	fields := map[string]interface{}{}
 | |
| 	fields["latency_minimum"] = 0.1
 | |
| 	fields["latency_maximum"] = 0.3
 | |
| 	fields["latency_average"] = 0.2
 | |
| 	fields["latency_sum"] = 123.0
 | |
| 	fields["latency_sample_count"] = 100.0
 | |
| 
 | |
| 	tags := map[string]string{}
 | |
| 	tags["region"] = "us-east-1"
 | |
| 	tags["load_balancer_name"] = "p-example"
 | |
| 
 | |
| 	assert.True(t, acc.HasMeasurement("cloudwatch_aws_elb"))
 | |
| 	acc.AssertContainsTaggedFields(t, "cloudwatch_aws_elb", fields, tags)
 | |
| }
 | |
| 
 | |
| type mockSelectMetricsCloudWatchClient struct{}
 | |
| 
 | |
| func (m *mockSelectMetricsCloudWatchClient) ListMetrics(params *cloudwatch.ListMetricsInput) (*cloudwatch.ListMetricsOutput, error) {
 | |
| 	metrics := []*cloudwatch.Metric{}
 | |
| 	// 4 metrics are available
 | |
| 	metricNames := []string{"Latency", "RequestCount", "HealthyHostCount", "UnHealthyHostCount"}
 | |
| 	// for 3 ELBs
 | |
| 	loadBalancers := []string{"lb-1", "lb-2", "lb-3"}
 | |
| 	// in 2 AZs
 | |
| 	availabilityZones := []string{"us-east-1a", "us-east-1b"}
 | |
| 	for _, m := range metricNames {
 | |
| 		for _, lb := range loadBalancers {
 | |
| 			// For each metric/ELB pair, we get an aggregate value across all AZs.
 | |
| 			metrics = append(metrics, &cloudwatch.Metric{
 | |
| 				Namespace:  aws.String("AWS/ELB"),
 | |
| 				MetricName: aws.String(m),
 | |
| 				Dimensions: []*cloudwatch.Dimension{
 | |
| 					{
 | |
| 						Name:  aws.String("LoadBalancerName"),
 | |
| 						Value: aws.String(lb),
 | |
| 					},
 | |
| 				},
 | |
| 			})
 | |
| 			for _, az := range availabilityZones {
 | |
| 				// We get a metric for each metric/ELB/AZ triplet.
 | |
| 				metrics = append(metrics, &cloudwatch.Metric{
 | |
| 					Namespace:  aws.String("AWS/ELB"),
 | |
| 					MetricName: aws.String(m),
 | |
| 					Dimensions: []*cloudwatch.Dimension{
 | |
| 						{
 | |
| 							Name:  aws.String("LoadBalancerName"),
 | |
| 							Value: aws.String(lb),
 | |
| 						},
 | |
| 						{
 | |
| 							Name:  aws.String("AvailabilityZone"),
 | |
| 							Value: aws.String(az),
 | |
| 						},
 | |
| 					},
 | |
| 				})
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	result := &cloudwatch.ListMetricsOutput{
 | |
| 		Metrics: metrics,
 | |
| 	}
 | |
| 	return result, nil
 | |
| }
 | |
| 
 | |
| func (m *mockSelectMetricsCloudWatchClient) GetMetricData(params *cloudwatch.GetMetricDataInput) (*cloudwatch.GetMetricDataOutput, error) {
 | |
| 	return nil, nil
 | |
| }
 | |
| 
 | |
| func TestSelectMetrics(t *testing.T) {
 | |
| 	duration, _ := time.ParseDuration("1m")
 | |
| 	internalDuration := internal.Duration{
 | |
| 		Duration: duration,
 | |
| 	}
 | |
| 	c := &CloudWatch{
 | |
| 		Region:    "us-east-1",
 | |
| 		Namespace: "AWS/ELB",
 | |
| 		Delay:     internalDuration,
 | |
| 		Period:    internalDuration,
 | |
| 		RateLimit: 200,
 | |
| 		Metrics: []*Metric{
 | |
| 			{
 | |
| 				MetricNames: []string{"Latency", "RequestCount"},
 | |
| 				Dimensions: []*Dimension{
 | |
| 					{
 | |
| 						Name:  "LoadBalancerName",
 | |
| 						Value: "*",
 | |
| 					},
 | |
| 					{
 | |
| 						Name:  "AvailabilityZone",
 | |
| 						Value: "*",
 | |
| 					},
 | |
| 				},
 | |
| 			},
 | |
| 		},
 | |
| 	}
 | |
| 	c.client = &mockSelectMetricsCloudWatchClient{}
 | |
| 	filtered, err := getFilteredMetrics(c)
 | |
| 	// We've asked for 2 (out of 4) metrics, over all 3 load balancers in all 2
 | |
| 	// AZs. We should get 12 metrics.
 | |
| 	assert.Equal(t, 12, len(filtered[0].metrics))
 | |
| 	assert.Nil(t, err)
 | |
| }
 | |
| 
 | |
| func TestGenerateStatisticsInputParams(t *testing.T) {
 | |
| 	d := &cloudwatch.Dimension{
 | |
| 		Name:  aws.String("LoadBalancerName"),
 | |
| 		Value: aws.String("p-example"),
 | |
| 	}
 | |
| 
 | |
| 	m := &cloudwatch.Metric{
 | |
| 		MetricName: aws.String("Latency"),
 | |
| 		Dimensions: []*cloudwatch.Dimension{d},
 | |
| 	}
 | |
| 
 | |
| 	duration, _ := time.ParseDuration("1m")
 | |
| 	internalDuration := internal.Duration{
 | |
| 		Duration: duration,
 | |
| 	}
 | |
| 
 | |
| 	c := &CloudWatch{
 | |
| 		Namespace: "AWS/ELB",
 | |
| 		Delay:     internalDuration,
 | |
| 		Period:    internalDuration,
 | |
| 	}
 | |
| 
 | |
| 	c.initializeCloudWatch()
 | |
| 
 | |
| 	now := time.Now()
 | |
| 
 | |
| 	c.updateWindow(now)
 | |
| 
 | |
| 	statFilter, _ := filter.NewIncludeExcludeFilter(nil, nil)
 | |
| 	queries, _ := c.getDataQueries([]filteredMetric{{metrics: []*cloudwatch.Metric{m}, statFilter: statFilter}})
 | |
| 	params := c.getDataInputs(queries)
 | |
| 
 | |
| 	assert.EqualValues(t, *params.EndTime, now.Add(-c.Delay.Duration))
 | |
| 	assert.EqualValues(t, *params.StartTime, now.Add(-c.Period.Duration).Add(-c.Delay.Duration))
 | |
| 	require.Len(t, params.MetricDataQueries, 5)
 | |
| 	assert.Len(t, params.MetricDataQueries[0].MetricStat.Metric.Dimensions, 1)
 | |
| 	assert.EqualValues(t, *params.MetricDataQueries[0].MetricStat.Period, 60)
 | |
| }
 | |
| 
 | |
| func TestGenerateStatisticsInputParamsFiltered(t *testing.T) {
 | |
| 	d := &cloudwatch.Dimension{
 | |
| 		Name:  aws.String("LoadBalancerName"),
 | |
| 		Value: aws.String("p-example"),
 | |
| 	}
 | |
| 
 | |
| 	m := &cloudwatch.Metric{
 | |
| 		MetricName: aws.String("Latency"),
 | |
| 		Dimensions: []*cloudwatch.Dimension{d},
 | |
| 	}
 | |
| 
 | |
| 	duration, _ := time.ParseDuration("1m")
 | |
| 	internalDuration := internal.Duration{
 | |
| 		Duration: duration,
 | |
| 	}
 | |
| 
 | |
| 	c := &CloudWatch{
 | |
| 		Namespace: "AWS/ELB",
 | |
| 		Delay:     internalDuration,
 | |
| 		Period:    internalDuration,
 | |
| 	}
 | |
| 
 | |
| 	c.initializeCloudWatch()
 | |
| 
 | |
| 	now := time.Now()
 | |
| 
 | |
| 	c.updateWindow(now)
 | |
| 
 | |
| 	statFilter, _ := filter.NewIncludeExcludeFilter([]string{"average", "sample_count"}, nil)
 | |
| 	queries, _ := c.getDataQueries([]filteredMetric{{metrics: []*cloudwatch.Metric{m}, statFilter: statFilter}})
 | |
| 	params := c.getDataInputs(queries)
 | |
| 
 | |
| 	assert.EqualValues(t, *params.EndTime, now.Add(-c.Delay.Duration))
 | |
| 	assert.EqualValues(t, *params.StartTime, now.Add(-c.Period.Duration).Add(-c.Delay.Duration))
 | |
| 	require.Len(t, params.MetricDataQueries, 2)
 | |
| 	assert.Len(t, params.MetricDataQueries[0].MetricStat.Metric.Dimensions, 1)
 | |
| 	assert.EqualValues(t, *params.MetricDataQueries[0].MetricStat.Period, 60)
 | |
| }
 | |
| 
 | |
| func TestMetricsCacheTimeout(t *testing.T) {
 | |
| 	cache := &metricCache{
 | |
| 		metrics: []filteredMetric{},
 | |
| 		built:   time.Now(),
 | |
| 		ttl:     time.Minute,
 | |
| 	}
 | |
| 
 | |
| 	assert.True(t, cache.isValid())
 | |
| 	cache.built = time.Now().Add(-time.Minute)
 | |
| 	assert.False(t, cache.isValid())
 | |
| }
 | |
| 
 | |
| func TestUpdateWindow(t *testing.T) {
 | |
| 	duration, _ := time.ParseDuration("1m")
 | |
| 	internalDuration := internal.Duration{
 | |
| 		Duration: duration,
 | |
| 	}
 | |
| 
 | |
| 	c := &CloudWatch{
 | |
| 		Namespace: "AWS/ELB",
 | |
| 		Delay:     internalDuration,
 | |
| 		Period:    internalDuration,
 | |
| 	}
 | |
| 
 | |
| 	now := time.Now()
 | |
| 
 | |
| 	assert.True(t, c.windowEnd.IsZero())
 | |
| 	assert.True(t, c.windowStart.IsZero())
 | |
| 
 | |
| 	c.updateWindow(now)
 | |
| 
 | |
| 	newStartTime := c.windowEnd
 | |
| 
 | |
| 	// initial window just has a single period
 | |
| 	assert.EqualValues(t, c.windowEnd, now.Add(-c.Delay.Duration))
 | |
| 	assert.EqualValues(t, c.windowStart, now.Add(-c.Delay.Duration).Add(-c.Period.Duration))
 | |
| 
 | |
| 	now = time.Now()
 | |
| 	c.updateWindow(now)
 | |
| 
 | |
| 	// subsequent window uses previous end time as start time
 | |
| 	assert.EqualValues(t, c.windowEnd, now.Add(-c.Delay.Duration))
 | |
| 	assert.EqualValues(t, c.windowStart, newStartTime)
 | |
| }
 |