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
46 changes: 46 additions & 0 deletions bin/data-replication
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash

# Data replication script - creates read-only database role and keeps container running

set -e

echo "Starting data replication setup..."

if [ -z "$READ_ONLY_DB_PASSWORD" ]; then
echo "Error: READ_ONLY_DB_PASSWORD environment variable is not set"
exit 1
fi

echo "Creating read-only database role..."

bundle exec rails runner "
begin
ActiveRecord::Base.connection.execute(\"
DO \$\$
BEGIN
IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'grafana_ro') THEN
CREATE ROLE grafana_ro WITH LOGIN PASSWORD '#{ENV['READ_ONLY_DB_PASSWORD']}';
ELSE
ALTER ROLE grafana_ro WITH PASSWORD '#{ENV['READ_ONLY_DB_PASSWORD']}';
END IF;
END
\$\$;
\")

ActiveRecord::Base.connection.execute(\"
GRANT CONNECT ON DATABASE #{ActiveRecord::Base.connection.current_database} TO grafana_ro;
GRANT USAGE ON SCHEMA public TO grafana_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO grafana_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO grafana_ro;
\")

puts 'Read-only role created/updated successfully'
rescue => e
puts \"Error creating read-only role: \#{e.message}\"
exit 1
end
"

echo "Data replication setup completed. Keeping container running..."

exec tail -f /dev/null
5 changes: 4 additions & 1 deletion bin/docker-start
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ elif [ "$SERVER_TYPE" == "good-job" ]; then
elif [ "$SERVER_TYPE" == "sidekiq" ]; then
echo "Starting sidekiq server..."
exec "$BIN_DIR"/sidekiq
elif [ "$SERVER_TYPE" == "data-replication" ]; then
echo "Starting data replication server..."
exec "$BIN_DIR"/data-replication
elif [ "$SERVER_TYPE" == "none" ]; then
echo "No server started"
exec tail -f /dev/null # Keep container running
else
echo "SERVER_TYPE variable: '$SERVER_TYPE' unknown. Allowed values ['web','good-job', 'none']"
echo "SERVER_TYPE variable: '$SERVER_TYPE' unknown. Allowed values ['web','good-job', 'sidekiq', 'data-replication', 'none']"
exit 1
fi
3 changes: 1 addition & 2 deletions terraform/data_replication/ecs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ module "db_access_service" {
subnets = local.subnet_list
vpc_id = aws_vpc.vpc.id
}
server_type = "none"
server_type_name = "data-replication"
server_type = "data-replication"
task_config = {
environment = local.task_envs
secrets = local.task_secrets
Expand Down
3 changes: 2 additions & 1 deletion terraform/data_replication/iam.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ data "aws_iam_policy_document" "ecs_permissions" {
sid = "dbSecretSid"
actions = ["secretsmanager:GetSecretValue"]
resources = [
var.db_secret_arn
var.db_secret_arn,
aws_secretsmanager_secret.ro_db_password.arn
]
effect = "Allow"
}
Expand Down
2 changes: 1 addition & 1 deletion terraform/data_replication/rds.tf
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ resource "aws_rds_cluster" "cluster" {
}

lifecycle {
ignore_changes = [cluster_identifier]
ignore_changes = [cluster_identifier, snapshot_identifier]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that really what we want? I would imagine the cluster should be recreated if the snapshot_identifier changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we have an option in the workflow to decide whether or not we actually want to recreate the database I would only want to trigger the recreation when that option is specified, otherwise we will recreate every deploy that is 12 hours apart regardless of this option.

}
}

Expand Down
26 changes: 26 additions & 0 deletions terraform/data_replication/ssm_parameters.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Create a password that is automatically populated in secrets manager using the random password generator for aws

# Generate a random password for the read-only database user
ephemeral "aws_secretsmanager_random_password" "ro_db_password" {
}

# Store the generated password in AWS Secrets Manager
resource "aws_secretsmanager_secret" "ro_db_password" {
name = "${local.name_prefix}-ro-db-password-${substr(uuid(), 0, 4)}"
description = "Read-only database user password for data replication"
recovery_window_in_days = 7

tags = {
Name = "${local.name_prefix}-ro-db-password"
}
lifecycle {
ignore_changes = [name]
replace_triggered_by = [aws_rds_cluster.cluster]
}
}

resource "aws_secretsmanager_secret_version" "ro_db_password" {
secret_id = aws_secretsmanager_secret.ro_db_password.id
secret_string_wo = ephemeral.aws_secretsmanager_random_password.ro_db_password.random_password
secret_string_wo_version = 1
}
4 changes: 4 additions & 0 deletions terraform/data_replication/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ locals {
{
name = "RAILS_MASTER_KEY"
valueFrom = var.rails_master_key_path
},
{
name = "READ_ONLY_DB_PASSWORD"
valueFrom = aws_secretsmanager_secret.ro_db_password.arn
}
]
}
Expand Down
Loading