Skip to content

Commit 192f8d3

Browse files
ci(infra): Post 0.7.x Fargate Terraform Stack (TracecatHQ#344)
Co-authored-by: Jason Ostrom <[email protected]>
1 parent 6a98f06 commit 192f8d3

24 files changed

+1668
-22
lines changed

alembic/env.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
from alembic import context
99
from tracecat.db import schemas # noqa: F401
1010

11+
12+
TRACECAT__DB_URI = os.getenv("TRACECAT__DB_URI")
13+
if not TRACECAT__DB_URI:
14+
username = os.getenv("TRACECAT__DB_USER", "postgres")
15+
password = os.getenv("TRACECAT__DB_PASS")
16+
host = os.getenv("TRACECAT__DB_ENDPOINT")
17+
port = os.getenv("TRACECAT__DB_PORT", 5432)
18+
database = os.getenv("TRACECAT__DB_NAME", "postgres")
19+
TRACECAT__DB_URI = f"postgresql+psycopg://{username}:{password}@{host}:{port!s}/{database}"
20+
21+
1122
# this is the Alembic Config object, which provides
1223
# access to the values within the .ini file in use.
1324
config = context.config
@@ -31,9 +42,8 @@
3142

3243
def run_migrations_offline() -> None:
3344
"""Run migrations in 'offline' mode."""
34-
url = os.environ["TRACECAT__DB_URI"]
3545
context.configure(
36-
url=url,
46+
url=TRACECAT__DB_URI,
3747
target_metadata=target_metadata,
3848
literal_binds=True,
3949
dialect_opts={"paramstyle": "named"},
@@ -49,7 +59,7 @@ def run_migrations_online() -> None:
4959
configuration=config.get_section(config.config_ini_section, {}),
5060
prefix="sqlalchemy.",
5161
poolclass=pool.NullPool,
52-
url=os.environ["TRACECAT__DB_URI"],
62+
url=TRACECAT__DB_URI
5363
)
5464

5565
with connectable.connect() as connection:

deployments/aws/fargate/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.env
2+
.env.example
3+
env.sh

deployments/aws/fargate/acm.tf

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
resource "aws_acm_certificate" "this" {
2+
domain_name = var.domain_name
3+
validation_method = "DNS"
4+
5+
lifecycle {
6+
create_before_destroy = true
7+
}
8+
}
9+
10+
resource "aws_route53_record" "cert_validation" {
11+
for_each = {
12+
for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
13+
name = dvo.resource_record_name
14+
record = dvo.resource_record_value
15+
type = dvo.resource_record_type
16+
}
17+
}
18+
19+
allow_overwrite = true
20+
name = each.value.name
21+
records = [each.value.record]
22+
ttl = 60
23+
type = each.value.type
24+
zone_id = var.hosted_zone_id
25+
}
26+
27+
resource "aws_acm_certificate_validation" "this" {
28+
certificate_arn = aws_acm_certificate.this.arn
29+
validation_record_fqdns = [for record in aws_route53_record.cert_validation : record.fqdn]
30+
}

