Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Integration Test For Metric Number Of Dimension #451

Merged
merged 1 commit into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/integrationTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,8 @@ jobs:
path: go.mod
key: ec2-linux-integration-test-${{ github.sha }}-${{ matrix.arrays.os }}

- name: Echo OS
run: echo run on ec2 instance os ${{ matrix.arrays.os }}
- name: Echo Test Info
run: echo run on ec2 instance os ${{ matrix.arrays.os }} test dir ${{ matrix.arrays.test_dir }}

- name: Verify Terraform version
run: terraform --version
Expand Down Expand Up @@ -392,7 +392,7 @@ jobs:
-var="key_name=${KEY_NAME}" \
-var="test_name=cw-integ-test-${{ matrix.arrays.os }}" \
-var="iam_instance_profile=${IAM_ROLE}" \
-var="tag=${{ matrix.arrays.tag }}" ; then terraform destroy -auto-approve
-var="test_dir=${{ matrix.arrays.test_dir }}" ; then terraform destroy -auto-approve
else
terraform destroy -auto-approve && exit 1
fi
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ require (
github.com/aws/aws-sdk-go-v2 v1.16.2
github.com/aws/aws-sdk-go-v2/config v1.15.3
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.18.1
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.15.4
github.com/aws/aws-sdk-go-v2/service/ec2 v1.29.0
github.com/aws/smithy-go v1.11.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3 h1:9stUQR/u2KXU6HkFJYl
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10 h1:by9P+oy3P/CwggN4ClnW2D4oL91QV7pBzBICi1chZvQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.18.1 h1:8PHGmLw1QbTdXfgEpXclOk3kob72vkc/cEoyBxkmR0M=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.18.1/go.mod h1:Z+8JhhltQDM1vIHvEtQLr1wVVAqQVLpvCDMVqYBrwr8=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.15.4 h1:mBqjBKtZzvAc9j7gU+FEHbhTKSr02iqMOdQIL/7GZ78=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.15.4/go.mod h1:R49Py2lGoKH7bCpwhjN9l7MfR/PU6zHXn1tCRR8cwOs=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.29.0 h1:7jk4NfzDnnSbaR9E4mOBWRZXQThq5rsqjlDC+uu9dsI=
Expand Down
25 changes: 14 additions & 11 deletions integration/generator/test_case_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,27 @@ const (
linux = "linux"
windows = "windows"
mac = "mac"
tag = "tag"
testDir = "test_dir"
)

//you can't have a const map in golang
var osToTagMap = map[string][]string{
linux: {"integration"},
windows: {},
var osToTestDirMap = map[string][]string{
linux: {"./integration/test/ca_bundle",
"./integration/test/cloudwatchlogs",
"./integration/test/metrics_number_dimension"},
// @TODO add real tests
windows: {""},
mac: {},
}

func main() {
for osType, tags := range osToTagMap {
testMatrix := genMatrix(osType, tags)
for osType, testDir := range osToTestDirMap {
testMatrix := genMatrix(osType, testDir)
writeTestMatrixFile(osType, testMatrix)
}
}

func genMatrix(targetOS string, tags []string) []map[string]string {
func genMatrix(targetOS string, testDirList []string) []map[string]string {
openTestMatrix, err := os.Open(fmt.Sprintf("integration/generator/resources/%v_test_matrix.json", targetOS))
if err != nil {
log.Panicf("can't read file %v_test_matrix.json err %v", targetOS, err)
Expand All @@ -47,11 +50,11 @@ func genMatrix(targetOS string, tags []string) []map[string]string {

var testMatrixComplete []map[string]string
for _, test := range testMatrix {
testLine := copyMap(test)
for _, testTag := range tags {
testLine[tag] = testTag
for _, testDirectory := range testDirList {
testLine := copyMap(test)
testLine[testDir] = testDirectory
testMatrixComplete = append(testMatrixComplete, testLine)
}
testMatrixComplete = append(testMatrixComplete, testLine)
}
return testMatrixComplete
}
Expand Down
1 change: 1 addition & 0 deletions integration/terraform/ec2/linux/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ for how to easily generate a new policy.
"Action": [
"cloudwatch:GetMetricData",
"cloudwatch:PutMetricData",
"cloudwatch:ListMetrics"
"ec2:DescribeVolumes",
"ec2:DescribeTags",
"logs:PutLogEvents",
Expand Down
3 changes: 2 additions & 1 deletion integration/terraform/ec2/linux/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ resource "aws_instance" "integration-test" {
"export AWS_REGION=${var.region}",
"echo run tests with the tag integration, one at a time, and verbose",
"cd ~/amazon-cloudwatch-agent",
"go test ./integration/test/... -p 1 -v --tags=${var.tag}"
"echo run sanity test && go test ./integration/test/sanity -p 1 -v --tags=integration",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting. Question - is there any reason we wouldn't want to break out the sanity test as a separate workflow from the integration tests? 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sanity is here to test if things are working on the box. Will tell us our agent is working. I like sanity for every test case. Just makes sense to me. We can talk about this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we break out all of the integration tests by directory, does that mean we would use different hosts to run like the CA bundle tests versus the dimensions tests versus the CWL tests? If that's the case, then yeah this makes sense to me. I think when I was reading it last night, I was still in the frame of reference where the tests would be executed on the same hosts, so we only need to run sanity once, not per directory.

"go test ${var.test_dir} -p 1 -v --tags=integration"
]
connection {
type = "ssh"
Expand Down
2 changes: 1 addition & 1 deletion integration/terraform/ec2/linux/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ variable "test_name" {
default = ""
}

variable "tag" {
variable "test_dir" {
type = string
default = ""
}
29 changes: 29 additions & 0 deletions integration/test/agent_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
package test

import (
"context"
"fmt"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"log"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -93,3 +97,28 @@ func ReplaceLocalStackHostName(pathIn string) {
log.Fatal(fmt.Sprint(err) + string(out))
}
}

func GetInstanceId() string {
ctx := context.Background()
c, err := config.LoadDefaultConfig(ctx)
if err != nil {
// fail fast so we don't continue the test
log.Fatalf("Error occurred while creating SDK config: %v", err)
}

// TODO: this only works for EC2 based testing
client := imds.NewFromConfig(c)
metadata, err := client.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
if err != nil {
log.Fatalf("Error occurred while retrieving EC2 instance ID: %v", err)
}
return metadata.InstanceID
}

func GetCWClient(cxt context.Context) *cloudwatch.Client {
defaultConfig, err := config.LoadDefaultConfig(cxt)
if err != nil {
log.Fatalf("err occurred while creating config %v", err)
}
return cloudwatch.NewFromConfig(defaultConfig)
}
18 changes: 1 addition & 17 deletions integration/test/cloudwatchlogs/publish_logs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@
package cloudwatchlogs

import (
"context"
"fmt"
"github.com/aws/amazon-cloudwatch-agent/integration/test"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"log"
"os"

Expand Down Expand Up @@ -54,20 +51,7 @@ var testParameters = []input{
func TestWriteLogsToCloudWatch(t *testing.T) {
// this uses the {instance_id} placeholder in the agent configuration,
// so we need to determine the host's instance ID for validation
ctx := context.Background()
c, err := config.LoadDefaultConfig(ctx)
if err != nil {
// fail fast so we don't continue the test
t.Fatalf("Error occurred while creating SDK config: %v", err)
}

// TODO: this only works for EC2 based testing
client := imds.NewFromConfig(c)
metadata, err := client.GetInstanceIdentityDocument(ctx, &imds.GetInstanceIdentityDocumentInput{})
if err != nil {
t.Fatalf("Error occurred while retrieving EC2 instance ID: %v", err)
}
instanceId := metadata.InstanceID
instanceId := test.GetInstanceId()
log.Printf("Found instance id %s", instanceId)

defer cleanUp(instanceId)
Expand Down
14 changes: 0 additions & 14 deletions integration/test/empty/empty_test.go

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

//go:build linux && integration
// +build linux,integration

package metrics_number_dimension

import (
"context"
"fmt"
"github.com/aws/amazon-cloudwatch-agent/integration/test"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"log"
"strings"
"testing"
"time"
)

const configOutputPath = "/opt/aws/amazon-cloudwatch-agent/bin/config.json"
const configJSON = "/config.json"
const namespace = "MetricNumberDimensionTest"
sethAmazon marked this conversation as resolved.
Show resolved Hide resolved
const instanceId = "InstanceId"
const appendMetric = "append"

// @TODO use the value from plugins/outputs/cloudwatch/cloudwatch.go when https://github.com/aws/amazon-cloudwatch-agent/pull/361 is merged
const maxDimension = 30

//Let the agent run for 2 minutes. This will give agent enough time to call server
const agentRuntime = 2 * time.Minute

const targetString = "max MaxDimensions %v is less than than number of dimensions %v thus only taking the max number"

type input struct {
resourcePath string
findTarget bool
numberDimensionsInCW int
metricName string
}

type metric struct {
name string
value string
}

//Must run this test with parallel 1 since this will fail if more than one test is running at the same time
func TestNumberMetricDimension(t *testing.T) {

parameters := []input{
{
resourcePath: "resources/10_dimension",
findTarget: false,
numberDimensionsInCW: 10,
metricName: "mem_used_percent",
},
// @TODO add when https://github.com/aws/amazon-cloudwatch-agent/pull/361 is merged
// {resourcePath: "resources/30_dimension", findTarget: false, numberDimensionsInCW: 30, metricName: "mem_used_percent",},
// {resourcePath: "resources/35_dimension", findTarget: true, numberDimensionsInCW: 30, metricName: "mem_used_percent",},
}

for _, parameter := range parameters {
//before test run
log.Printf("resource file location %s find target %t input number dimension %d metric name %s",
parameter.resourcePath, parameter.findTarget, parameter.numberDimensionsInCW, parameter.metricName)

target := fmt.Sprintf(targetString, maxDimension, parameter.numberDimensionsInCW)

t.Run(fmt.Sprintf("resource file location %s find target %t", parameter.resourcePath, parameter.findTarget), func(t *testing.T) {
test.CopyFile(parameter.resourcePath+configJSON, configOutputPath)
test.StartAgent(configOutputPath)
time.Sleep(agentRuntime)
log.Printf("Agent has been running for : %s", agentRuntime.String())
test.StopAgent()

// test for target string
output := test.ReadAgentOutput(agentRuntime)
containsTarget := outputLogContainsTarget(output, target)
if (parameter.findTarget && !containsTarget) || (!parameter.findTarget && containsTarget) {
t.Errorf("Find target is %t contains target is %t", parameter.findTarget, containsTarget)
}

// test for cloud watch metrics
cxt := context.Background()
dimensionFilter := buildDimensionFilterList(parameter.numberDimensionsInCW)
client := test.GetCWClient(cxt)
listMetricsInput := cloudwatch.ListMetricsInput{
MetricName: aws.String(parameter.metricName),
Namespace: aws.String(namespace),
Dimensions: dimensionFilter,
}
data, err := client.ListMetrics(cxt, &listMetricsInput)
if err != nil {
t.Errorf("Error getting metric data %v", err)
}
if len(data.Metrics) == 0 {
SaxyPandaBear marked this conversation as resolved.
Show resolved Hide resolved
metrics := make([]metric, len(dimensionFilter))
for i, filter := range dimensionFilter {
metrics[i] = metric{
name: *filter.Name,
value: *filter.Value,
}
}
t.Errorf("No metrics found for dimension %v metric name %v namespace %v",
metrics, parameter.metricName, namespace)
}
})
}
}

func buildDimensionFilterList(appendDimension int) []types.DimensionFilter {
// we append dimension from 0 to max number - 2
// then we add dimension instance id
// thus for max dimension 10, 0 to 8 + instance id = 10 dimension
ec2InstanceId := test.GetInstanceId()
dimensionFilter := make([]types.DimensionFilter, appendDimension)
for i := 0; i < appendDimension-1; i++ {
dimensionFilter[i] = types.DimensionFilter{
Name: aws.String(fmt.Sprintf("%s%d", appendMetric, i)),
Value: aws.String(fmt.Sprintf("%s%d", appendMetric, i)),
}
}
dimensionFilter[appendDimension-1] = types.DimensionFilter{
Name: aws.String(instanceId),
Value: aws.String(ec2InstanceId),
}
return dimensionFilter
}

func outputLogContainsTarget(output string, targetString string) bool {
log.Printf("Log file %s", output)
contains := strings.Contains(output, targetString)
log.Printf("Log file contains target string %t", contains)
return contains
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"agent": {
"metrics_collection_interval": 60,
"run_as_user": "root",
"debug": true,
"logfile": ""
},
"metrics": {
"namespace": "MetricNumberDimensionTest",
"append_dimensions": {
"InstanceId": "${aws:InstanceId}"
},
"metrics_collected": {
"mem": {
"measurement": [
"mem_used_percent"
],
"metrics_collection_interval": 60,
"append_dimensions": {
"append0": "append0",
"append1": "append1",
"append2": "append2",
"append3": "append3",
"append4": "append4",
"append5": "append5",
"append6": "append6",
"append7": "append7",
"append8": "append8"
}
}
}
}
}
Loading