Skip to content

Commit bce5d2f

Browse files
author
aadam19
committed
[feat] Integration tests for PV and PVC metrics
1 parent 6fa7a14 commit bce5d2f

File tree

6 files changed

+558
-0
lines changed

6 files changed

+558
-0
lines changed
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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+
data "aws_eks_cluster_auth" "this" {
16+
name = aws_eks_cluster.this.name
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+
resource "aws_eks_node_group" "this" {
30+
cluster_name = aws_eks_cluster.this.name
31+
node_group_name = "cwagent-pv-pvc-node-${module.common.testing_id}"
32+
node_role_arn = aws_iam_role.node_role.arn
33+
subnet_ids = module.basic_components.public_subnet_ids
34+
35+
scaling_config {
36+
desired_size = 1
37+
max_size = 1
38+
min_size = 1
39+
}
40+
41+
ami_type = var.ami_type
42+
capacity_type = "ON_DEMAND"
43+
disk_size = 20
44+
instance_types = [var.instance_type]
45+
46+
depends_on = [
47+
aws_iam_role_policy_attachment.node_AmazonEKSWorkerNodePolicy,
48+
aws_iam_role_policy_attachment.node_AmazonEC2ContainerRegistryReadOnly,
49+
aws_iam_role_policy_attachment.node_CloudWatchAgentServerPolicy
50+
]
51+
}
52+
53+
resource "aws_iam_role" "node_role" {
54+
name = "cwagent-pv-pvc-Worker-Role-${module.common.testing_id}"
55+
assume_role_policy = jsonencode({
56+
Version = "2012-10-17"
57+
Statement = [{
58+
Effect = "Allow"
59+
Principal = { Service = "ec2.amazonaws.com" }
60+
Action = "sts:AssumeRole"
61+
}]
62+
})
63+
}
64+
65+
resource "aws_iam_role_policy_attachment" "node_AmazonEKSWorkerNodePolicy" {
66+
policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
67+
role = aws_iam_role.node_role.name
68+
}
69+
70+
resource "aws_iam_role_policy_attachment" "node_AmazonEC2ContainerRegistryReadOnly" {
71+
policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
72+
role = aws_iam_role.node_role.name
73+
}
74+
75+
resource "aws_iam_role_policy_attachment" "node_CloudWatchAgentServerPolicy" {
76+
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
77+
role = aws_iam_role.node_role.name
78+
}
79+
80+
# Minimal PV/PVC setup for testing metrics
81+
resource "kubernetes_namespace" "test_ns" {
82+
depends_on = [aws_eks_node_group.this]
83+
metadata {
84+
name = "pv-pvc-test"
85+
}
86+
}
87+
88+
# Create PVs in different states
89+
resource "kubernetes_persistent_volume" "test_pv_bound" {
90+
depends_on = [kubernetes_namespace.test_ns]
91+
metadata {
92+
name = "test-pv-bound"
93+
}
94+
spec {
95+
capacity = { storage = "1Gi" }
96+
access_modes = ["ReadWriteOnce"]
97+
persistent_volume_source {
98+
host_path { path = "/tmp/pv-bound" }
99+
}
100+
}
101+
}
102+
103+
resource "kubernetes_persistent_volume" "test_pv_available" {
104+
depends_on = [kubernetes_namespace.test_ns]
105+
metadata {
106+
name = "test-pv-available"
107+
}
108+
spec {
109+
capacity = { storage = "1Gi" }
110+
access_modes = ["ReadWriteOnce"]
111+
persistent_volume_source {
112+
host_path { path = "/tmp/pv-available" }
113+
}
114+
}
115+
}
116+
117+
# Create PVCs in different states
118+
resource "kubernetes_persistent_volume_claim" "test_pvc_bound" {
119+
depends_on = [kubernetes_persistent_volume.test_pv_bound]
120+
metadata {
121+
name = "test-pvc-bound"
122+
namespace = "pv-pvc-test"
123+
}
124+
spec {
125+
access_modes = ["ReadWriteOnce"]
126+
resources {
127+
requests = { storage = "1Gi" }
128+
}
129+
volume_name = kubernetes_persistent_volume.test_pv_bound.metadata[0].name
130+
}
131+
}
132+
133+
resource "kubernetes_persistent_volume_claim" "test_pvc_pending" {
134+
depends_on = [kubernetes_namespace.test_ns]
135+
metadata {
136+
name = "test-pvc-pending"
137+
namespace = "pv-pvc-test"
138+
}
139+
spec {
140+
access_modes = ["ReadWriteOnce"]
141+
storage_class_name = "non-existent-storage-class" # This will naturally stay pending
142+
resources {
143+
requests = { storage = "1Gi" }
144+
}
145+
}
146+
}
147+
148+
# Create a PV that will be deleted to simulate Lost status
149+
resource "kubernetes_persistent_volume" "test_pv_to_delete" {
150+
depends_on = [kubernetes_namespace.test_ns]
151+
metadata {
152+
name = "test-pv-to-delete"
153+
}
154+
spec {
155+
capacity = { storage = "1Gi" }
156+
access_modes = ["ReadWriteOnce"]
157+
persistent_volume_source {
158+
host_path { path = "/tmp/pv-to-delete" }
159+
}
160+
}
161+
}
162+
163+
resource "kubernetes_persistent_volume_claim" "test_pvc_will_be_lost" {
164+
depends_on = [kubernetes_persistent_volume.test_pv_to_delete]
165+
metadata {
166+
name = "test-pvc-will-be-lost"
167+
namespace = "pv-pvc-test"
168+
}
169+
spec {
170+
access_modes = ["ReadWriteOnce"]
171+
resources {
172+
requests = { storage = "1Gi" }
173+
}
174+
volume_name = kubernetes_persistent_volume.test_pv_to_delete.metadata[0].name
175+
}
176+
}
177+
178+
# Job to simulate Lost status by deleting the PV after PVC is bound
179+
resource "kubernetes_job" "create_lost_pvc" {
180+
depends_on = [kubernetes_persistent_volume_claim.test_pvc_will_be_lost]
181+
metadata {
182+
name = "create-lost-pvc"
183+
namespace = "pv-pvc-test"
184+
}
185+
spec {
186+
template {
187+
metadata {}
188+
spec {
189+
restart_policy = "Never"
190+
container {
191+
name = "kubectl"
192+
image = "bitnami/kubectl:latest"
193+
command = ["/bin/bash", "-c"]
194+
args = [
195+
# Wait for PVC to bind, remove finalizers, then delete PV to create Lost status
196+
"kubectl wait --for=condition=Bound pvc/test-pvc-will-be-lost --timeout=30s && sleep 5 && kubectl patch pvc test-pvc-will-be-lost -p '{\"metadata\":{\"finalizers\":null}}' && kubectl delete pv test-pv-to-delete --force --grace-period=0"
197+
]
198+
}
199+
service_account_name = "pvc-manager"
200+
}
201+
}
202+
}
203+
}
204+
205+
# Service account and RBAC for the job
206+
resource "kubernetes_service_account" "pvc_manager" {
207+
depends_on = [kubernetes_namespace.test_ns]
208+
metadata {
209+
name = "pvc-manager"
210+
namespace = "pv-pvc-test"
211+
}
212+
}
213+
214+
resource "kubernetes_cluster_role" "pvc_manager_role" {
215+
depends_on = [kubernetes_namespace.test_ns]
216+
metadata {
217+
name = "pvc-manager-role"
218+
}
219+
rule {
220+
api_groups = [""]
221+
resources = ["persistentvolumes", "persistentvolumeclaims"]
222+
verbs = ["get", "list", "watch", "delete", "patch"]
223+
}
224+
}
225+
226+
resource "kubernetes_cluster_role_binding" "pvc_manager_binding" {
227+
depends_on = [kubernetes_cluster_role.pvc_manager_role]
228+
metadata {
229+
name = "pvc-manager-binding"
230+
}
231+
role_ref {
232+
api_group = "rbac.authorization.k8s.io"
233+
kind = "ClusterRole"
234+
name = "pvc-manager-role"
235+
}
236+
subject {
237+
kind = "ServiceAccount"
238+
name = "pvc-manager"
239+
namespace = "pv-pvc-test"
240+
}
241+
}
242+
243+
# Install CloudWatch Agent via helm
244+
data "external" "clone_helm_chart" {
245+
program = ["bash", "-c", <<-EOT
246+
rm -rf ./helm-charts
247+
git clone -b ${var.helm_chart_branch} https://github.com/aws-observability/helm-charts.git ./helm-charts
248+
echo '{"status":"ready"}'
249+
EOT
250+
]
251+
}
252+
253+
resource "helm_release" "aws_observability" {
254+
name = "amazon-cloudwatch-observability"
255+
chart = "./helm-charts/charts/amazon-cloudwatch-observability"
256+
namespace = "amazon-cloudwatch"
257+
create_namespace = true
258+
259+
set = [
260+
{
261+
name = "clusterName"
262+
value = aws_eks_cluster.this.name
263+
},
264+
{
265+
name = "region"
266+
value = var.region
267+
}
268+
]
269+
depends_on = [
270+
aws_eks_cluster.this,
271+
aws_eks_node_group.this,
272+
data.external.clone_helm_chart,
273+
]
274+
}
275+
276+
resource "null_resource" "update_image" {
277+
depends_on = [helm_release.aws_observability]
278+
provisioner "local-exec" {
279+
command = <<-EOT
280+
aws eks --region ${var.region} update-kubeconfig --name ${aws_eks_cluster.this.name}
281+
kubectl -n amazon-cloudwatch patch AmazonCloudWatchAgent cloudwatch-agent --type='json' -p='[{"op": "replace", "path": "/spec/image", "value": "${var.cwagent_image_repo}:${var.cwagent_image_tag}"}]'
282+
sleep 10
283+
EOT
284+
}
285+
}
286+
287+
resource "null_resource" "validator" {
288+
depends_on = [
289+
kubernetes_persistent_volume_claim.test_pvc_bound,
290+
kubernetes_persistent_volume_claim.test_pvc_pending,
291+
kubernetes_job.create_lost_pvc,
292+
helm_release.aws_observability,
293+
null_resource.update_image,
294+
]
295+
296+
provisioner "local-exec" {
297+
command = <<-EOT
298+
cd ../../../..
299+
# Wait a bit for the Lost PVC state to be created
300+
sleep 30
301+
go test ${var.test_dir} -timeout 1h -eksClusterName=${aws_eks_cluster.this.name} -computeType=EKS -v -eksDeploymentStrategy=DAEMON
302+
EOT
303+
}
304+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
provider "aws" {
2+
region = var.region
3+
}
4+
5+
provider "kubernetes" {
6+
exec {
7+
api_version = "client.authentication.k8s.io/v1beta1"
8+
command = "aws"
9+
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.this.name]
10+
}
11+
host = aws_eks_cluster.this.endpoint
12+
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority.0.data)
13+
token = data.aws_eks_cluster_auth.this.token
14+
}
15+
16+
provider "helm" {
17+
kubernetes = {
18+
host = aws_eks_cluster.this.endpoint
19+
cluster_ca_certificate = base64decode(aws_eks_cluster.this.certificate_authority.0.data)
20+
exec = {
21+
api_version = "client.authentication.k8s.io/v1beta1"
22+
args = ["eks", "get-token", "--cluster-name", aws_eks_cluster.this.name]
23+
command = "aws"
24+
}
25+
}
26+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
variable "region" {
2+
type = string
3+
default = "us-west-2"
4+
}
5+
6+
variable "test_dir" {
7+
type = string
8+
default = "./test/pv_pvc"
9+
}
10+
11+
variable "cwagent_image_repo" {
12+
type = string
13+
default = "public.ecr.aws/cloudwatch-agent/cloudwatch-agent"
14+
}
15+
16+
variable "cwagent_image_tag" {
17+
type = string
18+
default = "latest"
19+
}
20+
21+
variable "k8s_version" {
22+
type = string
23+
default = "1.32"
24+
}
25+
26+
variable "ami_type" {
27+
type = string
28+
default = "AL2_x86_64"
29+
}
30+
31+
variable "instance_type" {
32+
type = string
33+
default = "t3a.medium"
34+
}
35+
36+
variable "helm_chart_branch" {
37+
type = string
38+
default = "main"
39+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-04/schema#",
3+
"title": "persistent volume schema",
4+
"description": "json schema for persistent volume metrics",
5+
"type": "object",
6+
"properties": {
7+
"CloudWatchMetrics": {},
8+
"ClusterName": {},
9+
"Timestamp": {},
10+
"Type": {},
11+
"Version": {},
12+
"kubernetes": {},
13+
"persistent_volume_count": {}
14+
},
15+
"required": [
16+
"ClusterName",
17+
"Timestamp",
18+
"Type",
19+
"Version",
20+
"CloudWatchMetrics"
21+
]
22+
}

0 commit comments

Comments
 (0)