@ariestools/cli 0.1.16 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -11
- package/dist/bin/aries.mjs +94138 -253208
- package/dist/bin/daemons/chain-server.mjs +9862 -12
- package/dist/bin/daemons/dapp-server.mjs +55 -56
- package/dist/bin/daemons/datalake-dev.mjs +10282 -4342
- package/dist/node/datalake.mjs +7 -5
- package/dist/terraform/cluster-aws/cluster.tf +61 -0
- package/dist/terraform/cluster-aws/encryption.tofu.example +37 -0
- package/dist/terraform/cluster-aws/iam.tf +81 -0
- package/dist/terraform/cluster-aws/irsa_ebs_csi.tf +37 -0
- package/dist/terraform/cluster-aws/kubeconfig.tf +49 -0
- package/dist/terraform/cluster-aws/network.tf +108 -0
- package/dist/terraform/cluster-aws/nodegroup.tf +42 -0
- package/dist/terraform/cluster-aws/outputs.tf +80 -0
- package/dist/terraform/cluster-aws/providers.tf +7 -0
- package/dist/terraform/cluster-aws/terraform.tfvars.example +21 -0
- package/dist/terraform/cluster-aws/variables.tf +120 -0
- package/dist/terraform/cluster-aws/versions.tf +18 -0
- package/dist/terraform/cluster-azure/cluster.tf +83 -0
- package/dist/terraform/cluster-azure/encryption.tofu.example +37 -0
- package/dist/terraform/cluster-azure/identity.tf +9 -0
- package/dist/terraform/cluster-azure/kubeconfig.tf +18 -0
- package/dist/terraform/cluster-azure/network.tf +23 -0
- package/dist/terraform/cluster-azure/outputs.tf +69 -0
- package/dist/terraform/cluster-azure/providers.tf +16 -0
- package/dist/terraform/cluster-azure/terraform.tfvars.example +24 -0
- package/dist/terraform/cluster-azure/variables.tf +126 -0
- package/dist/terraform/cluster-azure/versions.tf +14 -0
- package/dist/terraform/cluster-gcp/cluster.tf +71 -0
- package/dist/terraform/cluster-gcp/encryption.tofu.example +35 -0
- package/dist/terraform/cluster-gcp/kubeconfig.tf +47 -0
- package/dist/terraform/cluster-gcp/network.tf +47 -0
- package/dist/terraform/cluster-gcp/outputs.tf +70 -0
- package/dist/terraform/cluster-gcp/providers.tf +14 -0
- package/dist/terraform/cluster-gcp/terraform.tfvars.example +17 -0
- package/dist/terraform/cluster-gcp/variables.tf +86 -0
- package/dist/terraform/cluster-gcp/versions.tf +14 -0
- package/dist/terraform/serverless-aws/alb.tf +127 -0
- package/dist/terraform/serverless-aws/database.tf +70 -0
- package/dist/terraform/serverless-aws/dns.tf +80 -0
- package/dist/terraform/serverless-aws/ecr.tf +63 -0
- package/dist/terraform/serverless-aws/ecs.tf +199 -0
- package/dist/terraform/serverless-aws/encryption.tofu.example +38 -0
- package/dist/terraform/serverless-aws/iam.tf +68 -0
- package/dist/terraform/serverless-aws/network.tf +190 -0
- package/dist/terraform/serverless-aws/outputs.tf +64 -0
- package/dist/terraform/serverless-aws/providers.tf +19 -0
- package/dist/terraform/serverless-aws/storage.tf +51 -0
- package/dist/terraform/serverless-aws/terraform.tfvars.example +19 -0
- package/dist/terraform/serverless-aws/variables.tf +151 -0
- package/dist/terraform/serverless-aws/versions.tf +14 -0
- package/package.json +22 -19
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
resource "aws_ecs_cluster" "main" {
|
|
2
|
+
name = "${local.name_prefix}-cluster"
|
|
3
|
+
|
|
4
|
+
setting {
|
|
5
|
+
name = "containerInsights"
|
|
6
|
+
value = "enabled"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
resource "aws_cloudwatch_log_group" "control" {
|
|
11
|
+
name = "/aws/ecs/${local.name_prefix}-control"
|
|
12
|
+
retention_in_days = 30
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "aws_cloudwatch_log_group" "plane" {
|
|
16
|
+
name = "/aws/ecs/${local.name_prefix}-plane"
|
|
17
|
+
retention_in_days = 30
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
locals {
|
|
21
|
+
# When ECR repos are created in-stack (legacy flow), fall back to a `:bootstrap`
|
|
22
|
+
# tag against the local repo URI if the caller didn't set var.control_image yet.
|
|
23
|
+
# When ECR repos are owned by `aries infra images` instead (new flow), the caller
|
|
24
|
+
# MUST set var.control_image / var.plane_image — empty falls through and Fargate
|
|
25
|
+
# will refuse to launch a task with no image.
|
|
26
|
+
control_image = (
|
|
27
|
+
length(var.control_image) > 0 ? var.control_image :
|
|
28
|
+
var.create_ecr_repos ? "${aws_ecr_repository.control[0].repository_url}:bootstrap" :
|
|
29
|
+
""
|
|
30
|
+
)
|
|
31
|
+
plane_image = (
|
|
32
|
+
length(var.plane_image) > 0 ? var.plane_image :
|
|
33
|
+
var.create_ecr_repos ? "${aws_ecr_repository.plane[0].repository_url}:bootstrap" :
|
|
34
|
+
""
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
control_origin_url = local.has_domain ? "https://${local.data_fqdn}" : "http://${aws_lb.main.dns_name}/plane"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
resource "aws_ecs_task_definition" "control" {
|
|
41
|
+
family = "${local.name_prefix}-control"
|
|
42
|
+
cpu = var.control_task_cpu
|
|
43
|
+
memory = var.control_task_memory
|
|
44
|
+
network_mode = "awsvpc"
|
|
45
|
+
requires_compatibilities = ["FARGATE"]
|
|
46
|
+
execution_role_arn = aws_iam_role.task_execution.arn
|
|
47
|
+
task_role_arn = aws_iam_role.control_task.arn
|
|
48
|
+
|
|
49
|
+
runtime_platform {
|
|
50
|
+
operating_system_family = "LINUX"
|
|
51
|
+
cpu_architecture = "ARM64"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
container_definitions = jsonencode([{
|
|
55
|
+
name = "control"
|
|
56
|
+
image = local.control_image
|
|
57
|
+
essential = true
|
|
58
|
+
|
|
59
|
+
portMappings = [{
|
|
60
|
+
containerPort = 8787
|
|
61
|
+
protocol = "tcp"
|
|
62
|
+
}]
|
|
63
|
+
|
|
64
|
+
environment = [
|
|
65
|
+
{ name = "STORE", value = "postgres" },
|
|
66
|
+
{ name = "PORT", value = "8787" },
|
|
67
|
+
{ name = "HOST", value = "0.0.0.0" },
|
|
68
|
+
{ name = "DATALAKE_ORIGIN_URL", value = local.control_origin_url },
|
|
69
|
+
{ name = "LOG_LEVEL", value = "info" },
|
|
70
|
+
{ name = "CONTROL_VERSION", value = "0.1.0" },
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
secrets = [
|
|
74
|
+
{ name = "DATABASE_URL", valueFrom = aws_secretsmanager_secret.database_url.arn },
|
|
75
|
+
{ name = "TOKEN_SIGNING_SECRET", valueFrom = aws_secretsmanager_secret.token_signing.arn },
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
logConfiguration = {
|
|
79
|
+
logDriver = "awslogs"
|
|
80
|
+
options = {
|
|
81
|
+
"awslogs-group" = aws_cloudwatch_log_group.control.name
|
|
82
|
+
"awslogs-region" = var.region
|
|
83
|
+
"awslogs-stream-prefix" = "control"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}])
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
resource "aws_ecs_task_definition" "plane" {
|
|
90
|
+
family = "${local.name_prefix}-plane"
|
|
91
|
+
cpu = var.plane_task_cpu
|
|
92
|
+
memory = var.plane_task_memory
|
|
93
|
+
network_mode = "awsvpc"
|
|
94
|
+
requires_compatibilities = ["FARGATE"]
|
|
95
|
+
execution_role_arn = aws_iam_role.task_execution.arn
|
|
96
|
+
task_role_arn = aws_iam_role.plane_task.arn
|
|
97
|
+
|
|
98
|
+
runtime_platform {
|
|
99
|
+
operating_system_family = "LINUX"
|
|
100
|
+
cpu_architecture = "ARM64"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
container_definitions = jsonencode([{
|
|
104
|
+
name = "plane"
|
|
105
|
+
image = local.plane_image
|
|
106
|
+
essential = true
|
|
107
|
+
|
|
108
|
+
portMappings = [{
|
|
109
|
+
containerPort = 8788
|
|
110
|
+
protocol = "tcp"
|
|
111
|
+
}]
|
|
112
|
+
|
|
113
|
+
environment = [
|
|
114
|
+
{ name = "CONTROL_STORE", value = "postgres" },
|
|
115
|
+
{ name = "PAYLOAD_STORE", value = "postgres" },
|
|
116
|
+
{ name = "PORT", value = "8788" },
|
|
117
|
+
{ name = "HOST", value = "0.0.0.0" },
|
|
118
|
+
{ name = "AWS_REGION", value = var.region },
|
|
119
|
+
{ name = "PAYLOAD_S3_BUCKET", value = aws_s3_bucket.payloads.bucket },
|
|
120
|
+
{ name = "DATALAKE_ROUTE_PREFIX", value = local.has_domain ? "" : "/plane" },
|
|
121
|
+
{ name = "LOG_LEVEL", value = "info" },
|
|
122
|
+
{ name = "PLANE_VERSION", value = "0.1.0" },
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
secrets = [
|
|
126
|
+
{ name = "DATABASE_URL", valueFrom = aws_secretsmanager_secret.database_url.arn },
|
|
127
|
+
{ name = "TOKEN_SIGNING_SECRET", valueFrom = aws_secretsmanager_secret.token_signing.arn },
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
logConfiguration = {
|
|
131
|
+
logDriver = "awslogs"
|
|
132
|
+
options = {
|
|
133
|
+
"awslogs-group" = aws_cloudwatch_log_group.plane.name
|
|
134
|
+
"awslogs-region" = var.region
|
|
135
|
+
"awslogs-stream-prefix" = "plane"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}])
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
resource "aws_ecs_service" "control" {
|
|
142
|
+
name = "${local.name_prefix}-control"
|
|
143
|
+
cluster = aws_ecs_cluster.main.id
|
|
144
|
+
task_definition = aws_ecs_task_definition.control.arn
|
|
145
|
+
desired_count = var.control_desired_count
|
|
146
|
+
launch_type = "FARGATE"
|
|
147
|
+
propagate_tags = "SERVICE"
|
|
148
|
+
|
|
149
|
+
network_configuration {
|
|
150
|
+
subnets = [for s in aws_subnet.private : s.id]
|
|
151
|
+
security_groups = [aws_security_group.service.id]
|
|
152
|
+
assign_public_ip = false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
load_balancer {
|
|
156
|
+
target_group_arn = aws_lb_target_group.control.arn
|
|
157
|
+
container_name = "control"
|
|
158
|
+
container_port = 8787
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
deployment_minimum_healthy_percent = 50
|
|
162
|
+
deployment_maximum_percent = 200
|
|
163
|
+
|
|
164
|
+
lifecycle {
|
|
165
|
+
ignore_changes = [desired_count]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
depends_on = [aws_lb_listener.http]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
resource "aws_ecs_service" "plane" {
|
|
172
|
+
name = "${local.name_prefix}-plane"
|
|
173
|
+
cluster = aws_ecs_cluster.main.id
|
|
174
|
+
task_definition = aws_ecs_task_definition.plane.arn
|
|
175
|
+
desired_count = var.plane_desired_count
|
|
176
|
+
launch_type = "FARGATE"
|
|
177
|
+
propagate_tags = "SERVICE"
|
|
178
|
+
|
|
179
|
+
network_configuration {
|
|
180
|
+
subnets = [for s in aws_subnet.private : s.id]
|
|
181
|
+
security_groups = [aws_security_group.service.id]
|
|
182
|
+
assign_public_ip = false
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
load_balancer {
|
|
186
|
+
target_group_arn = aws_lb_target_group.plane.arn
|
|
187
|
+
container_name = "plane"
|
|
188
|
+
container_port = 8788
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
deployment_minimum_healthy_percent = 50
|
|
192
|
+
deployment_maximum_percent = 200
|
|
193
|
+
|
|
194
|
+
lifecycle {
|
|
195
|
+
ignore_changes = [desired_count]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
depends_on = [aws_lb_listener.http]
|
|
199
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# OpenTofu state encryption — opt-in.
|
|
2
|
+
#
|
|
3
|
+
# Encrypts both `terraform.tfstate` and any saved plan files at rest using
|
|
4
|
+
# AES-GCM with a key derived from `var.tfstate_passphrase` via PBKDF2.
|
|
5
|
+
#
|
|
6
|
+
# To enable:
|
|
7
|
+
# 1. cp encryption.tofu.example encryption.tofu (in the working dir)
|
|
8
|
+
# 2. export TF_VAR_tfstate_passphrase='at-least-16-characters'
|
|
9
|
+
# 3. tofu init -reconfigure
|
|
10
|
+
#
|
|
11
|
+
# Notes:
|
|
12
|
+
# - This file uses the `.tofu` extension so Terraform does NOT load it.
|
|
13
|
+
# Only OpenTofu engages encryption. Terraform users skip this block.
|
|
14
|
+
# - If you lose the passphrase, the state is unrecoverable — keep it in
|
|
15
|
+
# a password manager / Secrets Manager / etc.
|
|
16
|
+
# - To rotate the passphrase: add a second `key_provider` block with
|
|
17
|
+
# the old passphrase, set it as `fallback`, then `tofu apply` to
|
|
18
|
+
# re-encrypt with the new key. Drop the fallback once migrated.
|
|
19
|
+
|
|
20
|
+
terraform {
|
|
21
|
+
encryption {
|
|
22
|
+
key_provider "pbkdf2" "default" {
|
|
23
|
+
passphrase = var.tfstate_passphrase
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
method "aes_gcm" "default" {
|
|
27
|
+
keys = key_provider.pbkdf2.default
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
state {
|
|
31
|
+
method = method.aes_gcm.default
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
plan {
|
|
35
|
+
method = method.aes_gcm.default
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
data "aws_iam_policy_document" "ecs_task_assume" {
|
|
2
|
+
statement {
|
|
3
|
+
actions = ["sts:AssumeRole"]
|
|
4
|
+
principals {
|
|
5
|
+
type = "Service"
|
|
6
|
+
identifiers = ["ecs-tasks.amazonaws.com"]
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
resource "aws_iam_role" "task_execution" {
|
|
12
|
+
name = "${local.name_prefix}-task-execution"
|
|
13
|
+
assume_role_policy = data.aws_iam_policy_document.ecs_task_assume.json
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
resource "aws_iam_role_policy_attachment" "task_execution_basic" {
|
|
17
|
+
role = aws_iam_role.task_execution.name
|
|
18
|
+
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
data "aws_iam_policy_document" "task_execution_secrets" {
|
|
22
|
+
statement {
|
|
23
|
+
actions = ["secretsmanager:GetSecretValue"]
|
|
24
|
+
resources = [
|
|
25
|
+
aws_secretsmanager_secret.database_url.arn,
|
|
26
|
+
aws_secretsmanager_secret.token_signing.arn,
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
resource "aws_iam_role_policy" "task_execution_secrets" {
|
|
32
|
+
name = "${local.name_prefix}-task-execution-secrets"
|
|
33
|
+
role = aws_iam_role.task_execution.id
|
|
34
|
+
policy = data.aws_iam_policy_document.task_execution_secrets.json
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Task role: scoped to the service's runtime needs. Plane needs S3 rw on the
|
|
38
|
+
# payloads bucket; control plane currently needs nothing beyond logs.
|
|
39
|
+
resource "aws_iam_role" "control_task" {
|
|
40
|
+
name = "${local.name_prefix}-control-task"
|
|
41
|
+
assume_role_policy = data.aws_iam_policy_document.ecs_task_assume.json
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
resource "aws_iam_role" "plane_task" {
|
|
45
|
+
name = "${local.name_prefix}-plane-task"
|
|
46
|
+
assume_role_policy = data.aws_iam_policy_document.ecs_task_assume.json
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
data "aws_iam_policy_document" "plane_s3" {
|
|
50
|
+
statement {
|
|
51
|
+
actions = [
|
|
52
|
+
"s3:GetObject",
|
|
53
|
+
"s3:PutObject",
|
|
54
|
+
"s3:DeleteObject",
|
|
55
|
+
"s3:ListBucket",
|
|
56
|
+
]
|
|
57
|
+
resources = [
|
|
58
|
+
aws_s3_bucket.payloads.arn,
|
|
59
|
+
"${aws_s3_bucket.payloads.arn}/*",
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
resource "aws_iam_role_policy" "plane_s3" {
|
|
65
|
+
name = "${local.name_prefix}-plane-s3"
|
|
66
|
+
role = aws_iam_role.plane_task.id
|
|
67
|
+
policy = data.aws_iam_policy_document.plane_s3.json
|
|
68
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
data "aws_availability_zones" "available" {
|
|
2
|
+
state = "available"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
locals {
|
|
6
|
+
azs = slice(data.aws_availability_zones.available.names, 0, var.availability_zone_count)
|
|
7
|
+
|
|
8
|
+
public_subnet_cidrs = [for i, _ in local.azs : cidrsubnet(var.vpc_cidr, 8, i)]
|
|
9
|
+
private_subnet_cidrs = [for i, _ in local.azs : cidrsubnet(var.vpc_cidr, 8, i + 16)]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
resource "aws_vpc" "main" {
|
|
13
|
+
cidr_block = var.vpc_cidr
|
|
14
|
+
enable_dns_support = true
|
|
15
|
+
enable_dns_hostnames = true
|
|
16
|
+
|
|
17
|
+
tags = { Name = "${local.name_prefix}-vpc" }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
resource "aws_internet_gateway" "main" {
|
|
21
|
+
vpc_id = aws_vpc.main.id
|
|
22
|
+
tags = { Name = "${local.name_prefix}-igw" }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
resource "aws_subnet" "public" {
|
|
26
|
+
for_each = { for i, az in local.azs : az => i }
|
|
27
|
+
|
|
28
|
+
vpc_id = aws_vpc.main.id
|
|
29
|
+
availability_zone = each.key
|
|
30
|
+
cidr_block = local.public_subnet_cidrs[each.value]
|
|
31
|
+
map_public_ip_on_launch = true
|
|
32
|
+
|
|
33
|
+
tags = {
|
|
34
|
+
Name = "${local.name_prefix}-public-${each.key}"
|
|
35
|
+
Tier = "public"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
resource "aws_subnet" "private" {
|
|
40
|
+
for_each = { for i, az in local.azs : az => i }
|
|
41
|
+
|
|
42
|
+
vpc_id = aws_vpc.main.id
|
|
43
|
+
availability_zone = each.key
|
|
44
|
+
cidr_block = local.private_subnet_cidrs[each.value]
|
|
45
|
+
|
|
46
|
+
tags = {
|
|
47
|
+
Name = "${local.name_prefix}-private-${each.key}"
|
|
48
|
+
Tier = "private"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
resource "aws_eip" "nat" {
|
|
53
|
+
domain = "vpc"
|
|
54
|
+
tags = { Name = "${local.name_prefix}-nat" }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
resource "aws_nat_gateway" "main" {
|
|
58
|
+
allocation_id = aws_eip.nat.id
|
|
59
|
+
subnet_id = aws_subnet.public[local.azs[0]].id
|
|
60
|
+
|
|
61
|
+
tags = { Name = "${local.name_prefix}-nat" }
|
|
62
|
+
depends_on = [aws_internet_gateway.main]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
resource "aws_route_table" "public" {
|
|
66
|
+
vpc_id = aws_vpc.main.id
|
|
67
|
+
|
|
68
|
+
route {
|
|
69
|
+
cidr_block = "0.0.0.0/0"
|
|
70
|
+
gateway_id = aws_internet_gateway.main.id
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
tags = { Name = "${local.name_prefix}-public-rt" }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resource "aws_route_table" "private" {
|
|
77
|
+
vpc_id = aws_vpc.main.id
|
|
78
|
+
|
|
79
|
+
route {
|
|
80
|
+
cidr_block = "0.0.0.0/0"
|
|
81
|
+
nat_gateway_id = aws_nat_gateway.main.id
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
tags = { Name = "${local.name_prefix}-private-rt" }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
resource "aws_route_table_association" "public" {
|
|
88
|
+
for_each = aws_subnet.public
|
|
89
|
+
subnet_id = each.value.id
|
|
90
|
+
route_table_id = aws_route_table.public.id
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
resource "aws_route_table_association" "private" {
|
|
94
|
+
for_each = aws_subnet.private
|
|
95
|
+
subnet_id = each.value.id
|
|
96
|
+
route_table_id = aws_route_table.private.id
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
# --- security groups -------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
resource "aws_security_group" "alb" {
|
|
102
|
+
name = "${local.name_prefix}-alb"
|
|
103
|
+
description = "Internet-facing ingress for the ALB"
|
|
104
|
+
vpc_id = aws_vpc.main.id
|
|
105
|
+
|
|
106
|
+
ingress {
|
|
107
|
+
description = "HTTPS from anywhere"
|
|
108
|
+
from_port = 443
|
|
109
|
+
to_port = 443
|
|
110
|
+
protocol = "tcp"
|
|
111
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
ingress {
|
|
115
|
+
description = "HTTP from anywhere (redirected to HTTPS at the listener)"
|
|
116
|
+
from_port = 80
|
|
117
|
+
to_port = 80
|
|
118
|
+
protocol = "tcp"
|
|
119
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
egress {
|
|
123
|
+
description = "Outbound to anywhere (ALB to Fargate tasks)"
|
|
124
|
+
from_port = 0
|
|
125
|
+
to_port = 0
|
|
126
|
+
protocol = "-1"
|
|
127
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
tags = { Name = "${local.name_prefix}-alb-sg" }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
resource "aws_security_group" "service" {
|
|
134
|
+
name = "${local.name_prefix}-service"
|
|
135
|
+
description = "Fargate tasks; only accepts traffic from the ALB"
|
|
136
|
+
vpc_id = aws_vpc.main.id
|
|
137
|
+
|
|
138
|
+
egress {
|
|
139
|
+
description = "Egress to the internet via NAT (ECR pulls, third-party APIs)"
|
|
140
|
+
from_port = 0
|
|
141
|
+
to_port = 0
|
|
142
|
+
protocol = "-1"
|
|
143
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
tags = { Name = "${local.name_prefix}-service-sg" }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
resource "aws_security_group_rule" "service_from_alb_8787" {
|
|
150
|
+
type = "ingress"
|
|
151
|
+
from_port = 8787
|
|
152
|
+
to_port = 8787
|
|
153
|
+
protocol = "tcp"
|
|
154
|
+
source_security_group_id = aws_security_group.alb.id
|
|
155
|
+
security_group_id = aws_security_group.service.id
|
|
156
|
+
description = "Control plane from ALB"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
resource "aws_security_group_rule" "service_from_alb_8788" {
|
|
160
|
+
type = "ingress"
|
|
161
|
+
from_port = 8788
|
|
162
|
+
to_port = 8788
|
|
163
|
+
protocol = "tcp"
|
|
164
|
+
source_security_group_id = aws_security_group.alb.id
|
|
165
|
+
security_group_id = aws_security_group.service.id
|
|
166
|
+
description = "Data plane from ALB"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
resource "aws_security_group" "database" {
|
|
170
|
+
name = "${local.name_prefix}-db"
|
|
171
|
+
description = "RDS Postgres; only accepts traffic from the Fargate service SG"
|
|
172
|
+
vpc_id = aws_vpc.main.id
|
|
173
|
+
|
|
174
|
+
ingress {
|
|
175
|
+
description = "Postgres from Fargate tasks"
|
|
176
|
+
from_port = 5432
|
|
177
|
+
to_port = 5432
|
|
178
|
+
protocol = "tcp"
|
|
179
|
+
security_groups = [aws_security_group.service.id]
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
egress {
|
|
183
|
+
from_port = 0
|
|
184
|
+
to_port = 0
|
|
185
|
+
protocol = "-1"
|
|
186
|
+
cidr_blocks = ["0.0.0.0/0"]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
tags = { Name = "${local.name_prefix}-db-sg" }
|
|
190
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
output "alb_dns_name" {
|
|
2
|
+
description = "Public DNS of the ALB. Use this when `domain_name` is empty."
|
|
3
|
+
value = aws_lb.main.dns_name
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
output "api_url" {
|
|
7
|
+
description = "Control plane URL. When `domain_name` is set, https://api.<domain>; otherwise the ALB DNS over HTTP."
|
|
8
|
+
value = local.has_domain ? "https://${local.api_fqdn}" : "http://${aws_lb.main.dns_name}"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
output "data_url" {
|
|
12
|
+
description = "Data plane URL. When `domain_name` is set, https://data.<domain>; otherwise the ALB DNS with /plane path prefix."
|
|
13
|
+
value = local.has_domain ? "https://${local.data_fqdn}" : "http://${aws_lb.main.dns_name}/plane"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
output "ecr_control_repository" {
|
|
17
|
+
description = "ECR repository URI for the control plane image. Empty when the stack does not own its ECR (create_ecr_repos=false)."
|
|
18
|
+
value = var.create_ecr_repos ? aws_ecr_repository.control[0].repository_url : ""
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
output "ecr_plane_repository" {
|
|
22
|
+
description = "ECR repository URI for the data plane image. Empty when the stack does not own its ECR (create_ecr_repos=false)."
|
|
23
|
+
value = var.create_ecr_repos ? aws_ecr_repository.plane[0].repository_url : ""
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
output "rds_endpoint" {
|
|
27
|
+
description = "RDS Postgres host (internal to the VPC; not publicly reachable)."
|
|
28
|
+
value = aws_db_instance.main.address
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
output "payloads_bucket" {
|
|
32
|
+
description = "S3 bucket holding archive-tier payloads."
|
|
33
|
+
value = aws_s3_bucket.payloads.bucket
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
output "vpc_id" {
|
|
37
|
+
description = "Dedicated VPC id."
|
|
38
|
+
value = aws_vpc.main.id
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
output "ecs_cluster_name" {
|
|
42
|
+
description = "ECS cluster name used for live status checks."
|
|
43
|
+
value = aws_ecs_cluster.main.name
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
output "control_service_name" {
|
|
47
|
+
description = "Control-plane ECS service name used for live status checks."
|
|
48
|
+
value = aws_ecs_service.control.name
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
output "plane_service_name" {
|
|
52
|
+
description = "Data-plane ECS service name used for live status checks."
|
|
53
|
+
value = aws_ecs_service.plane.name
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
output "database_url_secret_arn" {
|
|
57
|
+
description = "Secrets Manager ARN containing the Postgres connection string."
|
|
58
|
+
value = aws_secretsmanager_secret.database_url.arn
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
output "token_signing_secret_arn" {
|
|
62
|
+
description = "Secrets Manager ARN for the HMAC signing secret shared by both planes."
|
|
63
|
+
value = aws_secretsmanager_secret.token_signing.arn
|
|
64
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
provider "aws" {
|
|
2
|
+
region = var.region
|
|
3
|
+
|
|
4
|
+
default_tags {
|
|
5
|
+
tags = local.common_tags
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
# us-east-1 is required for ACM certificates fronting CloudFront. We do not
|
|
10
|
+
# use CloudFront in phase 1 but keep this aliased provider available so phase
|
|
11
|
+
# 5 (multi-region + edge router) can attach without restructuring providers.
|
|
12
|
+
provider "aws" {
|
|
13
|
+
alias = "us_east_1"
|
|
14
|
+
region = "us-east-1"
|
|
15
|
+
|
|
16
|
+
default_tags {
|
|
17
|
+
tags = local.common_tags
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
resource "aws_s3_bucket" "payloads" {
|
|
2
|
+
bucket = "${local.name_prefix}-payloads-${data.aws_caller_identity.current.account_id}-${var.region}"
|
|
3
|
+
force_destroy = var.environment != "prod"
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
resource "aws_s3_bucket_public_access_block" "payloads" {
|
|
7
|
+
bucket = aws_s3_bucket.payloads.id
|
|
8
|
+
|
|
9
|
+
block_public_acls = true
|
|
10
|
+
block_public_policy = true
|
|
11
|
+
ignore_public_acls = true
|
|
12
|
+
restrict_public_buckets = true
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
resource "aws_s3_bucket_server_side_encryption_configuration" "payloads" {
|
|
16
|
+
bucket = aws_s3_bucket.payloads.id
|
|
17
|
+
|
|
18
|
+
rule {
|
|
19
|
+
apply_server_side_encryption_by_default {
|
|
20
|
+
sse_algorithm = "AES256"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
resource "aws_s3_bucket_versioning" "payloads" {
|
|
26
|
+
bucket = aws_s3_bucket.payloads.id
|
|
27
|
+
versioning_configuration {
|
|
28
|
+
status = "Enabled"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
resource "aws_s3_bucket_lifecycle_configuration" "payloads" {
|
|
33
|
+
bucket = aws_s3_bucket.payloads.id
|
|
34
|
+
|
|
35
|
+
rule {
|
|
36
|
+
id = "expire-old-versions"
|
|
37
|
+
status = "Enabled"
|
|
38
|
+
|
|
39
|
+
filter {}
|
|
40
|
+
|
|
41
|
+
noncurrent_version_expiration {
|
|
42
|
+
noncurrent_days = 30
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
abort_incomplete_multipart_upload {
|
|
46
|
+
days_after_initiation = 7
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
data "aws_caller_identity" "current" {}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
region = "us-west-2"
|
|
2
|
+
aws_profile = "xylabs-datalake"
|
|
3
|
+
environment = "staging"
|
|
4
|
+
project = "aries-datalake"
|
|
5
|
+
|
|
6
|
+
# A major-version prefix selects the latest matching version offered in the region.
|
|
7
|
+
db_engine_version = "16"
|
|
8
|
+
|
|
9
|
+
# Set this to your domain when you're ready to put real DNS in front of the
|
|
10
|
+
# stack. Until then leave it empty and reach the ALB over its AWS DNS name.
|
|
11
|
+
# domain_name = "datalake.xylabs.com"
|
|
12
|
+
# create_route53_zone = false # set true if OpenTofu should create the zone
|
|
13
|
+
|
|
14
|
+
# Set these after `docker push` runs against the ECR repos that this stack
|
|
15
|
+
# creates. On the very first `terraform apply` leave them at their defaults;
|
|
16
|
+
# OpenTofu will provision the repos with a placeholder image tag, and you
|
|
17
|
+
# then push real images and re-apply to pick them up.
|
|
18
|
+
# control_image = "123456789012.dkr.ecr.us-west-2.amazonaws.com/aries-datalake-staging-control:0.1.0"
|
|
19
|
+
# plane_image = "123456789012.dkr.ecr.us-west-2.amazonaws.com/aries-datalake-staging-plane:0.1.0"
|