Skip to content

Instantly share code, notes, and snippets.

@bvanhou
Created September 2, 2020 21:35
Show Gist options
  • Select an option

  • Save bvanhou/f3cc7bcd706f832c3c6344f8141b4fd6 to your computer and use it in GitHub Desktop.

Select an option

Save bvanhou/f3cc7bcd706f832c3c6344f8141b4fd6 to your computer and use it in GitHub Desktop.
CircleCI Database Export
#!/bin/bash
##
# CircleCI Database Export Script
#
# This script is for installations running CircleCI server 2.0 or higher.
#
# This script will create a tar ball of the PostgreSQL and Mongo databases.
# This should generally be used when you are planning on switching from
# the default embedded databases to an external database source.
#
# This script will also archive application data for:
# Rabbitmq
# Replicated
# Vault
# Circle customizations
# File server data
#
# This script should be run as root from the CircleCI Services Box. CircleCI and any
# additional postgresql or mongo containers should be shut down to eliminate
# any chances of data corruption.
##
set -e
SKIP_DATABASE_EXPORT="false"
MONGO_VERSION="3.6.6"
ARGS="${@:1}"
help_init_options() {
# Help message for Init menu
echo " -s|--skip-databases Skip exporting local Postgres and Mongodb"
echo " -m|--mongo-version Mongodb version to use during export. Must match version of installed release"
echo " -h|--help Print help text"
}
init_options() {
# Handles arguments passed into the init menu
POSITIONAL=()
while [[ $# -gt 0 ]]
do
key="${1}"
case $key in
-s|--skip-databases)
SKIP_DATABASE_EXPORT="true"
shift # past argument
;;
-m|--mongo-version)
MONGO_VERSION="${2}"
shift # past argument
shift # past value
;;
-h|--help)
help_init_options
exit 0
;;
*) # unknown option
POSITIONAL+=("${1}") # save it in an array for later
shift # past argument
;;
esac
done
if [ ${#POSITIONAL[@]} -gt 0 ]
then
help_init_options
exit 1
fi
}
# Constants
DATE=$(date +"%Y-%m-%d-%s")
MONGO_DATA="/data/circle/mongo"
MONGO_BU="circleci-mongo-export-${DATE}"
TMP_MONGO="circle-mongo-export"
PG_BU="circleci-pg-export-${DATE}"
PGDATA="/data/circle/postgres/9.5/data"
PGVERSION="9.5.8"
TMP_PSQL="circle-postgres-export"
##
# Preflight checks
# make sure script is running as root
# make sure circle, mongo, and postgres are shut down
##
function preflight_checks() {
if [ $(id -u) -ne 0 ]
then
echo "Please run this script as root"
exit 1
elif [ -n "$(docker ps | grep circleci-frontend | head -n1 )" ]
then
echo "Please shut down CircleCI from the replicated console at https://<YOUR_CIRCLE_URL>:8800 before running this script."
exit 1
elif [ -n "$(docker ps | grep mongo | head -n1 )" ]
then
echo "Please shut down any other Mongo containers before running this script"
exit 1
elif [ -n "$(docker ps | grep -v replicated | grep postgres | head -n1 )" ]
then
echo "Please shut down any other PostgreSQL containers before running this script"
exit 1
fi
if ! command -v jq >/dev/null 2>&1; then
echo ... installing jq
apt-get update
apt-get install -y jq
fi
}
##
# Start temporary postgreSQL container with mounted data volumen
##
function start_postgres() {
echo ... starting new postgres container with existing volume
docker run --name $TMP_PSQL -d -v $PGDATA:/var/lib/postgresql/data postgres:$PGVERSION
}
##
# Stop the PostgreSQL container that we created with the start_postgres function
##
function stop_postgres() {
echo ... stopping postgresql container
docker rm -f $TMP_PSQL
}
##
# Export PostgreSQL database once the container has started and is accepting connections
##
function export_postgres() {
echo ... exporting postgresql database
mkdir -p $PG_BU
pushd $PG_BU
until docker exec $TMP_PSQL psql -U postgres -c '\l'; do
>&2 echo "Postgres is starting up ... will try again momentarily"
sleep 3
done
docker exec $TMP_PSQL pg_dumpall -U postgres > circle.sql
popd
}
##
# Basic sanity check / smoke test to ensure that the postgresql export performed by
# the export_postgres function contains what we expect it to contain
##
function check_postgres() {
echo ... verifying postgres export file
pushd $PG_BU
if [ -z "$(cat circle.sql | grep circle_migrations | head -n1)" ]
then
echo "[FATAL] Something is wrong with the postgresql export file for 'circle' database, please contact CircleCI support at enterprise-support@circleci.com for further assistance."
stop_postgres
exit 1
fi
if [ -z "$(cat circle.sql | grep build_jobs | head -n1)" ]
then
echo "[FATAL] Something is wrong with the postgresql export file for 'conductor_production' database, please contact CircleCI support at enterprise-support@circleci.com for further assistance."
stop_postgres
exit 1
fi
if [ -z "$(cat circle.sql | grep contexts | head -n1)" ]
then
echo "[FATAL] Something is wrong with the postgresql export file for 'contexts_service_production' database, please contact CircleCI support at enterprise-support@circleci.com for further assistance."
stop_postgres
exit 1
fi
if [ -z "$(cat circle.sql | grep qrtz_blob_triggers | head -n1)" ]
then
echo "[FATAL] Something is wrong with the postgresql export file for 'cron_service_production' database, please contact CircleCI support at enterprise-support@circleci.com for further assistance."
stop_postgres
exit 1
fi
if [ -z "$(cat circle.sql | grep tasks | head -n1)" ]
then
echo "[WARN] 'vms' database was not correctly exported."
fi
popd
}
##
# Start temporary Mongo container with mounted data volume
##
function start_mongo() {
echo ... starting new mongo container with existing volume
docker run --rm --name $TMP_MONGO -d -v $MONGO_DATA:/data/db mongo:$MONGO_VERSION
}
##
# Stop the Mongo container tht we created with the start_mongo function
##
function stop_mongo() {
echo ... stopping mongo container
docker rm -f $TMP_MONGO
}
##
# Export Mongo database once the container has started and is accepting connections
##
function export_mongo() {
echo ... exporting mongo database
until docker exec $TMP_MONGO mongo --eval "db.stats()"; do
>&2 echo "Mongo is starting up ... will try again momentarily"
sleep 3
done
# note that this file is generated inside of the container and then moved to the
# current working directory.
docker exec $TMP_MONGO bash -c "mkdir -p /data/db/dump-${DATE} && cd /data/db/dump-${DATE} && mongodump"
mv /data/circle/mongo/dump-${DATE}/dump $(pwd)/$MONGO_BU
rm -rf $MONGO_BU/admin
}
##
# Basic santiy check / smoke test to ensure that the mongo export performed by
# the export_mongo function contains what we expect it to contain.
##
function check_mongo() {
echo ... verifying mongo export files
CHECK=$(ls -al $MONGO_BU | grep circle_ghe)
if [ -z "$CHECK" ]
then
echo "[FATAL] Something is wrong with the mongo export, please contact CircleCI support at enterprise-support@circleci.com for further assistance."
exit 1
fi
}
##
# Persist non-exportable data
# Rabbitmq
# Replicated
# Vault
# Circle customizations
# File server data
##
function archive_data() {
echo ... copying over application data
mkdir -p $(pwd)/circle-data
rsync -a \
--exclude lost\+found \
--exclude mongo \
--exclude postgres \
--exclude replicated \
--exclude replicated-operator \
/data/circle/ $(pwd)/circle-data
cp -r /etc/circleconfig $(pwd)/circle-data/circleconfig
if [[ -d /etc/circle-installation-customizations ]]; then
cp -r /etc/circle-installation-customizations $(pwd)/circle-data/circle-installation-customizations
fi
export_replicated
tar cfz circleci_application_data_${DATE}.tar.gz circle-data
rm -r $(pwd)/circle-data
}
function export_replicated() {
echo "$(replicatedctl app-config export)" | \
jq -r '. | del(.aws_access_key_id,.aws_region,.s3_bucket,.sqs_queue_url,.vm_aws_region,.vm_sg,.vm_subnet)' | \
tee $(pwd)/circle-data/replicated-app-settings.conf
}
##
# Create tar ball with the postgreSQL and Mongo exports
##
function compress() {
echo ... compressing exported files
tar cfz circleci_database_export_${DATE}.tar.gz $PG_BU $MONGO_BU
rm -rf $MONGO_BU $PG_BU
}
##
# Main function
##
function circleci_database_export() {
echo "Starting CircleCI Database Export"
init_options $ARGS
preflight_checks
if [ ! "$SKIP_DATABASE_IMPORT" = "true" ]; then
start_mongo
export_mongo
check_mongo
stop_mongo
start_postgres
export_postgres
check_postgres
stop_postgres
compress
fi
archive_data
echo "CircleCI Server Export Complete."
echo "Your exported files can be found at $(pwd)/circleci_database_export_${DATE}.tar.gz"
echo "Your application data can be found at $(pwd)/circleci_application_data_${DATE}.tar.gz"
}
circleci_database_export
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment