Skip to content
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
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
[![Merge](https://github.com/bcgov/quickstart-openshift-helpers/actions/workflows/merge.yml/badge.svg)](https://github.com/bcgov/quickstart-openshift-helpers/actions/workflows/merge.yml)
[![PR Closed](https://github.com/bcgov/quickstart-openshift-helpers/actions/workflows/pr-close.yml/badge.svg)](https://github.com/bcgov/quickstart-openshift-helpers/actions/workflows/pr-close.yml)

# QuickStart OpenShift - Helpers
Workflows and any other common code used by bcgov/quickstart-openshift (template).
Expand All @@ -25,3 +24,26 @@ jobs:
packages: backend client migrations
oc_server: ${{ secrets.OC_SERVER }} # REMOVED - OPTIONAL!
```

# ./oc_scripts

`rename_deployment.sh` - rename a deployment (metadata, labels)
`db_transfer.sh` - stream pg_dump from one container to pg_restore in another

These scripts can be used to migrate a postgres database.
```
# 1. Scale down stack
# Use web console

# 2. Rename the old db
./rename_deployment.sh fom-test-db

# 3. Deploy the new db
oc process -f openshift.deploy.yml -p ZONE=test -p TAG=test | oc apply -f -

# 4. Stream dump from old to new db
./db_transfer.sh fom-test-db fom-test-db-prev

# 5. Scale up stack
# Use web console
```
74 changes: 74 additions & 0 deletions oc_scripts/db_transfer.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/bin/bash
#
# Database Transfer Script for OpenShift
# Usage:
# ./db_transfer.sh <source-deployment-name> <target-deployment-name>
#
# This script takes a PostgreSQL database dump from the <source-deployment-name> and restores it into the <target-deployment-name>.
# Requirements:
# - Both deployments must have running pods managed by the given deployment names.
# - The database template should use a PersistentVolumeClaim with a different name for the new deployment.
# - The new deployment should be ready to accept a restore.
# - The script assumes the container name is the default or the first in the pod spec.
#
# Notes:
# - If the target database is not empty, you may see errors like "schema ... already exists".
# These are expected if objects already exist and can usually be ignored, but always review
# the output for unexpected or critical errors.

# Strict mode: exit on error, unset vars, or failed pipes
set -euo pipefail

# Usage
if [[ $# -lt 2 ]]; then
grep -v '^#!' "$0" | awk '/^#/ { sub(/^# ?/, ""); print; next } NF==0 { exit }'
exit 1
fi

SOURCE_DEPLOYMENT="${1}"
TARGET_DEPLOYMENT="${2}"
DUMP_PARAMETERS="${DUMP_PARAMETERS:---exclude-schema=tiger --exclude-schema=tiger_data --exclude-schema=topology}"

# Fail fast if pods aren't found
if ! oc get po -l deployment="${SOURCE_DEPLOYMENT}" | grep -q .; then
echo "No pods found for deployment '${SOURCE_DEPLOYMENT}'."
exit 2
fi
if ! oc get po -l deployment="${TARGET_DEPLOYMENT}" | grep -q .; then
echo "No pods found for deployment '${TARGET_DEPLOYMENT}'."
exit 2
fi

# Safety check: compare PVC ages to prevent accidental reverse transfers
SOURCE_PVC=$(oc get deployment "${SOURCE_DEPLOYMENT}" -o jsonpath='{.spec.template.spec.volumes[?(@.persistentVolumeClaim)].persistentVolumeClaim.claimName}')
TARGET_PVC=$(oc get deployment "${TARGET_DEPLOYMENT}" -o jsonpath='{.spec.template.spec.volumes[?(@.persistentVolumeClaim)].persistentVolumeClaim.claimName}')

if [[ -n "${SOURCE_PVC}" && -n "${TARGET_PVC}" ]]; then
SOURCE_PVC_CREATION_TIME=$(oc get pvc "${SOURCE_PVC}" -o jsonpath='{.metadata.creationTimestamp}')
TARGET_PVC_CREATION_TIME=$(oc get pvc "${TARGET_PVC}" -o jsonpath='{.metadata.creationTimestamp}')
SOURCE_PVC_EPOCH=$(date -d "${SOURCE_PVC_CREATION_TIME}" +%s)
TARGET_PVC_EPOCH=$(date -d "${TARGET_PVC_CREATION_TIME}" +%s)

if [[ ${SOURCE_PVC_EPOCH} -gt ${TARGET_PVC_EPOCH} ]]; then
echo "WARNING: Source PVC '${SOURCE_PVC}' ($(date -d "${SOURCE_PVC_CREATION_TIME}" '+%Y-%m-%d %H:%M')) is NEWER than target PVC '${TARGET_PVC}' ($(date -d "${TARGET_PVC_CREATION_TIME}" '+%Y-%m-%d %H:%M'))."
echo "This may be a reverse transfer that could overwrite newer data with older data."
echo -n "Are you sure you want to continue? (yes/no): "
read -r CONFIRM
if [[ "${CONFIRM}" != "yes" ]]; then
echo "Transfer cancelled by user."
exit 2
fi
else
echo "Safety check passed: Source PVC is older than target PVC."
fi
else
echo "Warning: Could not find PVCs for comparison. Proceeding without age check."
fi

# Stream dump directly from old deployment to new deployment
echo -e "\nDatabase transfer from '${SOURCE_DEPLOYMENT}' to '${TARGET_DEPLOYMENT}' beginning."
oc exec -i deployment/"${SOURCE_DEPLOYMENT}" -- bash -c "pg_dump -U \${POSTGRES_USER} -d \${POSTGRES_DB} -Fc ${DUMP_PARAMETERS[@]}" \
| oc exec -i deployment/"${TARGET_DEPLOYMENT}" -- bash -c "pg_restore -U \${POSTGRES_USER} -d \${POSTGRES_DB} -Fc"

# Results
echo -e "\nDatabase transfer from '${SOURCE_DEPLOYMENT}' to '${TARGET_DEPLOYMENT}' complete."
68 changes: 68 additions & 0 deletions oc_scripts/rename_deployment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/bash
#
# Usage:
# ./rename_deployment.sh <source-deployment-name> [target-deployment-name]
#
# If [target-deployment-name] is not provided, defaults to <source-deployment-name>-prev
#
# This script renames an OpenShift deployment by exporting its manifest, updating the name,
# deleting the old deployment, and applying the new one.

# Strict mode: exit on error, unset vars, or failed pipes
set -euo pipefail

# Show usage from header if not enough arguments
if [[ $# -lt 1 ]]; then
grep -v '^#!' "${0}" | awk '/^#/ { sub(/^# ?/, ""); print; next } NF==0 { exit }'
exit 1
fi

SOURCE_DEPLOYMENT="${1}"
TARGET_DEPLOYMENT="${2:-${SOURCE_DEPLOYMENT}-prev}"
MANIFEST=$(mktemp "/tmp/${SOURCE_DEPLOYMENT}_$(date +%Y%m%d)_XXXXXX.json")
trap 'rm -f "${MANIFEST}"' EXIT

# Fail fast if the new deployment already exists
if oc get deployment "${TARGET_DEPLOYMENT}" &>/dev/null; then
echo "Deployment '${TARGET_DEPLOYMENT}' already exists. Aborting to avoid overwrite."
exit 2
fi

# Check if the old deployment exists
if ! oc get deployment "${SOURCE_DEPLOYMENT}" &>/dev/null; then
echo "Deployment '${SOURCE_DEPLOYMENT}' not found."
exit 0
fi

# Export, clean, and update deployment manifest
oc get deployment "${SOURCE_DEPLOYMENT}" -o json \
| jq 'del(
.metadata.uid,
.metadata.resourceVersion,
.metadata.selfLink,
.metadata.creationTimestamp,
.metadata.generation,
.metadata.managedFields,
.status
)
| .metadata.name = "'"${TARGET_DEPLOYMENT}"'"
| .spec.selector.matchLabels.deployment = "'"${TARGET_DEPLOYMENT}"'"
| .spec.template.metadata.labels.deployment = "'"${TARGET_DEPLOYMENT}"'"' \
> "${MANIFEST}"

# Delete the old deployment and apply the new one
oc delete deployment "${SOURCE_DEPLOYMENT}"
oc apply -f "${MANIFEST}"

# Clean up

# Wait for the new deployment to become available
echo "Waiting for deployment '${TARGET_DEPLOYMENT}' to become available..."
if ! oc rollout status deployment/"${TARGET_DEPLOYMENT}" --timeout=120s; then
echo "Error: Deployment '${TARGET_DEPLOYMENT}' did not become available in time."
exit 3
fi

# Show matching deployments for confirmation
echo -e "\nMatching deployments after renaming:"
oc get deployments -o name | grep -iE "^deployment\.apps/(${SOURCE_DEPLOYMENT}|${TARGET_DEPLOYMENT})$"