Skip to content

Commit 402356f

Browse files
committed
[gantry] Do not change replicas after updating.
1 parent 2c13597 commit 402356f

8 files changed

Lines changed: 202 additions & 29 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ You can configure the most behaviors of *Gantry* via environment variables.
9292
| GANTRY_ROLLBACK_OPTIONS | | [Options](https://docs.docker.com/engine/reference/commandline/service_update/#options) added to the `docker service update --rollback` command for all services. You can apply a different value to a particular service via [labels](#labels). |
9393
| GANTRY_UPDATE_JOBS | false | Set to `true` to update `replicated-job` or `global-job`. Set to `false` to disable updating jobs. *Gantry* adds additional options to `docker service update` when there is [no running tasks](docs/faq.md#how-to-update-services-with-no-running-tasks). You can apply a different value to a particular service via [labels](#labels). |
9494
| GANTRY_UPDATE_NUM_WORKERS | 1 | The maximum number of updates that can run in parallel. |
95-
| GANTRY_UPDATE_OPTIONS | | [Options](https://docs.docker.com/engine/reference/commandline/service_update/#options) added to the `docker service update` command for all services. You can apply a different value to a particular service via [labels](#labels). |
95+
| GANTRY_UPDATE_OPTIONS | | [Options](https://docs.docker.com/engine/reference/commandline/service_update/#options) added to the `docker service update` command for all services. This also overrides any automatically added options. You can apply a different value to a particular service via [labels](#labels). |
9696
| GANTRY_UPDATE_TIMEOUT_SECONDS | 0 | Error out if updating of a single service takes longer than the given time. Set to `0` to disable timeout. You can apply a different value to a particular service via [labels](#labels). |
9797

9898
### After updating

docs/faq.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ You can start *Gantry* as a docker swarm service and use [`swarm-cronjob`](https
2626

2727
## How to update services with no running tasks?
2828

29-
As discussed in [docker/cli/issues/627](https://github.com/docker/cli/issues/627), the CLI will hang when running `docker service update` on a service with no running tasks. We must add `--detach=true` option to the `docker service update`.
29+
As discussed in [docker/cli/issues/627](https://github.com/docker/cli/issues/627), the Docker CLI hangs when `docker service update` is running on a service with no running tasks, requiring the option `--detach=true` to be added.
3030

31-
*Gantry* will check whether there are running tasks in a service. If there is no running task, *Gantry* automatically adds the option `--detach=true`. In addition to the detach option, *Gantry* also adds `--replicas=0` for services in replicated mode. You don't need to add these options manually.
31+
*Gantry* checks whether a service has running tasks. When none are found, it automatically adds the option `--detach=true`.
32+
33+
To keep replicas at zero after an update, which prevents tasks from starting, add the [label](../README.md#labels) `gantry.update.options=--replicas=0` to the service. Note that `--replicas=0` only applies to services in replicated mode, it causes errors in global mode. See the [example](../examples/cronjob).
3234

3335
## Which `GANTRY_MANIFEST_CMD` to use?
3436

examples/cronjob/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
Use [*swarm-cronjob*](https://github.com/crazy-max/swarm-cronjob) to launch [*Gantry*](https://github.com/shizunge/gantry) at a specific time.
44

5-
In this example, we only want to run *Gantry* at the specific time, therefore we set `replicas` to `0` on the *Gantry* service to avoid running it as soon as the service is deployed. We also need to set `restart_policy.condition` to `none` to prevent *Gantry* from restarting automatically after a cronjob.
5+
In this example, we only want to run *Gantry* at the specific time, therefore we set `replicas` to `0` on the *Gantry* service to avoid running it as soon as the service is deployed. We also need to set `restart_policy.condition` to `none` to prevent *Gantry* from restarting automatically after a cronjob. Refer to the [*swarm-cronjob* document](https://crazymax.dev/swarm-cronjob/) for more information.
66

7-
Refer to the [*swarm-cronjob* document](https://crazymax.dev/swarm-cronjob/) for more information.
7+
*service_to_be_updated* is another service managed by the *swarm-cronjob* and updated by *Gantry*. The label `gantry.update.options=--replicas=0` is set on the service so that only *swarm-cronjob* starts it. Updating the image does not start the service.

examples/cronjob/docker-compose.yml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ services:
1616
restart_policy:
1717
condition: none
1818
labels:
19-
- swarm.cronjob.enable=true
20-
- swarm.cronjob.schedule=45 23 0 * * *
21-
- swarm.cronjob.skip-running=true
19+
- "swarm.cronjob.enable=true"
20+
- "swarm.cronjob.schedule=45 23 0 * * *"
21+
- "swarm.cronjob.skip-running=true"
2222

2323
cronjob:
2424
image: crazymax/swarm-cronjob:latest
@@ -28,3 +28,25 @@ services:
2828
placement:
2929
constraints:
3030
- node.role==manager
31+
32+
# Another service to be updated.
33+
service_to_be_updated:
34+
image: docker
35+
command: ["docker", "system", "prune", "-f"]
36+
volumes:
37+
- "/var/run/docker.sock:/var/run/docker.sock"
38+
deploy:
39+
replicas: 0
40+
placement:
41+
constraints:
42+
- node.role==manager
43+
restart_policy:
44+
condition: none
45+
labels:
46+
- "swarm.cronjob.enable=true"
47+
- "swarm.cronjob.schedule=0 */5 * * * *"
48+
- "swarm.cronjob.skip-running=false"
49+
# Add "gantry.update.options=--replicas=0" to prevent tasks from starting after the update.
50+
# Start the tasks via cronjob only.
51+
# "--replicas=0" only applies to services in replicated mode, it cannot be used with services in global mode.
52+
- "gantry.update.options=--replicas=0"

src/lib-gantry.sh

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,22 +1013,20 @@ _get_service_update_additional_options() {
10131013
local SERVICE_NAME="${1}"
10141014
local IMAGE_UPDATE_TO="${2}"
10151015
local AUTH_CONFIG="${3}"
1016-
local NUM_RUNS=
1017-
NUM_RUNS=$(_get_number_of_running_tasks "${SERVICE_NAME}")
1018-
! is_number "${NUM_RUNS}" && log WARN "NUM_RUNS \"${NUM_RUNS}\" is not a number." && return 1
1016+
local RETURN_VALUE=0
10191017
local OPTIONS=
10201018
local SPACE=
1021-
if [ "${NUM_RUNS}" = "0" ]; then
1022-
# Add "--detach=true" when there is no running tasks.
1023-
# https://github.com/docker/cli/issues/627
1024-
OPTIONS="${OPTIONS}${SPACE}--detach=true"
1025-
SPACE=" "
1026-
local MODE=
1027-
# Do not start a new task. Only works for replicated, not global.
1028-
if MODE=$(_service_is_replicated "${SERVICE_NAME}"); then
1029-
OPTIONS="${OPTIONS}${SPACE}--replicas=0"
1019+
local NUM_RUNS=
1020+
NUM_RUNS=$(_get_number_of_running_tasks "${SERVICE_NAME}")
1021+
if is_number "${NUM_RUNS}"; then
1022+
if [ "${NUM_RUNS}" = "0" ]; then
1023+
# Add "--detach=true" when there is no running tasks.
1024+
# https://github.com/docker/cli/issues/627
1025+
OPTIONS="${OPTIONS}${SPACE}--detach=true"
10301026
SPACE=" "
10311027
fi
1028+
else
1029+
log WARN "NUM_RUNS \"${NUM_RUNS}\" is not a number." && RETURN_VALUE=1
10321030
fi
10331031
# Add `--with-registry-auth` if needed.
10341032
local WITH_REGISTRY_AUTH=
@@ -1048,6 +1046,31 @@ _get_service_update_additional_options() {
10481046
SPACE=" "
10491047
fi
10501048
echo "${OPTIONS}"
1049+
return "${RETURN_VALUE}"
1050+
}
1051+
1052+
_warn_no_running_task() {
1053+
local SERVICE_NAME="${1}"
1054+
local OPTIONS="${2}"
1055+
local NUM_RUNS=
1056+
NUM_RUNS=$(_get_number_of_running_tasks "${SERVICE_NAME}")
1057+
! is_number "${NUM_RUNS}" && log WARN "NUM_RUNS \"${NUM_RUNS}\" is not a number." && return 1
1058+
if [ "${NUM_RUNS}" != "0" ]; then
1059+
return 0
1060+
fi
1061+
# Warning for replicated, not global, as "--replicas" only works on replicated, not global.
1062+
local MODE=
1063+
if MODE=$(! _service_is_replicated "${SERVICE_NAME}"); then
1064+
return 0
1065+
fi
1066+
# Now, the number of running task is 0, the mode is replicated.
1067+
# If "--replicas=" is already part of the option, no warning.
1068+
if [ -n "${OPTIONS}" ] && echo "${OPTIONS}" | grep_q "--replicas="; then
1069+
return 0
1070+
fi
1071+
local MSG="Service ${SERVICE_NAME} is in ${MODE} mode and has no running task before updating."
1072+
MSG="${MSG} Adding label \"gantry.update.options=--replicas=0\" to the service to prevent tasks from starting after the update."
1073+
log WARN "${MSG}"
10511074
}
10521075

10531076
_get_service_rollback_additional_options() {
@@ -1132,6 +1155,7 @@ _update_single_service() {
11321155
[ -n "${AUTH_CONFIG}" ] && log INFO "Adding options \"${AUTH_CONFIG}\" to the command ${CMD_STRING} for ${SERVICE_NAME}."
11331156
[ -n "${AUTOMATIC_OPTIONS}" ] && log INFO "Adding options \"${AUTOMATIC_OPTIONS}\" automatically to the command ${CMD_STRING} for ${SERVICE_NAME}."
11341157
[ -n "${UPDATE_OPTIONS}" ] && log INFO "Adding options \"${UPDATE_OPTIONS}\" specified by user to the command ${CMD_STRING} for ${SERVICE_NAME}."
1158+
_warn_no_running_task "${SERVICE_NAME}" "${AUTOMATIC_OPTIONS} ${UPDATE_OPTIONS}"
11351159
local TIMEOUT_COMMAND=
11361160
TIMEOUT_COMMAND=$(_get_timeout_command "${SERVICE_NAME}") || return 1
11371161
local SPACE_T=

tests/gantry_login_spec.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ Describe 'login'
7979
The stderr should satisfy spec_expect_message "${NUM_SERVICES_UPDATING}"
8080
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*${SERVICE_NAME}"
8181
# 2 "--with-registry-auth" for SERVICE_NAME. One is automatically added. The other is from user.
82-
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*automatically.*${SERVICE_NAME}"
83-
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*specified by user.*${SERVICE_NAME}"
82+
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${AUTOMATICALLY}.*${SERVICE_NAME}"
83+
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SPECIFIED_BY_USER}.*${SERVICE_NAME}"
8484
The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_IMAGE_DIGEST_WARNING}.*${SERVICE_NAME}"
8585
The stderr should satisfy spec_expect_no_message "${DOES_NOT_HAVE_A_DIGEST}"
8686
The stderr should satisfy spec_expect_message "${UPDATED}.*${SERVICE_NAME}"

0 commit comments

Comments
 (0)