-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
## Summary Fixes #1547 ### Time to review: __5 mins__ ### Overview We need to deploy the analytics service to ECS. The problem is, is that the analytics service is entirely task-based. It is not a web server. Which means, it is not truly a great fit for being deployed by our current `infra/modules/service` module. Specifically, the task service doesn't need a load balancer or any of its associated networking components. This created a decision for me, with 3 options: 1. Make the analytics service deploy itself with a useless load balancer. This would waste money, and would significantly confuse our security scans. 2. Add conditionals into the existing `infra/modules/service` module to only conditionally deploy the load balancer. This would require doing a lot of `terraform state mv` migration since terraform resources with `count` based conditionals have slightly different terraform state paths. 3. Create a new module without the load balancer. This creates a large git diff to review. Ultimately I chose **option 3**, so that is what you see here. A new terraform module that is simply the same ECS service module we already had, but without a load balancer. ## My asks of reviewers 1. Do you think that option 3 was a reasonable choice here? 2. I named the module `infra/task-service`, can you think of a better name for it? ## Changes proposed This PR is, once again, 95% copy paste. - `infra/analytics/service` changes were copy pasted from `infra/api/service` - `infra/modules/task-service` changes were copy pasted from `infra/modules/service` ## Testing Deployed service: <img width="1290" alt="image" src="https://github.com/HHS/simpler-grants-gov/assets/5768468/a4f85ece-0128-4096-b593-4c92df5acf3d"> Deployed task definition: <img width="1297" alt="image" src="https://github.com/HHS/simpler-grants-gov/assets/5768468/0e670642-7377-42ef-a326-102385aad1fc">
- Loading branch information
1 parent
306210f
commit 2f9329e
Showing
17 changed files
with
794 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,7 @@ output "service_config" { | |
secrets = toset(local.secrets) | ||
} | ||
} | ||
|
||
output "domain" { | ||
value = var.domain | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
bucket = "simpler-grants-gov-315341936575-us-east-1-tf" | ||
key = "infra/analytics/service/dev.tfstate" | ||
dynamodb_table = "simpler-grants-gov-315341936575-us-east-1-tf-state-locks" | ||
region = "us-east-1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Make the "image_tag" variable optional so that "terraform plan" | ||
# and "terraform apply" work without any required variables. | ||
# | ||
# This works as follows: | ||
|
||
# 1. Accept an optional variable during a terraform plan/apply. (see "image_tag" variable in variables.tf) | ||
|
||
# 2. Read the output used from the last terraform state using "terraform_remote_state". | ||
# Get the backend config by parsing the backend config file | ||
locals { | ||
backend_config_file_path = "${path.module}/${var.environment_name}.s3.tfbackend" | ||
backend_config_file = file("${path.module}/${var.environment_name}.s3.tfbackend") | ||
|
||
# Use regex to parse backend config file to get a map of variables to their | ||
# defined values since there is no built-in terraform function that does that | ||
# | ||
# The backend config file consists of lines that look like | ||
# <variable_name> = "<variable_value" | ||
# so our regex is (\w+)\s+= "(.+)" | ||
# Note that backslashes in the regex need to be escaped in Terraform | ||
# so they will appear as \\ instead of \ | ||
# (see https://developer.hashicorp.com/terraform/language/functions/regex) | ||
backend_config_regex = "(\\w+)\\s+= \"(.+)\"" | ||
backend_config = { for match in regexall(local.backend_config_regex, local.backend_config_file) : match[0] => match[1] } | ||
tfstate_bucket = local.backend_config["bucket"] | ||
tfstate_key = local.backend_config["key"] | ||
} | ||
data "terraform_remote_state" "current_image_tag" { | ||
# Don't do a lookup if image_tag is provided explicitly. | ||
# This saves some time and also allows us to do a first deploy, | ||
# where the tfstate file does not yet exist. | ||
count = var.image_tag == null ? 1 : 0 | ||
backend = "s3" | ||
|
||
config = { | ||
bucket = local.tfstate_bucket | ||
key = local.tfstate_key | ||
region = local.service_config.region | ||
} | ||
|
||
defaults = { | ||
image_tag = null | ||
} | ||
} | ||
|
||
# 3. Prefer the given variable if provided, otherwise default to the value from last time. | ||
locals { | ||
image_tag = (var.image_tag == null | ||
? data.terraform_remote_state.current_image_tag[0].outputs.image_tag | ||
: var.image_tag) | ||
} | ||
|
||
# 4. Store the final value used as a terraform output for next time. | ||
output "image_tag" { | ||
value = local.image_tag | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc | ||
data "aws_vpc" "network" { | ||
filter { | ||
name = "tag:Name" | ||
values = [module.project_config.network_configs[var.environment_name].vpc_name] | ||
} | ||
} | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet | ||
data "aws_subnets" "private" { | ||
filter { | ||
name = "vpc-id" | ||
values = [data.aws_vpc.network.id] | ||
} | ||
filter { | ||
name = "tag:subnet_type" | ||
values = ["private"] | ||
} | ||
} | ||
|
||
# docs: https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/subnet | ||
data "aws_subnets" "public" { | ||
filter { | ||
name = "vpc-id" | ||
values = [data.aws_vpc.network.id] | ||
} | ||
filter { | ||
name = "tag:subnet_type" | ||
values = ["public"] | ||
} | ||
} | ||
|
||
locals { | ||
# The prefix key/value pair is used for Terraform Workspaces, which is useful for projects with multiple infrastructure developers. | ||
# By default, Terraform creates a workspace named “default.” If a non-default workspace is not created this prefix will equal “default”, | ||
# if you choose not to use workspaces set this value to "dev" | ||
prefix = terraform.workspace == "default" ? "" : "${terraform.workspace}-" | ||
|
||
# Add environment specific tags | ||
tags = merge(module.project_config.default_tags, { | ||
environment = var.environment_name | ||
description = "Application resources created in ${var.environment_name} environment" | ||
}) | ||
|
||
service_name = "${local.prefix}${module.app_config.app_name}-${var.environment_name}" | ||
|
||
is_temporary = startswith(terraform.workspace, "t-") | ||
|
||
environment_config = module.app_config.environment_configs[var.environment_name] | ||
service_config = local.environment_config.service_config | ||
database_config = local.environment_config.database_config | ||
domain = local.environment_config.domain | ||
} | ||
|
||
terraform { | ||
required_version = ">= 1.2.0, < 2.0.0" | ||
|
||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "~> 5.34.0" | ||
} | ||
} | ||
|
||
backend "s3" { | ||
encrypt = "true" | ||
} | ||
} | ||
|
||
provider "aws" { | ||
region = local.service_config.region | ||
default_tags { | ||
tags = local.tags | ||
} | ||
} | ||
|
||
module "project_config" { | ||
source = "../../project-config" | ||
} | ||
|
||
module "app_config" { | ||
source = "../app-config" | ||
} | ||
|
||
data "aws_rds_cluster" "db_cluster" { | ||
count = 1 | ||
cluster_identifier = local.database_config.cluster_name | ||
} | ||
|
||
data "aws_acm_certificate" "cert" { | ||
count = local.domain != null ? 1 : 0 | ||
domain = local.domain | ||
} | ||
|
||
data "aws_iam_policy" "app_db_access_policy" { | ||
count = 1 | ||
name = local.database_config.app_access_policy_name | ||
} | ||
|
||
data "aws_iam_policy" "migrator_db_access_policy" { | ||
count = 1 | ||
name = local.database_config.migrator_access_policy_name | ||
} | ||
|
||
module "service" { | ||
source = "../../modules/task-service" | ||
service_name = local.service_name | ||
is_temporary = false | ||
image_repository_name = module.app_config.image_repository_name | ||
image_tag = local.image_tag | ||
vpc_id = data.aws_vpc.network.id | ||
public_subnet_ids = data.aws_subnets.public.ids | ||
private_subnet_ids = data.aws_subnets.private.ids | ||
cpu = 1024 | ||
memory = 2048 | ||
|
||
# This is a task based service, not a web server, so we don't need to run any instances of the service at rest. | ||
desired_instance_count = 0 | ||
|
||
cert_arn = local.domain != null ? data.aws_acm_certificate.cert[0].arn : null | ||
|
||
db_vars = { | ||
security_group_ids = data.aws_rds_cluster.db_cluster[0].vpc_security_group_ids | ||
app_access_policy_arn = data.aws_iam_policy.app_db_access_policy[0].arn | ||
migrator_access_policy_arn = data.aws_iam_policy.migrator_db_access_policy[0].arn | ||
connection_info = { | ||
host = data.aws_rds_cluster.db_cluster[0].endpoint | ||
port = data.aws_rds_cluster.db_cluster[0].port | ||
user = local.database_config.app_username | ||
db_name = data.aws_rds_cluster.db_cluster[0].database_name | ||
schema_name = local.database_config.schema_name | ||
} | ||
} | ||
|
||
extra_environment_variables = local.service_config.extra_environment_variables | ||
secrets = local.service_config.secrets | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
output "service_cluster_name" { | ||
value = module.service.cluster_name | ||
} | ||
|
||
output "service_name" { | ||
value = local.service_name | ||
} | ||
|
||
output "application_log_group" { | ||
value = module.service.application_log_group | ||
} | ||
|
||
output "application_log_stream_prefix" { | ||
value = module.service.application_log_stream_prefix | ||
} | ||
|
||
output "migrator_role_arn" { | ||
value = module.service.migrator_role_arn | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
variable "environment_name" { | ||
type = string | ||
description = "name of the application environment" | ||
} | ||
|
||
variable "image_tag" { | ||
type = string | ||
description = "image tag to deploy to the environment" | ||
default = null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# modules/task-service | ||
|
||
This module is functionally the same module as `modules/service`, but with the load balancer and associated networking components removed. | ||
|
||
This module (eg. `modules/task-service`) is meant for use with services that composed of individually run tasks. The modules it was based off of | ||
(eg. `modules/service`) is meant for use with web servers. |
Oops, something went wrong.