deployments/aws/fargate/alb.tf

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Application Load Balancer
2+
resource "aws_alb" "this" {
3+
name = "tracecat-alb"
4+
internal = false
5+
load_balancer_type = "application"
6+
subnets = aws_subnet.public[*].id
7+
security_groups = [aws_security_group.alb.id]
8+
9+
tags = {
10+
Name = "tracecat-alb"
11+
}
12+
}
13+
14+
# Target Group for Caddy
15+
resource "aws_alb_target_group" "caddy" {
16+
name = "tracecat-caddy-tg"
17+
port = 80
18+
protocol = "HTTP"
19+
vpc_id = aws_vpc.tracecat.id
20+
target_type = "ip"
21+
22+
health_check {
23+
healthy_threshold = "3"
24+
interval = "30"
25+
protocol = "HTTP"
26+
matcher = "200"
27+
timeout = "3"
28+
path = "/"
29+
unhealthy_threshold = "2"
30+
}
31+
}
32+
33+
# HTTPS Listener
34+
resource "aws_alb_listener" "https" {
35+
load_balancer_arn = aws_alb.this.id
36+
port = "443"
37+
protocol = "HTTPS"
38+
39+
ssl_policy = "ELBSecurityPolicy-2016-08"
40+
certificate_arn = aws_acm_certificate.this.arn
41+
42+
default_action {
43+
target_group_arn = aws_alb_target_group.caddy.id
44+
type = "forward"
45+
}
46+
}
47+
48+
# HTTP to HTTPS Redirect
49+
resource "aws_alb_listener" "http" {
50+
load_balancer_arn = aws_alb.this.id
51+
port = "80"
52+
protocol = "HTTP"
53+
54+
default_action {
55+
type = "redirect"
56+
redirect {
57+
port = "443"
58+
protocol = "HTTPS"
59+
status_code = "HTTP_301"
60+
}
61+
}
62+
}

deployments/aws/fargate/cloudwatch.tf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "aws_cloudwatch_log_group" "tracecat_log_group" {
2+
name = "/ecs/tracecat"
3+
retention_in_days = 30
4+
}

deployments/aws/fargate/dns.tf

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
resource "aws_route53_record" "a_record" {
2+
zone_id = var.hosted_zone_id
3+
name = var.domain_name
4+
type = "A"
5+
6+
alias {
7+
name = aws_alb.this.dns_name
8+
zone_id = aws_alb.this.zone_id
9+
evaluate_target_health = true
10+
}
11+
}

deployments/aws/fargate/ecs-api.tf

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ECS Task Definition for API Service
2+
resource "aws_ecs_task_definition" "api_task_definition" {
3+
family = "TracecatApiTaskDefinition"
4+
network_mode = "awsvpc"
5+
requires_compatibilities = ["FARGATE"]
6+
cpu = var.api_cpu
7+
memory = var.api_memory
8+
execution_role_arn = aws_iam_role.api_execution.arn
9+
task_role_arn = aws_iam_role.api_worker_task.arn
10+
11+
container_definitions = jsonencode([
12+
{
13+
name = "TracecatApiContainer"
14+
image = "${var.tracecat_image}:${var.tracecat_image_tag}"
15+
portMappings = [
16+
{
17+
containerPort = 8000
18+
hostPort = 8000
19+
name = "api"
20+
appProtocol = "http"
21+
}
22+
]
23+
logConfiguration = {
24+
logDriver = "awslogs"
25+
options = {
26+
awslogs-group = aws_cloudwatch_log_group.tracecat_log_group.name
27+
awslogs-region = var.aws_region
28+
awslogs-stream-prefix = "api"
29+
}
30+
}
31+
environment = concat(local.api_env, [
32+
{
33+
name = "TRACECAT__DB_ENDPOINT"
34+
value = local.core_db_hostname
35+
}
36+
])
37+
secrets = local.tracecat_secrets
38+
dockerPullConfig = {
39+
maxAttempts = 3
40+
backoffTime = 10
41+
}
42+
}
43+
])
44+
45+
depends_on = [
46+
aws_ecs_service.temporal_service
47+
]
48+
}
49+
50+
resource "aws_ecs_service" "tracecat_api" {
51+
name = "tracecat-api"
52+
cluster = aws_ecs_cluster.tracecat_cluster.id
53+
task_definition = aws_ecs_task_definition.api_task_definition.arn
54+
launch_type = "FARGATE"
55+
desired_count = 1
56+
force_new_deployment = var.force_new_deployment
57+
58+
network_configuration {
59+
subnets = aws_subnet.private[*].id
60+
security_groups = [
61+
aws_security_group.core.id,
62+
aws_security_group.core_db.id,
63+
]
64+
}
65+
66+
service_connect_configuration {
67+
enabled = true
68+
namespace = local.local_dns_namespace
69+
service {
70+
port_name = "api"
71+
discovery_name = "api-service"
72+
client_alias {
73+
port = 8000
74+
dns_name = "api-service"
75+
}
76+
}
77+
78+
log_configuration {
79+
log_driver = "awslogs"
80+
options = {
81+
awslogs-group = aws_cloudwatch_log_group.tracecat_log_group.name
82+
awslogs-region = var.aws_region
83+
awslogs-stream-prefix = "service-connect-api"
84+
}
85+
}
86+
}
87+
88+
depends_on = [
89+
aws_ecs_service.temporal_service,
90+
]
91+
92+
}

