Skip to content

Commit cc276ef

Browse files
committed
tests(example): fully verify date exclusion feature
Write an end-to-end test to verify that the Azure function does not run the scheduler when the date exclusion configuration is set up for the current day.
1 parent 35983aa commit cc276ef

File tree

12 files changed

+241
-51
lines changed

12 files changed

+241
-51
lines changed

examples/scheduler-with-exclusion-dates/main.tf renamed to examples/date-exclusion/main.tf

Lines changed: 22 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,12 @@ data "aws_ami" "ubuntu" {
1313
owners = ["099720109477"] # Canonical
1414
}
1515

16-
resource "aws_vpc" "test" {
17-
cidr_block = "10.0.0.0/16"
18-
enable_dns_hostnames = true
19-
enable_dns_support = true
20-
}
21-
22-
resource "aws_internet_gateway" "test" {
23-
vpc_id = aws_vpc.test.id
24-
25-
tags = {
26-
Name = "scheduler-exclusion-igw-${random_pet.suffix.id}"
27-
}
28-
}
29-
30-
resource "aws_subnet" "public" {
31-
vpc_id = aws_vpc.test.id
32-
cidr_block = "10.0.1.0/24"
33-
availability_zone = data.aws_availability_zones.available.names[0]
34-
map_public_ip_on_launch = true
35-
}
36-
37-
resource "aws_route_table" "public" {
38-
vpc_id = aws_vpc.test.id
39-
40-
route {
41-
cidr_block = "0.0.0.0/0"
42-
gateway_id = aws_internet_gateway.test.id
43-
}
44-
}
45-
46-
resource "aws_route_table_association" "public" {
47-
subnet_id = aws_subnet.public.id
48-
route_table_id = aws_route_table.public.id
49-
}
50-
51-
data "aws_availability_zones" "available" {
52-
state = "available"
53-
}
5416

