Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AWS SNS Sink #111

Merged
merged 9 commits into from
May 9, 2023
Merged
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
9 changes: 9 additions & 0 deletions build/config/substation.libsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@
// TODO(v1.0.0): set format and compression defaults
settings: { bucket: null, prefix: null, prefix_key: null, file_path: null, file_format: null, file_compression: null },
},
aws_sns: {
settings: { arn: null },
},
aws_sqs: {
settings: { queue: null },
},
Expand Down Expand Up @@ -568,6 +571,12 @@
type: 'aws_s3',
settings: s,
},
aws_sns(settings=$.defaults.sink.aws_sns.settings): {
local s = std.mergePatch($.defaults.sink.aws_sns.settings, settings),

type: 'aws_sns',
settings: s,
},
aws_sqs(settings=$.defaults.sink.aws_sqs.settings): {
local s = std.mergePatch($.defaults.sink.aws_sqs.settings, settings),

Expand Down
2 changes: 1 addition & 1 deletion build/terraform/aws/dynamodb/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ resource "aws_dynamodb_table" "table" {

# Streams are only charged for read operations and reads from AWS Lambda are free
# https://aws.amazon.com/dynamodb/pricing/
stream_enabled = true
stream_enabled = true
stream_view_type = var.stream_view_type

dynamic "attribute" {
Expand Down
4 changes: 4 additions & 0 deletions build/terraform/aws/dynamodb/output.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
output "arn" {
value = aws_dynamodb_table.table.arn
}

output "stream_arn" {
value = aws_dynamodb_table.table.stream_arn
}
10 changes: 10 additions & 0 deletions build/terraform/aws/iam/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,16 @@ data "aws_iam_policy_document" "secretsmanager_read" {
}
}

data "aws_iam_policy_document" "sns_write" {
statement {
effect = "Allow"
actions = [
"sns:Publish",
]
resources = var.resources
}
}

data "aws_iam_policy_document" "sqs_read" {
statement {
effect = "Allow"
Expand Down
8 changes: 8 additions & 0 deletions build/terraform/aws/iam/output.tf
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ output "dynamodb_write_policy" {
value = data.aws_iam_policy_document.dynamodb_write.json
}

output "dynamodb_stream_read_policy" {
value = data.aws_iam_policy_document.dynamodb_stream_read.json
}

output "kms_read_policy" {
value = data.aws_iam_policy_document.kms_read.json
}
Expand All @@ -58,6 +62,10 @@ output "secretsmanager_read_policy" {
value = data.aws_iam_policy_document.secretsmanager_read.json
}

output "sns_write_policy" {
value = data.aws_iam_policy_document.sns_write.json
}

output "sqs_read_policy" {
value = data.aws_iam_policy_document.sqs_read.json
}
Expand Down
5 changes: 0 additions & 5 deletions build/terraform/aws/sns/_variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,6 @@ variable "kms_key_id" {
type = string
}

variable "fifo" {
type = bool
default = false
}

variable "tags" {
type = map(any)
default = {}
Expand Down
4 changes: 2 additions & 2 deletions build/terraform/aws/sns/main.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
resource "aws_sns_topic" "topic" {
name = var.name
kms_master_key_id = var.kms_key_id
fifo_topic = var.fifo ? true : false
content_based_deduplication = var.fifo ? true : false
fifo_topic = endswith(var.name, ".fifo") ? true : false
content_based_deduplication = endswith(var.name, ".fifo") ? true : false

tags = var.tags
}
5 changes: 0 additions & 5 deletions build/terraform/aws/sqs/_variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ variable "visibility_timeout_seconds" {
default = 300
}

variable "fifo" {
type = bool
default = false
}

variable "tags" {
type = map(any)
default = {}
Expand Down
4 changes: 2 additions & 2 deletions build/terraform/aws/sqs/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ resource "aws_sqs_queue" "queue" {
visibility_timeout_seconds = var.visibility_timeout_seconds
kms_master_key_id = var.kms_key_id
kms_data_key_reuse_period_seconds = 300
fifo_queue = var.fifo ? true : false
content_based_deduplication = var.fifo ? true : false
fifo_queue = endswith(var.name, ".fifo") ? true : false
content_based_deduplication = endswith(var.name, ".fifo") ? true : false

tags = var.tags
}
25 changes: 25 additions & 0 deletions examples/aws/pubsub/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# pubsub

This example deploys a data pipeline that uses a [publish/subscribe (pub/sub) pattern](https://aws.amazon.com/what-is/pub-sub-messaging/). The publisher receives change data capture (CDC) events from a DynamoDB table and publishes them to an SNS topic from which three subscribers consume them.

The data pipeline is visualized below:
```mermaid

flowchart TD
%% core infrastructure
ddb[DynamoDB\nTable]
sns[SNS Topic]

%% nodes
publisher(Publisher)
subscriber_x(Subscriber)
subscriber_y(Subscriber)
subscriber_z(Subscriber)

%% connections
ddb --> publisher
publisher -->sns
sns-->subscriber_x
sns-->subscriber_y
sns-->subscriber_z
```
11 changes: 11 additions & 0 deletions examples/aws/pubsub/config/publisher/config.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local sub = import '../../../../../build/config/substation.libsonnet';

{
sink: sub.interfaces.sink.aws_sns(
// change SNS topic ARN to match the resource created by Terraform
settings = { arn: 'arn:aws:sns:us-east-1:123456789012:my-topic'}
),
transform: {
type: 'noop',
},
}
8 changes: 8 additions & 0 deletions examples/aws/pubsub/config/subscriber_x/config.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local sub = import '../../../../../build/config/substation.libsonnet';

{
sink: sub.interfaces.sink.stdout,
transform: {
type: 'noop',
},
}
8 changes: 8 additions & 0 deletions examples/aws/pubsub/config/subscriber_y/config.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local sub = import '../../../../../build/config/substation.libsonnet';

{
sink: sub.interfaces.sink.stdout,
transform: {
type: 'noop',
},
}
8 changes: 8 additions & 0 deletions examples/aws/pubsub/config/subscriber_z/config.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
local sub = import '../../../../../build/config/substation.libsonnet';

{
sink: sub.interfaces.sink.stdout,
transform: {
type: 'noop',
},
}
85 changes: 85 additions & 0 deletions examples/aws/pubsub/terraform/bootstrap.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
data "aws_caller_identity" "caller" {}

# KMS encryption key that is shared by all Substation infrastructure
module "kms" {
source = "../../../../build/terraform/aws/kms"
name = "alias/substation"
policy = <<POLICY
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Principal": {
"Service": "cloudwatch.amazonaws.com"
},
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "kms:*",
"Principal": {
"AWS": "arn:aws:iam::${data.aws_caller_identity.caller.account_id}:root"
},
"Resource": "*"
}
]
}
POLICY
}

# AppConfig application that is shared by all Substation apps
resource "aws_appconfig_application" "substation" {
name = "substation"
description = "Stores compiled configuration files for Substation"
}

# use the prod environment for production resources
resource "aws_appconfig_environment" "prod" {
name = "prod"
description = "Stores production Substation configuration files"
application_id = aws_appconfig_application.substation.id
}

# use the dev environment for development resources
resource "aws_appconfig_environment" "dev" {
name = "dev"
description = "Stores development Substation configuration files"
application_id = aws_appconfig_application.substation.id
}

# AppConfig doesn't have useful support for non-linear, non-instant deployments on AWS Lambda, so this deployment strategy is used to deploy configurations as quickly as possible
# todo: add configuration rollback via CloudWatch Lambda monitoring
resource "aws_appconfig_deployment_strategy" "instant" {
name = "Instant"
description = "This strategy deploys the configuration to all targets immediately with zero bake time."
deployment_duration_in_minutes = 0
final_bake_time_in_minutes = 0
growth_factor = 100
growth_type = "LINEAR"
replicate_to = "NONE"
}

# repository for the core Substation app
module "ecr_substation" {
source = "../../../../build/terraform/aws/ecr"
name = "substation"
kms_arn = module.kms.arn
}

# repository for the validation app
module "ecr_validation" {
source = "../../../../build/terraform/aws/ecr"
name = "substation_validation"
kms_arn = module.kms.arn
}

module "sns" {
source = "../../../../build/terraform/aws/sns"
kms_key_id = module.kms.key_id
name = "substation_sns"
}
21 changes: 21 additions & 0 deletions examples/aws/pubsub/terraform/iam_appconfig.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
################################################
# appconfig permissions
# all Lambda must have this policy
################################################

module "iam_appconfig_read" {
source = "../../../../build/terraform/aws/iam"
resources = ["${aws_appconfig_application.substation.arn}/*"]
}

module "iam_appconfig_read_attachment" {
source = "../../../../build/terraform/aws/iam_attachment"
id = "substation_appconfig_read"
policy = module.iam_appconfig_read.appconfig_read_policy
roles = [
module.publisher.role,
module.subscriber_x.role,
module.subscriber_y.role,
module.subscriber_z.role,
]
}
47 changes: 47 additions & 0 deletions examples/aws/pubsub/terraform/iam_kms.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
################################################
# KMS read permissions
# all Lambda must have this policy
################################################

module "iam_kms_read" {
source = "../../../../build/terraform/aws/iam"
resources = [
module.kms.arn,
]
}

module "iam_kms_read_attachment" {
source = "../../../../build/terraform/aws/iam_attachment"
id = "substation_kms_read"
policy = module.iam_kms_read.kms_read_policy
roles = [
module.publisher.role,
module.subscriber_x.role,
module.subscriber_y.role,
module.subscriber_z.role,
]
}

################################################
# KMS write permissions
# all Lambda must have this policy
################################################

module "iam_kms_write" {
source = "../../../../build/terraform/aws/iam"
resources = [
module.kms.arn,
]
}

module "iam_kms_write_attachment" {
source = "../../../../build/terraform/aws/iam_attachment"
id = "substation_kms_write"
policy = module.iam_kms_write.kms_write_policy
roles = [
module.publisher.role,
module.subscriber_x.role,
module.subscriber_y.role,
module.subscriber_z.role,
]
}
4 changes: 4 additions & 0 deletions examples/aws/pubsub/terraform/provider.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
provider "aws" {
# profile = "default"
region = "us-east-1"
}
Loading