Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions tf/environments/dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,64 @@ module "fastpath_builder" {
codepipeline_bucket = aws_s3_bucket.ooniapi_codepipeline_bucket.bucket
}

module "reuploader_builder" {
source = "../../modules/ooni_docker_build"
trigger_tag = ""

service_name = "reuploader"
repo = "ooni/backend"
branch_name = "add_fastpath_reuploader"
environment = local.environment
buildspec_path = "reuploader/buildspec.yml"
trigger_path = "reuploader/**"
codestar_connection_arn = aws_codestarconnections_connection.oonidevops.arn

codepipeline_bucket = aws_s3_bucket.ooniapi_codepipeline_bucket.bucket
}

module "reuploader" {
source = "../../modules/scheduled_service"

task_memory = 256

vpc_id = module.network.vpc_id

first_run = true
service_name = "reuploader"
default_docker_image_url = "ooni/reuploader:20260611-f9cf0ff7"
schedule_expression = "cron(0 * * * ? 2000-2199)"
stage = local.environment
dns_zone_ooni_io = local.dns_zone_ooni_io
key_name = module.adm_iam_roles.oonidevops_key_name
scheduled_task_cluster = module.ooniapi_cluster.cluster_name
ecs_cluster_id = module.ooniapi_cluster.cluster_id

task_secrets = {
AWS_SECRET_ACCESS_KEY = module.ooniapi_user.aws_secret_access_key_arn
AWS_ACCESS_KEY_ID = module.ooniapi_user.aws_access_key_id_arn

#ROLE_ARN =
#ROLE_DURATION_SECONDS = "3600"
AWS_REGION = var.aws_region
BATCH_SIZE = 10
# required
BUCKET_NAME = aws_s3_bucket.ooniprobe_failed_reports.bucket
DRY_RUN = true
# PREFIX # s3 path prefix
# fastpath API endpoint; use the last (fallback) fastpath instance in set
FASTPATH_API = "http://${local.fastpath_hosts[length(local.fastpath_hosts) - 1]}:8472"
}

ooniapi_service_security_groups = [
module.ooniapi_cluster.web_security_group_id
]

tags = merge(
local.tags,
{ Name = "ooni-tier0-reuploader" }
)
}

#### OONI Run service

module "ooniapi_oonirun_deployer" {
Expand Down
3 changes: 2 additions & 1 deletion tf/modules/adm_iam_roles/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ resource "aws_iam_policy" "oonidevops" {
"secretsmanager:*",
"cloudhsm:*",
"athena:*",
"glue:*"
"glue:*",
"events:*"
],
"Resource": "*"
}
Expand Down
68 changes: 67 additions & 1 deletion tf/modules/ooniapi_service/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,72 @@ resource "aws_iam_role_policy" "ooniapi_service_task" {
policy = templatefile("${path.module}/templates/profile_policy.json", {})
}

resource "aws_iam_role" "events_run_task" {
count = var.run_on_schedule ? 1 : 0
name = "${local.name}-events-run-task-role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "events.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
EOF

tags = var.tags
}

resource "aws_iam_role_policy" "events_run_task_policy" {
count = var.run_on_schedule ? 1 : 0
name = "${local.name}-events-run-task-policy"
role = aws_iam_role.events_run_task[0].id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecs:RunTask",
"iam:PassRole",
"ecs:StartTask",
"ecs:DescribeClusters",
"ecs:DescribeTasks"
]
Resource = "*"
}
]
})
}

resource "aws_cloudwatch_event_rule" "scheduled_run" {
count = var.run_on_schedule ? 1 : 0
name = "${local.name}-schedule"
schedule_expression = var.schedule_expression
tags = var.tags
}

resource "aws_cloudwatch_event_target" "run_ecs_task" {
count = var.run_on_schedule ? 1 : 0
rule = aws_cloudwatch_event_rule.scheduled_run[0].name
arn = data.aws_ecs_cluster.target[0].arn

role_arn = aws_iam_role.events_run_task[0].arn

ecs_target {
task_definition_arn = aws_ecs_task_definition.ooniapi_service.arn
task_count = 1
}
}

data "aws_ecs_cluster" "target" {
count = var.run_on_schedule ? 1 : 0
cluster_name = var.scheduled_task_cluster
}

