From 28955abeb664ff90b73fadfd2f250dbb612172fb Mon Sep 17 00:00:00 2001 From: Tyrone Faulhaber <20131658+spectrapulse@users.noreply.github.com> Date: Fri, 5 Jul 2024 22:36:33 +0200 Subject: [PATCH] Initial Commit --- .github/workflows/build.yml | 65 ++++++++++++++++++++++++++++++++ Dockerfile | 26 +++++++++++++ backup.sh | 74 +++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 .github/workflows/build.yml create mode 100644 Dockerfile create mode 100644 backup.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..cbd8b25 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,65 @@ +name: Build images + +on: + push: + workflow_dispatch: + +permissions: + packages: write + contents: read + +jobs: + build: + name: Build image + runs-on: ubuntu-20.04 + env: + IMAGE: ghcr.io/${{ github.repository }} + strategy: + fail-fast: true + matrix: + arch: [ amd64, arm64v8 ] + type: [ mariadb, postgresql ] + include: + - arch: amd64 + platform: linux/amd64 + - arch: arm64v8 + platform: linux/arm64 + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-qemu-action@v3 + - uses: docker/metadata-action@v5 + id: image-metadata + with: + images: ghcr.io/${{ github.repository }} + tags: type=raw,value=${{ matrix.type }}-${{ matrix.arch }} + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - uses: docker/build-push-action@v5 + with: + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + platforms: ${{ matrix.platform }} + tags: ${{ steps.image-metadata.outputs.tags }} + build-args: IMAGE_TAG=${{ matrix.type }} + + merge: + runs-on: ubuntu-20.04 + needs: [ build ] + strategy: + fail-fast: true + matrix: + type: [ mariadb, postgresql ] + steps: + - uses: actions/checkout@v4 + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - run: | + docker buildx imagetools create \ + --tag ${IMAGE}:${{ matrix.type }} ${IMAGE}:${{ matrix.type }}-{amd64,arm64v8} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8d4507b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM alpine:3.20 + +ARG IMAGE_TAG + +RUN apk add --no-cache \ + restic \ + bash \ + curl \ + rclone + +RUN if [ -z "$IMAGE_TAG" ]; then \ + echo "No IMAGE_TAG specified. Exiting." && exit 1 \ + elif [ "$IMAGE_TAG" = "mariadb" ]; then \ + apk add --no-cache mariadb-client \ + elif [ "$IMAGE_TAG" = "postgresql"]; then \ + apk add --no-cache postgresql-client \ + fi + + +COPY backup.sh /usr/local/bin/backup.sh + +RUN chmod +x /usr/local/bin/backup.sh + +VOLUME [ "/backups" ] + +ENTRYPOINT [ "/usr/local/bin/backup.sh" ] \ No newline at end of file diff --git a/backup.sh b/backup.sh new file mode 100644 index 0000000..759b578 --- /dev/null +++ b/backup.sh @@ -0,0 +1,74 @@ +#! /usr/bin/env sh + +# restic password (optional) +RESTIC_PASSWORD=${RESTIC_PASSWORD:-""} + +# Database Credentials +DB_HOST=${DB_HOST?Variable not set} +DB_NAME=${DB_NAME?Variable not set} +DB_USERNAME=${DB_USERNAME?Variable not set} +DB_PASSWORD=${DB_PASSWORD?Variable not set} + +# Destinations +BACKUP_DIR=${BACKUP_DIR:-"/backups"} +if [ -z "$RESTIC_PASSWORD" ]; then + BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}-$(date +%Y%m%d%H%M%S).sql" +else + BACKUP_FILE="/tmp/${DB_NAME}.sql" +fi + +# Rclone remote (optional) +RCLONE_REMOTE=${RCLONE_REMOTE:-""} +RCLONE_CONFIG=${RCLONE_CONFIG:-"/rclone.conf"} + +# Create backup dir if it doesn't exist +mkdir -p "${BACKUP_DIR}" + +# Perform the database dump based on what db utility is installed +if command -v mysqldump >/dev/null 2>&1; then + mysqldump -h "$DB_HOST" -P "${DB_PORT:-'3306'}" -u "$DB_USERNAME" -p"$DB_PASSWORD" "$DB_NAME" > "$BACKUP_FILE" +elif command -v pg_dump >/dev/null 2>&1; then + export PGPASSWORD="${DB_PASSWORD}" + pg_dump -h "$DB_HOST" -p "${DB_PORT:-'5432'}" -U "$DB_USERNAME" "$DB_NAME" > "$BACKUP_FILE" +else + echo "This condition should be impossible to reach... What did you do??!!" + exit 1 +fi + +# Check if the dump was successful +if [ $? -eq 0 ]; then + echo "Database dump successful: ${BACKUP_FILE}" +else + echo "Database dump failed" + exit 1 +fi + +# Backup file using Restic if $RESTIC_PASSWORD is set +if [ -n "$RESTIC_PASSWORD" ]; then + echo "$RESTIC_PASSWORD" > /tmp/restic_password + + # Initialize restic repo if it doesn't exist + if ! restic --repo "$BACKUP_DIR" snapshots --password-file=/tmp/restic_password &>/dev/null; then + restic init --repo "$BACKUP_DIR" --password-file=/tmp/restic_password + fi + + # Backup sql dump file + restic --repo "$BACKUP_DIR" backup "$BACKUP_FILE" --tag "sqldump" --host "dbackup" --password-file=/tmp/restic_password + + # Cleanup + [ -n "$RESTIC_PRUNE_ARGS" ] && restic --repo "$BACKUP_DIR" forget --password-file=/tmp/restic_password $RESTIC_PRUNE_ARGS + + rm /tmp/restic_password $BACKUP_FILE +fi + +# Sync backup dir to remote using Rclone if $RCLONE_REMOTE is set +if [ -n "$RCLONE_REMOTE" ]; then + rclone --config="$RCLONE_CONFIG" sync "$BACKUP_DIR" "$RCLONE_REMOTE" + + if [ $? -eq 0 ]; then + echo "Rclone sync to remote successful" + else + echo "Rclone sync to remote failed" + exit 1 + fi +fi