Skip to content

Commit e4a17cd

Browse files
authored
Merge pull request #228 from scicco/master
RESTORE_OPTS env variable support added
2 parents a071f62 + 6658eca commit e4a17cd

File tree

8 files changed

+145
-15
lines changed

8 files changed

+145
-15
lines changed

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ test_cron:
2222
test_source_target:
2323
cd test && ./test_source_target.sh
2424

25-
test: test_dump test_cron test_source_target
25+
test_restore:
26+
cd test && ./test_restore.sh
27+
28+
test: test_dump test_restore test_cron test_source_target
2629

2730
.PHONY: clean-test-stop clean-test-remove clean-test
2831
clean-test-stop:

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,15 +308,17 @@ __You should consider the [use of `--env-file=`](https://docs.docker.com/engine/
308308
* `DB_NAMES`: name of database to restore to. Required if `SINGLE_DATABASE=true`, otherwise has no effect. Although the name is plural, it must contain exactly one database name.
309309
* `SINGLE_DATABASE`: If is set to `true`, `DB_NAMES` is required and mysql command will run with `--database=$DB_NAMES` flag. This avoids the need of `USE <database>;` statement, which is useful when restoring from a file saved with `SINGLE_DATABASE` set to `true`.
310310
* `DB_RESTORE_TARGET`: path to the actual restore file, which should be a compressed dump file. The target can be an absolute path, which should be volume mounted, an smb or S3 URL, similar to the target.
311+
* `RESTORE_OPTS`: A string of options to pass to `mysql` restore command, e.g. `--ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem` will run `mysql --ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem -h $DB_SERVER -P $DB_PORT $DBUSER $DBPASS $DBDATABASE`, default is empty ('')
311312
* `DB_DUMP_DEBUG`: if `true`, dump copious outputs to the container logs while restoring.
312313
* To use the S3 driver `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_DEFAULT_REGION` will need to be defined.
313314

314315

315316
Examples:
316317

317318
1. Restore from a local file: `docker run -e DB_SERVER=gotodb.example.com -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=/backup/db_backup_201509271627.gz -v /local/path:/backup databack/mysql-backup`
318-
2. Restore from an SMB file: `docker run -e DB_SERVER=gotodb.example.com -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=smb://smbserver/share1/backup/db_backup_201509271627.gz databack/mysql-backup`
319-
3. Restore from an S3 file: `docker run -e DB_SERVER=gotodb.example.com -e AWS_ACCESS_KEY_ID=awskeyid -e AWS_SECRET_ACCESS_KEY=secret -e AWS_DEFAULT_REGION=eu-central-1 -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=s3://bucket/path/db_backup_201509271627.gz databack/mysql-backup`
319+
2. Restore from a local file using ssl: `docker run -e DB_SERVER=gotodb.example.com -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=/backup/db_backup_201509271627.gz -e RESTORE_OPTS="--ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem" -v /local/path:/backup -v /local/certs:/certs databack/mysql-backup`
320+
3. Restore from an SMB file: `docker run -e DB_SERVER=gotodb.example.com -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=smb://smbserver/share1/backup/db_backup_201509271627.gz databack/mysql-backup`
321+
4. Restore from an S3 file: `docker run -e DB_SERVER=gotodb.example.com -e AWS_ACCESS_KEY_ID=awskeyid -e AWS_SECRET_ACCESS_KEY=secret -e AWS_DEFAULT_REGION=eu-central-1 -e DB_USER=user123 -e DB_PASS=pass123 -e DB_RESTORE_TARGET=s3://bucket/path/db_backup_201509271627.gz databack/mysql-backup`
320322

321323
### Restore when using docker-compose
322324
`docker-compose` automagically creates a network when started. `docker run` simply attaches to the bridge network. If you are trying to communicate with a mysql container started by docker-compose, you'll need to specify the network in your command arguments. You can use `docker network ls` to see what network is being used, or you can declare a network in your docker-compose.yml.

entrypoint

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ if [[ -n "$DB_RESTORE_TARGET" ]]; then
145145
rm -rf $workdir
146146
mkdir -p $workdir
147147
$UNCOMPRESS < $TMPRESTORE | tar -C $workdir -xvf -
148-
cat $workdir/* | mysql -h $DB_SERVER -P $DB_PORT $DBUSER $DBPASS $DBDATABASE
148+
RESTORE_OPTS=${RESTORE_OPTS:-}
149+
cat $workdir/* | mysql $RESTORE_OPTS -h $DB_SERVER -P $DB_PORT $DBUSER $DBPASS $DBDATABASE
149150
rm -rf $workdir
150151
/bin/rm -f $TMPRESTORE
151152
else

test/_functions.sh

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ BACKUP_IMAGE=mysqlbackup_backup_test:latest
1111
BACKUP_TESTER_IMAGE=mysqlbackup_backup_test_harness:latest
1212
SMB_IMAGE=mysqlbackup_smb_test:latest
1313
BACKUP_VOL=mysqlbackup-test
14+
CERTS_VOL=mysqlbackup-certs
15+
MYSQLDUMP_OPTS="--ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem"
16+
RESTORE_OPTS="--ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem"
1417
MYSQLUSER=user
1518
MYSQLPW=abcdefg
1619
MYSQL_IMAGE=mysql:8.0
@@ -36,7 +39,7 @@ function create_backup_file() {
3639
tmpdumpfile=backup.sql
3740
docker exec $mysql_cid mysqldump -hlocalhost --protocol=tcp -u$MYSQLUSER -p$MYSQLPW --compact --databases tester > $tmpdumpdir/$tmpdumpfile
3841
tar -C $tmpdumpdir -cvf - $tmpdumpfile | gzip > ${target}
39-
cat $target | docker run --label mysqltest --name mysqlbackup-data-source -i --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} save_dump
42+
cat $target | docker run --label mysqltest --name mysqlbackup-data-source -i --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e DEBUG=${DEBUG} -e MYSQLDUMP_OPTS="${MYSQLDUMP_OPTS}" ${BACKUP_TESTER_IMAGE} save_dump
4043
rm -rf $tmpdumpdir $target
4144
}
4245

@@ -107,6 +110,7 @@ function makevolume() {
107110
local EXISTING_VOLS=$(docker volume ls --filter label=mysqltest -q)
108111
[ -n "${EXISTING_VOLS}" ] && docker volume rm ${EXISTING_VOLS}
109112
docker volume create --label mysqltest $BACKUP_VOL
113+
docker volume create --label mysqltest $CERTS_VOL
110114
}
111115
function makesmb() {
112116
# build the service images we need
@@ -117,11 +121,13 @@ function start_service_containers() {
117121
# run the test images we need
118122
[[ "$DEBUG" != "0" ]] && echo "Running smb, s3 and mysql containers"
119123
smb_cid=$(docker run --label mysqltest --net mysqltest --name=smb -d -p 445:445 -v ${BACKUP_VOL}:/share/backups -t ${SMB_IMAGE})
120-
mysql_cid=$(docker run --label mysqltest --net mysqltest --name mysql -d -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=tester -e MYSQL_USER=$MYSQLUSER -e MYSQL_PASSWORD=$MYSQLPW $MYSQL_IMAGE)
124+
mysql_cid=$(docker run --label mysqltest --net mysqltest --name mysql -d -v ${CERTS_VOL}:/certs -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=tester -e MYSQL_USER=$MYSQLUSER -e MYSQL_PASSWORD=$MYSQLPW $MYSQL_IMAGE --require-secure-transport)
125+
docker exec -i mysql bash -c "until [[ -f /var/lib/mysql/client-cert.pem ]]; do sleep 5; done; cp /var/lib/mysql/client-cert.pem /certs"
126+
docker exec -i mysql bash -c "until [[ -f /var/lib/mysql/client-key.pem ]]; do sleep 5; done; cp /var/lib/mysql/client-key.pem /certs"
121127
# need process privilege, set it up after waiting for the mysql to be ready
122-
s3_cid=$(docker run --label mysqltest --net mysqltest --name s3 -d -v ${BACKUP_VOL}:/fakes3_root/s3/mybucket lphoward/fake-s3 -r /fakes3_root -p 443)
128+
s3_cid=$(docker run --label mysqltest --net mysqltest --name s3 -d -v ${CERTS_VOL}:/certs -v ${BACKUP_VOL}:/fakes3_root/s3/mybucket lphoward/fake-s3 -r /fakes3_root -p 443)
123129
# Allow up to 20 seconds for the database to be ready
124-
db_connect="docker exec -i $mysql_cid mysql -u$MYSQLUSER -p$MYSQLPW --protocol=tcp -h127.0.0.1 --wait --connect_timeout=20 tester"
130+
db_connect="docker exec -i $mysql_cid mysql ${MYSQLDUMP_OPTS} -u$MYSQLUSER -p$MYSQLPW --protocol=tcp -h127.0.0.1 --wait --connect_timeout=20 tester"
125131
retry_count=0
126132
retryMax=20
127133
retrySleep=1
@@ -140,7 +146,7 @@ function start_service_containers() {
140146
return 1
141147
fi
142148
# ensure the user has the right privileges
143-
docker exec -i mysql mysql -uroot -proot --protocol=tcp -h127.0.0.1 -e "grant process on *.* to user;"
149+
docker exec -i mysql mysql ${MYSQLDUMP_OPTS} -uroot -proot --protocol=tcp -h127.0.0.1 -e "grant process on *.* to user;"
144150
}
145151
function rm_service_containers() {
146152
local smb_cid="$1"
@@ -176,6 +182,7 @@ function rm_network() {
176182
function rm_volume() {
177183
[[ "$DEBUG" != "0" ]] && echo "Removing docker volume"
178184
docker volume rm ${BACKUP_VOL}
185+
docker volume rm ${CERTS_VOL}
179186
}
180187
function run_dump_test() {
181188
local t=$1
@@ -205,7 +212,14 @@ function run_dump_test() {
205212
# change our target
206213
# ensure that we remove leading whitespace from targets
207214
allTargets=$(echo $allTargets | awk '{$1=$1;print}')
208-
cid=$(docker container create --label mysqltest --name mysqlbackup-${sequence} --net mysqltest -v ${BACKUP_VOL}:/backups --link ${s3_cid}:mybucket.s3.amazonaws.com ${DBDEBUG} -e DB_USER=$MYSQLUSER -e DB_PASS=$MYSQLPW -e DB_DUMP_FREQ=60 -e DB_DUMP_BEGIN=+0 -e DB_DUMP_TARGET="${allTargets}" -e AWS_ACCESS_KEY_ID=abcdefg -e AWS_SECRET_ACCESS_KEY=1234567 -e AWS_ENDPOINT_URL=http://s3:443/ -e DB_SERVER=mysql -e MYSQLDUMP_OPTS="--compact" ${BACKUP_IMAGE})
215+
if [[ "$sequence" -lt 1 ]]; then
216+
# on first run we need to fix /certs/*.pem permissions and assign it to appuser otherwise mysqldump command fails
217+
c_with_wrong_permission=$(docker container create --label mysqltest --name mysqltest-fix-certs-permissions -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs ${DBDEBUG} -e DB_USER=$MYSQLUSER -e DB_PASS=$MYSQLPW -e DB_DUMP_FREQ=60 -e DB_DUMP_BEGIN=+0 -e DB_DUMP_TARGET="${allTargets}" -e DB_SERVER=mysql -e MYSQLDUMP_OPTS="--compact ${MYSQLDUMP_OPTS}" ${BACKUP_IMAGE})
218+
docker container start ${c_with_wrong_permission} >/dev/null
219+
docker exec -u 0 ${c_with_wrong_permission} chown -R appuser /certs>/dev/null
220+
rm_containers $c_with_wrong_permission
221+
fi
222+
cid=$(docker container create --label mysqltest --name mysqlbackup-${sequence} --net mysqltest -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs --link ${s3_cid}:mybucket.s3.amazonaws.com ${DBDEBUG} -e DB_USER=$MYSQLUSER -e DB_PASS=$MYSQLPW -e DB_DUMP_FREQ=60 -e DB_DUMP_BEGIN=+0 -e DB_DUMP_TARGET="${allTargets}" -e AWS_ACCESS_KEY_ID=abcdefg -e AWS_SECRET_ACCESS_KEY=1234567 -e AWS_ENDPOINT_URL=http://s3:443/ -e DB_SERVER=mysql -e MYSQLDUMP_OPTS="--compact ${MYSQLDUMP_OPTS}" ${BACKUP_IMAGE})
209223
linkfile=/tmp/link.$$
210224
ln -s /backups/$sequence ${linkfile}
211225
docker cp ${linkfile} $cid:/scripts.d

test/ctr/entrypoint_test.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,10 @@ save_dump)
218218
cron)
219219
/cron_test.sh
220220
;;
221+
*)
222+
echo "unrecognized command: ${cmd}"
223+
exit 2
224+
;;
221225
esac
222226

223227

test/test_dump.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ seq=0
5555
[[ "$DEBUG" != "0" ]] && echo "Populating volume for each target"
5656
for ((i=0; i< ${#targets[@]}; i++)); do
5757
t=${targets[$i]}
58-
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} populate "$t" $seq
59-
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} prepare_pre_post "$t" $seq
58+
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e MYSQLDUMP_OPTS="${MYSQLDUMP_OPTS}" -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} populate "$t" $seq
59+
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e MYSQLDUMP_OPTS="${MYSQLDUMP_OPTS}" -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} prepare_pre_post "$t" $seq
6060
# increment our counter
6161
((seq++)) || true
6262
done
@@ -89,7 +89,7 @@ declare -a pass
8989
seq=0
9090
for ((i=0; i< ${#targets[@]}; i++)); do
9191
t=${targets[$i]}
92-
results=$(docker run --label mysqltest --name mysqlbackup-data-check --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} check "$t" $seq)
92+
results=$(docker run --label mysqltest --name mysqlbackup-data-check --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e MYSQLDUMP_OPTS="${MYSQLDUMP_OPTS}" -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} check "$t" $seq)
9393
# save the passes and fails
9494
# | cat - so that it doesn't return an error on no-match
9595
passes=$(echo "$results" | grep '^PASS:' | cat)

test/test_restore.sh

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#!/bin/bash
2+
set -ex
3+
4+
source ./_functions.sh
5+
6+
#cleanup all temporary folders
7+
mkdir -p backups
8+
rm -rf backups/*.tgz
9+
mkdir -p certs
10+
rm -rf certs/*.pem
11+
12+
makevolume
13+
14+
makenetwork
15+
16+
make_test_images
17+
18+
makesmb
19+
20+
start_service_containers
21+
22+
#temporary use original certificate location
23+
MYSQLDUMP_OPTS="--ssl-cert /var/lib/mysql/client-cert.pem --ssl-key /var/lib/mysql/client-key.pem"
24+
db_connect="docker exec -i $mysql_cid mysql ${MYSQLDUMP_OPTS} -u$MYSQLUSER -p$MYSQLPW --protocol=tcp -h127.0.0.1 --wait --connect_timeout=20 tester"
25+
$db_connect -e 'select 1;'
26+
echo 'use tester; create table t1 (id INT, name VARCHAR(20)); INSERT INTO t1 (id,name) VALUES (1, "John"), (2, "Jill"), (3, "Sam"), (4, "Sarah");' | $db_connect
27+
28+
29+
#fix /certs/*.pem files permissions
30+
c_with_wrong_permission=$(docker container create --label mysqltest --net mysqltest --name mysqltest-fix-certs-permissions -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs ${DBDEBUG} -e DB_USER=$MYSQLUSER -e DB_PASS=$MYSQLPW -e DB_DUMP_FREQ=60 -e DB_DUMP_BEGIN=+0 -e DB_SERVER=mysql -e MYSQLDUMP_OPTS="--compact ${MYSQLDUMP_OPTS}" ${BACKUP_IMAGE})
31+
docker container start ${c_with_wrong_permission} >/dev/null
32+
docker exec -u 0 ${c_with_wrong_permission} chown -R appuser /certs>/dev/null
33+
rm_containers $c_with_wrong_permission
34+
35+
# now we can reset to /certs
36+
MYSQLDUMP_OPTS="--ssl-cert /certs/client-cert.pem --ssl-key /certs/client-key.pem"
37+
38+
# copy our certificates locally
39+
docker cp mysql:/certs/client-key.pem $PWD/certs/client-key.pem
40+
docker cp mysql:/certs/client-cert.pem $PWD/certs/client-cert.pem
41+
42+
cid_dump=$( \
43+
docker container create \
44+
-u 0 \
45+
--label mysqltest \
46+
--name mysqldump \
47+
--net mysqltest \
48+
-v $PWD/backups/:/backups \
49+
-v $PWD/certs/client-key.pem:/certs/client-key.pem \
50+
-v $PWD/certs/client-cert.pem:/certs/client-cert.pem \
51+
-e DB_DUMP_TARGET=/backups \
52+
-e DB_DUMP_DEBUG=0 \
53+
-e DB_SERVER=mysql \
54+
-e DB_USER=$MYSQLUSER \
55+
-e DB_PASS=$MYSQLPW \
56+
-e RUN_ONCE=1 \
57+
-e MYSQLDUMP_OPTS="${MYSQLDUMP_OPTS}" \
58+
${BACKUP_IMAGE})
59+
docker container start $cid_dump
60+
61+
sleepwait 5
62+
63+
# remove tester database so we can be sure that restore actually works restoring tester database
64+
docker exec -i mysql mysql ${MYSQLDUMP_OPTS} -uroot -proot --protocol=tcp -h127.0.0.1 -e "drop database tester;"
65+
66+
ls -l $PWD/backups
67+
backup_name=$(ls $PWD/backups)
68+
if [[ "$backup_name" == "" ]]; then
69+
echo "***********************************"
70+
echo "backup file was not created, see container log output:"
71+
docker logs $cid_dump
72+
echo "***********************************"
73+
exit 1
74+
fi
75+
76+
cid_restore=$( \
77+
docker container create \
78+
-u 0 \
79+
--label mysqltest \
80+
--name mysqlrestore \
81+
--net mysqltest \
82+
-v $PWD/certs/client-key.pem:/certs/client-key.pem \
83+
-v $PWD/certs/client-cert.pem:/certs/client-cert.pem \
84+
-v $PWD/backups/:/backups \
85+
-e DB_DUMP_DEBUG=0 \
86+
-e DB_SERVER=mysql \
87+
-e DB_USER=$MYSQLUSER \
88+
-e DB_PASS=$MYSQLPW \
89+
-e DB_RESTORE_TARGET=/backups/${backup_name} \
90+
-e RESTORE_OPTS="${RESTORE_OPTS}" \
91+
${BACKUP_IMAGE})
92+
docker container start $cid_restore
93+
94+
sleepwait 5
95+
96+
db_command="docker exec -i $mysql_cid mysql ${MYSQLDUMP_OPTS} -u$MYSQLUSER -p$MYSQLPW --protocol=tcp -h127.0.0.1 tester"
97+
echo 'use tester; select * from t1' | $db_command
98+
99+
rm_service_containers $smb_cid $mysql_cid $s3_cid
100+
docker rm $cid_dump $cid_restore
101+
102+
rm -rf backups/*.tgz
103+
rmdir backups
104+
rm -rf certs/*.pem
105+
rmdir certs
106+

test/test_source_target.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ seq=0
5757
[[ "$DEBUG" != "0" ]] && echo "Populating volume for each target"
5858
for ((i=0; i< ${#targets[@]}; i++)); do
5959
t=${targets[$i]}
60-
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} populate "$t" $seq
60+
docker run --label mysqltest --name mysqlbackup-data-populate --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} populate "$t" $seq
6161
# increment our counter
6262
((seq++)) || true
6363
done
@@ -92,7 +92,7 @@ declare -a pass
9292
seq=0
9393
for ((i=0; i< ${#targets[@]}; i++)); do
9494
t=${targets[$i]}
95-
results=$(docker run --label mysqltest --name mysqlbackup-data-check --rm -v ${BACKUP_VOL}:/backups -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} check_source_target "$t" $seq $(get_default_source))
95+
results=$(docker run --label mysqltest --name mysqlbackup-data-check --rm -v ${BACKUP_VOL}:/backups -v ${CERTS_VOL}:/certs -e DEBUG=${DEBUG} ${BACKUP_TESTER_IMAGE} check_source_target "$t" $seq $(get_default_source))
9696
# save the passes and fails
9797
# | cat - so that it doesn't return an error on no-match
9898
passes=$(echo "$results" | grep '^PASS:' | cat)

0 commit comments

Comments
 (0)