Skip to content

Commit

Permalink
[docs][tests] clarify documents about authentication and filter. Test…
Browse files Browse the repository at this point in the history
… --no-resolve-image for local images.
  • Loading branch information
shizunge committed Dec 4, 2024
1 parent a08caa0 commit e68255b
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 23 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ You can configure the most behaviors of *Gantry* via environment variables.
| Environment Variable | Default | Description |
|-----------------------|---------|-------------|
| GANTRY_SERVICES_EXCLUDED | | A space separated list of services names that are excluded from updating. |
| GANTRY_SERVICES_EXCLUDED_FILTERS | `label=gantry.services.excluded=true` | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter), e.g. `label=project=project-a`. Exclude services which match the given filters from updating. The default value allows you to add label `gantry.services.excluded=true` to services to exclude them from updating. Note that multiple filters will be logical **ANDED**. |
| GANTRY_SERVICES_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter) that are accepted by `docker service ls --filter` to select services to update, e.g. `label=project=project-a`. Note that multiple filters will be logical **ANDED**. Also see [How to filters multiple services by name](docs/faq.md#how-to-filters-multiple-services-by-name). |
| GANTRY_SERVICES_EXCLUDED_FILTERS | `label=gantry.services.excluded=true` | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter), e.g. `label=project=project-a`. Exclude services which match the given filters from updating. The default value allows you to add label `gantry.services.excluded=true` to services to exclude them from updating. Note that multiple filters will be logical **ANDED**. An empty string means no filters, as a result *Gantry* will not exclude any services. |
| GANTRY_SERVICES_FILTERS | | A space separated list of [filters](https://docs.docker.com/engine/reference/commandline/service_ls/#filter) that are accepted by `docker service ls --filter` to select services to update, e.g. `label=project=project-a`. Note that multiple filters will be logical **ANDED**. An empty string means no filters, as a result *Gantry* will update all services. Also see [How to filters multiple services by name](docs/faq.md#how-to-filters-multiple-services-by-name). |

> NOTE: *Gantry* reads labels on the services not on the containers. The labels need to go to the [deploy](https://docs.docker.com/reference/compose-file/deploy/#labels) section, if you are using docker compose files to setup your services.
Expand Down
6 changes: 4 additions & 2 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ If the images of services are hosted on multiple registries that are required au

You can use `GANTRY_REGISTRY_CONFIGS_FILE` together with other authentication environment variables.

You can login to multiple registries using the same Docker configuration. However if you login to the same registry with different user names for different services, you should use different Docker configurations.
You can login to multiple registries using the same Docker configuration. For example you can set all the configurations to the default Docker configuration location `${HOME}/.docker/`. However if you login to the same registry with different user names for different services, you should use different Docker configurations.

> NOTE: If you use the image built from this repository and run the container using the default user `root`, the default Docker configuration location inside the container will be `/root/.docker/`.
### Select Docker configurations for services

Expand Down Expand Up @@ -60,4 +62,4 @@ You can use an existing Docker configuration from the host machines for authoriz
* Set the environment variable `DOCKER_CONFIG` on the *Gantry* container to specify the location of the Docker configuration folder inside the container. You can skip this step when you mount the folder to the default Docker configuration location `/root/.docker/` inside the container.
* Add `--with-registry-auth` to `GANTRY_UPDATE_OPTIONS` manually.

> Note that [`docker buildx imagetools inspect`](https://docs.docker.com/engine/reference/commandline/buildx_imagetools_inspect/) writes data to the Docker configuration folder `${DOCKER_CONFIG}/buildx`, which therefore needs to be writable. You can set `GANTRY_MANIFEST_CMD` to `manifest` to avoid writing to the Docker configuration folder.
> Note that [`docker buildx imagetools inspect`](https://docs.docker.com/engine/reference/commandline/buildx_imagetools_inspect/) writes data to the Docker configuration folder `${DOCKER_CONFIG}/buildx`, which therefore needs to be writable. If you want to use `buildx` and mount the configuration files read-only, you should just mount the file `config.json` and leave the folder writeable. If you have to mount the entire folder read-only, you can set `GANTRY_MANIFEST_CMD` to `manifest` to avoid writing to the Docker configuration folder. Also see [Which `GANTRY_MANIFEST_CMD` to use](../docs/faq.md#which-gantry_manifest_cmd-to-use).
4 changes: 3 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ It will not work by setting multiple filters with different names, because filte

To filter multiple services, you can set a label on each service then let *Gantry* filter on that label via `GANTRY_SERVICES_FILTERS`. Or you can run multiple *Gantry* instances.

Setting `GANTRY_SERVICES_FILTERS` to an empty string means no filters, as a result *Gantry* will update all services.

### How to run *Gantry* on a cron schedule?

You can start *Gantry* as a docker swarm service and use [`swarm-cronjob`](https://github.com/crazy-max/swarm-cronjob) to run it at a given time. When use `swarm-cronjob`, you need to set `GANTRY_SLEEP_SECONDS` to 0. See the [example](../examples/cronjob).
Expand All @@ -36,7 +38,7 @@ Before updating a service, *Gantry* will try to obtain the image's meta data to

`manifest` is kept for debugging purpose. The only known advantage of [`docker manifest inspect`](https://docs.docker.com/engine/reference/commandline/manifest_inspect/) is that it does not require the write permission to the [Docker configuration folder](https://docs.docker.com/engine/reference/commandline/cli/#configuration-files). If the Docker configuration folder is read-only, you need to use `manifest` to avoid permission deny errors.

`none` can be used to disable the image inspection. One use case of `none` is that you want to add `--force` to the `docker service update` command via `GANTRY_UPDATE_OPTIONS`, which updates the services even if there is nothing changed. Another use case is to debug image inspection. Please report the bug through a [GitHub issue](https://github.com/shizunge/gantry/issues), thanks.
`none` can be used to disable the image inspection. One use case of `none` is that you want to add `--no-resolve-image` to the `docker service update` command via `GANTRY_UPDATE_OPTIONS`, which allows you to update services using local images. Another use case is to add `--force` to the `docker service update` command via `GANTRY_UPDATE_OPTIONS`, which updates the services even if there is nothing changed. `none` can also be used to debug image inspection.

### Can *Gantry* report Docker Hub rate for non-anonymous account?

Expand Down
12 changes: 8 additions & 4 deletions src/lib-gantry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1037,13 +1037,17 @@ _update_single_service() {
_static_variable_add_unique_to_list STATIC_VAR_SERVICES_UPDATE_FAILED "${SERVICE_NAME}"
return 1
fi
local TIME_ELAPSED=
TIME_ELAPSED=$(time_elapsed_since "${START_TIME}")
local PREVIOUS_IMAGE=
local CURRENT_IMAGE=
PREVIOUS_IMAGE=$(_get_service_previous_image "${SERVICE_NAME}")
PREVIOUS_DIGEST=$(extract_string "${PREVIOUS_IMAGE}" '@' 2)
[ -z "${PREVIOUS_DIGEST}" ] && log DEBUG "After updating, the previous image ${PREVIOUS_IMAGE} of ${SERVICE_NAME} does not have a digest."
local CURRENT_IMAGE=
CURRENT_IMAGE=$(_get_service_image "${SERVICE_NAME}")
if [ "${PREVIOUS_IMAGE}" = "${CURRENT_IMAGE}" ]; then
CURRENT_DIGEST=$(extract_string "${CURRENT_IMAGE}" '@' 2)
[ -z "${CURRENT_DIGEST}" ] && log WARN "After updating, the current image ${CURRENT_IMAGE} of ${SERVICE_NAME} does not have a digest."
local TIME_ELAPSED=
TIME_ELAPSED=$(time_elapsed_since "${START_TIME}")
if [ -n "${CURRENT_DIGEST}" ] && [ "${PREVIOUS_DIGEST}" = "${CURRENT_DIGEST}" ]; then
log INFO "No updates for ${SERVICE_NAME}. Use ${TIME_ELAPSED}."
return 0
fi
Expand Down
10 changes: 5 additions & 5 deletions tests/gantry_manifest_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ Describe 'manifest-command'
SUITE_NAME="manifest-command"
BeforeAll "initialize_all_tests ${SUITE_NAME}"
AfterAll "finish_all_tests ${SUITE_NAME}"
Describe "test_MANIFEST_CMD_none"
TEST_NAME="test_MANIFEST_CMD_none"
Describe "test_MANIFEST_CMD_none_force"
TEST_NAME="test_MANIFEST_CMD_none_force"
IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}")
SERVICE_NAME=$(get_test_service_name "${TEST_NAME}")
test_MANIFEST_CMD_none() {
test_MANIFEST_CMD_none_force() {
local TEST_NAME="${1}"
local SERVICE_NAME="${2}"
reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}"
Expand All @@ -34,13 +34,13 @@ Describe 'manifest-command'
BeforeEach "common_setup_no_new_image ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}"
AfterEach "common_cleanup ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}"
It 'run_test'
When run test_MANIFEST_CMD_none "${TEST_NAME}" "${SERVICE_NAME}"
When run test_MANIFEST_CMD_none_force "${TEST_NAME}" "${SERVICE_NAME}"
The status should be success
The stdout should satisfy display_output
The stdout should satisfy spec_expect_no_message ".+"
The stderr should satisfy display_output
The stderr should satisfy spec_expect_no_message "${START_WITHOUT_A_SQUARE_BRACKET}"
# Do not set GANTRY_SERVICES_SELF, it should be set autoamtically
# Do not set GANTRY_SERVICES_SELF, it should be set automatically.
# If we are not testing gantry inside a container, it should failed to find the service name.
# To test gantry container, we need to use run_gantry_container.
The stderr should satisfy spec_expect_no_message ".*GANTRY_SERVICES_SELF.*"
Expand Down
60 changes: 60 additions & 0 deletions tests/gantry_service_single_spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ Describe 'service-single-service'
local TEST_NAME="${1}"
local IMAGE_WITH_TAG="${2}"
local SERVICE_NAME="${3}"
initialize_test "${TEST_NAME}"
# Start a service with image not available on the registry, the digest will not be available.
build_test_image "${IMAGE_WITH_TAG}"
start_replicated_service "${SERVICE_NAME}" "${IMAGE_WITH_TAG}" 2>&1
Expand Down Expand Up @@ -168,6 +169,65 @@ Describe 'service-single-service'
The stderr should satisfy spec_expect_no_message "${SCHEDULE_NEXT_UPDATE_AT}"
End
End
Describe "test_new_image_local"
TEST_NAME="test_new_image_local"
IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}")
SERVICE_NAME=$(get_test_service_name "${TEST_NAME}")
test_start() {
local TEST_NAME="${1}"
local IMAGE_WITH_TAG="${2}"
local SERVICE_NAME="${3}"
initialize_test "${TEST_NAME}"
# Start a service with image not available on the registry, the digest will not be available.
build_test_image "${IMAGE_WITH_TAG}"
start_replicated_service "${SERVICE_NAME}" "${IMAGE_WITH_TAG}" 2>&1
# Build a new local image. Do not push to registry to test `--no-resolve-image`.
build_test_image "${IMAGE_WITH_TAG}"
}
test_new_image_local() {
local TEST_NAME="${1}"
local SERVICE_NAME="${2}"
reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}"
export GANTRY_MANIFEST_CMD="none"
export GANTRY_UPDATE_OPTIONS="--no-resolve-image"
run_gantry "${SUITE_NAME}" "${TEST_NAME}"
}
BeforeEach "test_start ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}"
AfterEach "common_cleanup ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}"
It 'run_test'
When run test_new_image_local "${TEST_NAME}" "${SERVICE_NAME}"
The status should be success
The stdout should satisfy display_output
The stdout should satisfy spec_expect_no_message ".+"
The stderr should satisfy display_output
# Gantry is still trying to update the service.
# It will see a new local image.
The stderr should satisfy spec_expect_no_message "${SKIP_UPDATING}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_message "${PERFORM_UPDATING}.*${SERVICE_NAME}.*${PERFORM_REASON_MANIFEST_CMD_IS_NONE}"
The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_SKIP_JOBS}"
The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_INSPECT_FAILURE}"
The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_NO_NEW_IMAGES}"
The stderr should satisfy spec_expect_message "${NUM_SERVICES_UPDATING}"
The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--no-resolve-image.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_message "${UPDATED}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_no_message "${NO_UPDATES}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_no_message "${ROLLING_BACK}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_no_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}"
The stderr should satisfy spec_expect_no_message "${NO_SERVICES_UPDATED}"
The stderr should satisfy spec_expect_message "${NUM_SERVICES_UPDATED}"
The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATE_FAILED}"
The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_ERRORS}"
The stderr should satisfy spec_expect_no_message "${NO_IMAGES_TO_REMOVE}"
The stderr should satisfy spec_expect_message "${REMOVING_NUM_IMAGES}"
The stderr should satisfy spec_expect_no_message "${SKIP_REMOVING_IMAGES}"
# Failed to removing the old image due to it has no digest.
The stderr should satisfy spec_expect_no_message "${REMOVED_IMAGE}.*${IMAGE_WITH_TAG}"
The stderr should satisfy spec_expect_message "${FAILED_TO_REMOVE_IMAGE}.*${IMAGE_WITH_TAG}"
The stderr should satisfy spec_expect_message "${DONE_REMOVING_IMAGES}"
The stderr should satisfy spec_expect_no_message "${SCHEDULE_NEXT_UPDATE_AT}"
End
End
Describe "test_new_image_SERVICES_SELF"
TEST_NAME="test_new_image_SERVICES_SELF"
IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}")
Expand Down
34 changes: 25 additions & 9 deletions tests/spec_gantry_test_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export NUM_SERVICES_INSPECT_FAILURE="Failed to inspect [0-9]+ service\(s\)"
export NUM_SERVICES_NO_NEW_IMAGES="No new images for [0-9]+ service\(s\)"
export NUM_SERVICES_UPDATING="Updating [0-9]+ service\(s\)"
export NO_UPDATES="No updates"
export AFTER_UPDATING_CURRENT_IMAGE="After updating, the current image"
export AFTER_UPDATING_PREVIOUS_IMAGE="After updating, the previous image"
export DOES_NOT_HAVE_A_DIGEST="does not have a digest"
export UPDATED="Updated"
export ROLLING_BACK="Rolling back"
export FAILED_TO_ROLLBACK="Failed to roll back"
Expand Down Expand Up @@ -642,14 +645,27 @@ build_test_image() {
if [ -n "${EXIT_SECONDS}" ] && [ "${EXIT_SECONDS}" -gt "0" ]; then
EXIT_CMD="sleep ${EXIT_SECONDS};"
fi
local FILE=
FILE=$(make_test_temp_file)
echo "FROM $(_get_test_service_image)" > "${FILE}"
echo "ENTRYPOINT [\"sh\", \"-c\", \"echo $(unique_id); trap \\\"${EXIT_CMD}\\\" HUP INT TERM; ${TASK_CMD}\"]" >> "${FILE}"
pull_image_if_not_exist "$(_get_test_service_image)"
echo "Building image ${IMAGE_WITH_TAG} from ${FILE}"
docker build --quiet --tag "${IMAGE_WITH_TAG}" --file "${FILE}" .
rm "${FILE}"
local RETURN_VALUE=1
local TRIES=0
local MAX_RETRIES=60
while [ "${RETURN_VALUE}" != "0" ]; do
if [ "${TRIES}" -ge "${MAX_RETRIES}" ]; then
echo "build_test_image Reach MAX_RETRIES ${MAX_RETRIES}" >&2
return 1
fi
TRIES=$((TRIES+1))
local FILE=
FILE=$(make_test_temp_file)
echo "FROM $(_get_test_service_image)" > "${FILE}"
echo "ENTRYPOINT [\"sh\", \"-c\", \"echo $(unique_id); trap \\\"${EXIT_CMD}\\\" HUP INT TERM; ${TASK_CMD}\"]" >> "${FILE}"
pull_image_if_not_exist "$(_get_test_service_image)"
echo "Building image ${IMAGE_WITH_TAG} from ${FILE}"
docker build --quiet --tag "${IMAGE_WITH_TAG}" --file "${FILE}" . 2>&1
RETURN_VALUE=$?
rm "${FILE}"
[ "${RETURN_VALUE}" != "0" ] && sleep 1
done
return 0
}

build_and_push_test_image() {
Expand All @@ -666,7 +682,7 @@ build_and_push_test_image() {
prune_local_test_image() {
local IMAGE_WITH_TAG="${1}"
echo "Removing image ${IMAGE_WITH_TAG}"
docker image rm "${IMAGE_WITH_TAG}" --force
docker image rm "${IMAGE_WITH_TAG}" --force 2>&1
}

docker_service_update() {
Expand Down

0 comments on commit e68255b

Please sign in to comment.