resource "aws_cloudwatch_log_group" "ooniapi_service" {
name = "ooni-ecs-group/${local.name}"
}
Expand Down Expand Up @@ -99,7 +165,7 @@ resource "aws_ecs_service" "ooniapi_service" {
name = local.name
cluster = var.ecs_cluster_id
task_definition = aws_ecs_task_definition.ooniapi_service.arn
desired_count = var.service_desired_count
desired_count = var.run_on_schedule ? 0 : var.service_desired_count

deployment_minimum_healthy_percent = 50
deployment_maximum_percent = 200
Expand Down
22 changes: 22 additions & 0 deletions tf/modules/ooniapi_service/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,25 @@ variable "autoscale_policies" {

default = []
}

variable "run_on_schedule" {
type = bool
default = false

validation {
condition = !(var.run_on_schedule == true && var.scheduled_task_cluster == null)
error_message = "scheduled_task_cluster must be set when run_on_schedule = true."
}
}

variable "schedule_expression" {
type = string
default = "cron(0 6 ? * MON-FRI *)" # example default; callers override
}

variable "scheduled_task_cluster" {
type = string
description = "Name of the ECS cluster to run the scheduled task on (required when run_on_schedule = True)."
nullable = true
default = null
}
154 changes: 154 additions & 0 deletions tf/modules/scheduled_service/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
locals {
name = "scheduled-service-${var.service_name}"
# We construct a stripped name that is without the "ooni" substring and all
# vocals are stripped.
stripped_name = replace(replace(var.service_name, "ooni", ""), "[aeiou]", "")
# Short prefix should be less than 5 characters
short_prefix = "O${substr(local.stripped_name, 0, 3)}"
}

resource "aws_iam_role" "scheduled_service_task" {
name = "${local.name}-task-role"

tags = var.tags

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}

resource "aws_iam_role_policy" "scheduled_service_task" {
name = "${local.name}-task-role"
role = aws_iam_role.scheduled_service_task.name

policy = templatefile("${path.module}/templates/profile_policy.json", {})
}

resource "aws_iam_role" "events_run_task" {
name = "${local.name}-events-run-task-role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "events.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}
EOF

tags = var.tags
}

resource "aws_iam_role_policy" "events_run_task_policy" {
name = "${local.name}-events-run-task-policy"
role = aws_iam_role.events_run_task.id

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ecs:RunTask",
"iam:PassRole",
"ecs:StartTask",
"ecs:DescribeClusters",
"ecs:DescribeTasks",
"events:TagResource",
"events:PutRule",
"events:PutTargets",
]
Resource = "*"
}
]
})
}

resource "aws_cloudwatch_event_rule" "scheduled_run" {
name = "${local.name}-schedule"
schedule_expression = var.schedule_expression
tags = var.tags
}

resource "aws_cloudwatch_event_target" "run_ecs_task" {
rule = aws_cloudwatch_event_rule.scheduled_run.name
arn = data.aws_ecs_cluster.target.arn

role_arn = aws_iam_role.events_run_task.arn

ecs_target {
task_definition_arn = aws_ecs_task_definition.scheduled_service.arn
task_count = 1
}
}

data "aws_ecs_cluster" "target" {
cluster_name = var.scheduled_task_cluster
}

resource "aws_cloudwatch_log_group" "scheduled_service" {
name = "ooni-ecs-group/${local.name}"
}

// This is done to retrieve the image name of the current task definition
// It's important to keep aligned the container_name and task_definitions
data "aws_ecs_container_definition" "scheduled_service_current" {
task_definition = "${local.name}-td"
container_name = local.name
count = var.first_run ? 0 : 1
}

resource "aws_ecs_task_definition" "scheduled_service" {
family = "${local.name}-td"
network_mode = "bridge"

container_definitions = jsonencode([
{
memoryReservation = var.task_memory,
memory = var.memory_hard_limit
essential = true,
image = try(
data.aws_ecs_container_definition.scheduled_service_current[0].image,
var.default_docker_image_url
),
name = local.name,

environment = [
for k, v in var.task_environment : {
name = k,
value = v
}
],
secrets = [
for k, v in var.task_secrets : {
name = k,
valueFrom = v
}
],
logConfiguration = {
logDriver = "awslogs",
options = {
awslogs-group = aws_cloudwatch_log_group.scheduled_service.name,
awslogs-region = var.aws_region
}
}
}
])
execution_role_arn = aws_iam_role.scheduled_service_task.arn
tags = var.tags
track_latest = true
}
Empty file.
Loading
Loading