Skip to content

Commit a766359

Browse files
tloubrieu-jplThomas Loubrieu
andauthored
Modernized terraform script for deployment as a module of nasa-pds/registry (#765)
* wip: renew terraform for registry integration * ready to deploy in dev, not tested yet though * modernize the terraform script so to integrate it as a module called in the registry main deployment. * switch error status from 400 ot 404, as expected in integration tests * remove unused github credentials --------- Co-authored-by: Thomas Loubrieu <loubrieu@jpl.nasa.gov>
1 parent c2325eb commit a766359

10 files changed

Lines changed: 212 additions & 71 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ src/test/temp/
6565

6666
# terraform
6767
.terraform/
68+
terraform/*.tfvars
69+
!terraform/*.tfvars.example
70+
!.terraform.lock.hcl
6871

6972
# other stuff
7073
*.xpr

service/src/main/java/gov/nasa/pds/api/registry/controllers/ProductsController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ public ResponseEntity<Object> classList(String propertyClass, List<String> userR
617617
try {
618618
pdsProductClass = PdsProductClasses.fromSwaggerName(propertyClass);
619619
} catch (IllegalArgumentException err) {
620-
throw new BadRequestException(err.getMessage());
620+
throw new NotFoundException(err.getMessage());
621621
}
622622

623623
RegistrySearchRequestBuilder searchRequestBuilder =

terraform/README.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,22 @@ These interfaces are going to be used a arguments of the terraform scripts.
2424

2525
## Deploy
2626

27+
Initialize the parameters, starting from the terraform.tfvars.example file provided.
28+
29+
Copy it:
30+
31+
cp terraform.tfvars.example terraform.tfvars
32+
33+
And update the values.
34+
2735

2836
Run the terraform scripts:
2937

38+
39+
40+
3041
```
31-
terraform apply \
32-
-var 'ecs_task_role=your-task-role-arn' \
33-
-var 'ecs_task_execution_role=your-task-execution-role-arn' \
34-
-var 'venue=your-venue' \
35-
-var 'aws_fg_vpc=your-vpc-arn' \
36-
-var 'aws_fg_security_groups=["your security group, e.g. sg-1223455..."]' \
37-
-var 'aws_fg_subnets=["your subnet e.g. subnet-1234..."]' \
38-
-var 'aws_fg_image=your-docker-image-available-on-ECR' \
39-
-var 'aws_acm_certificate_arn=ssl certificate for the load balancer listener' \
40-
-var 'spring_boot_args=--openSearch.host=your-opensearch-url-without-http --openSearch.CCSEnabled=true --openSearch.username=our-username-empty-for-opensearch-serverless --openSearch.disciplineNodes=the-prefixes-of-the-registry-indices-in-opensearch --registry.service.version=the-version-of-the-api-to-be-displayed-in-the-application'
42+
terraform init -backend-config=backend-config.tfvars
43+
terraform plan
44+
terraform apply
4145
```
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Example backend configuration for S3
2+
# Copy this file to backend-config.tfvars and customize the values
3+
# Initialize with: terraform init -backend-config=backend-config.tfvars
4+
5+
bucket = "my-terraform-state-bucket"
6+
key = "registry/opensearch/terraform.tfstate"
7+
region = "us-east-1"
8+
dynamodb_table = "terraform-state-lock"
9+
encrypt = true
10+
11+
# Optional: Use a specific profile
12+
# profile = "my-aws-profile"

terraform/backend.tf

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Backend configuration for S3 state storage
2+
# Variables are not supported in backend blocks. Instead, provide configuration via:
3+
#
4+
# 1. backend-config.tfvars file:
5+
# terraform init -backend-config=backend-config.tfvars
6+
#
7+
# 2. Command line arguments:
8+
# terraform init -backend-config="bucket=${TFSTATE_BUCKET}" -backend-config="key=${TFSTATE_KEY}"
9+
#
10+
# 3. Environment variables or interactive prompts
11+
#
12+
# See https://stackoverflow.com/questions/63048738/how-to-declare-variables-for-s3-backend-in-terraform
13+
14+
terraform {
15+
backend "s3" {
16+
# Backend configuration values provided via backend-config.tfvars
17+
# Example backend-config.tfvars content:
18+
# bucket = "pds-infra"
19+
# key = "registry/opensearch/terraform.tfstate"
20+
# region = "us-east-1"
21+
# dynamodb_table = "terraform-state-lock"
22+
# encrypt = true
23+
# profile = "your-aws-profile"
24+
}
25+
}

terraform/ecs.tf renamed to terraform/main.tf

Lines changed: 71 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1+
locals {
2+
3+
# Concatenate the load balancer domain to spring boot args
4+
spring_boot_args_with_host = "${var.spring_boot_args} --server.authorizedForwardedHost=${aws_lb.registry-api-lb.dns_name},${var.cloudfront_dns}"
5+
}
6+
17
resource "aws_lb" "registry-api-lb" {
2-
name = "registry-api-lb-new"
8+
name = "registry-api-lb"
39
internal = false
410
load_balancer_type = "application"
511
security_groups = var.aws_fg_security_groups
@@ -9,26 +15,17 @@ resource "aws_lb" "registry-api-lb" {
915

1016
access_logs {
1117
bucket = var.aws_s3_bucket_logs_id
12-
prefix = "registry-api-lb"
18+
prefix = "registry/registry-api-lb"
1319
enabled = true
1420
}
1521

16-
tags = {
17-
Alfa = var.node_name_abbr
18-
Bravo = var.venue
19-
Charlie = "registry"
20-
}
22+
tags = var.common_tags
2123
}
2224

23-
resource "aws_ssm_parameter" "load_balancer_domain" {
24-
name = "/pds/registry/load-balancer-domain"
25-
type = "String"
26-
overwrite = true
27-
value = aws_lb.registry-api-lb.dns_name
28-
}
25+
2926

3027
resource "aws_lb_target_group" "pds-registry-api-target-group" {
31-
name = "pds-${var.venue}-registry-tgt"
28+
name = "pds-registry-tg"
3229
port = 80
3330
protocol = "HTTP"
3431
target_type = "ip"
@@ -44,6 +41,8 @@ resource "aws_lb_target_group" "pds-registry-api-target-group" {
4441
matcher = "200"
4542
interval = 300
4643
}
44+
45+
tags = var.common_tags
4746
}
4847

4948
resource "aws_lb_listener" "registry-api-ld-listener" {
@@ -55,6 +54,7 @@ resource "aws_lb_listener" "registry-api-ld-listener" {
5554
type = "forward"
5655
target_group_arn = aws_lb_target_group.pds-registry-api-target-group.arn
5756
}
57+
tags = var.common_tags
5858
}
5959

6060
resource "aws_lb_listener_rule" "pds-registry-forward-rule" {
@@ -75,44 +75,64 @@ resource "aws_lb_listener_rule" "pds-registry-forward-rule" {
7575
}
7676
}
7777

78-
# Define the cluster
79-
resource "aws_ecs_cluster" "pds-registry-api-ecs" {
80-
name = "pds-${var.venue}-registry-api-ecs"
8178

82-
tags = {
83-
Alfa = var.node_name_abbr
84-
Bravo = var.venue
85-
Charlie = "registry"
86-
}
79+
# Credentials for ECR pull through cache from GHCR
80+
resource "aws_secretsmanager_secret" "github_ecr_credentials" {
81+
count = var.create_github_secret_credentials
82+
83+
name = "ecr-pullthroughcache/github-credentials"
84+
tags = var.common_tags
8785
}
8886

89-
# Do we need individual dev/test/prod repositories?
90-
# I don't think we do, but then we need to use prod account instead of the dev account, would that work ?
91-
data "aws_ecr_repository" "pds-registry-api-service" {
92-
name = "pds-registry-api-service"
87+
resource "aws_secretsmanager_secret_version" "github_ecr_credentials" {
88+
count = var.create_github_secret_credentials
89+
90+
secret_id = aws_secretsmanager_secret.github_ecr_credentials[count.index].id
91+
secret_string = jsonencode({
92+
username = var.github_username
93+
accessToken = var.github_token
94+
})
95+
}
96+
97+
# Look up the secret when it is not created by this script
98+
data "aws_secretsmanager_secret" "github_ecr_credentials" {
99+
count = 1 - var.create_github_secret_credentials
100+
name = "ecr-pullthroughcache/github-credentials"
101+
}
102+
103+
locals {
104+
github_ecr_credentials_arn = var.create_github_secret_credentials == 1 ? aws_secretsmanager_secret.github_ecr_credentials[0].arn : data.aws_secretsmanager_secret.github_ecr_credentials[0].arn
105+
}
106+
107+
# Add a Pull Through Cache rule for GHCR
108+
resource "aws_ecr_pull_through_cache_rule" "ghcr" {
109+
ecr_repository_prefix = "ghcr"
110+
upstream_registry_url = "ghcr.io"
111+
credential_arn = local.github_ecr_credentials_arn
112+
}
113+
114+
resource "aws_ecr_repository" "ghcr_registry_api" {
115+
name = "ghcr/nasa-pds/registry-api"
116+
tags = var.common_tags
93117
}
94118

95119
# Log groups hold logs from our app.
96120
resource "aws_cloudwatch_log_group" "pds-registry-log-group" {
97-
name = "/ecs/pds-${var.venue}-registry-api-svc-task"
121+
name = "/ecs/pds-registry-api-task"
98122

99-
tags = {
100-
Alfa = var.node_name_abbr
101-
Bravo = var.venue
102-
Charlie = "registry"
103-
}
123+
tags = var.common_tags
104124
}
105125

106126

107127
# The task definition for app.
108128
resource "aws_ecs_task_definition" "pds-registry-ecs-task" {
109-
family = "pds-${var.venue}-registry-api-svc-task"
129+
family = "pds-registry-api-task"
110130

111131
container_definitions = <<EOF
112132
[
113133
{
114-
"name": "pds-${var.venue}-reg-container",
115-
"image": "${var.aws_fg_image}",
134+
"name": "registry-api-container",
135+
"image": "${var.registry_api_docker_image}",
116136
"portMappings": [
117137
{
118138
"containerPort": 80
@@ -138,7 +158,7 @@ resource "aws_ecs_task_definition" "pds-registry-ecs-task" {
138158
},
139159
"environment": [
140160
{"name": "SERVER_PORT", "value": "80"},
141-
{"name": "SPRING_BOOT_APP_ARGS", "value": "${var.spring_boot_args}"}
161+
{"name": "SPRING_BOOT_APP_ARGS", "value": "${local.spring_boot_args_with_host}"}
142162
]
143163
}
144164
]
@@ -149,25 +169,27 @@ EOF
149169
task_role_arn = var.ecs_task_role
150170

151171
# These are the minimum values for Fargate containers.
152-
cpu = 256
153-
memory = 512
172+
cpu = var.aws_fg_cpu_units
173+
memory = var.aws_fg_ram_units
154174
requires_compatibilities = ["FARGATE"]
155175

156176
# This is required for Fargate containers
157177
network_mode = "awsvpc"
158178

159-
tags = {
160-
Alfa = var.node_name_abbr
161-
Bravo = var.venue
162-
Charlie = "registry"
163-
}
179+
tags = var.common_tags
164180
}
165181

182+
# Define the cluster
183+
resource "aws_ecs_cluster" "pds-registry-api-ecs" {
184+
name = "pds-registry-api-cluster"
185+
186+
tags = var.common_tags
187+
}
166188

167189

168190
# The main service.
169191
resource "aws_ecs_service" "pds-registry-reg-service" {
170-
name = "pds-${var.venue}-registry-api-service"
192+
name = "pds-registry-api-service"
171193
task_definition = aws_ecs_task_definition.pds-registry-ecs-task.arn
172194
cluster = aws_ecs_cluster.pds-registry-api-ecs.id
173195
launch_type = "FARGATE"
@@ -176,7 +198,7 @@ resource "aws_ecs_service" "pds-registry-reg-service" {
176198

177199
load_balancer {
178200
target_group_arn = aws_lb_target_group.pds-registry-api-target-group.arn
179-
container_name = "pds-${var.venue}-reg-container"
201+
container_name = "registry-api-container"
180202
container_port = "80"
181203
}
182204

@@ -186,9 +208,8 @@ resource "aws_ecs_service" "pds-registry-reg-service" {
186208
subnets = var.aws_fg_subnets
187209
}
188210

189-
tags = {
190-
Alfa = var.node_name_abbr
191-
Bravo = var.venue
192-
Charlie = "registry"
193-
}
211+
tags = var.common_tags
212+
213+
depends_on = [aws_ecr_repository.ghcr_registry_api, aws_ecr_pull_through_cache_rule.ghcr]
194214
}
215+

terraform/output.tf

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
locals {
2+
3+
module_relative_path = replace(abspath(path.module), "/^.*\\/terraform(\\/|$)/", "")
4+
ssm_prefix = "/pds/${var.component_name}${local.module_relative_path}"
5+
}
6+
7+
8+
output "load_balancer_domain" {
9+
description = "Registry API load balancer domain"
10+
value = aws_lb.registry-api-lb.dns_name
11+
}
12+
13+
resource "aws_ssm_parameter" "load_balancer_domain" {
14+
name = "${local.ssm_prefix}/api-load-balancer-domain"
15+
description = "Registry API load balancer domain"
16+
type = "String"
17+
value = aws_lb.registry-api-lb.dns_name
18+
tags = var.common_tags
19+
}

terraform/provider.tf

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
provider "aws" {
2-
version = "~> 3.0"
32
region = var.aws_region
43
profile = var.aws_profile
54
}
@@ -12,7 +11,12 @@ terraform {
1211
# using environment variables (as shown) or explicit values.
1312
# See https://stackoverflow.com/questions/63048738/how-to-declare-variables-for-s3-backend-in-terraform
1413
#
15-
backend "s3" {
16-
bucket = "pds-infra"
14+
required_version = ">= 1.0"
15+
16+
required_providers {
17+
aws = {
18+
source = "hashicorp/aws"
19+
version = "~> 6.32.1"
20+
}
1721
}
1822
}

terraform/terraform.tfvars.example

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
aws_region="<aws-region>"
2+
spring_boot_args="--openSearch.host=<opensearch-collection-id>.<aws-region>.aoss.amazonaws.com --openSearch.CCSEnabled=true --openSearch.username='' --openSearch.disciplineNodes=en,geo --registry.service.version=1.0.0"
3+
aws_s3_bucket_logs_id="<s3-logs-bucket-name>"
4+
5+
registry_api_docker_image="<aws-account-id>.dkr.ecr.<aws-region>.amazonaws.com/ghcr/nasa-pds/registry-api:develop"
6+
7+
# network stuff
8+
# TODO have them in the pds infra, save in SSM parameters
9+
aws_fg_vpc = "vpc-<vpc-id>"
10+
aws_fg_security_groups = ["sg-<security-group-id>"]
11+
aws_fg_subnets = ["subnet-<subnet-id-1>","subnet-<subnet-id-2>"]
12+
aws_lb_subnets = ["subnet-<lb-subnet-id-1>", "subnet-<lb-subnet-id-2>"]
13+
aws_acm_certificate_arn = "arn:aws:acm:<aws-region>:<aws-account-id>:certificate/<certificate-id>"
14+
ecs_task_role = "arn:aws:iam::<aws-account-id>:role/<task-role-name>"
15+
ecs_task_execution_role = "arn:aws:iam::<aws-account-id>:role/<task-execution-role-name>"
16+
17+
# GitHub credentials for ECR pull through cache
18+
github_username = "<github-username>"
19+
github_token = "<github-personal-access-token>"
20+
21+
cloudfront_dns = "www.example.com"
22+
23+
# Common tags applied to all resources
24+
common_tags = {
25+
tenant = "<tenant>"
26+
venue = "<venue>"
27+
component = "registry"
28+
cicd = "iac"
29+
managedby = "<your-email>"
30+
}

0 commit comments

Comments
 (0)