Skip to content

Commit 9a2c3c7

Browse files
authored
Add OTEL LIS CSI MetricsV2 integration tests (#693)
Add integration tests for Local Instance Store CSI driver metrics validation in the OTEL container insights pipeline. Validates all 13 metrics (9 volume-scoped counters/gauge, 2 latency histograms, 2 collector-internal counters) with proper attribute assertions. - Add test/otel/lis_csi/ test suite following EBS CSI v2 pattern - Add terraform/eks/daemon/otel-lis-csi/ infrastructure - Add otel/lis_csi entry to test case generator - Split metrics into volume-scoped and collector-internal slices for correct volume_id/instance_id assertion scoping
1 parent d0f4aaa commit 9a2c3c7

9 files changed

Lines changed: 854 additions & 0 deletions

File tree

generator/test_case_generator.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,13 @@ var testTypeToTestConfig = map[string][]testConfig{
452452
instanceType: "g4dn.xlarge",
453453
ami: "AL2023_x86_64_NVIDIA",
454454
},
455+
{
456+
testDir: "./test/otel/lis_csi",
457+
terraformDir: "terraform/eks/daemon/otel-lis-csi",
458+
targets: map[string]map[string]struct{}{"arc": {"amd64": {}}},
459+
instanceType: "i7i.xlarge",
460+
ami: "AL2023_x86_64_STANDARD",
461+
},
455462
{
456463
testDir: "./test/otel/multi_efa",
457464
terraformDir: "terraform/eks/daemon/otel-multi-efa",
Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT
3+
4+
module "common" {
5+
source = "../../../common"
6+
cwagent_image_repo = var.cwagent_image_repo
7+
cwagent_image_tag = var.cwagent_image_tag
8+
}
9+
10+
module "basic_components" {
11+
source = "../../../basic_components"
12+
region = var.region
13+
}
14+
15+
locals {
16+
aws_eks = "aws eks --region ${var.region}"
17+
}
18+
19+
resource "aws_eks_cluster" "this" {
20+
name = "cwagent-eks-integ-${module.common.testing_id}"
21+
role_arn = module.basic_components.role_arn
22+
version = var.k8s_version
23+
vpc_config {
24+
subnet_ids = module.basic_components.public_subnet_ids
25+
security_group_ids = [module.basic_components.security_group]
26+
}
27+
}
28+
29+
# EKS Node Group
30+
resource "aws_eks_node_group" "this" {
31+
cluster_name = aws_eks_cluster.this.name
32+
node_group_name = "cwagent-otel-liscsi-integ-node-${module.common.testing_id}"
33+
node_role_arn = aws_iam_role.node_role.arn
34+
subnet_ids = module.basic_components.public_subnet_ids
35+
36+
scaling_config {
37+
desired_size = 1
38+
max_size = 1
39+
min_size = 1
40+
}
41+
42+
ami_type = var.ami_type
43+
capacity_type = "ON_DEMAND"
44+
disk_size = 20
45+
instance_types = [var.instance_type]
46+
47+
depends_on = [
48+
aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly,
49+
aws_iam_role_policy_attachment.node_AmazonEKS_CNI_Policy,
50+
aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy,
51+
aws_iam_role_policy_attachment.node_CloudWatchAgentServerPolicy,
52+
]
53+
}
54+
55+
# EKS Node IAM Role
56+
resource "aws_iam_role" "node_role" {
57+
name = "cwagent-otel-liscsi-eks-Worker-Role-${module.common.testing_id}"
58+
assume_role_policy = jsonencode({
59+
Version = "2012-10-17"
60+
Statement = [{
61+
Effect = "Allow"
62+
Principal = { Service = "ec2.amazonaws.com" }
63+
Action = "sts:AssumeRole"
64+
}]
65+
})
66+
}
67+
68+
resource "aws_iam_role_policy_attachment" "node_AmazonEKSWorkerNodePolicy" {
69+
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
70+
role = aws_iam_role.node_role.name
71+
}
72+
73+
resource "aws_iam_role_policy_attachment" "node_AmazonEKS_CNI_Policy" {
74+
policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
75+
role = aws_iam_role.node_role.name
76+
}
77+
78+
resource "aws_iam_role_policy_attachment" "node_AmazonEC2ContainerRegistryReadOnly" {
79+
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
80+
role = aws_iam_role.node_role.name
81+
}
82+
83+
resource "aws_iam_role_policy_attachment" "node_CloudWatchAgentServerPolicy" {
84+
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
85+
role = aws_iam_role.node_role.name
86+
}
87+
88+
# Pod Identity IAM Role
89+
resource "aws_iam_role" "pod_identity_role" {
90+
name = "cwagent-otel-liscsi-pod-identity-${module.common.testing_id}"
91+
assume_role_policy = jsonencode({
92+
Version = "2012-10-17"
93+
Statement = [{
94+
Effect = "Allow"
95+
Principal = { Service = "pods.eks.amazonaws.com" }
96+
Action = ["sts:AssumeRole", "sts:TagSession"]
97+
}]
98+
})
99+
}
100+
101+
resource "aws_iam_role_policy_attachment" "pod_identity_CloudWatchAgentServerPolicy" {
102+
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
103+
role = aws_iam_role.pod_identity_role.name
104+
}
105+
106+
# --- EKS Addon: Pod Identity agent ---
107+
108+
resource "aws_eks_addon" "pod_identity_agent" {
109+
depends_on = [aws_eks_node_group.this]
110+
cluster_name = aws_eks_cluster.this.name
111+
addon_name = "eks-pod-identity-agent"
112+
}
113+
114+
# --- Update kubeconfig ---
115+
116+
resource "null_resource" "kubectl" {
117+
depends_on = [aws_eks_cluster.this, aws_eks_node_group.this]
118+
provisioner "local-exec" {
119+
command = "${local.aws_eks} update-kubeconfig --name ${aws_eks_cluster.this.name}"
120+
}
121+
}
122+
123+
# --- LIS CSI addon ---
124+
125+
resource "aws_eks_addon" "lis_csi_addon" {
126+
depends_on = [aws_eks_node_group.this]
127+
cluster_name = aws_eks_cluster.this.name
128+
addon_name = "aws-ec2-local-instance-store-csi-driver"
129+
configuration_values = jsonencode({ metrics = { enabled = true } })
130+
}
131+
132+
resource "null_resource" "wait_for_lis_csi" {
133+
depends_on = [aws_eks_addon.lis_csi_addon, null_resource.kubectl]
134+
provisioner "local-exec" {
135+
command = <<-EOT
136+
echo "Waiting for LIS CSI DaemonSet rollout..."
137+
kubectl rollout status daemonset/ec2-instance-store-plugin -n kube-system --timeout=300s
138+
echo "LIS CSI pods:"
139+
kubectl get pods -n kube-system -l app.kubernetes.io/name=ec2-instance-store-plugin -o wide
140+
echo "StorageClasses:"
141+
kubectl get sc
142+
EOT
143+
}
144+
}
145+
146+
# --- Helm chart install ---
147+
148+
data "external" "clone_helm_chart" {
149+
program = ["bash", "-c", <<-EOT
150+
rm -rf ./helm-charts
151+
git clone -b ${var.helm_chart_branch} https://github.com/aws-observability/helm-charts.git ./helm-charts
152+
echo '{"status":"ready"}'
153+
EOT
154+
]
155+
}
156+
157+
resource "helm_release" "aws_observability" {
158+
name = "amazon-cloudwatch-observability"
159+
chart = "./helm-charts/charts/amazon-cloudwatch-observability"
160+
namespace = "amazon-cloudwatch"
161+
create_namespace = true
162+
163+
set = [
164+
{ name = "clusterName", value = aws_eks_cluster.this.name },
165+
{ name = "region", value = var.region }
166+
]
167+
168+
depends_on = [
169+
aws_eks_addon.pod_identity_agent,
170+
null_resource.kubectl,
171+
data.external.clone_helm_chart,
172+
]
173+
}
174+
175+
# --- Pod Identity association (after Helm creates the service account) ---
176+
177+
resource "aws_eks_pod_identity_association" "cloudwatch_agent" {
178+
depends_on = [helm_release.aws_observability]
179+
cluster_name = aws_eks_cluster.this.name
180+
namespace = "amazon-cloudwatch"
181+
service_account = "cloudwatch-agent"
182+
role_arn = aws_iam_role.pod_identity_role.arn
183+
}
184+
185+
# --- Patch agent image ---
186+
187+
resource "null_resource" "update_image" {
188+
depends_on = [helm_release.aws_observability, null_resource.kubectl]
189+
triggers = { timestamp = timestamp() }
190+
provisioner "local-exec" {
191+
command = <<-EOT
192+
sleep 30
193+
kubectl -n amazon-cloudwatch patch AmazonCloudWatchAgent cloudwatch-agent --type='json' \
194+
-p='[{"op": "replace", "path": "/spec/image", "value": "${var.cwagent_image_repo}:${var.cwagent_image_tag}"}]'
195+
sleep 10
196+
EOT
197+
}
198+
}
199+
200+
# --- Restart pods to pick up Pod Identity + new image ---
201+
202+
resource "null_resource" "restart_pods" {
203+
depends_on = [aws_eks_pod_identity_association.cloudwatch_agent, null_resource.update_image]
204+
triggers = { timestamp = timestamp() }
205+
provisioner "local-exec" {
206+
command = <<-EOT
207+
kubectl -n amazon-cloudwatch rollout restart daemonset/cloudwatch-agent
208+
kubectl -n amazon-cloudwatch rollout status daemonset/cloudwatch-agent --timeout=120s
209+
EOT
210+
}
211+
}
212+
213+
# --- Test workload: nginx ---
214+
215+
resource "kubernetes_deployment_v1" "nginx_test" {
216+
depends_on = [aws_eks_node_group.this]
217+
metadata {
218+
name = "nginx-test"
219+
namespace = "default"
220+
}
221+
spec {
222+
replicas = 1
223+
selector { match_labels = { app = "nginx-test" } }
224+
template {
225+
metadata { labels = { app = "nginx-test" } }
226+
spec {
227+
container {
228+
name = "nginx"
229+
image = "public.ecr.aws/nginx/nginx:latest"
230+
port { container_port = 80 }
231+
}
232+
}
233+
}
234+
}
235+
}
236+
237+
# --- LIS CSI IO workload ---
238+
239+
resource "kubernetes_deployment_v1" "lis_csi_io_workload" {
240+
depends_on = [null_resource.wait_for_lis_csi]
241+
metadata {
242+
name = "liscsi-integ-test-io-workload"
243+
namespace = "default"
244+
labels = {
245+
app = "liscsi-integ-test"
246+
}
247+
}
248+
spec {
249+
replicas = 1
250+
selector {
251+
match_labels = {
252+
app = "liscsi-integ-test"
253+
}
254+
}
255+
template {
256+
metadata {
257+
labels = {
258+
app = "liscsi-integ-test"
259+
}
260+
}
261+
spec {
262+
container {
263+
name = "liscsi-integ-test-writer"
264+
image = "busybox:1.35"
265+
command = ["sh", "-c", "while true; do dd if=/dev/zero of=/data/out.txt bs=1M count=10; sleep 5; done"]
266+
volume_mount {
267+
name = "lis-storage"
268+
mount_path = "/data"
269+
}
270+
}
271+
volume {
272+
name = "lis-storage"
273+
ephemeral {
274+
volume_claim_template {
275+
spec {
276+
access_modes = ["ReadWriteOnce"]
277+
storage_class_name = "ec2-instance-store-sc"
278+
resources {
279+
requests = {
280+
storage = "1Gi"
281+
}
282+
}
283+
}
284+
}
285+
}
286+
}
287+
}
288+
}
289+
}
290+
}
291+
292+
# --- Test runner ---
293+
294+
resource "null_resource" "validator" {
295+
depends_on = [
296+
null_resource.restart_pods,
297+
null_resource.wait_for_lis_csi,
298+
kubernetes_deployment_v1.nginx_test,
299+
kubernetes_deployment_v1.lis_csi_io_workload,
300+
]
301+
302+
triggers = { always_run = timestamp() }
303+
304+
provisioner "local-exec" {
305+
command = <<-EOT
306+
echo "Running OTEL LIS CSI integration tests"
307+
cd ../../../..
308+
309+
echo "Waiting 3 minutes for metrics to propagate..."
310+
sleep 180
311+
312+
go test -tags integration -timeout 1h -v ${var.test_dir} \
313+
-eksClusterName=${aws_eks_cluster.this.name} \
314+
-computeType=EKS \
315+
-eksDeploymentStrategy=DAEMON \
316+
-region=${var.region}
317+
EOT
318+
}
319+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: MIT
3+
4+
provider "aws" {
5+
region = var.region
6+
}
7+
8+
provider "kubernetes" {
9+
host = aws_eks_cluster.this.endpoint
10+
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority.0.data)
11+
exec {
12+
api_version = "client.authentication.k8s.io/v1beta1"
13+
command = "aws"
14+
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.this.name]
15+
}
16+
}
17+
18+
provider "helm" {
19+
kubernetes = {
20+
host = aws_eks_cluster.this.endpoint
21+
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority.0.data)
22+
exec = {
23+
api_version = "client.authentication.k8s.io/v1beta1"
24+
command = "aws"
25+
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.this.name]
26+
}
27+
}
28+
}

0 commit comments

Comments
 (0)