diff --git a/Dockerfile b/Dockerfile index a64f476..770408f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,12 @@ RUN nimble build -y FROM alpine EXPOSE 80 +RUN apk add --no-cache \ + python3 \ + py3-pip \ + && pip3 install --upgrade pip \ + && pip3 install --no-cache-dir awscli \ + && rm -rf /var/cache/apk/* + COPY --from=build /toclerbe/toclerbe / -COPY ministrysuite_api.config.docker.json /ministrysuite_api.config.json -CMD ["/toclerbe", "serve"] +CMD ["/toclerbe", "/toclerbe/data/urls.txt"] diff --git a/operations/terraform/ecs.tf b/operations/terraform/ecs.tf new file mode 100644 index 0000000..f73ec37 --- /dev/null +++ b/operations/terraform/ecs.tf @@ -0,0 +1,77 @@ +resource "aws_secretsmanager_secret" "toclerbe" { + name = "${var.app_name}-config" +} + +resource "aws_ecs_task_definition" "toclerbe" { + family = var.app_name + network_mode = "bridge" + requires_compatibilities = ["EC2"] + execution_role_arn = aws_iam_role.ecs_task.arn + + # See https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html + container_definitions = jsonencode([ + { + name = var.app_name + image = "${aws_ecr_repository.toclerbe.repository_url}:${data.external.git_describe.result.version}" + cpu = 128 + memory = 128 + memoryReservation = 32 + environment = [ + { + name = "TOCLERBE_PORT" + value = "80" + } + ] + mountPoints = [ + { + containerPath = "/toclerbe/data" + sourceVolume = "efs-toclerbe-data" + } + ] + portMappings = [ + { + protocol = "tcp" + containerPort = 80 + } + ] + secrets = [ + { + name = "API_KEYS" + description = "API keys allowed to configure the service.." + valueFrom = "${aws_secretsmanager_secret.toclerbe.arn}:apiKeys::" + } + ] + } + ]) + + volume { + name = "efs-toclerbe-data" + + efs_volume_configuration { + file_system_id = data.terraform_remote_state.jdbsoft.outputs.sobeck-efs.id + root_directory = "/toclerbe/data" + } + } + + tags = { + Name = var.app_domain + } +} + +resource "aws_ecs_service" "toclerbe" { + name = var.app_name + cluster = data.terraform_remote_state.jdbsoft.outputs.aws_ecs_cluster_ortis.id + task_definition = aws_ecs_task_definition.toclerbe.arn + desired_count = 1 + launch_type = "EC2" + + load_balancer { + target_group_arn = aws_lb_target_group.toclerbe.arn + container_name = var.app_name + container_port = 80 + } + + tags = { + Name = var.app_domain + } +} diff --git a/operations/terraform/iam.tf b/operations/terraform/iam.tf new file mode 100644 index 0000000..f66eaee --- /dev/null +++ b/operations/terraform/iam.tf @@ -0,0 +1,68 @@ +resource "aws_iam_role" "ecs_task" { + name = "${var.app_name}-EcsTaskRole" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + Service = "ecs-tasks.amazonaws.com" + } + } + ] + }) + + inline_policy { + name = "AllowSecretsAccessFor${var.app_name}Tasks" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "secretsmanager:GetSecretValue", + "kms:Decrypt" + ] + Resource = [ + aws_secretsmanager_secret.toclerbe.arn + ] + } + ] + }) + } + + inline_policy { + name = "AllowAccessToEcrFor${var.app_name}Tasks" + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = [ + "ecr:GetAuthorizationToken" + ] + Resource = [ "*" ] + }, + { + Effect = "Allow" + Action = [ + "ecr:BatchGetImage", + "ecr:BatchCheckLayerAvailability", + "ecr:DescribeImages", + "ecr:GetDownloadUrlForLayer" + ] + Resource = [ + aws_ecr_repository.toclerbe.arn + ] + } + ] + }) + } + + tags = { + Name = "${var.app_name}-EcsTaskRole" + } +} diff --git a/operations/terraform/load-balancer.tf b/operations/terraform/load-balancer.tf new file mode 100644 index 0000000..f8d65b5 --- /dev/null +++ b/operations/terraform/load-balancer.tf @@ -0,0 +1,41 @@ +resource "aws_lb_target_group" "toclerbe" { + name = "${var.app_name}-${substr(uuid(), 0, 2)}" + port = 80 + protocol = "HTTP" + target_type = "instance" + vpc_id = data.terraform_remote_state.jdbsoft.outputs.aws_vpc_jdbsoft.id + + health_check { + enabled = true + matcher = "200" + path = "/version" + } + + lifecycle { + create_before_destroy = true + ignore_changes = [name] + } + + tags = { + Name = var.app_name + } +} + +resource "aws_lb_listener_rule" "toclerbe" { + listener_arn = data.terraform_remote_state.jdbsoft.outputs.aws_lb_listener_https.arn + + action { + type = "forward" + target_group_arn = aws_lb_target_group.toclerbe.arn + } + + condition { + host_header { + values = [ var.app_domain ] + } + } + + tags = { + Name = "${var.app_domain} HTTPS" + } +} diff --git a/operations/terraform/main.tf b/operations/terraform/main.tf new file mode 100644 index 0000000..2600543 --- /dev/null +++ b/operations/terraform/main.tf @@ -0,0 +1,53 @@ +### Setup/Configuration +provider "aws" { + region = var.aws_region +} + +terraform { + backend "s3" { + bucket = "operations.jdb-software.com" + region = "us-west-2" + key = "terraform/toclerbe.tfstate" + dynamodb_table = "terraform-state-lock.jdb-software.com" + } +} + +data "terraform_remote_state" "jdbsoft" { + backend = "s3" + + config = { + bucket = "operations.jdb-software.com" + region = "us-west-2" + key = "terraform/operations.tfstate" + dynamodb_table = "terraform-state-lock.jdb-software.com" + } +} +### Variables + +variable "aws_region" { + description = "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html" + default = "us-west-2" # Oregon +} + +variable "app_domain" { + description = "Name of the app domain." + default = "to.cler.be" +} + +variable "app_name" { + description = "Name of the app domain." + default = "toclerbe" +} + +data "external" "git_describe" { + program = ["sh", "-c", "git describe | xargs printf '{\"version\": \"%s\"}'"] +} + +resource "aws_ecr_repository" "toclerbe" { + name = "${var.app_name}" + image_tag_mutability = "IMMUTABLE" + + image_scanning_configuration { + scan_on_push = true + } +}