Skip to content

Commit

Permalink
feat: supersafe mode
Browse files Browse the repository at this point in the history
  • Loading branch information
williarin committed Jan 7, 2022
1 parent 3c46d95 commit 7b0fded
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,15 @@ name: Build and deploy

on:
push:
branches:
- '**'
tags:
- '**'
paths-ignore:
- 'README.md'
pull_request:
branches:
- master

jobs:
test:
name: Test

runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2
with:
submodules: recursive

- name: Launch Docker containers
run: docker compose -f docker-compose.test.yml up -d --build

- name: Test
run: docker compose -f docker-compose.test.yml exec backup make test

build:
name: Build

runs-on: ubuntu-20.04

needs: test

if: startsWith(github.ref, 'refs/tags/')

steps:
- uses: actions/checkout@v2

Expand Down Expand Up @@ -75,12 +48,10 @@ jobs:
deploy:
name: Deploy

needs: [test, build]
needs: [build]

runs-on: ubuntu-20.04

if: startsWith(github.ref, 'refs/tags/')

steps:
- uses: FranzDiebold/github-env-vars-action@v2

Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Test

on:
push:
branches:
- '**'
pull_request:
branches:
- master

jobs:
test:
name: Test

runs-on: ubuntu-20.04

steps:
- uses: actions/checkout@v2
with:
submodules: recursive

- name: Launch Docker containers
run: docker compose -f docker-compose.test.yml up -d --build

- name: Test
run: docker compose -f docker-compose.test.yml exec backup make test
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ all: test

.PHONY: test
test:
@printf "▶ Check scripts syntax\n"
@for file in $$(find ./src -type f); do shellcheck -e 1091,2012 --format=tty $$file; done;
@printf "\n▶ Run unit tests\n"
@./test/bats/bin/bats test/unit
@printf "\n▶ Run functional tests\n"
@./test/bats/bin/bats test/functional
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,36 @@

This Docker image will backup your MySQL/MariaDB databases following the Grandfather-Father-Son (GFS) retention scheme.

## Features

* GFS backups retention scheme
* AES256 encryption/decryption
* Single or multiple databases backups
* Grouped or individual archives
* Parallelized compression

## GFS retention scheme

This means that you'll always have:

* A backup for every day of the last week
* A backup for every week of the last month
* A backup for every month of the last year
* A backup for every day of the last week (6)
* A backup for every week of the last month (4)
* A backup for every month of the last year (12)
* A backup for every previous year (unlimited)

For a 100MB backup, it will cost you around 2GB for the current year + 100MB for each previous year.

In SuperSafe mode, you'll have:

* A backup for every day of the last month (28~31)
* A backup for every week of the last year (48)
* A backup for every previous year (unlimited)

For a 100MB backup, backups will cost you around 8GB for the current year + 100MB for each previous year.

Backups are run by default every day at 00:00 UTC.


## Usage

```bash
Expand All @@ -32,6 +54,7 @@ As an example, you can use: `MYSQL_PASSWORD_FILE=/run/secret/mysql-root-password

