Skip to content

Commit

Permalink
Create database backup bastion.
Browse files Browse the repository at this point in the history
  • Loading branch information
casey-rapnicki-bixal committed Jun 21, 2024
1 parent 50e063b commit 48d1352
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 32 deletions.
42 changes: 41 additions & 1 deletion .github/workflows/database-backup-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,22 @@ jobs:
run: ./scripts/pipeline/deb-mysql-client-install.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
- name: Start Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: |
cf start "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}"
- name: Backup Database (dev)
id: backup
shell: bash
env:
CF_USER: "${{ secrets.CF_USER }}"
Expand All @@ -40,6 +55,31 @@ jobs:
PROJECT: "${{ secrets.PROJECT }}"
run: |
export TIMESTAMP=$(date --utc +%FT%TZ | tr ':', '-')
source ./scripts/pipeline/cloud-gov-login.sh
source ./scripts/pipeline/database-backup.sh
source ./scripts/pipeline/s3-backup-upload.sh
stopBastion:
name: Stop Bastion
runs-on: ubuntu-latest
needs: backup-database
if: ${{ always() }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set env.BRANCH
run: echo "BRANCH=dev" >> $GITHUB_ENV
- name: Install basic dependancies
run: ./scripts/pipeline/deb-basic-deps.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
- name: Stop Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: cf stop "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
46 changes: 43 additions & 3 deletions .github/workflows/database-backup-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,55 @@ jobs:
run: ./scripts/pipeline/deb-mysql-client-install.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
- name: Start Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: |
cf start "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}"
- name: Backup Database (main)
id: backup
shell: bash
env:
CF_USER: "${{ secrets.CF_USER }}"
CF_PASSWORD: "${{ secrets.CF_PASSWORD }}"
CF_ORG: "${{ secrets.CF_ORG }}"
PROJECT: "${{ secrets.PROJECT }}"
run: |
export TIMESTAMP=$(date --utc +%FT%TZ | tr ':', '-')
source ./scripts/pipeline/database-backup.sh
source ./scripts/pipeline/s3-backup-upload.sh
stopBastion:
name: Stop Bastion
runs-on: ubuntu-latest
needs: backup-database
if: ${{ always() }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set env.BRANCH
run: echo "BRANCH=main" >> $GITHUB_ENV
- name: Install basic dependancies
run: ./scripts/pipeline/deb-basic-deps.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
export TIMESTAMP=$(date --utc +%FT%TZ | tr ':', '-')
source ./scripts/pipeline/cloud-gov-login.sh
source ./scripts/pipeline/database-backup.sh
source ./scripts/pipeline/s3-backup-upload.sh
- name: Stop Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: cf stop "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
41 changes: 40 additions & 1 deletion .github/workflows/database-restore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ jobs:
run: ./scripts/pipeline/deb-mysql-client-install.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
- name: Start Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: |
cf start "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
./scripts/pipeline/cloud-gov-wait-for-app-start.sh "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}"
- name: Restore database
shell: bash
env:
Expand All @@ -32,8 +46,33 @@ jobs:
PROJECT: '${{ secrets.PROJECT }}'
run: |
export S3_FILE_PATH=${{ github.event.inputs.database_file_override }}
source ./scripts/pipeline/cloud-gov-login.sh
source ./scripts/pipeline/s3-backup-download.sh
source ./scripts/pipeline/database-restore.sh
source ./scripts/pipeline/cloud-gov-post-deploy.sh
source ./scripts/pipeline/s3-backup-post-restore.sh
stopBastion:
name: Stop Bastion
runs-on: ubuntu-latest
needs: restore-database
if: ${{ always() }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set env.BRANCH
run: echo "BRANCH=main" >> $GITHUB_ENV
- name: Install basic dependancies
run: ./scripts/pipeline/deb-basic-deps.sh
- name: Install Cloudfoundry CLI
run: ./scripts/pipeline/deb-cf-install.sh
- name: Cloud.gov login
env:
CF_USER: '${{ secrets.CF_USER }}'
CF_PASSWORD: '${{ secrets.CF_PASSWORD }}'
CF_ORG: '${{ secrets.CF_ORG }}'
PROJECT: '${{ secrets.PROJECT }}'
run: |
source ./scripts/pipeline/cloud-gov-login.sh
- name: Stop Bastion
env:
DATABASE_BACKUP_BASTION_NAME: '${{ secrets.DATABASE_BACKUP_BASTION_NAME }}'
run: cf stop "${PROJECT}-${DATABASE_BACKUP_BASTION_NAME}-${BRANCH}" >/dev/null 2>&1
7 changes: 7 additions & 0 deletions terraform/applications/database-backup-bastion/apt.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
packages:
- curl
- gettext
- mysql-client
- postgresql-client
- wget
67 changes: 67 additions & 0 deletions terraform/applications/database-backup-bastion/start
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/bin/bash

home="/home/vcap"
#app_path="${home}/app"

echo "export PATH=${PATH}:${home}/deps/0/bin" > "${home}/exports.sh"
echo "alias terraform=tofu" >> "${home}/exports.sh"
echo "alias tf=tofu" >> "${home}/exports.sh"

AWS_ACCESS_KEY_ID=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.access_key_id')
AWS_SECRET_ACCESS_KEY=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.secret_access_key')
AWS_DEFAULT_REGION=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.region')
AWS_BUCKET=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.bucket')
AWS_ENDPOINT=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.endpoint')
AWS_FIPS_ENDPOINT=$(echo "${VCAP_SERVICES}" | jq '.s3[] | select(.name | contains("backup")) | .credentials.fips_endpoint')

if [ -n "${AWS_ACCESS_KEY_ID}" ]; then
{
echo "export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}"
echo "export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}"
echo "export AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION}"
echo "export AWS_BUCKET=${AWS_BUCKET}"
echo "export AWS_ENDPOINT=${AWS_ENDPOINT}"
echo "export AWS_FIPS_ENDPOINT=${AWS_FIPS_ENDPOINT}"
} >> "${home}/exports.sh"
fi

MYSQL_CONN_STR=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.uri')
MYSQL_DATABASE=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.db_name')
MYSQL_HOST=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.host')
MYSQL_PASSWORD=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.password')
MYSQL_PORT=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.port')
MYSQL_USER=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("mysql")) | .credentials.username')

if [ -n "${MYSQL_CONN_STR}" ]; then
{
echo "export MYSQL_CONN_STR=${MYSQL_CONN_STR}"
echo "export MYSQL_DATABASE=${MYSQL_DATABASE}"
echo "export MYSQL_HOST=${MYSQL_HOST}"
echo "export MYSQL_PASSWORD=${MYSQL_PASSWORD}"
echo "export MYSQL_PORT=${MYSQL_PORT}"
echo "export MYSQL_USER=${MYSQL_USER}"
} >> "${home}/exports.sh"
fi

PG_CONN_STR=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.uri')
PGDATABASE=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.db_name')
PGHOST=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.host')
PGPASSWORD=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.password')
PGPORT=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.port')
PGUSER=$(echo "${VCAP_SERVICES}" | jq '."aws-rds"[] | select(.plan | contains("psql")) | .credentials.username')

if [ -n "${PG_CONN_STR}" ]; then
{
echo "export PG_CONN_STR=${PG_CONN_STR}"
echo "export PGDATABASE=${PGDATABASE}"
echo "export PGHOST=${PGHOST}"
echo "export PGPASSWORD=${PGPASSWORD}"
echo "export PGPORT=${PGPORT}"
echo "export PGUSER=${PGUSER}"
} >> "${home}/exports.sh"
fi

sed -i '1s/^/source exports.sh\n/' "${home}/.bashrc"

while : ; do sleep 500 ; done

Binary file not shown.
70 changes: 62 additions & 8 deletions terraform/infra/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ locals {
tf_backend = {
type = "pg"
name_pattern_psql = "${local.project}-terraform-backend-bootstrap"
name_pattern_secrets = "${local.project}--pg-secrets-bootstrap "
name_pattern_secrets = "${local.project}--pg-secrets-bootstrap"
space = "main"
}

## "Common" applications and services that are deployed to every space.
Expand Down Expand Up @@ -127,6 +128,61 @@ locals {
}
]
}
database-backup-bastion = {

## Should the application have access to the internet?
allow_egress = true

## Buildpacks to use with this application.
## List buildpacks avalible with: cf buildpacks
buildpacks = [
"https://github.com/cloudfoundry/apt-buildpack",
"binary_buildpack"
]

## Command to run when container starts.
command = "./start"

## Ephemeral disk storage.
disk_quota = 1024

## Should SSH be enabled?
enable_ssh = true

## Environmental variables. Avoid sensitive variables.
environment = {}

## Timeout for health checks, in seconds.
health_check_timeout = 180

## Type of health check.
## Options: port, process, http
health_check_type = "process"

## Number of instances of application to deploy.
instances = 1

## Labels to add to the application.
labels = {
environment = terraform.workspace
}

## Maximum amount of memory the application can use.
memory = 64

services_external = [
"${local.project}-mysql-${terraform.workspace}",
"${local.project}-backup-${terraform.workspace}",
terraform.workspace == local.tf_backend.space ? "${local.project}-terraform-backend-bootstrap" : null
]

## The source file should be a directory or a zip file.
source = "../applications/database-backup-bastion"

space = terraform.workspace

stopped = true
}
}

