Skip to content

Commit

Permalink
deploy with Terraform (#20)
Browse files Browse the repository at this point in the history
* deploy with Terraform

* Add Makefile (#21)

* Add Makefile

* set the splitter to 20

* neater permission
  • Loading branch information
paul-butcher authored Jul 23, 2024
1 parent e6f0670 commit fa9b91f
Show file tree
Hide file tree
Showing 19 changed files with 389 additions and 0 deletions.
49 changes: 49 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.PHONY: shoots/clean %.sliced
.SECONDARY:

# Remove intermediate/final files from the shoots folder
shoots/clean:
rm shoots/*restored
rm shoots/*transferred
rm shoots/*slice*

# Request the Glacier restoration of the shoots in the given file
# The file is expected to contain one shoot identifier per line.
# In order to run this, set your AWS profile to one with authority in the workflow account.
%.restored : %
cat $< | python src/restore.py
cp $< $@


# Request the Glacier transfer of the shoots in the given file
# This rule depends on restoration having completed, which is not guaranteed
# (or even likely) if you run this rule without having previously requested the restoration
# Any shoots that are not yet fully restored will result in a DLQ message that can eventually
# be redriven when the s3 objects are finally available for download
# In order to run this, set your AWS profile to one with authority in the digitisation account.

# transfer to staging (see above)
%.transferred.staging: %.restored
cat $< | python src/start_transfers.py staging
cp $< $@


# transfer to production (see above)
%.transferred.production: %.restored
cat $< | python src/start_transfers.py production
cp $< $@

# Slice a given input file into manageable chunks, so that you can run them through the
# transfer process separately without overwhelming the target system.
# The right number for archivematica is probably about 20.

%.sliced: %
split -l 20 $< $<.

# Touch the files already on AWS. This will stimulate the corresponding transfer lambdas
# In order to run this, set your AWS profile to one with authority in the digitisation account.
%.touched.staging: %
cat % | python src/touch.py staging

%.touched.production: %
cat % | python src/touch.py production
43 changes: 43 additions & 0 deletions terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions terraform/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
locals {
event_batching_window_timeout = 20
lambda_timeout = 120 //two minutes

# The lambda event source pulls messages from SQS in batches, finally triggering the lambda
# when either it has enough messages, or enough time has elapsed.
# A message becomes invisible when it joins the event source buffer, so could wait for
# the whole timeout window plus the whole execution time before being confirmed.
# The value of visibility timeout must be at least 20 seconds more than the lambda timeout
# This doesn't necessarily need to exist with a longer batching window, but
# always adding 20 here should mean that you can safely set batching window to 0
# if you wish.
# See: https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
# "Lambda might wait for up to 20 seconds before invoking your function."
queue_visibility_timeout = local.event_batching_window_timeout + local.lambda_timeout + 20
}

data "archive_file" "lambda_zip" {
type = "zip"
output_path = "lambda.zip"
source_dir = "../src"
}

module "staging_lambda" {
source = "./modules/transferrer_pipe"
environment = "staging"
queue_visibility_timeout = local.queue_visibility_timeout
lambda_zip = data.archive_file.lambda_zip
}

module "production_lambda" {
source = "./modules/transferrer_pipe"
environment = "production"
queue_visibility_timeout = local.queue_visibility_timeout
lambda_zip = data.archive_file.lambda_zip
}
22 changes: 22 additions & 0 deletions terraform/modules/notification_queue/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

module "transfer_shoots_topic" {
source = "github.com/wellcomecollection/terraform-aws-sns-topic.git?ref=v1.0.1"
name = "transfer-shoots-${var.environment}"
}

module "dlq_alarm_topic" {
source = "github.com/wellcomecollection/terraform-aws-sns-topic.git?ref=v1.0.1"
name = "transfer-shoots-alarm-${var.environment}"
}

module "input_queue" {
source = "github.com/wellcomecollection/terraform-aws-sqs//queue?ref=v1.2.1"

queue_name = "transfer-shoots-${var.environment}"

topic_arns = [module.transfer_shoots_topic.arn]
visibility_timeout_seconds = var.queue_visibility_timeout
max_receive_count = 1
message_retention_seconds = 1200
alarm_topic_arn = module.dlq_alarm_topic.arn
}
3 changes: 3 additions & 0 deletions terraform/modules/notification_queue/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "queue_arn" {
value = module.input_queue.arn
}
7 changes: 7 additions & 0 deletions terraform/modules/notification_queue/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
11 changes: 11 additions & 0 deletions terraform/modules/notification_queue/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
variable "queue_visibility_timeout" {
type = number
}

variable "environment" {
type = string
validation {
condition = contains(["staging", "production"], var.environment)
error_message = "environment must be one of staging or production"
}
}
33 changes: 33 additions & 0 deletions terraform/modules/sqs_lambda_trigger/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

data "aws_iam_policy_document" "allow_sqs_pull" {
statement {
actions = [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
]
resources = [
var.queue_arn
]
}
}

resource "aws_iam_role_policy" "allow_sqs_pull" {
name = "${var.trigger_name}-pull-from-queue"
role = var.role_name
policy = data.aws_iam_policy_document.allow_sqs_pull.json
}

resource "aws_lambda_event_source_mapping" "lambda_trigger" {
event_source_arn = var.queue_arn
enabled = true
function_name = var.function_name
batch_size = var.batch_size
}

resource "aws_lambda_permission" "allow_lambda_sqs_trigger" {
action = "lambda:InvokeFunction"
function_name = var.function_name
principal = "sqs.amazonaws.com"
source_arn = var.queue_arn
}
7 changes: 7 additions & 0 deletions terraform/modules/sqs_lambda_trigger/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
19 changes: 19 additions & 0 deletions terraform/modules/sqs_lambda_trigger/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
variable "queue_arn" {
type = string
}

variable "function_name" {
type = string
}

variable "role_name" {
type = string
}

variable "trigger_name" {
type = string
}
variable "batch_size" {
type = number
default = 1
}
74 changes: 74 additions & 0 deletions terraform/modules/transferrer_lambda/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
locals {
lambda_name = "editorial-photography-transfer-${var.environment}"
lambda_timeout = 300 //five minutes
buckets = tomap(
{
staging = "wellcomecollection-archivematica-staging-transfer-source",
production = "wellcomecollection-archivematica-transfer-source"
}
)
target_bucket = lookup(local.buckets, var.environment)

}


module "transfer_lambda" {
source = "git@github.com:wellcomecollection/terraform-aws-lambda?ref=v1.2.0"

name = local.lambda_name
runtime = "python3.12"
handler = "lambda_function.lambda_handler"

filename = var.lambda_zip.output_path
memory_size = 2048
timeout = local.lambda_timeout

environment = {
variables = {
ACCESSION_NUMBER = "2754"
TARGET_BUCKET = local.target_bucket
}
}
source_code_hash = var.lambda_zip.output_base64sha256
ephemeral_storage = {
size = 4096
}
}

resource "aws_iam_role_policy" "write_to_archivematica_transfer_source" {
role = module.transfer_lambda.lambda_role.name
name = "write_to_archivematica_transfer_source-${var.environment}"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::${local.target_bucket}/*"
},
]
}
)
}

resource "aws_iam_role_policy" "read_from_editorial_photography" {
role = module.transfer_lambda.lambda_role.name
name = "read_from_editorial_photography-${var.environment}"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
"Effect" = "Allow",
"Action" = [
"s3:GetObject",
"s3:ListBucket"
],
"Resource" = [
"arn:aws:s3:::wellcomecollection-editorial-photography",
"arn:aws:s3:::wellcomecollection-editorial-photography/*"
],
},
]
})

}
7 changes: 7 additions & 0 deletions terraform/modules/transferrer_lambda/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
output "lambda" {
value = module.transfer_lambda.lambda
}

output "role" {
value = module.transfer_lambda.lambda_role
}
7 changes: 7 additions & 0 deletions terraform/modules/transferrer_lambda/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
16 changes: 16 additions & 0 deletions terraform/modules/transferrer_lambda/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
variable "environment" {
type = string
validation {
condition = contains(["staging", "production"], var.environment)
error_message = "environment must be one of staging or production"
}
}

variable "lambda_zip" {
type = object(
{
output_path = string,
output_base64sha256 = string
}
)
}
20 changes: 20 additions & 0 deletions terraform/modules/transferrer_pipe/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

module "transfer_lambda" {
source = "../transferrer_lambda"
environment = var.environment
lambda_zip = var.lambda_zip
}

module "input_queue" {
source = "../notification_queue"
environment = var.environment
queue_visibility_timeout = var.queue_visibility_timeout
}

module "trigger" {
source = "../sqs_lambda_trigger"
queue_arn = module.input_queue.queue_arn
function_name = module.transfer_lambda.lambda.function_name
role_name = module.transfer_lambda.role.name
trigger_name = "editorial-photography-${var.environment}"
}
7 changes: 7 additions & 0 deletions terraform/modules/transferrer_pipe/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
}
}
}
Loading

0 comments on commit fa9b91f

Please sign in to comment.