Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
065b076
Add reuploader builder task to dev
aagbsn Jun 8, 2026
ccda78d
fix trigger path
aagbsn Jun 8, 2026
01ab568
Extend ooniapi_service to provide scheduled run
aagbsn Jun 8, 2026
e8e191e
add scheduled_service module
aagbsn Jun 9, 2026
1d82bdb
add reuploader scheduled service (hourly)
aagbsn Jun 9, 2026
d245b73
set failed reports bucket
aagbsn Jun 9, 2026
4dd954e
reuploader: set DRY_RUN=true
aagbsn Jun 11, 2026
b6e2ba7
reuploader: set BATCH_SIZE=10
aagbsn Jun 11, 2026
0b94e88
reuploader: set AWS_SECRET_ACCESS_KEY from module
aagbsn Jun 11, 2026
22f35d5
reuploader: set scheduled_task_cluster
aagbsn Jun 11, 2026
3a0b895
reuploader: remove unused outputs
aagbsn Jun 11, 2026
d171d28
reuploader: add first_run to create container definition
aagbsn Jun 11, 2026
c2ffbf1
reuploader: pin to tagged container
aagbsn Jun 11, 2026
6964ab5
Merge remote-tracking branch 'origin/main' into add_reuploader_builder
aagbsn Jun 11, 2026
d55b502
remove redundant count
aagbsn Jun 11, 2026
d9361d9
Merge remote-tracking branch 'origin/main' into add_reuploader_builder
aagbsn Jun 11, 2026
dbe872f
singleton requires no index
aagbsn Jun 11, 2026
27f9c59
FIXME: try to add events:PutRule et al to profile
aagbsn Jun 11, 2026
093c98f
Add permission to the ooni_devops role to modify events
LDiazN Jun 11, 2026
94f3638
unmix environment from secrets
aagbsn Jun 11, 2026
92fb38a
use bucket from https://github.com/ooni/devops/issues/398
aagbsn Jun 11, 2026
bafa175
update reuploader, fix env
aagbsn Jun 11, 2026
ed341e5
add AWS_REGION to task_environment
aagbsn Jun 11, 2026
bb5a163
set S3_BUCKET_NAME env
aagbsn Jun 11, 2026
bea658f
test reading primary failed reports bucket
aagbsn Jun 16, 2026
a5fdbc1
add iam_role_policy for reuploader task
aagbsn Jun 16, 2026
e9ebb3f
remove task secrets from reuploader; container uses ecs task role
aagbsn Jun 16, 2026
1e6802e
output scheduled_service_task.arn as task_role_arn
aagbsn Jun 17, 2026
8a603fa
set task_role_arn = scheduled_service_task.arn
aagbsn Jun 17, 2026
20b309a
WIP: use the bucket from ticket https://github.com/ooni/devops/issues…
aagbsn Jun 17, 2026
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
82 changes: 82 additions & 0 deletions tf/environments/dev/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,10 @@ resource "aws_s3_bucket" "ooniprobe_failed_reports" {
bucket = "ooniprobe-failed-reports-${var.aws_region}"
}

data "aws_s3_bucket" "ooniprobe_failed_reports_2026_04_10" {
bucket = "ooniprobe-failed-reports-eu-central-1-1d24426a"
}

resource "aws_s3_bucket" "ooniapi_codepipeline_bucket" {
bucket = "codepipeline-ooniapi-${var.aws_region}-${random_id.artifact_id.hex}"
}
Expand Down Expand Up @@ -936,6 +940,84 @@ 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:20260617-8b35a38f"
schedule_expression = "cron(0/5 * * * ? 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_environment = {
AWS_REGION = var.aws_region
BATCH_SIZE = 10
S3_BUCKET_NAME = data.aws_s3_bucket.ooniprobe_failed_reports_2026_04_10.bucket
DRY_RUN = true
FASTPATH_API = "http://${local.fastpath_hosts[length(local.fastpath_hosts) - 1]}:8472"
}

task_secrets = {
}

ooniapi_service_security_groups = [
module.ooniapi_cluster.web_security_group_id
]

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

# For reuploader accessing the failed reports s3 bucket
resource "aws_iam_role_policy" "reuploader_role" {
name = "${local.name}-task-role"
role = module.reuploader.task_role_name

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = ""
Effect = "Allow"
Action = ["s3:GetObject"]
Resource = "${data.aws_s3_bucket.ooniprobe_failed_reports_2026_04_10.arn}/*"
},
{
Sid = ""
Effect = "Allow"
Action = ["s3:ListBucket"]
Resource = data.aws_s3_bucket.ooniprobe_failed_reports_2026_04_10.arn
}
]
})
}


#### 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
}
155 changes: 155 additions & 0 deletions tf/modules/scheduled_service/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
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
}
}
}
])
task_role_arn = aws_iam_role.scheduled_service_task.arn
execution_role_arn = aws_iam_role.scheduled_service_task.arn
tags = var.tags
track_latest = true
}
14 changes: 14 additions & 0 deletions tf/modules/scheduled_service/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
output "task_role_name" {
description = "IAM role name used for scheduled task"
value = aws_iam_role.scheduled_service_task.name
}

output "task_role_id" {
description = "IAM role ID for the scheduled task"
value = aws_iam_role.scheduled_service_task.id
}

output "task_role_arn" {
description = "IAM role ARN for the scheduled task"
value = aws_iam_role.scheduled_service_task.arn
}
Loading
Loading