5517
resource "aws_instance" "scheduled" {
5618
count = 2
5719
ami = data.aws_ami.ubuntu.id
5820
instance_type = "t2.micro"
59-
subnet_id = aws_subnet.public.id
21+
subnet_id = aws_subnet.test.id
6022

6123
tags = {
6224
tostop = "true-${random_pet.suffix.id}"
@@ -67,7 +29,7 @@ resource "aws_instance" "scheduled" {
6729
resource "aws_instance" "not_scheduled" {
6830
ami = data.aws_ami.ubuntu.id
6931
instance_type = "t2.micro"
70-
subnet_id = aws_subnet.public.id
32+
subnet_id = aws_subnet.test.id
7133

7234
tags = {
7335
tostop = "false"
@@ -85,6 +47,16 @@ module "ec2_stop_with_exclusions" {
8547
rds_schedule = false
8648
autoscaling_schedule = false
8749
cloudwatch_alarm_schedule = false
50+
scheduler_excluded_dates = [
51+
"01-01", # New Year's Day
52+
"12-25", # Christmas Day
53+
"12-24", # Christmas Eve
54+
"07-04", # Independence Day (US)
55+
"11-24", # Thanksgiving (example date)
56+
"05-01", # Labor Day
57+
"12-31", # New Year's Eve
58+
formatdate("MM-DD", timestamp()) # Current date (for tests purposes)
59+
]
8860

8961
scheduler_tag = {
9062
key = "tostop"
@@ -103,19 +75,18 @@ module "ec2_start_with_exclusions" {
10375
autoscaling_schedule = false
10476
cloudwatch_alarm_schedule = false
10577

106-
scheduler_excluded_dates = [
107-
"01-01", # New Year's Day
108-
"12-25", # Christmas Day
109-
"12-24", # Christmas Eve
110-
"07-04", # Independence Day (US)
111-
"11-24", # Thanksgiving (example date)
112-
"05-01", # Labor Day
113-
"12-31", # New Year's Eve
114-
formatdate("MM-DD", timestamp()) # Current date (for tests purposes)
115-
]
116-
11778
scheduler_tag = {
11879
key = "tostop"
11980
value = "true-${random_pet.suffix.id}"
12081
}
12182
}
83+
84+
module "test_execution" {
85+
count = var.test_mode ? 1 : 0
86+
source = "./test-execution"
87+
88+
lambda_stop_name = module.ec2_stop_with_exclusions.scheduler_lambda_name
89+
instance_1_to_scheduled_id = aws_instance.scheduled[0].id
90+
instance_2_to_scheduled_id = aws_instance.scheduled[1].id
91+
instance_not_to_scheduled_id = aws_instance.not_scheduled.id
92+
}
File renamed without changes.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
run "create_test_infrastructure" {
2+
command = apply
3+
4+
variables {
5+
test_mode = true
6+
}
7+
8+
assert {
9+
condition = module.ec2_stop_with_exclusions.scheduler_lambda_name == "stop-ec2-exclusions-${random_pet.suffix.id}"
10+
error_message = "Invalid Stop lambda name"
11+
}
12+
13+
assert {
14+
condition = module.ec2_start_with_exclusions.scheduler_lambda_name == "start-ec2-exclusions-${random_pet.suffix.id}"
15+
error_message = "Invalid Start lambda name"
16+
}
17+
18+
assert {
19+
condition = module.test_execution[0].instance_1_scheduled_state == "running"
20+
error_message = "Instance 1 should remain running (current date is excluded)"
21+
}
22+
23+
assert {
24+
condition = module.test_execution[0].instance_2_scheduled_state == "running"
25+
error_message = "Instance 2 should remain running (current date is excluded)"
26+
}
27+
28+
assert {
29+
condition = module.test_execution[0].instance_not_scheduled_state == "running"
30+
error_message = "Instance not scheduled should remain running"
31+
}
32+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
resource "null_resource" "wait_running_state" {
2+
provisioner "local-exec" {
3+
command = <<-EOT
4+
python3 ${path.module}/wait_instances.py running \
5+
${var.instance_1_to_scheduled_id} \
6+
${var.instance_2_to_scheduled_id}
7+
EOT
8+
}
9+
}
10+
11+
resource "aws_lambda_invocation" "this" {
12+
function_name = var.lambda_stop_name
13+
14+
input = jsonencode({
15+
key1 = "value1"
16+
key2 = "value2"
17+
})
18+
19+
depends_on = [null_resource.wait_running_state]
20+
}
21+
22+
resource "null_resource" "wait_exclusion_check" {
23+
provisioner "local-exec" {
24+
command = <<-EOT
25+
echo "Waiting for exclusion date verification..."
26+
sleep 30
27+
EOT
28+
}
29+
30+
depends_on = [aws_lambda_invocation.this]
31+
}
32+
33+
data "aws_instance" "instance_1_to_scheduled_id" {
34+
instance_id = var.instance_1_to_scheduled_id
35+
36+
depends_on = [null_resource.wait_exclusion_check]
37+
}
38+
39+
data "aws_instance" "instance_2_to_scheduled_id" {
40+
instance_id = var.instance_2_to_scheduled_id
41+
42+
depends_on = [null_resource.wait_exclusion_check]
43+
}
44+
45+
data "aws_instance" "instance_not_to_scheduled_id" {
46+
instance_id = var.instance_not_to_scheduled_id
47+
48+
depends_on = [null_resource.wait_exclusion_check]
49+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
output "instance_1_scheduled_state" {
2+
description = "State of instance 1 after scheduler execution"
3+
value = data.aws_instance.instance_1_to_scheduled_id.instance_state
4+
}
5+
6+
output "instance_2_scheduled_state" {
7+
description = "State of instance 2 after scheduler execution"
8+
value = data.aws_instance.instance_2_to_scheduled_id.instance_state
9+
}
10+
11+
output "instance_not_scheduled_state" {
12+
description = "State of instance not scheduled after scheduler execution"
13+
value = data.aws_instance.instance_not_to_scheduled_id.instance_state
14+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
variable "lambda_stop_name" {
2+
description = "Name of the lambda function to stop EC2"
3+
type = string
4+
}
5+
6+
variable "instance_1_to_scheduled_id" {
7+
description = "Instance 1 ID to test scheduled action"
8+
type = string
9+
}
10+
11+
variable "instance_2_to_scheduled_id" {
12+
description = "Instance 2 ID to test scheduled action"
13+
type = string
14+
}
15+
16+
variable "instance_not_to_scheduled_id" {
17+
description = "Instance ID not to be scheduled"
18+
type = string
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
terraform {
2+
required_version = ">= 1.3"
3+
required_providers {
4+
aws = {
5+
source = "hashicorp/aws"
6+
version = ">= 5.0"
7+
}
8+
null = {
9+
source = "hashicorp/null"
10+
version = ">= 3.0"
11+
}
12+
}
13+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env python3
2+
"""Script to wait for AWS instances status."""
3+
4+
import sys
5+
import time
6+
from typing import List
7+
8+
import boto3
9+
from botocore.exceptions import ClientError
10+
11+
12+
def wait_for_instances_status(
13+
instance_ids: List[str], desired_status: str, region: str = None
14+
) -> None:
15+
"""Wait for instances to reach desired status.
16+
17+
Args:
18+
instance_ids: List of instance IDs to check
19+
desired_status: Desired status to wait for (e.g. 'running', 'stopped')
20+
region: AWS region name
21+
"""
22+
if not instance_ids:
23+
return
24+
25+
ec2 = boto3.client("ec2", region_name=region) if region else boto3.client("ec2")
26+
start_time = time.time()
27+
timeout = 600 # 10 minutes timeout
28+
29+
while True:
30+
try:
31+
# Check if timeout has been reached
32+
if time.time() - start_time > timeout:
33+
print(
34+
f"Timeout reached after {timeout} seconds. Some instances may not have reached the desired status."
35+
)
36+
sys.exit(1)
37+
38+
response = ec2.describe_instances(InstanceIds=instance_ids)
39+
all_instances_in_desired_state = True
40+
41+
for reservation in response["Reservations"]:
42+
for instance in reservation["Instances"]:
43+
current_state = instance["State"]["Name"]
44+
if current_state != desired_status:
45+
all_instances_in_desired_state = False
46+
break
47+
48+
if not all_instances_in_desired_state:
49+
break
50+
51+
if all_instances_in_desired_state:
52+
print(f"All instances are now {desired_status}")
53+
return
54+
55+
print(f"Waiting for instances to be {desired_status}...")
56+
time.sleep(10) # Wait 10 seconds before checking again
57+
58+
except ClientError as e:
59+
print(f"Error checking instance status: {e}")
60+
sys.exit(1)
61+
62+
63+
if __name__ == "__main__":
64+
if len(sys.argv) < 3:
65+
print(
66+
"Usage: python wait_instances.py <desired_status> <instance_id1> [instance_id2 ...]"
67+
)
68+
sys.exit(1)
69+
70+
desired_status = sys.argv[1]
71+
instance_ids = sys.argv[2:]
72+
73+
wait_for_instances_status(instance_ids, desired_status)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
variable "test_mode" {
2+
description = "Enable test mode"
3+
type = bool
4+
default = false
5+
}
File renamed without changes.

0 commit comments

Comments
 (0)