deployments/aws/fargate/ecs-caddy.tf

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# ECS Task Definition for Caddy Service
2+
resource "aws_ecs_task_definition" "caddy_task_definition" {
3+
family = "TracecatCaddyTaskDefinition"
4+
network_mode = "awsvpc"
5+
requires_compatibilities = ["FARGATE"]
6+
cpu = "256"
7+
memory = "512"
8+
execution_role_arn = aws_iam_role.caddy_execution.arn
9+
task_role_arn = aws_iam_role.caddy_task.arn
10+
11+
container_definitions = jsonencode([
12+
{
13+
name = "TracecatCaddyContainer"
14+
image = "caddy:2.8.4-alpine"
15+
portMappings = [
16+
{
17+
containerPort = 80
18+
hostPort = 80
19+
name = "caddy"
20+
appProtocol = "http"
21+
}
22+
]
23+
command = [
24+
"/bin/sh",
25+
"-c",
26+
<<EOT
27+
cat <<EOF > /etc/caddy/Caddyfile
28+
:80 {
29+
handle_path /api* {
30+
reverse_proxy http://api-service:8000
31+
}
32+
reverse_proxy http://ui-service:3000
33+
}
34+
EOF
35+
caddy run --config /etc/caddy/Caddyfile
36+
EOT
37+
]
38+
39+
logConfiguration = {
40+
logDriver = "awslogs"
41+
options = {
42+
awslogs-group = aws_cloudwatch_log_group.tracecat_log_group.name
43+
awslogs-region = var.aws_region
44+
awslogs-stream-prefix = "caddy"
45+
}
46+
}
47+
dockerPullConfig = {
48+
maxAttempts = 3
49+
backoffTime = 10
50+
}
51+
}
52+
])
53+
}
54+
55+
resource "aws_ecs_service" "tracecat_caddy" {
56+
name = "tracecat-caddy"
57+
cluster = aws_ecs_cluster.tracecat_cluster.id
58+
task_definition = aws_ecs_task_definition.caddy_task_definition.arn
59+
launch_type = "FARGATE"
60+
desired_count = 1
61+
62+
network_configuration {
63+
subnets = aws_subnet.private[*].id
64+
security_groups = [
65+
aws_security_group.caddy.id,
66+
aws_security_group.core.id
67+
]
68+
}
69+
70+
service_connect_configuration {
71+
enabled = true
72+
namespace = local.local_dns_namespace
73+
74+
service {
75+
port_name = "caddy"
76+
discovery_name = "caddy-service"
77+
client_alias {
78+
port = 80
79+
dns_name = "caddy-service"
80+
}
81+
}
82+
83+
log_configuration {
84+
log_driver = "awslogs"
85+
options = {
86+
awslogs-group = aws_cloudwatch_log_group.tracecat_log_group.name
87+
awslogs-region = var.aws_region
88+
awslogs-stream-prefix = "service-connect-caddy"
89+
}
90+
}
91+
}
92+
93+
load_balancer {
94+
target_group_arn = aws_alb_target_group.caddy.id
95+
container_name = "TracecatCaddyContainer"
96+
container_port = 80
97+
}
98+
99+
depends_on = [
100+
aws_ecs_service.tracecat_api,
101+
aws_ecs_service.tracecat_ui
102+
]
103+
}

0 commit comments

Comments
 (0)