| Variable | Description | Default |
| -------- | ----------- | ------- |
| `SUPERSAFE_MODE` | Run backups in SuperSafe mode. This means many more backups ([see details](#gfs-retention-scheme)). | `false` |
| `MYSQL_HOST` | The host of your MySQL/MariaDB database. | `mysql` |
| `MYSQL_PORT` | The port number of your MySQL/MariaDB database. | `3306` |
| `MYSQL_USER` | The username of your MySQL/MariaDB database. | `root` |
Expand Down
5 changes: 5 additions & 0 deletions docker-compose.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ services:
restart: 'no'
depends_on:
- database
volumes:
- ./src:/usr/local/bin
- ./src:/usr/src/secure-mysql-backups/src
- ./test:/usr/src/secure-mysql-backups/test
- ./Makefile:/usr/src/secure-mysql-backups/Makefile
20 changes: 17 additions & 3 deletions src/backup
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,37 @@ host="$(get_env "MYSQL_HOST" "mysql")"
user="$(get_env "MYSQL_USER" "root")"
port="$(get_env "MYSQL_PORT" "3306")"
password="$(get_env "MYSQL_PASSWORD")"
databases="$(get_env "MYSQL_DATABASE" "$(get_databases_list "$host" "$port" "$user" "$password")")"
individual_backups="$(get_env "INDIVIDUAL_BACKUPS" false)"
backup_name="$(get_env "BACKUP_NAME" "main-backup")"
supersafe_mode="$(get_env "SUPERSAFE_MODE" false)"

wait_for_database "$host" "$port" "$user" "$password" "60"

databases="$(get_env "MYSQL_DATABASE" "$(get_databases_list "$host" "$port" "$user" "$password")")"

echo "Backup started at $(date)."

for database in $databases; do
dump_database "$host" "$port" "$user" "$password" "$database" || continue

if [ "$individual_backups" = true ]; then
archive_file="$(get_archive_name "$backup_name.$database")"
if [ "$supersafe_mode" = true ]; then
archive_file="$(get_archive_name_supersafe_mode "$backup_name.$database")"
else
archive_file="$(get_archive_name "$backup_name.$database")"
fi

create_archive "$archive_file" "$database.sql"
fi
done

if [ "$individual_backups" = false ]; then
archive_file="$(get_archive_name "$backup_name")"
if [ "$supersafe_mode" = true ]; then
archive_file="$(get_archive_name_supersafe_mode "$backup_name")"
else
archive_file="$(get_archive_name "$backup_name")"
fi

create_archive "$archive_file" "*.sql"
fi

Expand Down
46 changes: 40 additions & 6 deletions src/lib/file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@ get_file_gid () {
|| ls -ld "$1" | awk '{print $4}'
}

#/ Get an archive name corresponding to the current name
#/ Get an archive name corresponding to the current day
#/
#/ @usage var="$(get_archive_name "my-backup-name")"
#/
#/ @param $1 Backup name
#/ @return Prints the result
#/ @return Prints the generated archive name
get_archive_name () {
if [ "$#" -lt 1 ]; then
echo "At least 1 argument required, $# provided"
exit 1
fi

local day day_num month_num week_file backup_name archive_file
local day day_num month_num year_num week_file backup_name archive_file
backup_name="$1"

# Find which week of the month 1-4 it is.
Expand All @@ -74,7 +74,10 @@ get_archive_name () {

# Create archive filename.
day=$(date +%A)
if [ "$(date +%-d)" -eq "$(date -d "$(date +%-m)/1 + 1 month - 1 day" "+%d")" ]; then
if [ "$(date +%m-%d)" = "12-31" ]; then
year_num=$(date +%Y)
archive_file="$backup_name.year$year_num.tgz"
elif [ "$(date +%-d)" -eq "$(date -d "$(date +%-m)/1 + 1 month - 1 day" "+%d")" ]; then
month_num=$(date +%m)
archive_file="$backup_name.month$month_num.tgz"
elif [ "$day" != "Saturday" ]; then
Expand All @@ -87,6 +90,38 @@ get_archive_name () {
echo "$archive_file"
}

#/ Get an archive name corresponding to the current day
#/
#/ @usage var="$(get_archive_name "my-backup-name")"
#/
#/ @param $1 Backup name
#/ @return Prints the generated archive name
get_archive_name_supersafe_mode () {
if [ "$#" -lt 1 ]; then
echo "At least 1 argument required, $# provided"
exit 1
fi

local day_num day_num_pad week_num year_num backup_name archive_file
backup_name="$1"

day_num=$(date +%-d)
day_num_pad=$(date +%d)
week_num=$(date +%V)
year_num=$(date +%Y)

# Create archive filename.
if [ "$(date +%m-%d)" = "12-31" ]; then
archive_file="$backup_name.year$year_num.tgz"
elif [ "$((day_num % 7))" -eq 0 ]; then
archive_file="$backup_name.week$week_num.tgz"
else
archive_file="$backup_name.day$day_num_pad.tgz"
fi

echo "$archive_file"
}

#/ Create an archive given an archive name and files list
#/
#/ @usage create_archive "my-backup-name.my_db.day1-Monday.tgz" "my_db.sql"
Expand Down Expand Up @@ -120,8 +155,7 @@ create_archive () {
fi

bash -c "rm -f /tmp/backup/$2 /tmp/backup/$archive_file"
chown -f "$chown_files" "/backup/$archive_file" "/backup/$archive_file.aes"
exit 0
chown -f "$chown_files" "/backup/$archive_file" "/backup/$archive_file.aes" || true
}


Expand Down
12 changes: 7 additions & 5 deletions src/run-cron
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ source "$SCRIPT_DIR/lib/env.sh"
cron_minute=$(get_env "CRON_MINUTE" "0")
cron_hour=$(get_env "CRON_HOUR" "0")
cron_time=$(get_env "CRON_TIME" "$cron_minute $cron_hour * * *")

tail -F /var/log/backup.log &

echo "Launching cron service..."
test_env=$(get_env "TEST_ENV" false)

echo "$cron_time backup >> /var/log/backup.log" > /etc/crontabs/root
exec crond -f -L /var/log/cron.log

if [ "$test_env" = false ]; then
echo "Launching cron service..."
tail -F /var/log/backup.log &
exec crond -f -L /var/log/cron.log
fi
Loading

0 comments on commit 7b0fded

Please sign in to comment.