Skip to content

Commit 0ce19a4

Browse files
authored
Fall back to default behavior when deployed to a worker node (#593)
* Add test demonstrating failure when running on worker node * Consider manager status when checking for swarm related features * Update documentation
1 parent 5291c5c commit 0ce19a4

File tree

9 files changed

+131
-7
lines changed

9 files changed

+131
-7
lines changed

cmd/backup/stop_restart.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func isSwarm(c interface {
8989
if err != nil {
9090
return false, errwrap.Wrap(err, "error getting docker info")
9191
}
92-
return info.Swarm.LocalNodeState != "" && info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive, nil
92+
return info.Swarm.LocalNodeState != "" && info.Swarm.LocalNodeState != swarm.LocalNodeStateInactive && info.Swarm.ControlAvailable, nil
9393
}
9494

9595
// stopContainersAndServices stops all Docker containers that are marked as to being

cmd/backup/stop_restart_test.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,26 @@ func TestIsSwarm(t *testing.T) {
3030
&mockInfoClient{
3131
result: system.Info{
3232
Swarm: swarm.Info{
33-
LocalNodeState: swarm.LocalNodeStateActive,
33+
LocalNodeState: swarm.LocalNodeStateActive,
34+
ControlAvailable: true,
3435
},
3536
},
3637
},
3738
true,
3839
false,
3940
},
41+
{
42+
"worker",
43+
&mockInfoClient{
44+
result: system.Info{
45+
Swarm: swarm.Info{
46+
LocalNodeState: swarm.LocalNodeStateActive,
47+
},
48+
},
49+
},
50+
false,
51+
false,
52+
},
4053
{
4154
"compose",
4255
&mockInfoClient{

docs/how-tos/use-with-docker-swarm.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ nav_order: 13
88
# Use with Docker Swarm
99

1010
{: .note }
11-
The mechanisms described in this page __do only apply when Docker is running in [Swarm mode][swarm]__.
11+
The mechanisms described in this page __do only apply when Docker is running in [Swarm mode][swarm]__ and __when placing the `docker-volume-backup` container on a manager node__.
12+
Containers that are placed on worker nodes function as if the Docker engine is not running in Swarm mode, i.e. there is no access to services and there is no way to interact with resources that are running on different host nodes.
1213

1314
[swarm]: https://docs.docker.com/engine/swarm/
1415

test/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,11 @@ A test case can signal it wants to run in swarm mode by placing an empty `.swarm
6767
In case the swarm setup should be compose of multiple nodes, a `.multinode` file can be used.
6868

6969
A multinode setup will contain one manager (`manager`) and two worker nodes (`worker1` and `worker2`).
70+
71+
If a test is expected to run in the context of a node other than the `manager`, the hostname can be put in the `.multinode` file.
72+
73+
> [!IMPORTANT]
74+
> When running against a multi-node setup and targeting a non-manager node, the test script will automatically deploy a stack named `test_stack` based on the compose file in the test directory.
75+
> This is required because the non-manager node cannot deploy the stack itself from within the test script.
76+
> This also means, you cannot mount local directories created in your test script, as the containers are already created when the script runs.
77+
> You can work around this limitation by creating named volumes and then `docker cp`ing the contents your test needs to inspect.

test/docker-compose.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
services:
22
manager: &node
3+
hostname: manager
34
privileged: true
45
image: offen/docker-volume-backup:test-sandbox
56
healthcheck:
@@ -8,17 +9,19 @@ services:
89
timeout: 5s
910
retries: 50
1011
volumes:
11-
- $SOURCE:/code
12-
- $TARBALL:/cache/image.tar.gz
12+
- ./:/code
13+
- ${TARBALL:-.}:/cache/image.tar.gz
1314
- docker_volume_backup_test_sandbox_image:/var/lib/docker/image
1415
- docker_volume_backup_test_sandbox_overlay2:/var/lib/docker/overlay2
1516

1617
worker1:
1718
<<: *node
19+
hostname: worker1
1820
profiles:
1921
- multinode
2022
worker2:
2123
<<: *node
24+
hostname: worker2
2225
profiles:
2326
- multinode
2427

test/test.sh

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ for dir in $(find $find_args | sort); do
4545
fi
4646

4747
docker compose --profile $compose_profile up -d --wait
48+
test_context=manager
49+
if [ -f "${dir}/.multinode" ] && [ -s "${dir}/.multinode" ]; then
50+
test_context=$(cat $dir/.multinode)
51+
echo "Running tests on $test_context instead of manager"
52+
fi
53+
docker compose exec $test_context /bin/sh -c "docker load -i /cache/image.tar.gz"
4854

4955
if [ -f "${dir}/.swarm" ]; then
5056
docker compose exec manager docker swarm init
@@ -54,10 +60,13 @@ for dir in $(find $find_args | sort); do
5460
token=$(docker compose exec manager docker swarm join-token -q worker)
5561
docker compose exec worker1 docker swarm join --token $token $manager_ip:2377
5662
docker compose exec worker2 docker swarm join --token $token $manager_ip:2377
63+
64+
if [ "$test_context" != "manager" ]; then
65+
docker compose exec -w "/code/$dir" manager docker stack deploy --compose-file="docker-compose.yml" test_stack
66+
fi
5767
fi
5868

59-
docker compose exec manager /bin/sh -c "docker load -i /cache/image.tar.gz"
60-
docker compose exec -e TEST_VERSION=$IMAGE_TAG manager /bin/sh -c "/code/test/$test"
69+
docker compose exec -e TEST_VERSION=$IMAGE_TAG $test_context /bin/sh -c "/code/$test"
6170

6271
docker compose --profile $compose_profile down
6372
echo ""

test/worker-node/.multinode

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
worker1
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
services:
2+
database:
3+
image: mariadb:10.7
4+
deploy:
5+
restart_policy:
6+
condition: on-failure
7+
placement:
8+
constraints:
9+
- node.hostname == worker1
10+
environment:
11+
MARIADB_ROOT_PASSWORD: test
12+
MARIADB_DATABASE: backup
13+
labels:
14+
- docker-volume-backup.archive-pre=/bin/sh -c 'mysqldump -ptest --all-databases > /tmp/volume/dump.sql'
15+
- docker-volume-backup.copy-post=/bin/sh -c 'echo "post" > /tmp/volume/post.txt'
16+
- docker-volume-backup.stop-during-backup=true
17+
volumes:
18+
- app_data:/tmp/volume
19+
20+
other_database:
21+
image: mariadb:10.7
22+
deploy:
23+
placement:
24+
constraints:
25+
- node.hostname == manager
26+
restart_policy:
27+
condition: on-failure
28+
environment:
29+
MARIADB_ROOT_PASSWORD: test
30+
MARIADB_DATABASE: backup
31+
labels:
32+
- docker-volume-backup.archive-pre=touch /tmp/volume/not-relevant.txt
33+
- docker-volume-backup.exec-label=not-relevant
34+
volumes:
35+
- app_data:/tmp/volume
36+
37+
backup:
38+
image: offen/docker-volume-backup:${TEST_VERSION:-canary}
39+
deploy:
40+
restart_policy:
41+
condition: on-failure
42+
placement:
43+
constraints:
44+
- node.hostname == worker1
45+
environment:
46+
BACKUP_FILENAME: test.tar.gz
47+
BACKUP_CRON_EXPRESSION: 0 0 5 31 2 ?
48+
EXEC_FORWARD_OUTPUT: "true"
49+
volumes:
50+
- backup_archive:/archive
51+
- app_data:/backup/data:ro
52+
- /var/run/docker.sock:/var/run/docker.sock:ro
53+
54+
volumes:
55+
app_data:
56+
backup_archive:

test/worker-node/run.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/sh
2+
3+
set -e
4+
5+
cd $(dirname $0)
6+
. ../util.sh
7+
current_test=$(basename $(pwd))
8+
9+
export TMP_DIR=$(mktemp -d)
10+
export LOCAL_DIR=$(mktemp -d)
11+
12+
while [ -z $(docker ps -q -f name=backup) ]; do
13+
info "Backup container not ready yet. Retrying."
14+
sleep 1
15+
done
16+
17+
sleep 20
18+
19+
docker exec $(docker ps -q -f name=backup) backup
20+
21+
mkdir -p /archive
22+
docker cp $(docker ps -q -f name=backup):/archive $LOCAL_DIR
23+
24+
tar -xvf "$LOCAL_DIR/archive/test.tar.gz" -C $TMP_DIR
25+
if [ ! -f "$TMP_DIR/backup/data/dump.sql" ]; then
26+
fail "Could not find file written by pre command."
27+
fi
28+
pass "Found expected file."
29+
30+
if [ -f "$TMP_DIR/backup/data/post.txt" ]; then
31+
fail "File created in post command was present in backup."
32+
fi
33+
pass "Did not find unexpected file."

0 commit comments

Comments
 (0)