diff --git a/generator/gen b/generator/gen new file mode 100755 index 000000000..97e923c57 Binary files /dev/null and b/generator/gen differ diff --git a/generator/resources/ec2_linux_test_matrix.json b/generator/resources/ec2_linux_test_matrix.json index 6d9a4d460..cd1b5c06a 100644 --- a/generator/resources/ec2_linux_test_matrix.json +++ b/generator/resources/ec2_linux_test_matrix.json @@ -2,7 +2,7 @@ { "os": "ubuntu-22.04", "username": "ubuntu", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go deb", "ami": "cloudwatch-agent-integration-test-ubuntu-LTS-22*", "caCertPath": "/etc/ssl/certs/ca-certificates.crt", @@ -13,7 +13,7 @@ { "os": "ubuntu-24", "username": "ubuntu", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go deb", "ami": "cloudwatch-agent-integration-test-ubuntu-24*", "caCertPath": "/etc/ssl/certs/ca-certificates.crt", @@ -24,7 +24,7 @@ { "os": "ubuntu-25", "username": "ubuntu", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go deb", "ami": "cloudwatch-agent-integration-test-ubuntu-25*", "caCertPath": "/etc/ssl/certs/ca-certificates.crt", @@ -46,7 +46,7 @@ { "os": "al2", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-al2*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -57,7 +57,7 @@ { "os": "al2", "username": "ec2-user", - "instanceType":"m7g.medium", + "instanceType":"c7g.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-arm64-al2*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -68,7 +68,7 @@ { "os": "al2023", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-x86-al2023*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -79,7 +79,7 @@ { "os": "al2023", "username": "ec2-user", - "instanceType":"m6g.medium", + "instanceType":"c7g.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-aarch64-al2023*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -90,7 +90,7 @@ { "os": "ol8", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-ol8*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -101,7 +101,7 @@ { "os": "ol9", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-ol9*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -112,7 +112,7 @@ { "os": "rocky-linux-8", "username": "rocky", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rocky-linux-8*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -123,7 +123,7 @@ { "os": "rocky-linux-9", "username": "rocky", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rocky-linux-9*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -134,7 +134,7 @@ { "os": "rocky-linux-10", "username": "rocky", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rocky-linux-10*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -145,7 +145,7 @@ { "os": "alma-linux-8", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-alma-linux-8*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -156,7 +156,7 @@ { "os": "alma-linux-9", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-alma-linux-9*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -167,7 +167,7 @@ { "os": "alma-linux-10", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-alma-linux-10*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -178,7 +178,7 @@ { "os": "rhel9", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rhel9*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -189,7 +189,7 @@ { "os": "rhel8", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rhel8-base*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", @@ -200,7 +200,7 @@ { "os": "rhel10", "username": "ec2-user", - "instanceType":"t3a.medium", + "instanceType":"c7a.medium", "installAgentCommand": "go run ./install/install_agent.go rpm", "ami": "cloudwatch-agent-integration-test-rhel10*", "caCertPath": "/etc/ssl/certs/ca-bundle.crt", diff --git a/generator/test_case_generator.go b/generator/test_case_generator.go index 5c25a37df..54e7eab87 100644 --- a/generator/test_case_generator.go +++ b/generator/test_case_generator.go @@ -7,9 +7,13 @@ import ( "encoding/json" "flag" "fmt" + "go/ast" + "go/parser" + "go/token" "io" "log" "os" + "os/exec" "strings" "github.com/mitchellh/mapstructure" @@ -19,28 +23,30 @@ import ( type matrixRow struct { TestName string `json:"testName"` TestDir string `json:"test_dir"` + TestFunc string `json:"-"` // internal, used to build TestRunFilter + TestRunFilter string `json:"testRunFilter,omitempty"` // -test.run regex pattern Os string `json:"os"` Family string `json:"family"` TestType string `json:"testType"` Arc string `json:"arc"` InstanceType string `json:"instanceType"` Ami string `json:"ami"` - BinaryName string `json:"binaryName"` - Username string `json:"username"` - InstallAgentCommand string `json:"installAgentCommand"` - AgentStartCommand string `json:"agentStartCommand"` - CaCertPath string `json:"caCertPath"` - ValuesPerMinute int `json:"values_per_minute"` // Number of metrics to be sent or number of log lines to write - K8sVersion string `json:"k8sVersion"` - Nodes int `json:"nodes"` - DeploymentStrategy string `json:"deploymentStrategy"` - TerraformDir string `json:"terraform_dir"` - UseSSM bool `json:"useSSM"` - ExcludedTests string `json:"excludedTests"` - MetadataEnabled string `json:"metadataEnabled"` - MaxAttempts int `json:"max_attempts"` - SELinuxBranch string `json:"selinux_branch"` - WIP bool `json:"wip,omitempty"` // Work In Progress - failures won't block CI + BinaryName string `json:"binaryName,omitempty"` + Username string `json:"username,omitempty"` + InstallAgentCommand string `json:"installAgentCommand,omitempty"` + AgentStartCommand string `json:"agentStartCommand,omitempty"` + CaCertPath string `json:"caCertPath,omitempty"` + ValuesPerMinute int `json:"values_per_minute,omitempty"` + K8sVersion string `json:"k8sVersion,omitempty"` + Nodes int `json:"nodes,omitempty"` + DeploymentStrategy string `json:"deploymentStrategy,omitempty"` + TerraformDir string `json:"terraform_dir,omitempty"` + UseSSM bool `json:"useSSM,omitempty"` + ExcludedTests string `json:"excludedTests,omitempty"` + MetadataEnabled string `json:"metadataEnabled,omitempty"` + MaxAttempts int `json:"max_attempts,omitempty"` + SELinuxBranch string `json:"selinux_branch,omitempty"` + WIP bool `json:"wip,omitempty"` } type testConfig struct { @@ -65,6 +71,12 @@ type testConfig struct { excludedTests string // wip marks test as Work In Progress - failures won't block CI wip bool + // splitByTestFunc splits this test directory into separate matrix entries per test function + splitByTestFunc bool + // splitNames is a hardcoded list of subtest names to split on (when AST parsing isn't possible) + splitNames []string + // skip excludes this test entirely from the matrix + skip bool } const ( @@ -87,8 +99,9 @@ var testTypeToTestConfig = map[string][]testConfig{ {testDir: "./test/cloudwatchlogs"}, }, testTypeKeyEc2Linux: { + {testDir: "./test/sanity"}, {testDir: "./test/ca_bundle"}, - {testDir: "./test/cloudwatchlogs"}, + {testDir: "./test/cloudwatchlogs", splitByTestFunc: true}, { testDir: "./test/log_state/logfile", targets: map[string]map[string]struct{}{"os": {"al2": {}}}, @@ -107,17 +120,23 @@ var testTypeToTestConfig = map[string][]testConfig{ targets: map[string]map[string]struct{}{"os": {"al2": {}}}, maxAttempts: 2, }, - {testDir: "./test/entity_metrics_benchmark"}, + {testDir: "./test/entity_metrics_benchmark", splitByTestFunc: true}, { testDir: "./test/metric_value_benchmark", instanceTypeByArch: map[string]string{ "amd64": "i3en.large", "arm64": "i4g.large", }, + splitByTestFunc: true, }, {testDir: "./test/run_as_user"}, - {testDir: "./test/collection_interval"}, - {testDir: "./test/metric_dimension"}, + {testDir: "./test/collection_interval", splitNames: []string{ + "No collection interval given default to 60s", + "Agent has 10s second collection interval", + "Metric disk has 10s collection interval", + "Agent has 60s collection interval, disk has 10s collection interval use disk collection interval", + }}, + {testDir: "./test/metric_dimension", splitByTestFunc: true}, {testDir: "./test/restart"}, {testDir: "./test/xray"}, {testDir: "./test/otlp"}, @@ -207,8 +226,13 @@ var testTypeToTestConfig = map[string][]testConfig{ }, //{testDir: "./test/metric_value_benchmark"}, // Skipping test until it is fixed! {testDir: "./test/run_as_user"}, - {testDir: "./test/collection_interval"}, - {testDir: "./test/metric_dimension"}, + {testDir: "./test/collection_interval", splitNames: []string{ + "No collection interval given default to 60s", + "Agent has 10s second collection interval", + "Metric disk has 10s collection interval", + "Agent has 60s collection interval, disk has 10s collection interval use disk collection interval", + }}, + {testDir: "./test/metric_dimension", splitByTestFunc: true}, {testDir: "./test/restart"}, {testDir: "./test/xray"}, {testDir: "./test/selinux_negative_test"}, @@ -454,6 +478,9 @@ var partitionTests = map[string]partition{ "arm64": "m6g.large", // Use m6g.large since instance store tests are excluded }, }, + "./test/entity_metrics_benchmark": { + skip: true, // ListEntitiesForMetric API not available in ITAR + }, }, }, "china": { @@ -469,6 +496,9 @@ var partitionTests = map[string]partition{ "arm64": "m6g.large", // Use m6g.large since instance store tests are excluded }, }, + "./test/entity_metrics_benchmark": { + skip: true, // ListEntitiesForMetric API not available in China + }, }, }, } @@ -540,6 +570,9 @@ func genMatrix(testType string, testConfigs []testConfig, ami []string, override // Apply partition-specific overrides if available if overrides != nil { if override, ok := overrides[testConfig.testDir]; ok { + if override.skip { + continue // Skip this test entirely for this partition + } if override.excludedTests != "" { testConfig.excludedTests = override.excludedTests } @@ -602,6 +635,36 @@ func genMatrix(testType string, testConfigs []testConfig, ami []string, override } if testConfig.targets == nil || shouldAddTest(&row, testConfig.targets) { + // If splitByTestFunc is enabled, create separate entries for each test function + if testConfig.splitByTestFunc || len(testConfig.splitNames) > 0 { + var testFuncs []string + // splitNames = plain t.Run subtests (2 levels: TestFunc/name) + // splitByTestFunc = testify suite subtests (3 levels: Suite/TestAll/name) + isSuiteSubtest := false + if len(testConfig.splitNames) > 0 { + testFuncs = testConfig.splitNames + } else { + testFuncs = listTestFunctions(testConfig.testDir) + isSuiteSubtest = true + } + if len(testFuncs) > 0 { + for _, fn := range testFuncs { + splitRow := row + splitRow.TestFunc = fn + splitRow.TestName = row.TestName + "/" + fn + normalized := strings.ReplaceAll(fn, " ", "_") + if strings.HasPrefix(fn, "Test") { + splitRow.TestRunFilter = "^" + normalized + "$" + } else if isSuiteSubtest { + splitRow.TestRunFilter = ".*/.*/^" + normalized + "$" + } else { + splitRow.TestRunFilter = ".*/^" + normalized + "$" + } + testMatrixComplete = append(testMatrixComplete, splitRow) + } + continue + } + } testMatrixComplete = append(testMatrixComplete, row) } } @@ -643,3 +706,62 @@ func writeTestMatrixFile(testType string, testMatrix []matrixRow) { log.Panicf("Can't write json to file for target os %v, err %v", testType, err) } } + +// listTestFunctions gets test function/subtest names from a test directory. +// For suites with splitByTestFunc, it parses GetTestName() return values using Go AST. +// Otherwise falls back to "go test -list". +func listTestFunctions(testDir string) []string { + // Try AST parsing for GetTestName() methods (used by metric_value_benchmark) + if names := parseGetTestNames(testDir); len(names) > 0 { + return names + } + + // Fall back to go test -list for top-level test functions + cmd := exec.Command("go", "test", "-list", ".*", testDir) + output, err := cmd.Output() + if err != nil { + log.Printf("Warning: could not list tests for %s: %v", testDir, err) + return nil + } + + var funcs []string + for _, line := range strings.Split(string(output), "\n") { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "Test") { + funcs = append(funcs, line) + } + } + return funcs +} + +// parseGetTestNames uses Go AST to extract return values from GetTestName() methods +func parseGetTestNames(dir string) []string { + fset := token.NewFileSet() + pkgs, err := parser.ParseDir(fset, dir, func(fi os.FileInfo) bool { + return strings.HasSuffix(fi.Name(), "_test.go") + }, 0) + if err != nil { + return nil + } + + var names []string + for _, pkg := range pkgs { + for _, file := range pkg.Files { + ast.Inspect(file, func(n ast.Node) bool { + fn, ok := n.(*ast.FuncDecl) + if !ok || fn.Name.Name != "GetTestName" || fn.Recv == nil { + return true + } + if fn.Body != nil && len(fn.Body.List) > 0 { + if ret, ok := fn.Body.List[0].(*ast.ReturnStmt); ok && len(ret.Results) > 0 { + if lit, ok := ret.Results[0].(*ast.BasicLit); ok && lit.Kind == token.STRING { + names = append(names, strings.Trim(lit.Value, `"`)) + } + } + } + return true + }) + } + } + return names +} diff --git a/terraform/ec2/assume_role/main.tf b/terraform/ec2/assume_role/main.tf index 2d5b8f1af..049618698 100644 --- a/terraform/ec2/assume_role/main.tf +++ b/terraform/ec2/assume_role/main.tf @@ -247,7 +247,19 @@ resource "null_resource" "integration_test_run" { "export PATH=$PATH:/snap/bin:/usr/local/go/bin", "echo run integration test", "cd ~/amazon-cloudwatch-agent-test", - "echo run sanity test && go test ./test/sanity -p 1 -v", + ], + + # Download pre-compiled test binaries from S3 + var.test_binaries_prefix != "" ? [ + "mkdir -p ~/test-binaries", + "aws s3 cp s3://${var.s3_bucket}/${var.test_binaries_prefix}/linux/${var.arc}/${basename(var.test_dir)}.test ~/test-binaries/${basename(var.test_dir)}.test --quiet", + "chmod +x ~/test-binaries/*", + ] : [], + + var.test_binaries_prefix != "" ? [ + "echo base assume role arn is ${aws_iam_role.roles["no_context_keys"].arn}", + "cd ~/amazon-cloudwatch-agent-test/${var.test_dir} && ~/test-binaries/${basename(var.test_dir)}.test -test.parallel 1 -test.timeout 1h -test.v -computeType=EC2 -bucket=${var.s3_bucket} -assumeRoleArn=${aws_iam_role.roles["no_context_keys"].arn} -instanceArn=${aws_instance.cwagent.arn} -accountId=${data.aws_caller_identity.account_id.account_id}" + ] : [ "echo base assume role arn is ${aws_iam_role.roles["no_context_keys"].arn}", "go test ${var.test_dir} -p 1 -timeout 1h -computeType=EC2 -bucket=${var.s3_bucket} -assumeRoleArn=${aws_iam_role.roles["no_context_keys"].arn} -instanceArn=${aws_instance.cwagent.arn} -accountId=${data.aws_caller_identity.account_id.account_id} -v" ], diff --git a/terraform/ec2/assume_role/variables.tf b/terraform/ec2/assume_role/variables.tf index f922aa925..1a6fe6230 100644 --- a/terraform/ec2/assume_role/variables.tf +++ b/terraform/ec2/assume_role/variables.tf @@ -128,3 +128,9 @@ variable "is_onprem" { type = bool default = false } + +variable "test_binaries_prefix" { + description = "S3 prefix for pre-compiled test binaries. Empty string uses go test compilation." + type = string + default = "" +} diff --git a/terraform/ec2/creds/main.tf b/terraform/ec2/creds/main.tf index 1969080ba..7bf6dbe45 100644 --- a/terraform/ec2/creds/main.tf +++ b/terraform/ec2/creds/main.tf @@ -204,7 +204,19 @@ resource "null_resource" "integration_test_run" { "export PATH=$PATH:/snap/bin:/usr/local/go/bin", "echo run integration test", "cd ~/amazon-cloudwatch-agent-test", - "echo run sanity test && go test ./test/sanity -p 1 -v", + ], + + # Download pre-compiled test binaries from S3 + var.test_binaries_prefix != "" ? [ + "mkdir -p ~/test-binaries", + "aws s3 cp s3://${var.s3_bucket}/${var.test_binaries_prefix}/linux/${var.arc}/${basename(var.test_dir)}.test ~/test-binaries/${basename(var.test_dir)}.test --quiet", + "chmod +x ~/test-binaries/*", + ] : [], + + var.test_binaries_prefix != "" ? [ + "echo assume role arn is ${aws_iam_role.assume_role.arn}", + "cd ~/amazon-cloudwatch-agent-test/${var.test_dir} && ~/test-binaries/${basename(var.test_dir)}.test -test.parallel 1 -test.timeout 1h -test.v -computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path} -assumeRoleArn=${aws_iam_role.assume_role.arn} -instanceId=${aws_instance.cwagent.id}" + ] : [ "echo assume role arn is ${aws_iam_role.assume_role.arn}", "go test ${var.test_dir} -p 1 -timeout 1h -computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path} -assumeRoleArn=${aws_iam_role.assume_role.arn} -instanceId=${aws_instance.cwagent.id} -v" ], diff --git a/terraform/ec2/creds/variables.tf b/terraform/ec2/creds/variables.tf index f3b908454..f565a2e76 100644 --- a/terraform/ec2/creds/variables.tf +++ b/terraform/ec2/creds/variables.tf @@ -128,3 +128,9 @@ variable "is_onprem" { type = bool default = false } + +variable "test_binaries_prefix" { + description = "S3 prefix for pre-compiled test binaries. Empty string uses go test compilation." + type = string + default = "" +} diff --git a/terraform/ec2/linux/main.tf b/terraform/ec2/linux/main.tf index c5550885a..87589718a 100644 --- a/terraform/ec2/linux/main.tf +++ b/terraform/ec2/linux/main.tf @@ -43,6 +43,34 @@ locals { // Pre-test setup command pre_test_setup_cmd = local.is_onprem ? "echo 'Pre-test setup: Replacing {instance_id} and $${aws:InstanceId} placeholders in test resource configs'; find . -path '*/resources/*.json' -exec sed -i 's/{instance_id}/${module.linux_common.cwagent_id}/g' {} \\; -exec sed -i 's/$${aws:InstanceId}/${module.linux_common.cwagent_id}/g' {} \\; && echo 'Updated all config files in resources directories'" : var.pre_test_setup + + // Pre-compiled test binary support + use_test_binaries = var.test_binaries_prefix != "" + test_binary_name = "${basename(var.test_dir)}.test" + + // Sanity test doesn't use standard test flags + is_sanity_test = var.test_dir == "./test/sanity" + + // Test function filter - passed from generator as a -test.run pattern + test_run_filter = var.test_run_filter != "" ? "-test.run='${var.test_run_filter}'" : "" + + // Common test flags used by most tests (not sanity) + common_test_flags = "-computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -excludedTests='${var.excluded_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path} -proxyUrl=${module.linux_common.proxy_instance_proxy_ip} -instanceId=${module.linux_common.cwagent_id}" + onprem_flag = local.is_onprem ? "-agentStartCommand='${var.agent_start}'" : "" + amp_flag = length(regexall("/amp", var.test_dir)) > 0 ? "-ampWorkspaceId=${module.amp[0].workspace_id}" : "" + + // Test execution commands broken into readable pieces + test_cmd_binary_sanity = "cd ~/amazon-cloudwatch-agent-test/${var.test_dir} && ~/test-binaries/${local.test_binary_name} -test.v ${local.test_run_filter}" + test_cmd_binary_full = "cd ~/amazon-cloudwatch-agent-test/${var.test_dir} && ~/test-binaries/${local.test_binary_name} -test.parallel 1 -test.timeout 1h -test.v ${local.test_run_filter} ${local.common_test_flags} ${local.onprem_flag} ${local.amp_flag}" + test_cmd_go_sanity = "go test ${var.test_dir} -p 1 -v ${local.test_run_filter}" + test_cmd_go_full = "go test ${var.test_dir} -p 1 -timeout 1h -v ${local.test_run_filter} ${local.common_test_flags} ${local.onprem_flag} ${local.amp_flag}" + + // Final test command selection + test_command = local.is_sanity_test ? ( + local.use_test_binaries ? local.test_cmd_binary_sanity : local.test_cmd_go_sanity + ) : ( + local.use_test_binaries ? local.test_cmd_binary_full : local.test_cmd_go_full + ) } ##################################################################### @@ -204,6 +232,15 @@ resource "null_resource" "integration_test_run" { "cd ~/amazon-cloudwatch-agent-test", ], + # Download pre-compiled test binaries from S3 (fail fast if download fails) + local.use_test_binaries ? [ + "echo 'Downloading pre-compiled test binary from S3...'", + "mkdir -p ~/test-binaries", + "aws s3 cp s3://${var.s3_bucket}/${var.test_binaries_prefix}/linux/${var.arc}/${local.test_binary_name} ~/test-binaries/${local.test_binary_name} --quiet || { echo 'ERROR: Failed to download test binary'; exit 1; }", + "chmod +x ~/test-binaries/${local.test_binary_name}", + "echo 'Test binary downloaded successfully'", + ] : [], + # On-premises specific environment variables local.is_onprem ? [ "export RUN_IN_AWS=false", @@ -222,15 +259,14 @@ resource "null_resource" "integration_test_run" { "echo 'Testing agent credentials:'", "sudo aws sts get-caller-identity || echo 'Agent credentials test failed'", "echo 'Pre-test setup: Replacing {instance_id} and $${aws:InstanceId} placeholders in test resource configs'; find . -path '${var.test_dir}/resources/*.json' -exec sed -i 's/{instance_id}/${module.linux_common.cwagent_id}/g' {} \\; -exec sed -i 's/$${aws:InstanceId}/${module.linux_common.cwagent_id}/g' {} \\; && echo 'Updated all config files in resources directories'" - ] : [ - "echo Running sanity test...", - "go test ./test/sanity -p 1 -v", - ], + ] : [], [ var.pre_test_setup, - # Integration test execution with conditional agent start command - "go test ${var.test_dir} -p 1 -timeout 1h -computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -excludedTests='${var.excluded_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path} -proxyUrl=${module.linux_common.proxy_instance_proxy_ip} -instanceId=${module.linux_common.cwagent_id} ${local.is_onprem ? "-agentStartCommand='${var.agent_start}'" : ""} ${length(regexall("/amp", var.test_dir)) > 0 ? "-ampWorkspaceId=${module.amp[0].workspace_id} " : ""}-v" + # DEBUG: Log test execution context (TODO: remove after debugging) + "echo '[DEBUG] is_sanity_test=${local.is_sanity_test} use_test_binaries=${local.use_test_binaries} test_dir=${var.test_dir} test_run_filter=${var.test_run_filter}'", + # Integration test execution + local.test_command ], ) } diff --git a/terraform/ec2/linux/variables.tf b/terraform/ec2/linux/variables.tf index 3eb84350c..062260e9d 100644 --- a/terraform/ec2/linux/variables.tf +++ b/terraform/ec2/linux/variables.tf @@ -82,6 +82,12 @@ variable "test_dir" { default = "" } +variable "test_run_filter" { + type = string + default = "" + description = "Go -test.run regex pattern to filter tests. Generated by test matrix." +} + variable "selinux_branch" { type = string default = "main" @@ -133,4 +139,10 @@ variable "is_onprem" { description = "Whether to run in on-premises mode instead of EC2 mode" type = bool default = false -} \ No newline at end of file +} + +variable "test_binaries_prefix" { + description = "S3 prefix for pre-compiled test binaries (e.g. integration-test/test-binaries/abc1234/linux/amd64). Empty string uses go test compilation." + type = string + default = "" +} diff --git a/terraform/ec2/userdata/main.tf b/terraform/ec2/userdata/main.tf index 925d86dbd..876dbde0d 100644 --- a/terraform/ec2/userdata/main.tf +++ b/terraform/ec2/userdata/main.tf @@ -97,7 +97,18 @@ resource "null_resource" "integration_test" { "timeout 120 bash -c 'until [ -f /home/ec2-user/amazon-cloudwatch-agent-test/test/sanity/resources/verifyUnixCtlScript.sh ]; do echo \"Waiting for verifyUnixCtlScript.sh...\"; sleep 2; done'", "cd ~/amazon-cloudwatch-agent-test", "sudo chmod 777 ~/amazon-cloudwatch-agent-test/test/sanity/resources/verifyUnixCtlScript.sh", - "echo run sanity test && go test ./test/sanity -p 1 -v", + ], + + # Download pre-compiled test binaries from S3 + var.test_binaries_prefix != "" ? [ + "mkdir -p ~/test-binaries", + "aws s3 cp s3://${var.s3_bucket}/${var.test_binaries_prefix}/linux/${var.arc}/${basename(var.test_dir)}.test ~/test-binaries/${basename(var.test_dir)}.test --quiet", + "chmod +x ~/test-binaries/*", + ] : [], + + var.test_binaries_prefix != "" ? [ + "cd ~/amazon-cloudwatch-agent-test/${var.test_dir} && ~/test-binaries/${basename(var.test_dir)}.test -test.parallel 1 -test.timeout 1h -test.v -computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path}" + ] : [ "go test ${var.test_dir} -p 1 -timeout 1h -computeType=EC2 -bucket=${var.s3_bucket} -plugins='${var.plugin_tests}' -cwaCommitSha=${var.cwa_github_sha} -caCertPath=${var.ca_cert_path} -v" ], ) diff --git a/terraform/ec2/userdata/variables.tf b/terraform/ec2/userdata/variables.tf index 92802e32a..8e117f364 100644 --- a/terraform/ec2/userdata/variables.tf +++ b/terraform/ec2/userdata/variables.tf @@ -128,4 +128,10 @@ variable "is_onprem" { description = "Whether to run in on-premises mode instead of EC2 mode" type = bool default = false -} \ No newline at end of file +} + +variable "test_binaries_prefix" { + description = "S3 prefix for pre-compiled test binaries. Empty string uses go test compilation." + type = string + default = "" +} diff --git a/test/collection_interval/collection_interval_test.go b/test/collection_interval/collection_interval_test.go index 1398f3303..7218f9875 100644 --- a/test/collection_interval/collection_interval_test.go +++ b/test/collection_interval/collection_interval_test.go @@ -6,7 +6,6 @@ package collection_interval import ( - "fmt" "os" "testing" "time" @@ -72,8 +71,7 @@ func TestCollectionInterval(t *testing.T) { } for _, parameter := range parameters { - t.Run(fmt.Sprintf("test description %s resource file location %s number of metrics lower bound %d and upper bound %d", - parameter.testDescription, parameter.dataInput, parameter.lowerBoundInclusive, parameter.upperBoundInclusive), func(t *testing.T) { + t.Run(parameter.testDescription, func(t *testing.T) { common.CopyFile(parameter.dataInput, common.ConfigOutputPath) hostName, err := os.Hostname() if err != nil { diff --git a/test/entity_metrics_benchmark/entity_metrics_benchmark_test.go b/test/entity_metrics_benchmark/entity_metrics_benchmark_test.go index b5679f0d9..00b0ff47a 100644 --- a/test/entity_metrics_benchmark/entity_metrics_benchmark_test.go +++ b/test/entity_metrics_benchmark/entity_metrics_benchmark_test.go @@ -54,8 +54,6 @@ func getEntityTestRunners(env *environment.MetaData) []*test_runner.TestRunner { {TestRunner: &CollectDEntityServiceAndEnvironmentFallback{test_runner.BaseTestRunner{DimensionFactory: factory}}}, {TestRunner: &StatsDEntityServiceAndEnvironmentFallback{test_runner.BaseTestRunner{DimensionFactory: factory}, make(chan bool)}}, } - } else { - log.Println("Not in us-west-2 (likely in cn/itar), so skipping entity metric tests") } return entityTestRunners } @@ -65,13 +63,20 @@ func (suite *EntityMetricBenchmarkTestSuite) TestAllInSuite() { // Entity tests are only supported in EC2 environment if env.ComputeType != computetype.EC2 { - log.Println("Not in EC2, skipping entity metric tests") - return + suite.T().Skip("Not in EC2, skipping entity metric tests") + } + + // Entity tests only supported in us-west-2 (no ListEntitiesForMetric in CN/ITAR) + if os.Getenv("AWS_REGION") != "us-west-2" { + suite.T().Skip("Not in us-west-2 (likely in cn/itar), skipping entity metric tests") } log.Println("Environment compute type is EC2") for _, testRunner := range getEntityTestRunners(env) { - suite.AddToSuiteResult(testRunner.Run()) + testRunner := testRunner + suite.T().Run(testRunner.TestRunner.GetTestName(), func(t *testing.T) { + suite.AddToSuiteResult(testRunner.Run()) + }) } suite.Assert().Equal(status.SUCCESSFUL, suite.Result.GetStatus(), "Entity Metric Benchmark Test Suite Failed") diff --git a/test/metric_dimension/metrics_dimension_test.go b/test/metric_dimension/metrics_dimension_test.go index 097f563ac..7bba553ed 100644 --- a/test/metric_dimension/metrics_dimension_test.go +++ b/test/metric_dimension/metrics_dimension_test.go @@ -57,7 +57,10 @@ func getTestRunners(env *environment.MetaData) []*test_runner.TestRunner { func (suite *MetricsAppendDimensionTestSuite) TestAllInSuite() { env := environment.GetEnvironmentMetaData() for _, testRunner := range getTestRunners(env) { - suite.AddToSuiteResult(testRunner.Run()) + testRunner := testRunner + suite.T().Run(testRunner.TestRunner.GetTestName(), func(t *testing.T) { + suite.AddToSuiteResult(testRunner.Run()) + }) } suite.Assert().Equal(status.SUCCESSFUL, suite.Result.GetStatus(), "Metric Append Dimension Test Suite Failed") } diff --git a/test/metric_value_benchmark/metrics_value_benchmark_test.go b/test/metric_value_benchmark/metrics_value_benchmark_test.go index 06dc90709..3d54e4237 100644 --- a/test/metric_value_benchmark/metrics_value_benchmark_test.go +++ b/test/metric_value_benchmark/metrics_value_benchmark_test.go @@ -156,8 +156,11 @@ func (suite *MetricBenchmarkTestSuite) TestAllInSuite() { default: // EC2 tests log.Println("Environment compute type is EC2") for _, testRunner := range getEc2TestRunners(env) { + testRunner := testRunner // capture loop variable if shouldRunEC2Test(env, testRunner) { - suite.AddToSuiteResult(testRunner.Run()) + suite.T().Run(testRunner.TestRunner.GetTestName(), func(t *testing.T) { + suite.AddToSuiteResult(testRunner.Run()) + }) } } }