## Services to deploy in this environment.
Expand Down Expand Up @@ -412,16 +468,14 @@ locals {

## Passwords that need to be generated for this environment.
## These will actually use the sha256 result from the random module.
passwords = [
{
name = "hash_salt"
passwords = {
hash_salt = {
length = 32
},
{
name = "cron_key"
}
cron_key = {
length = 32
}
]
}
}
)
}
Expand Down
32 changes: 15 additions & 17 deletions terraform/modules/application/data.tf
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
locals {

## Create a single list of external service names. Multiple applications
## could reference the same service, but the GUID only needs to be looked up once.
services_external = distinct(
flatten(
[
for key, value in try(var.env.apps, {}) : [
try(var.env.apps[key].services_external, [])
## Create a single list of external service names. Multiple applications
## could reference the same service, but the GUID only needs to be looked up once.
services_external = toset(
compact(
distinct(
flatten(
[
for value in try(var.env.apps, {}) : [
try(value.services_external, [])
]
]
]
)
)
)
}

output "name" {
value = data.cloudfoundry_service_instance.this
)
}

## Lookup up service instance GUID's for existing services.
## These can be externally deployed services or services deployed from different code sources.
## The GUID can then be refrenced by data.cloudfoundry_service_instance.this["service-name"].id
data "cloudfoundry_service_instance" "this" {
for_each = {
for key, value in local.services_external : value => value
}
name_or_id = each.value
space = try(var.cloudfoundry.space.id, null)
for_each = try(local.services_external, [])
name_or_id = each.value
space = try(var.cloudfoundry.space.id, null)
}
Loading

0 comments on commit 48d1352

Please sign in to comment.