Skip to content

Commit a989138

Browse files
committed
ecs: Add test for fargate and EC2 awsvpc
1 parent 2086ad3 commit a989138

File tree

12 files changed

+129
-43
lines changed

12 files changed

+129
-43
lines changed

terraform/ecs/extra_apps.tf

+14-6
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@ resource "aws_ecs_task_definition" "extra_apps" {
1515
for_each = var.ecs_extra_apps
1616
family = "taskdef-${module.common.testing_id}-${each.value.service_name}"
1717
container_definitions = data.template_file.extra_apps_defs[each.key].rendered
18-
network_mode = each.value.network_mode
19-
cpu = each.value.cpu
20-
memory = each.value.memory
21-
task_role_arn = module.basic_components.aoc_iam_role_arn
22-
execution_role_arn = module.basic_components.aoc_iam_role_arn
18+
requires_compatibilities = each.value.launch_type == "FARGATE" ? [
19+
"FARGATE"] : [
20+
"EC2"]
21+
network_mode = each.value.network_mode
22+
cpu = each.value.cpu
23+
memory = each.value.memory
24+
task_role_arn = module.basic_components.aoc_iam_role_arn
25+
execution_role_arn = module.basic_components.aoc_iam_role_arn
2326
}
2427

2528
resource "aws_ecs_service" "extra_apps" {
@@ -34,7 +37,8 @@ resource "aws_ecs_service" "extra_apps" {
3437
// NOTE: network configuration is only allowed for awsvpc
3538
// a hack for optional block https://github.com/hashicorp/terraform/issues/19898
3639
dynamic "network_configuration" {
37-
for_each = each.value.network_mode == "awsvpc" ? list(each.value.network_mode) : []
40+
for_each = each.value.network_mode == "awsvpc" ? tolist([
41+
each.value.network_mode]) : []
3842
content {
3943
subnets = module.basic_components.aoc_private_subnet_ids
4044
security_groups = [
@@ -47,4 +51,8 @@ output "extra_apps_defs_rendered" {
4751
value = {
4852
for k, v in data.template_file.extra_apps_defs : k => v.rendered
4953
}
54+
}
55+
56+
output "extra_app_task_defs" {
57+
value = aws_ecs_task_definition.extra_apps
5058
}

terraform/ecs/main.tf

+1-1
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ module "validator_without_sample_app" {
289289
aws_access_key_id = var.aws_access_key_id
290290
aws_secret_access_key = var.aws_secret_access_key
291291

292-
depends_on = [aws_ecs_service.aoc_without_sample_app]
292+
depends_on = [aws_ecs_service.aoc_without_sample_app, aws_ecs_service.extra_apps]
293293
}
294294

295295
data "template_file" "cloudwatch_context" {

terraform/setup/setup.tf

+9
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ resource "aws_security_group" "aoc_sg" {
9797
name = module.common.aoc_vpc_security_group
9898
vpc_id = module.vpc.vpc_id
9999

100+
# Allow all TCP ingress within the VPC so prometheus scrape can work with private IP.
101+
# https://stackoverflow.com/questions/49995417/self-reference-not-allowed-in-security-group-definition
102+
ingress {
103+
from_port = 0
104+
to_port = 65535
105+
protocol = "tcp"
106+
self = true
107+
}
108+
100109
ingress {
101110
from_port = 22
102111
to_port = 22

terraform/testcases/containerinsight_ecs_prometheus/README.md

+15-14
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,13 @@
55
This is e2e test
66
for [extension/ecsobserver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/extension/observer/ecsobserver)
77

8-
## TODO
9-
10-
- [ ] [cloudwatch_context.json](cloudwatch_context.json)
11-
- [ ] we need to update java code to include `taskDefinitionFamily` and `serviceName`
12-
- [ ] cluster name is used by other tests, need to pass it when rendering the template, could do this for the
13-
default template, it's empty
14-
- [x] app image repo is hardcoded and not used
15-
- [ ] log group name for sample applications' container log
16-
178
## Usage
189

19-
Non default image `123456.dkr.ecr.us-west-2.amazonaws.com/aoc:myfeature-0.2`
10+
If your AWS account is `123456` and you want to run your own image
11+
`123456.dkr.ecr.us-west-2.amazonaws.com/aoc:myfeature-0.2`.
2012

21-
- `ecs_launch_type` is the launch type for aoc itself, launch type for extra apps are defined in TODO(where?)
13+
- `ecs_launch_type` is the launch type for aoc itself, launch type for extra apps are defined
14+
in [parameters.tfvars](parameters.tfvars)
2215
- `aoc_image_repo` is repo for aoc without tag
2316
- `aoc_version` is the image tag for aoc
2417
- `ecs_extra_apps_image_repo` is the repo for all the extra apps, remaining part of image name and version are defined
@@ -53,7 +46,9 @@ There are multiple sample applications
5346

5447
```bash
5548
# Run at project root to make sure the validator code pass style check and compiles
56-
./gradlew :validator:build
49+
./gradlew :validator:build
50+
# Run at terraform/ecs to run validation without spinning up new infra
51+
make validate
5752
```
5853

5954
- validation config file name is specified
@@ -69,6 +64,12 @@ There are multiple sample applications
6964

7065
## Problems
7166

72-
`Unknown variable; There is no variable named`
67+
List of common problems you may encounter.
68+
69+
### Unknown variable
7370

74-
- used wrong variable name in template file
71+
You are using wrong variable name in template files, or you didn't define the var when rendering template.
72+
73+
```
74+
Unknown variable; There is no variable named
75+
```

terraform/testcases/containerinsight_ecs_prometheus/cloudwatch_context.json

+8-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@
22
"clusterName": "${cluster_name}",
33
"jmx": {
44
"job": "ecssd",
5-
"taskDefinitionFamily": "taskdef-${testing_id}-jmx"
5+
"taskDefinitionFamilies": [
6+
"taskdef-${testing_id}-jmx",
7+
"taskdef-${testing_id}-jmxawsvpc",
8+
"taskdef-${testing_id}-jmxfargate"
9+
]
610
},
711
"nginx": {
812
"job": "ecssd",
9-
"taskDefinitionFamily": "taskdef-${testing_id}-nginx-service",
13+
"taskDefinitionFamilies": [
14+
"taskdef-${testing_id}-nginx-service"
15+
],
1016
"serviceName": "aocservice-${testing_id}-nginx-service"
1117
}
1218
}

terraform/testcases/containerinsight_ecs_prometheus/parameters.tfvars

+26-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ validation_config = "ecs-container-insight-prometheus.yml"
44
sample_app_callable = false
55
# sample apps that emit ecs metrics
66
ecs_extra_apps = {
7-
# TODO: need both host network, awspvc and fargate
87
jmx = {
98
definition = "jmx.json"
109
service_name = "jmx"
@@ -16,6 +15,32 @@ ecs_extra_apps = {
1615
memory = 256
1716
}
1817

18+
# NOTE: for awsvpc to work form prometheus, we need change security group
19+
# to allow all traffic within the VPC
20+
jmxawsvpc = {
21+
definition = "jmx.json"
22+
service_name = "jmxawsvpc"
23+
service_type = "replica"
24+
replicas = 1
25+
network_mode = "awsvpc"
26+
launch_type = "EC2"
27+
cpu = 256
28+
memory = 256
29+
}
30+
31+
jmxfargate = {
32+
definition = "jmx.json"
33+
service_name = "jmxfargate"
34+
service_type = "replica"
35+
replicas = 1
36+
network_mode = "awsvpc"
37+
launch_type = "FARGATE"
38+
cpu = 256
39+
# Must set cpu and memory for fargate in specific ways
40+
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ecs-taskdefinition.html#:~:text=ContainerDefinition-,If%20your%20tasks%20will,cpu%20parameter,-512
41+
memory = 512
42+
}
43+
1944
nginx = {
2045
definition = "nginx.json"
2146
service_name = "nginx-service"

validator/src/main/java/com/amazon/aoc/models/CloudWatchContext.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static class App {
5858
private String namespace;
5959
private String job;
6060
// For ECS
61-
private String taskDefinitionFamily;
61+
private String[] taskDefinitionFamilies;
6262
private String serviceName;
6363
}
6464
}

validator/src/main/java/com/amazon/aoc/validators/ContainerIInsightECSPrometheusStructuredLogValidator.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ void init(Context context, FileConfig expectedDataTemplate) throws Exception {
5656
app.getName() + ".json"));
5757
String templateInput = mustacheHelper.render(fileConfig, context);
5858
// NOTE: EKS use namespace, we use task family for matching log event to schema.
59-
schemasToValidate.put(app.getTaskDefinitionFamily(), parseJsonSchema(templateInput));
59+
for (String taskDefinitionFamily : app.getTaskDefinitionFamilies()) {
60+
// We can deploy one workload in different ways (EC2, fargate etc.)
61+
// so we have a list of task definition families.
62+
schemasToValidate.put(taskDefinitionFamily, parseJsonSchema(templateInput));
63+
}
6064
logStreamNames.add(app.getJob());
6165
}
62-
log.info("apps to validate {}", validateApps.size());
66+
log.info("apps to validate {} schema to validate {}", validateApps.size(),
67+
schemasToValidate.keySet());
6368
}
6469

6570
@Override

validator/src/main/java/com/amazon/aoc/validators/ContainerInsightPrometheusMetricsValidator.java

+3
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
import com.fasterxml.jackson.core.type.TypeReference;
1010
import com.fasterxml.jackson.databind.ObjectMapper;
1111
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
12+
import lombok.extern.log4j.Log4j2;
1213
import org.apache.commons.io.FilenameUtils;
1314

1415
import java.nio.charset.StandardCharsets;
1516
import java.util.ArrayList;
1617
import java.util.List;
1718

19+
@Log4j2
1820
public class ContainerInsightPrometheusMetricsValidator extends AbstractCWMetricsValidator {
1921
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
2022

@@ -31,6 +33,7 @@ List<Metric> getExpectedMetrics(
3133
expectedDataTemplate.getPath().toString(),
3234
app.getName() + "_metrics.mustache"));
3335
String templateInput = mustacheHelper.render(fileConfig, context);
36+
// log.info("Rendered template {}", templateInput);
3437
List<Metric> appMetrics = mapper.readValue(templateInput.getBytes(StandardCharsets.UTF_8),
3538
new TypeReference<List<Metric>>() {
3639
});
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
# https://stackoverflow.com/questions/4067093/mustache-read-variables-from-parent-section-in-child-section
2+
# https://stackoverflow.com/questions/3954913/iterating-over-arrays-with-mustache
3+
{{#cloudWatchContext.jmx.taskDefinitionFamilies}}
14
- metricName: jvm_classes_loaded
25
namespace: ECS/ContainerInsights/Prometheus
36
dimensions:
47
- name: ClusterName
58
value: {{cloudWatchContext.clusterName}}
69
- name: TaskDefinitionFamily
7-
value: {{cloudWatchContext.jmx.taskDefinitionFamily}}
10+
value: {{.}}
811
- metricName: jvm_memory_pool_bytes_used
912
namespace: ECS/ContainerInsights/Prometheus
1013
dimensions:
1114
- name: ClusterName
1215
value: {{cloudWatchContext.clusterName}}
1316
- name: TaskDefinitionFamily
14-
value: {{cloudWatchContext.jmx.taskDefinitionFamily}}
17+
value: {{.}}
1518
- name: pool
1619
value: Metaspace
1720
- metricName: jvm_memory_bytes_used
@@ -20,6 +23,7 @@
2023
- name: ClusterName
2124
value: {{cloudWatchContext.clusterName}}
2225
- name: TaskDefinitionFamily
23-
value: {{cloudWatchContext.jmx.taskDefinitionFamily}}
26+
value: {{.}}
2427
- name: area
25-
value: heap
28+
value: heap
29+
{{/cloudWatchContext.jmx.taskDefinitionFamilies}}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
{{#cloudWatchContext.nginx.taskDefinitionFamilies}}
12
- metricName: nginx_up
23
namespace: ECS/ContainerInsights/Prometheus
34
dimensions:
45
- name: ClusterName
56
value: {{cloudWatchContext.clusterName}}
67
- name: TaskDefinitionFamily
7-
value: {{cloudWatchContext.nginx.taskDefinitionFamily}}
8+
value: {{.}}
89
- name: ServiceName
9-
value: {{cloudWatchContext.nginx.serviceName}}
10+
value: {{cloudWatchContext.nginx.serviceName}}
11+
{{/cloudWatchContext.nginx.taskDefinitionFamilies}}

validator/src/test/java/com/amazon/aoc/validators/ContainerInsightPrometheusMetricsValidatorTest.java

+33-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.amazon.aoc.fileconfigs.PredefinedExpectedTemplate;
88
import com.amazon.aoc.models.CloudWatchContext;
99
import com.amazon.aoc.models.Context;
10-
import com.amazon.aoc.models.ValidationConfig;
1110
import com.amazon.aoc.services.CloudWatchService;
1211
import com.amazonaws.services.cloudwatch.model.MetricDataResult;
1312
import org.junit.Test;
@@ -23,12 +22,6 @@ public class ContainerInsightPrometheusMetricsValidatorTest {
2322

2423
@Test
2524
public void testValidationSucceed() throws Exception {
26-
// fake a validation config
27-
ValidationConfig validationConfig = new ValidationConfig();
28-
validationConfig.setCallingType("http");
29-
validationConfig.setExpectedMetricTemplate(
30-
PredefinedExpectedTemplate.CONTAINER_INSIGHT_EKS_PROMETHEUS_METRIC.name());
31-
3225
// mock cloudwatch service
3326
CloudWatchService cloudWatchService = mock(CloudWatchService.class);
3427
List<MetricDataResult> metricDataResults = new ArrayList<>();
@@ -39,9 +32,7 @@ public void testValidationSucceed() throws Exception {
3932
ContainerInsightPrometheusMetricsValidator validator =
4033
new ContainerInsightPrometheusMetricsValidator();
4134
validator.init(
42-
getContext(),
43-
validationConfig,
44-
null,
35+
getContext(), null, null,
4536
PredefinedExpectedTemplate.CONTAINER_INSIGHT_EKS_PROMETHEUS_METRIC
4637
);
4738
validator.setCloudWatchService(cloudWatchService);
@@ -50,6 +41,25 @@ public void testValidationSucceed() throws Exception {
5041
validator.validate();
5142
}
5243

44+
@Test
45+
public void tesECS() throws Exception {
46+
CloudWatchService cloudWatchService = mock(CloudWatchService.class);
47+
List<MetricDataResult> metricDataResults = new ArrayList<>();
48+
metricDataResults.add(new MetricDataResult().withStatusCode("200").withValues(1.0));
49+
when(cloudWatchService.getMetricData(any(), any(), any())).thenReturn(metricDataResults);
50+
51+
ContainerInsightPrometheusMetricsValidator validator =
52+
new ContainerInsightPrometheusMetricsValidator();
53+
validator.init(
54+
getECSContext(), null, null,
55+
PredefinedExpectedTemplate.CONTAINER_INSIGHT_ECS_PROMETHEUS_METRIC
56+
);
57+
validator.setCloudWatchService(cloudWatchService);
58+
validator.setMaxRetryCount(1);
59+
validator.setInitialSleepTime(0);
60+
validator.validate();
61+
}
62+
5363
private Context getContext() {
5464
String namespace = "fakednamespace";
5565
String testingId = "fakedTesingId";
@@ -74,4 +84,17 @@ private Context getContext() {
7484
context.setCloudWatchContext(cloudWatchContext);
7585
return context;
7686
}
87+
88+
private Context getECSContext() {
89+
CloudWatchContext.App jmx = new CloudWatchContext.App();
90+
jmx.setJob("jmx");
91+
jmx.setTaskDefinitionFamilies(new String[]{"jmxawsvpc", "jmxfargate"});
92+
CloudWatchContext cloudWatchContext = new CloudWatchContext();
93+
cloudWatchContext.setJmx(jmx);
94+
cloudWatchContext.setClusterName(this.clusterName);
95+
96+
Context context = getContext();
97+
context.setCloudWatchContext(cloudWatchContext);
98+
return context;
99+
}
77100
}

0 commit comments

Comments
 (0)