From e003edef3dfdf28ca28e5331996a5543afd0da04 Mon Sep 17 00:00:00 2001 From: Shizun Ge Date: Fri, 29 Nov 2024 12:28:49 -0800 Subject: [PATCH] [gantry][tests] Handler warning better from docker command. Do not use /tmp folder for tests. Sometimes there could be a problem read files from /tmp. --- .gitignore | 1 + README.md | 2 +- src/docker_hub_rate.sh | 6 +- src/entrypoint.sh | 20 +--- src/lib-common.sh | 87 +++++++++------ src/lib-gantry.sh | 127 ++++++++++----------- tests/gantry_common_options_spec.sh | 7 +- tests/gantry_login_docker_config_spec.sh | 32 +++--- tests/gantry_login_negative_spec.sh | 71 ++++++------ tests/gantry_login_spec.sh | 34 +++--- tests/gantry_notify_spec.sh | 10 +- tests/gantry_rollback_spec.sh | 7 +- tests/gantry_service_single_spec.sh | 4 - tests/gantry_update_options_spec.sh | 56 +++++++++- tests/spec_gantry_test_helper.sh | 136 ++++++++++++++--------- 15 files changed, 343 insertions(+), 257 deletions(-) diff --git a/.gitignore b/.gitignore index 348c7ec..f08ef08 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .shellspec-quick.log coverage +gantry-test-tmp \ No newline at end of file diff --git a/README.md b/README.md index 1153267..36e65bb 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Labels can be added to services to modify the behavior of *Gantry* for particula | `gantry.update.options=` | Override [`GANTRY_UPDATE_OPTIONS`](#to-add-options-to-services-update). | | `gantry.update.timeout_seconds=` | Override [`GANTRY_UPDATE_TIMEOUT_SECONDS`](#to-add-options-to-services-update). | -> NOTE: You must apply the labels to the services not the containers. If you are using docker compose files to setup your services, you need to add the label to the [deploy](https://docs.docker.com/reference/compose-file/deploy/#labels) section. +> NOTE: You must apply the labels on the services not on the containers. If you are using docker compose files to setup your services, you need to add the label to the [deploy](https://docs.docker.com/reference/compose-file/deploy/#labels) section. ## FAQ diff --git a/src/docker_hub_rate.sh b/src/docker_hub_rate.sh index 0d46fe7..be4e750 100755 --- a/src/docker_hub_rate.sh +++ b/src/docker_hub_rate.sh @@ -16,7 +16,7 @@ # _curl_installed() { - curl --version 1>/dev/null 2>&1; + curl --version 1>/dev/null 2>/dev/null; } _docker_hub_rate_token() { @@ -66,10 +66,10 @@ _docker_hub_echo_error() { docker_hub_rate() { local IMAGE="${1:-ratelimitpreview/test}" local USER_AND_PASS="${2}" - if ! type log 1>/dev/null 2>&1; then + if ! type log 1>/dev/null 2>/dev/null; then log() { echo "${*}" >&2; } fi - if ! type log_lines 1>/dev/null 2>&1; then + if ! type log_lines 1>/dev/null 2>/dev/null; then # Usage: echo "${LOGS}" | log_lines LEVLE log_lines() { local LEVEL="${1}"; while read -r LINE; do [ -z "${LINE}" ] && continue; log "${LEVEL}" "${LINE}"; done; } fi diff --git a/src/entrypoint.sh b/src/entrypoint.sh index 1041ba1..57281ae 100755 --- a/src/entrypoint.sh +++ b/src/entrypoint.sh @@ -63,7 +63,7 @@ load_libraries() { _run_on_node() { local HOST_NAME= - if ! HOST_NAME=$(docker node inspect self --format "{{.Description.Hostname}}" 2>&1); then + if ! HOST_NAME=$(run_cmd docker node inspect self --format "{{.Description.Hostname}}"); then log DEBUG "Failed to run \"docker node inspect self\": ${HOST_NAME}" return 1 fi @@ -73,18 +73,9 @@ _run_on_node() { _read_docker_hub_rate() { local HOST PASSWORD USER - if ! PASSWORD=$(gantry_read_registry_password 2>&1); then - log ERROR "Failed to read registry PASSWORD: ${PASSWORD}"; - PASSWORD= - fi - if ! USER=$(gantry_read_registry_username 2>&1); then - log ERROR "Failed to read registry USER: ${USER}"; - USER= - fi - if ! HOST=$(gantry_read_registry_host 2>&1); then - log ERROR "Failed to read registry HOST: ${HOST}"; - HOST= - fi + USER=$(gantry_read_config "GANTRY_REGISTRY_USER") + PASSWORD=$(gantry_read_config "GANTRY_REGISTRY_PASSWORD") + HOST=$(gantry_read_config "GANTRY_REGISTRY_HOST") local USER_AND_PASS= if [ -n "${USER}" ] && [ -n "${PASSWORD}" ]; then if [ -z "${HOST}" ] || [ "${HOST}" = "docker.io" ]; then @@ -107,6 +98,7 @@ gantry() { START_TIME=$(date +%s) [ -n "${DOCKER_HOST}" ] && log DEBUG "DOCKER_HOST=${DOCKER_HOST}" + [ -n "${DOCKER_CONFIG}" ] && log DEBUG "DOCKER_CONFIG=${DOCKER_CONFIG}" local RUN_ON_NODE= if ! RUN_ON_NODE=$(_run_on_node); then local HOST_STRING="${DOCKER_HOST:-"the current node"}" @@ -123,7 +115,7 @@ gantry() { eval_cmd "pre-run" "${PRE_RUN_CMD}" ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + $?)) - log INFO "Starting." + log INFO "Starting Gantry." gantry_initialize "${STACK}" ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + $?)) diff --git a/src/lib-common.sh b/src/lib-common.sh index 5b5ada2..cfaa9c8 100755 --- a/src/lib-common.sh +++ b/src/lib-common.sh @@ -39,8 +39,8 @@ grep_q() { # "grep -q" will exit immediately when the first line of data matches, and leading to broken pipe errors. grep -q -- "${@}"; local GREP_RETURN=$?; - # Add "cat > /dev/null" to avoid broken pipe errors. - cat >/dev/null; + # Add "cat 1>/dev/null" to avoid broken pipe errors. + cat 1>/dev/null; return "${GREP_RETURN}" } @@ -49,7 +49,7 @@ grep_q() { grep_q_i() { grep -q -i -- "${@}"; local GREP_RETURN=$?; - cat >/dev/null; + cat 1>/dev/null; return "${GREP_RETURN}" } @@ -242,7 +242,7 @@ _log_docker_time() { # date -d "${TIME_INPUT}" +"$(_time_format)" 2>/dev/null && return 0 local EPOCH= if EPOCH=$(busybox date -d "${TIME_INPUT}" -D "%Y-%m-%dT%H:%M:%S" -u +%s 2>/dev/null); then - date -d "@${EPOCH}" +"$(_time_format)" 2>&1 + date -d "@${EPOCH}" +"$(_time_format)" return 0 fi if [ -n "${TIME_INPUT}" ]; then @@ -367,7 +367,7 @@ read_config() { cat "${CONFIG_FILE}" return $? elif [ -n "${CONFIG_FILE}" ]; then - echo "Failed to read ${CONFIG_FILE}" >&2 + echo "Failed to read file ${CONFIG_FILE}" >&2 return 1 fi eval "local CONFIG=\${${CONFIG_NAME}}" @@ -434,13 +434,36 @@ eval_cmd() { return "${RETURN_VALUE}" } +# When the command returns 0: +# Echo stdout and log stderr as a warning. Return 0. +# When the command returns non-zero: +# Echo stdout + stderr. Return the same value from the docker command. +run_cmd() { + local STDERR_STR= + local RETURN_VALUE= + # Use "3>&2 2>&1 1>&3" to swap stdout and stderr + { STDERR_STR=$("${@}" 3>&2 2>&1 1>&3); } 2>&1 + RETURN_VALUE=$? + + if [ -n "${STDERR_STR}" ]; then + if [ "${RETURN_VALUE}" = 0 ]; then + log WARN "${STDERR_STR} (From command: ${*})" + else + echo "${STDERR_STR}" + fi + fi + return "${RETURN_VALUE}" +} + swarm_network_arguments() { if [ -z "${NETWORK_NAME}" ]; then echo "" return 0 fi - NETWORK_NAME=$(docker network ls --filter "name=${NETWORK_NAME}" --format '{{.Name}}') - if [ -z "${NETWORK_NAME}" ]; then + local RETURN_VALUE= + NETWORK_NAME=$(run_cmd docker network ls --filter "name=${NETWORK_NAME}" --format '{{.Name}}') + RETURN_VALUE=$? + if [ "${RETURN_VALUE}" != "0" ] || [ -z "${NETWORK_NAME}" ]; then echo "" return 0 fi @@ -481,7 +504,7 @@ docker_service_logs() { _docker_service_exists() { local SERVICE_NAME="${1}" - docker service inspect --format '{{.ID}}' "${SERVICE_NAME}" >/dev/null 2>&1 + docker service inspect --format '{{.ID}}' "${SERVICE_NAME}" 1>/dev/null 2>/dev/null } _docker_wait_until_service_removed() { @@ -513,8 +536,8 @@ _docker_service_task_states() { local SERVICE_NAME="${1}" # We won't get the return value of the command via $? if we use "local STATES=$(command)". local STATES= - if ! STATES=$(docker service ps --no-trunc --format '[{{.Name}}][{{.Node}}] {{.CurrentState}} {{.Error}}' "${SERVICE_NAME}" 2>&1); then - echo "${STATES}" >&2 + if ! STATES=$(run_cmd docker service ps --no-trunc --format '[{{.Name}}][{{.Node}}] {{.CurrentState}} {{.Error}}' "${SERVICE_NAME}"); then + log ERROR "${STATES}" return 1 fi local NAME_LIST= @@ -591,7 +614,7 @@ wait_service_state() { local RETURN_VALUE=0 local DOCKER_CMD_ERROR=1 local STATES= - while STATES=$(_docker_service_task_states "${SERVICE_NAME}" 2>&1); do + while STATES=$(_docker_service_task_states "${SERVICE_NAME}"); do DOCKER_CMD_ERROR=0 RETURN_VALUE=$(_all_tasks_reach_state "${WANT_STATE}" "${CHECK_FAILURES}" "${STATES}") && break local SECONDS_ELAPSED= @@ -606,7 +629,7 @@ wait_service_state() { DOCKER_CMD_ERROR=1 done if [ "${DOCKER_CMD_ERROR}" != "0" ]; then - log ERROR "Failed to obtain task states of service ${SERVICE_NAME}: ${STATES}" + log ERROR "Failed to obtain task states of service ${SERVICE_NAME}." return 1 fi local LINE= @@ -621,12 +644,10 @@ docker_service_remove() { local POST_COMMAND="${2}" ! _docker_service_exists "${SERVICE_NAME}" && return 0 log DEBUG "Removing service ${SERVICE_NAME}." - local RETURN_VALUE=0 local LOG= - if ! LOG=$(docker service rm "${SERVICE_NAME}" 2>&1); then - RETURN_VALUE=$? + if ! LOG=$(run_cmd docker service rm "${SERVICE_NAME}"); then log ERROR "Failed to remove docker service ${SERVICE_NAME}: ${LOG}" - return "${RETURN_VALUE}" + return 1 fi if [ -n "${POST_COMMAND}" ]; then eval "${POST_COMMAND}" @@ -654,7 +675,7 @@ docker_global_job() { SERVICE_NAME=$(_get_docker_command_name_arg "${@}") log INFO "Starting global-job ${SERVICE_NAME}." local LOG= - if ! LOG=$(docker service create --mode global-job "${@}" 2>&1); then + if ! LOG=$(run_cmd docker service create --mode global-job "${@}"); then log ERROR "Failed to create global-job ${SERVICE_NAME}: ${LOG}" return 1 fi @@ -669,7 +690,7 @@ docker_replicated_job() { # The Docker CLI does not exit on failures. log INFO "Starting replicated-job ${SERVICE_NAME}." local LOG= - if ! LOG=$(docker service create --mode replicated-job --detach "${@}" 2>&1); then + if ! LOG=$(run_cmd docker service create --mode replicated-job --detach "${@}"); then log ERROR "Failed to create replicated-job ${SERVICE_NAME}: ${LOG}" return 1 fi @@ -682,10 +703,10 @@ docker_replicated_job() { docker_version() { local cver capi sver sapi - if ! cver=$(docker version --format '{{.Client.Version}}' 2>&1); then log ERROR "${cver}"; cver="error"; fi - if ! capi=$(docker version --format '{{.Client.APIVersion}}' 2>&1); then log ERROR "${capi}"; capi="error"; fi - if ! sver=$(docker version --format '{{.Server.Version}}' 2>&1); then log ERROR "${sver}"; sver="error"; fi - if ! sapi=$(docker version --format '{{.Server.APIVersion}}' 2>&1); then log ERROR "${sapi}"; sapi="error"; fi + if ! cver=$(run_cmd docker version --format '{{.Client.Version}}'); then log ERROR "${cver}"; cver="error"; fi + if ! capi=$(run_cmd docker version --format '{{.Client.APIVersion}}'); then log ERROR "${capi}"; capi="error"; fi + if ! sver=$(run_cmd docker version --format '{{.Server.Version}}'); then log ERROR "${sver}"; sver="error"; fi + if ! sapi=$(run_cmd docker version --format '{{.Server.APIVersion}}'); then log ERROR "${sapi}"; sapi="error"; fi echo "Docker version client ${cver} (API ${capi}) server ${sver} (API ${sapi})" } @@ -694,7 +715,7 @@ docker_version() { # return 1 when there is an error. docker_current_container_name() { local ALL_NETWORKS= - ALL_NETWORKS=$(docker network ls --format '{{.ID}}') || return 1; + ALL_NETWORKS=$(run_cmd docker network ls --format '{{.ID}}') || return 1; [ -z "${ALL_NETWORKS}" ] && return 0; local IPS=; # Get the string after "src": @@ -702,8 +723,8 @@ docker_current_container_name() { IPS=$(ip route | grep src | sed -n -E "s/.* src (\S+).*$/\1/p"); [ -z "${IPS}" ] && return 0; local GWBRIDGE_NETWORK HOST_NETWORK; - GWBRIDGE_NETWORK=$(docker network ls --format '{{.ID}}' --filter 'name=^docker_gwbridge$') || return 1; - HOST_NETWORK=$(docker network ls --format '{{.ID}}' --filter 'name=^host$') || return 1; + GWBRIDGE_NETWORK=$(run_cmd docker network ls --format '{{.ID}}' --filter 'name=^docker_gwbridge$') || return 1; + HOST_NETWORK=$(run_cmd docker network ls --format '{{.ID}}' --filter 'name=^host$') || return 1; local NID=; for NID in ${ALL_NETWORKS}; do # The output of gwbridge does not contain the container name. It looks like gateway_8f55496ce4f1/172.18.0.5/16. @@ -711,7 +732,7 @@ docker_current_container_name() { # The output of host does not contain an IP. [ "${NID}" = "${HOST_NETWORK}" ] && continue; local ALL_LOCAL_NAME_AND_IP=; - ALL_LOCAL_NAME_AND_IP=$(docker network inspect "${NID}" --format "{{range .Containers}}{{.Name}}/{{println .IPv4Address}}{{end}}") || return 1; + ALL_LOCAL_NAME_AND_IP=$(run_cmd docker network inspect "${NID}" --format "{{range .Containers}}{{.Name}}/{{println .IPv4Address}}{{end}}") || return 1; local NAME_AND_IP=; for NAME_AND_IP in ${ALL_LOCAL_NAME_AND_IP}; do [ -z "${NAME_AND_IP}" ] && continue; @@ -747,10 +768,10 @@ docker_remove() { fi log DEBUG "Removing container ${CNAME}." if [ "${STATUS}" = "running" ]; then - docker stop "${CNAME}" >/dev/null 2>/dev/null + docker container stop "${CNAME}" 1>/dev/null 2>/dev/null fi # If the container is created with "--rm", it will be removed automatically when being stopped. - docker rm -f "${CNAME}" >/dev/null; + docker container rm -f "${CNAME}" 1>/dev/null; log INFO "Removed container ${CNAME}." } @@ -758,15 +779,15 @@ docker_run() { local RETRIES=0 local MAX_RETRIES=5 local SLEEP_SECONDS=10 - local MSG= - while ! MSG=$(docker run "${@}" 2>&1); do + local LOG= + while ! LOG=$(run_cmd docker container run "${@}"); do if [ ${RETRIES} -ge ${MAX_RETRIES} ]; then - log ERROR "Failed to run docker. Reached the max retries ${MAX_RETRIES}. ${MSG}" + log ERROR "Failed to run docker. Reached the max retries ${MAX_RETRIES}. ${LOG}" return 1 fi RETRIES=$((RETRIES + 1)) sleep ${SLEEP_SECONDS} - log WARN "Retry docker run (${RETRIES}). ${MSG}" + log WARN "Retry docker container run (${RETRIES}). ${LOG}" done - echo "${MSG}" + echo "${LOG}" } diff --git a/src/lib-gantry.sh b/src/lib-gantry.sh index 3f0a21a..9315a64 100755 --- a/src/lib-gantry.sh +++ b/src/lib-gantry.sh @@ -33,7 +33,7 @@ _read_env_default() { gantry_read_number() { local ENV_NAME="${1}" local DEFAULT_VALUE="${2}" - ! is_number "${DEFAULT_VALUE}" && log ERROR "DEFAULT_VALUE must be a number. Got \"${DEFAULT_VALUE}\"." && return 1 + ! is_number "${DEFAULT_VALUE}" && log ERROR "DEFAULT_VALUE for ${ENV_NAME} must be a number. Got \"${DEFAULT_VALUE}\"." && return 1 local VALUE= VALUE=$(_read_env_default "${ENV_NAME}" "${DEFAULT_VALUE}") if ! is_number "${VALUE}"; then @@ -49,7 +49,7 @@ _get_label_from_service() { local SERVICE_NAME="${1}" local LABEL="${2}" local VALUE= - if ! VALUE=$(docker service inspect -f "{{index .Spec.Labels \"${LABEL}\"}}" "${SERVICE_NAME}" 2>&1); then + if ! VALUE=$(run_cmd docker service inspect -f "{{index .Spec.Labels \"${LABEL}\"}}" "${SERVICE_NAME}"); then log ERROR "Failed to obtain the value of label ${LABEL} from service ${SERVICE_NAME}. ${VALUE}" return 1 fi @@ -134,7 +134,7 @@ _login_registry() { local LOGIN_MSG= # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - if ! LOGIN_MSG=$(echo "${PASSWORD}" | docker ${AUTH_CONFIG} login --username="${USER}" --password-stdin "${HOST}" 2>&1); then + if ! LOGIN_MSG=$(echo "${PASSWORD}" | run_cmd docker ${AUTH_CONFIG} login --username="${USER}" --password-stdin "${HOST}"); then log ERROR "Failed to login to ${REGISTRY_CONFIG_MESSAGE}. ${LOGIN_MSG}" return 1 fi @@ -146,42 +146,24 @@ _login_registry() { return 0 } -gantry_read_registry_username() { - read_config GANTRY_REGISTRY_USER -} - -gantry_read_registry_password() { - read_config GANTRY_REGISTRY_PASSWORD -} - -gantry_read_registry_host() { - read_config GANTRY_REGISTRY_HOST +gantry_read_config() { + local CONFIG_NAME="${1}" + local CONFIG_VALUE= + if ! CONFIG_VALUE=$(read_config "${CONFIG_NAME}" 2>&1); then + log ERROR "Failed to read ${CONFIG_NAME}: ${CONFIG_VALUE}" + return 1 + fi + echo "${CONFIG_VALUE}" } _authenticate_to_registries() { local CONFIGS_FILE="${GANTRY_REGISTRY_CONFIGS_FILE:-""}" local ACCUMULATED_ERRORS=0 local CONFIG HOST PASSWORD USER - if ! CONFIG=$(read_config GANTRY_REGISTRY_CONFIG 2>&1); then - log ERROR "Failed to read registry CONFIG: ${CONFIG}" - ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) - CONFIG= - fi - if ! HOST=$(gantry_read_registry_host 2>&1); then - log ERROR "Failed to read registry HOST: ${HOST}" - ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) - HOST= - fi - if ! PASSWORD=$(gantry_read_registry_password 2>&1); then - log ERROR "Failed to read registry PASSWORD: ${PASSWORD}" - ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) - PASSWORD= - fi - if ! USER=$(gantry_read_registry_username 2>&1); then - log ERROR "Failed to read registry USER: ${USER}" - ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) - USER= - fi + CONFIG=$(gantry_read_config "GANTRY_REGISTRY_CONFIG") || ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) + HOST=$(gantry_read_config "GANTRY_REGISTRY_HOST") || ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) + PASSWORD=$(gantry_read_config "GANTRY_REGISTRY_PASSWORD") || ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) + USER=$(gantry_read_config "GANTRY_REGISTRY_USER") || ACCUMULATED_ERRORS=$((ACCUMULATED_ERRORS + 1)) if [ "${ACCUMULATED_ERRORS}" -gt 0 ]; then log ERROR "Skip logging in due to previous error(s)." else @@ -229,7 +211,7 @@ _send_notification() { local TYPE="${1}" local TITLE="${2}" local BODY="${3}" - if ! type notify_summary >/dev/null 2>&1; then + if ! type notify_summary 1>/dev/null 2>/dev/null; then return 0 fi notify_summary "${TYPE}" "${TITLE}" "${BODY}" @@ -266,9 +248,9 @@ _get_static_variables_folder() { } _remove_static_variables_folder() { - [ -z "${STATIC_VARIABLES_FOLDER}" ] && return 0 local TO_REMOVE_STATIC_VARIABLES_FOLDER= TO_REMOVE_STATIC_VARIABLES_FOLDER="$(_get_static_variables_folder_name)" + [ ! -d "${TO_REMOVE_STATIC_VARIABLES_FOLDER}" ] && return 0 log DEBUG "Removing STATIC_VARIABLES_FOLDER ${TO_REMOVE_STATIC_VARIABLES_FOLDER}" unset STATIC_VARIABLES_FOLDER rm -r "${TO_REMOVE_STATIC_VARIABLES_FOLDER}" @@ -286,14 +268,14 @@ _lock() { local NAME="${1}" local LOCK_NAME= LOCK_NAME="$(_get_static_variables_folder)/${NAME}-LOCK" - while ! mkdir "${LOCK_NAME}" >/dev/null 2>&1; do sleep 0.001; done + while ! mkdir "${LOCK_NAME}" 1>/dev/null 2>/dev/null; do sleep 0.001; done } _unlock() { local NAME="${1}" local LOCK_NAME= LOCK_NAME="$(_get_static_variables_folder)/${NAME}-LOCK" - rm -r "${LOCK_NAME}" >/dev/null 2>&1 + rm -r "${LOCK_NAME}" 1>/dev/null 2>/dev/null } _static_variable_read_list_core() { @@ -364,15 +346,15 @@ _remove_container() { local IMAGE="${1}"; local STATUS="${2}"; local CIDS= - if ! CIDS=$(docker container ls --all --filter "ancestor=${IMAGE}" --filter "status=${STATUS}" --format '{{.ID}}' 2>&1); then + if ! CIDS=$(run_cmd docker container ls --all --filter "ancestor=${IMAGE}" --filter "status=${STATUS}" --format '{{.ID}}'); then log ERROR "Failed to list ${STATUS} containers with image ${IMAGE}."; echo "${CIDS}" | log_lines ERROR return 1; fi local CID CNAME CRM_MSG for CID in ${CIDS}; do - CNAME=$(docker container inspect --format '{{.Name}}' "${CID}"); - if ! CRM_MSG=$(docker container rm "${CID}" 2>&1); then + CNAME=$(run_cmd docker container inspect --format '{{.Name}}' "${CID}"); + if ! CRM_MSG=$(run_cmd docker container rm "${CID}"); then log ERROR "Failed to remove ${STATUS} container ${CNAME}, which is using image ${IMAGE}."; echo "${CRM_MSG}" | log_lines ERROR continue; @@ -387,13 +369,13 @@ gantry_remove_images() { log DEBUG "$(docker_version)" local IMAGE= for IMAGE in ${IMAGES_TO_REMOVE}; do - if ! docker image inspect "${IMAGE}" 1>/dev/null 2>&1 ; then + if ! run_cmd docker image inspect "${IMAGE}" 1>/dev/null; then log DEBUG "There is no image ${IMAGE} on the node."; continue; fi _remove_container "${IMAGE}" exited; _remove_container "${IMAGE}" dead; - if ! RMI_MSG=$(docker rmi "${IMAGE}" 2>&1); then + if ! RMI_MSG=$(run_cmd docker image rm "${IMAGE}"); then log ERROR "Failed to remove image ${IMAGE}."; echo "${RMI_MSG}" | log_lines ERROR continue; @@ -446,6 +428,7 @@ _remove_images() { # shellcheck disable=SC2086 docker_global_job --name "${SERVICE_NAME}" \ --detach=true \ + --with-registry-auth \ --restart-condition on-failure \ --restart-max-attempts 1 \ --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock \ @@ -616,7 +599,9 @@ gantry_current_service_name() { CNAME=$(_current_container_name) || return 1 [ -z "${CNAME}" ] && return 0 local SNAME= - SNAME=$(docker container inspect "${CNAME}" --format '{{range $key,$value := .Config.Labels}}{{$key}}={{println $value}}{{end}}' \ + # SC2016 (info): Expressions don't expand in single quotes, use double quotes for that. + # shellcheck disable=SC2016 + SNAME=$(run_cmd docker container inspect "${CNAME}" --format '{{range $key,$value := .Config.Labels}}{{$key}}={{println $value}}{{end}}' \ | grep "com.docker.swarm.service.name" \ | sed -n -E "s/com.docker.swarm.service.name=(.*)$/\1/p") || return 1 _static_variable_add_unique_to_list STATIC_VAR_CURRENT_SERVICE_NAME "${SNAME}" @@ -643,19 +628,31 @@ _service_is_self() { _get_service_image() { local SERVICE_NAME="${1}" [ -z "${SERVICE_NAME}" ] && return 1 - docker service inspect -f '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "${SERVICE_NAME}" + local RETURN_VALUE= + local IMAGE_WITH_DIGEST= + IMAGE_WITH_DIGEST=$(run_cmd docker service inspect -f '{{.Spec.TaskTemplate.ContainerSpec.Image}}' "${SERVICE_NAME}") + RETURN_VALUE=$? + [ "${RETURN_VALUE}" != "0" ] && log ERROR "Failed to obtain image from service ${SERVICE_NAME}. ${IMAGE_WITH_DIGEST}" + echo "${IMAGE_WITH_DIGEST}" + return "${RETURN_VALUE}" } _get_service_previous_image() { local SERVICE_NAME="${1}" [ -z "${SERVICE_NAME}" ] && return 1 - docker service inspect -f '{{.PreviousSpec.TaskTemplate.ContainerSpec.Image}}' "${SERVICE_NAME}" + local RETURN_VALUE= + local IMAGE_WITH_DIGEST= + IMAGE_WITH_DIGEST=$(run_cmd docker service inspect -f '{{.PreviousSpec.TaskTemplate.ContainerSpec.Image}}' "${SERVICE_NAME}") + RETURN_VALUE=$? + [ "${RETURN_VALUE}" != "0" ] && log ERROR "Failed to obtain previous image from service ${SERVICE_NAME}. ${IMAGE_WITH_DIGEST}" + echo "${IMAGE_WITH_DIGEST}" + return "${RETURN_VALUE}" } _get_service_mode() { local SERVICE_NAME="${1}" local MODE= - if ! MODE=$(docker service ls --filter "name=${SERVICE_NAME}" --format '{{.Mode}} {{.Name}}' 2>&1); then + if ! MODE=$(run_cmd docker service ls --filter "name=${SERVICE_NAME}" --format '{{.Mode}} {{.Name}}'); then log ERROR "Failed to obtain the mode of the service ${SERVICE_NAME}: ${MODE}" return 1 fi @@ -751,13 +748,13 @@ _get_image_info() { [ -n "${MANIFEST_OPTIONS}" ] && log INFO "Adding options \"${MANIFEST_OPTIONS}\" to the command \"docker buildx imagetools inspect\"." # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - MSG=$(docker ${AUTH_CONFIG} buildx imagetools inspect ${MANIFEST_OPTIONS} "${IMAGE}" 2>&1); + MSG=$(run_cmd docker ${AUTH_CONFIG} buildx imagetools inspect ${MANIFEST_OPTIONS} "${IMAGE}"); RETURN_VALUE=$? elif echo "${MANIFEST_CMD}" | grep_q_i "manifest"; then [ -n "${MANIFEST_OPTIONS}" ] && log INFO "Adding options \"${MANIFEST_OPTIONS}\" to the command \"docker manifest inspect\"." # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - MSG=$(docker ${AUTH_CONFIG} manifest inspect ${MANIFEST_OPTIONS} "${IMAGE}" 2>&1); + MSG=$(run_cmd docker ${AUTH_CONFIG} manifest inspect ${MANIFEST_OPTIONS} "${IMAGE}"); RETURN_VALUE=$? elif echo "${MANIFEST_CMD}" | grep_q_i "none"; then # We should never reach here, the "none" command is already checked inside the function _inspect_image. @@ -782,10 +779,7 @@ _inspect_image() { local MANIFEST_CMD= MANIFEST_CMD=$(_read_env_or_label "${SERVICE_NAME}" "GANTRY_MANIFEST_CMD" "gantry.manifest.cmd" "buildx") local IMAGE_WITH_DIGEST= - if ! IMAGE_WITH_DIGEST=$(_get_service_image "${SERVICE_NAME}" 2>&1); then - log ERROR "Failed to obtain image from service ${SERVICE_NAME}. ${IMAGE_WITH_DIGEST}" - return 1 - fi + IMAGE_WITH_DIGEST=$(_get_service_image "${SERVICE_NAME}") || return $? local IMAGE= local DIGEST= IMAGE=$(extract_string "${IMAGE_WITH_DIGEST}" '@' 1) @@ -869,7 +863,7 @@ _inspect_service() { _get_number_of_running_tasks() { local SERVICE_NAME="${1}" local REPLICAS= - if ! REPLICAS=$(docker service ls --filter "name=${SERVICE_NAME}" --format '{{.Replicas}} {{.Name}}' 2>&1); then + if ! REPLICAS=$(run_cmd docker service ls --filter "name=${SERVICE_NAME}" --format '{{.Replicas}} {{.Name}}'); then log ERROR "Failed to obtain task states of service ${SERVICE_NAME}: ${REPLICAS}" return 1 fi @@ -903,8 +897,8 @@ _get_service_update_additional_options() { local NUM_RUNS= NUM_RUNS=$(_get_number_of_running_tasks "${SERVICE_NAME}") ! is_number "${NUM_RUNS}" && log WARN "NUM_RUNS \"${NUM_RUNS}\" is not a number." && return 1 - local OPTIONS="" - local SPACE="" + local OPTIONS= + local SPACE= if [ "${NUM_RUNS}" = "0" ]; then # Add "--detach=true" when there is no running tasks. # https://github.com/docker/cli/issues/627 @@ -956,7 +950,7 @@ _rollback_service() { # Add "-quiet" to suppress progress output. # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - if ! ROLLBACK_MSG=$(docker ${AUTH_CONFIG} service update --quiet ${AUTOMATIC_OPTIONS} ${ROLLBACK_OPTIONS} --rollback "${SERVICE_NAME}" 2>&1); then + if ! ROLLBACK_MSG=$(run_cmd docker ${AUTH_CONFIG} service update --quiet ${AUTOMATIC_OPTIONS} ${ROLLBACK_OPTIONS} --rollback "${SERVICE_NAME}"); then log ERROR "Failed to roll back ${SERVICE_NAME}. ${ROLLBACK_MSG}" return 1 fi @@ -976,7 +970,7 @@ _get_timeout_command() { _static_variable_add_unique_to_list STATIC_VAR_SERVICES_UPDATE_INPUT_ERROR "${SERVICE_NAME}" return 1 fi - local TIMEOUT_COMMAND="" + local TIMEOUT_COMMAND= if [ "${UPDATE_TIMEOUT_SECONDS}" != "0" ]; then TIMEOUT_COMMAND="timeout ${UPDATE_TIMEOUT_SECONDS}" log INFO "Set timeout to ${UPDATE_TIMEOUT_SECONDS} for updating ${SERVICE_NAME}." @@ -1008,16 +1002,17 @@ _update_single_service() { [ -n "${AUTH_CONFIG}" ] && log INFO "Adding options \"${AUTH_CONFIG}\" to docker commands for ${SERVICE_NAME}." [ -n "${AUTOMATIC_OPTIONS}" ] && log INFO "Adding options \"${AUTOMATIC_OPTIONS}\" automatically to the command \"docker service update\" for ${SERVICE_NAME}." [ -n "${UPDATE_OPTIONS}" ] && log INFO "Adding options \"${UPDATE_OPTIONS}\" specified by user to the command \"docker service update\" for ${SERVICE_NAME}." - local TIMEOUT_COMMAND="" + local TIMEOUT_COMMAND= TIMEOUT_COMMAND=$(_get_timeout_command "${SERVICE_NAME}") || return 1 - local UPDATE_COMMAND="${TIMEOUT_COMMAND} docker ${AUTH_CONFIG} service update" + # Add a space after TIMEOUT_COMMAND + [ -n "${TIMEOUT_COMMAND}" ] && TIMEOUT_COMMAND="${TIMEOUT_COMMAND} " + local UPDATE_COMMAND="${TIMEOUT_COMMAND}docker ${AUTH_CONFIG} service update" local UPDATE_RETURN_VALUE=0 local UPDATE_MSG= - # Add "2>/dev/null" outside the $(cmd) to suppress the "Terminated" message from "busybox timeout". # Add "-quiet" to suppress progress output. # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - UPDATE_MSG=$(${UPDATE_COMMAND} --quiet ${AUTOMATIC_OPTIONS} ${UPDATE_OPTIONS} --image="${IMAGE}" "${SERVICE_NAME}" 2>&1) 2>/dev/null; + UPDATE_MSG=$(run_cmd ${UPDATE_COMMAND} --quiet ${AUTOMATIC_OPTIONS} ${UPDATE_OPTIONS} --image="${IMAGE}" "${SERVICE_NAME}"); UPDATE_RETURN_VALUE=$? if [ "${UPDATE_RETURN_VALUE}" != 0 ]; then # When there is a timeout: @@ -1025,7 +1020,7 @@ _update_single_service() { # * busybox timeout returns 143 local TIMEOUT_RETURN_CODE=124 timeout --help 2>&1 | grep_q_i "BusyBox" && TIMEOUT_RETURN_CODE=143 - local TIMEOUT_MSG="" + local TIMEOUT_MSG= if [ -n "${TIMEOUT_COMMAND}" ] && [ "${UPDATE_RETURN_VALUE}" = "${TIMEOUT_RETURN_CODE}" ]; then TIMEOUT_MSG="The return value ${UPDATE_RETURN_VALUE} indicates the job timed out." fi @@ -1035,8 +1030,6 @@ _update_single_service() { _static_variable_add_unique_to_list STATIC_VAR_SERVICES_UPDATE_FAILED "${SERVICE_NAME}" return 1 fi - # Usually the UPDATE_MSG is same as the SERVICE_NAME. - [ "${UPDATE_MSG}" != "${SERVICE_NAME}" ] && log WARN "There are additional messages from updating ${SERVICE_NAME}: ${UPDATE_MSG}" local TIME_ELAPSED= TIME_ELAPSED=$(time_elapsed_since "${START_TIME}") local PREVIOUS_IMAGE= @@ -1092,13 +1085,15 @@ _get_services_filted() { local SERVICES_FILTERS="${1}" local SERVICES= local FILTERS= + local SPACE= local F= for F in ${SERVICES_FILTERS}; do - FILTERS="${FILTERS} --filter ${F}" + FILTERS="${FILTERS}${SPACE}--filter ${F}" + SPACE=" " done # SC2086: Double quote to prevent globbing and word splitting. # shellcheck disable=SC2086 - if ! SERVICES=$(docker service ls --quiet ${FILTERS} --format '{{.Name}}' 2>&1); then + if ! SERVICES=$(run_cmd docker service ls --quiet ${FILTERS} --format '{{.Name}}'); then log ERROR "Failed to obtain services list with \"${FILTERS}\". ${SERVICES}" return 1 fi diff --git a/tests/gantry_common_options_spec.sh b/tests/gantry_common_options_spec.sh index e6f8424..10066bd 100644 --- a/tests/gantry_common_options_spec.sh +++ b/tests/gantry_common_options_spec.sh @@ -102,9 +102,9 @@ Describe 'common-options' local TEST_NAME="${1}" local SERVICE_NAME="${2}" local ENV_BEFORE_RUN= - ENV_BEFORE_RUN=$(mktemp) + ENV_BEFORE_RUN=$(make_test_temp_file) local ENV_AFTER_RUN= - ENV_AFTER_RUN=$(mktemp) + ENV_AFTER_RUN=$(make_test_temp_file) reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" # There should be no warnings or errors. So it should work the same as LOG_LEVLE=NONE. @@ -113,7 +113,8 @@ Describe 'common-options' run_gantry "${SUITE_NAME}" "${TEST_NAME}" declare -p > "${ENV_AFTER_RUN}" # Allow the 3 mismatches LOG_LEVEL NODE_NAME LOG_SCOPE used in log() function. - # Allow the 2 mismatches LINENO _ for kcov coverage. + # Allow the 1 mismatch LINENO for kcov coverage. + # Allow the 1 mismatch _ for the previous command. for ALLOWED in LOG_LEVEL NODE_NAME LOG_SCOPE LINENO _; do sed -i "s/^declare .* ${ALLOWED}=.*//" "${ENV_BEFORE_RUN}" sed -i "s/^declare .* ${ALLOWED}=.*//" "${ENV_AFTER_RUN}" diff --git a/tests/gantry_login_docker_config_spec.sh b/tests/gantry_login_docker_config_spec.sh index 0bd8ece..d39b27a 100644 --- a/tests/gantry_login_docker_config_spec.sh +++ b/tests/gantry_login_docker_config_spec.sh @@ -42,7 +42,7 @@ Describe 'login-docker-config' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_docker_config_no_label() { local TEST_NAME="${1}" @@ -52,8 +52,8 @@ Describe 'login-docker-config' local USERNAME="${5}" local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" export GANTRY_TEST_DOCKER_CONFIG="${CONFIG}" export GANTRY_REGISTRY_CONFIG="${CONFIG}" @@ -91,7 +91,6 @@ Describe 'login-docker-config' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config.*" # Gantry adds --with-registry-auth, because DOCKER_CONFIG and the configuration name are same (default location). The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${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}" @@ -116,7 +115,7 @@ Describe 'login-docker-config' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_docker_config_default_config() { local TEST_NAME="${1}" @@ -126,8 +125,8 @@ Describe 'login-docker-config' local USERNAME="${5}" local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; # Do not set GANTRY_AUTH_CONFIG_LABEL on the service. reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" export GANTRY_TEST_DOCKER_CONFIG="${CONFIG}" @@ -138,7 +137,7 @@ Describe 'login-docker-config' local RETURN_VALUE= run_gantry "${SUITE_NAME}" "${TEST_NAME}" RETURN_VALUE="${?}" - docker logout "${REGISTRY}" > /dev/null + docker logout "${REGISTRY}" 1>/dev/null rm "${USER_FILE}" rm "${PASS_FILE}" [ -d "${CONFIG}" ] && rm -r "${CONFIG}" @@ -165,7 +164,6 @@ Describe 'login-docker-config' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config.*" # Gantry adds --with-registry-auth for using the default configuration. The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${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}" @@ -193,7 +191,8 @@ Describe 'login-docker-config' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) + INCORRECT_CONFIG=$(get_config_name "incorrect-") TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_start() { local TEST_NAME="${1}" @@ -211,13 +210,13 @@ Describe 'login-docker-config' local REGISTRY="${4}" local USERNAME="${5}" local PASSWORD="${6}" + local INCORRECT_CONFIG="${7}" local SERVICE_NAME0="${SERVICE_NAME}-0" local SERVICE_NAME1="${SERVICE_NAME}-1" local SERVICE_NAME2="${SERVICE_NAME}-2" - local INCORRECT_CONFIG="incorrect-${CONFIG}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; local CONFIGS_FILE= - CONFIGS_FILE=$(mktemp) + CONFIGS_FILE=$(make_test_temp_file) echo "${CONFIG} ${REGISTRY} ${USERNAME} ${PASSWORD}" > "${CONFIGS_FILE}" # Set GANTRY_AUTH_CONFIG_LABEL on SERVICE_NAME1, but not on SERVICE_NAME0. # Inspection of SERVICE_NAME0 should fail (incorrect label overrides DOCKER_CONFIG). @@ -250,7 +249,7 @@ Describe 'login-docker-config' BeforeEach "test_start ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}" AfterEach "test_end ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}" It 'run_test' - When run test_login_docker_config_label_override "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" + When run test_login_docker_config_label_override "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" "${INCORRECT_CONFIG}" The status should be failure The stdout should satisfy display_output The stdout should satisfy spec_expect_no_message ".+" @@ -258,11 +257,11 @@ Describe 'login-docker-config' The stderr should satisfy spec_expect_no_message "${START_WITHOUT_A_SQUARE_BRACKET}" The stderr should satisfy spec_expect_message "${LOGGED_INTO_REGISTRY}.*${TEST_REGISTRY}.*${AUTH_CONFIG}" The stderr should satisfy spec_expect_no_message "${FAILED_TO_LOGIN_TO_REGISTRY}" - The stderr should satisfy spec_expect_message "incorrect-${AUTH_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" + The stderr should satisfy spec_expect_message "${INCORRECT_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" # Check warnings The stderr should satisfy spec_expect_message "${THERE_ARE_NUM_CONFIGURATIONS}.*" The stderr should satisfy spec_expect_message "${USER_LOGGED_INTO_DEFAULT}.*" - The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config incorrect-${AUTH_CONFIG}.*${SERVICE_NAME0}" + The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${INCORRECT_CONFIG}.*${SERVICE_NAME0}" The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*${SERVICE_NAME1}" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*${SERVICE_NAME2}" The stderr should satisfy spec_expect_no_message "${PERFORM_UPDATING}.*${SERVICE_NAME0}.*" @@ -281,9 +280,6 @@ Describe 'login-docker-config' The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME1}" # Gantry adds --with-registry-auth, because DOCKER_CONFIG and the configuration name are same (default location). The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME2}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME0}.*" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME1}.*" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME2}.*" The stderr should satisfy spec_expect_no_message "${UPDATED}.*${SERVICE_NAME0}" The stderr should satisfy spec_expect_message "${UPDATED}.*${SERVICE_NAME1}" The stderr should satisfy spec_expect_message "${UPDATED}.*${SERVICE_NAME2}" diff --git a/tests/gantry_login_negative_spec.sh b/tests/gantry_login_negative_spec.sh index 0af2a88..3e6d59b 100644 --- a/tests/gantry_login_negative_spec.sh +++ b/tests/gantry_login_negative_spec.sh @@ -26,7 +26,7 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_no_login() { local TEST_NAME="${1}" @@ -55,7 +55,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" # No --with-registry-auth, because no login. The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -80,7 +79,7 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_incorrect_password() { local TEST_NAME="${1}" @@ -91,8 +90,8 @@ Describe 'login-negative' local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; local INCORRECT_PASSWORD="${PASSWORD}-incorrect-password" - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${INCORRECT_PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${INCORRECT_PASSWORD}" > "${PASS_FILE}"; docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${CONFIG}" "${SERVICE_NAME}" reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" export GANTRY_REGISTRY_CONFIG="${CONFIG}" @@ -127,7 +126,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_NO_NEW_IMAGES}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -149,7 +147,7 @@ Describe 'login-negative' TEST_NAME="test_login_read_only_file" IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}") SERVICE_NAME=$(get_test_service_name "${TEST_NAME}") - AUTH_CONFIG=$(mktemp -d) + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_read_only_file() { local TEST_NAME="${1}" @@ -163,8 +161,8 @@ Describe 'login-negative' # So do not run the test with a container/image. mkdir -p "${CONFIG}" chmod 444 "${CONFIG}" - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${CONFIG}" "${SERVICE_NAME}" reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" # Use GANTRY_TEST_HOST_TO_CONTAINER to mount the file from host to the container. @@ -178,7 +176,7 @@ Describe 'login-negative' RETURN_VALUE="${?}" rm "${USER_FILE}" rm "${PASS_FILE}" - # [ -d "${CONFIG}" ] && chmod 777 "${CONFIG}" && rm -r "${CONFIG}" + [ -d "${CONFIG}" ] && chmod 777 "${CONFIG}" && rm -r "${CONFIG}" return "${RETURN_VALUE}" } BeforeEach "common_setup_new_image ${TEST_NAME} ${IMAGE_WITH_TAG} ${SERVICE_NAME}" @@ -201,7 +199,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_NO_NEW_IMAGES}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -226,7 +223,8 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) + INCORRECT_CONFIG=$(get_config_name "incorrect-") TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_config_mismatch_default() { local TEST_NAME="${1}" @@ -235,13 +233,13 @@ Describe 'login-negative' local REGISTRY="${4}" local USERNAME="${5}" local PASSWORD="${6}" + local INCORRECT_CONFIG="${7}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local INCORRECT_CONFIG="incorrect-${CONFIG}" - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; # Also use CONFIGS_FILE to test a explicitly-set config. local CONFIGS_FILE= - CONFIGS_FILE=$(mktemp) + CONFIGS_FILE=$(make_test_temp_file) echo "${CONFIG} ${REGISTRY} ${USERNAME} ${PASSWORD}" >> "${CONFIGS_FILE}" # The config name on the service is different from the config name used in GANTRY_REGISTRY_CONFIG docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${INCORRECT_CONFIG}" "${SERVICE_NAME}" @@ -258,7 +256,7 @@ Describe 'login-negative' rm "${USER_FILE}" rm "${PASS_FILE}" rm "${CONFIGS_FILE}" - docker logout "${REGISTRY}" > /dev/null + docker logout "${REGISTRY}" 1>/dev/null [ -d "${CONFIG}" ] && rm -r "${CONFIG}" [ -d "${INCORRECT_CONFIG}" ] && rm -r "${INCORRECT_CONFIG}" return "${RETURN_VALUE}" @@ -266,7 +264,7 @@ Describe 'login-negative' BeforeEach "common_setup_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_login_config_mismatch_default "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" + When run test_login_config_mismatch_default "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" "${INCORRECT_CONFIG}" The status should be failure The stdout should satisfy display_output The stdout should satisfy spec_expect_no_message ".+" @@ -275,12 +273,12 @@ Describe 'login-negative' The stderr should satisfy spec_expect_message "${LOGGED_INTO_REGISTRY}.*${TEST_REGISTRY}.*${DEFAULT_CONFIGURATION}" The stderr should satisfy spec_expect_message "${LOGGED_INTO_REGISTRY}.*${TEST_REGISTRY}.*${AUTH_CONFIG}" The stderr should satisfy spec_expect_no_message "${FAILED_TO_LOGIN_TO_REGISTRY}" - The stderr should satisfy spec_expect_message "incorrect-${AUTH_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" + The stderr should satisfy spec_expect_message "${INCORRECT_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" # Check warnings The stderr should satisfy spec_expect_message "${THERE_ARE_NUM_CONFIGURATIONS}.*" The stderr should satisfy spec_expect_message "${USER_LOGGED_INTO_DEFAULT}.*" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*" - The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config incorrect-${AUTH_CONFIG}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${INCORRECT_CONFIG}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${SKIP_UPDATING}.*${SERVICE_NAME}.*${SKIP_REASON_MANIFEST_FAILURE}" The stderr should satisfy spec_expect_no_message "${PERFORM_UPDATING}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_SKIP_JOBS}" @@ -289,7 +287,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" # No --with-registry-auth, due to the incorrect configuration, image inspection failed, we did not reach the updating step. The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -314,7 +311,8 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) + INCORRECT_CONFIG=$(get_config_name "incorrect-") TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_config_mismatch_no_default() { local TEST_NAME="${1}" @@ -323,10 +321,10 @@ Describe 'login-negative' local REGISTRY="${4}" local USERNAME="${5}" local PASSWORD="${6}" - local INCORRECT_CONFIG="incorrect-${CONFIG}" + local INCORRECT_CONFIG="${7}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; # The config name on the service is different from the config name used in GANTRY_REGISTRY_CONFIG docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${INCORRECT_CONFIG}" "${SERVICE_NAME}" reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" @@ -347,7 +345,7 @@ Describe 'login-negative' BeforeEach "common_setup_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_login_config_mismatch_no_default "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" + When run test_login_config_mismatch_no_default "${TEST_NAME}" "${SERVICE_NAME}" "${AUTH_CONFIG}" "${TEST_REGISTRY}" "${TEST_USERNAME}" "${TEST_PASSWORD}" "${INCORRECT_CONFIG}" The status should be failure The stdout should satisfy display_output The stdout should satisfy spec_expect_no_message ".+" @@ -356,13 +354,13 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${LOGGED_INTO_REGISTRY}.*${TEST_REGISTRY}.*${DEFAULT_CONFIGURATION}" The stderr should satisfy spec_expect_message "${LOGGED_INTO_REGISTRY}.*${TEST_REGISTRY}.*${AUTH_CONFIG}" The stderr should satisfy spec_expect_no_message "${FAILED_TO_LOGIN_TO_REGISTRY}" - The stderr should satisfy spec_expect_message "incorrect-${AUTH_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" + The stderr should satisfy spec_expect_message "${INCORRECT_CONFIG}.*${CONFIG_IS_NOT_A_DIRECTORY}" # Check warnings The stderr should satisfy spec_expect_message "${THERE_ARE_NUM_CONFIGURATIONS}.*" # This message does not present, because we don't login with the default configuration. The stderr should satisfy spec_expect_no_message "${USER_LOGGED_INTO_DEFAULT}.*" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*" - The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config incorrect-${AUTH_CONFIG}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${INCORRECT_CONFIG}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${SKIP_UPDATING}.*${SERVICE_NAME}.*${SKIP_REASON_MANIFEST_FAILURE}" The stderr should satisfy spec_expect_no_message "${PERFORM_UPDATING}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_SKIP_JOBS}" @@ -371,7 +369,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" # No --with-registry-auth, due to the incorrect configuration, image inspection failed, we did not reach the updating step. The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -401,7 +398,7 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_start() { local TEST_NAME="${1}" @@ -429,8 +426,8 @@ Describe 'login-negative' local SERVICE_NAME0="${SERVICE_NAME}-0" local SERVICE_NAME1="${SERVICE_NAME}-1" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; # Set GANTRY_AUTH_CONFIG_LABEL on SERVICE_NAME1, but not on SERVICE_NAME0. # Inspection of SERVICE_NAME0 should fail, because GANTRY_AUTH_CONFIG_LABEL is not found. # Inspection of SERVICE_NAME1 should pass, because configuration is set via GANTRY_AUTH_CONFIG_LABEL. @@ -486,8 +483,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME0}" # Gantry adds --with-registry-auth for finding GANTRY_AUTH_CONFIG_LABEL on the service. The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME1}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME0}.*" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME1}.*" The stderr should satisfy spec_expect_no_message "${UPDATED}.*${SERVICE_NAME0}" The stderr should satisfy spec_expect_message "${UPDATED}.*${SERVICE_NAME1}" The stderr should satisfy spec_expect_no_message "${NO_UPDATES}.*${SERVICE_NAME0}" @@ -514,7 +509,7 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_REGISTRY_CONFIGS_FILE_bad_format() { local TEST_NAME="${1}" @@ -525,7 +520,7 @@ Describe 'login-negative' local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; local CONFIGS_FILE= - CONFIGS_FILE=$(mktemp) + CONFIGS_FILE=$(make_test_temp_file) # Add an extra item to the line. echo "${CONFIG} ${REGISTRY} ${USERNAME} ${PASSWORD} Extra" >> "${CONFIGS_FILE}" # Missing an item from the line. @@ -562,7 +557,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_NO_NEW_IMAGES}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config.*" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -587,7 +581,7 @@ Describe 'login-negative' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_file_not_exist() { local TEST_NAME="${1}" @@ -631,7 +625,6 @@ Describe 'login-negative' The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_NO_NEW_IMAGES}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATING}" The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config.*" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" diff --git a/tests/gantry_login_spec.sh b/tests/gantry_login_spec.sh index 7aaf316..f46fc32 100644 --- a/tests/gantry_login_spec.sh +++ b/tests/gantry_login_spec.sh @@ -15,8 +15,8 @@ # along with this program. If not, see . # -# This warning is generated by a docker command. -export IMAGE_DIGEST_WARNING="image .* could not be accessed on a registry to record its digest. Each node will access .* independently, possibly leading to different nodes running different versions of the image" +# FROM_DOCKER_IMAGE_DIGEST_WARNING is generated by a docker command. +export FROM_DOCKER_IMAGE_DIGEST_WARNING="image .* could not be accessed on a registry to record its digest. Each node will access .* independently, possibly leading to different nodes running different versions of the image" Describe 'login' SUITE_NAME="login" @@ -29,7 +29,7 @@ Describe 'login' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_config() { local TEST_NAME="${1}" @@ -39,8 +39,8 @@ Describe 'login' local USERNAME="${5}" local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${CONFIG}" "${SERVICE_NAME}" reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" export GANTRY_REGISTRY_CONFIG="${CONFIG}" @@ -81,7 +81,7 @@ Describe 'login' # 2 "--with-registry-auth" for SERVICE_NAME. One is automatically added. The other is from user. The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*automatically.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*specified by user.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" + The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_IMAGE_DIGEST_WARNING}.*${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}" @@ -113,8 +113,8 @@ Describe 'login' local USERNAME="${5}" local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; - local USER_FILE=; USER_FILE=$(mktemp); echo "${USERNAME}" > "${USER_FILE}"; - local PASS_FILE=; PASS_FILE=$(mktemp); echo "${PASSWORD}" > "${PASS_FILE}"; + local USER_FILE=; USER_FILE=$(make_test_temp_file); echo "${USERNAME}" > "${USER_FILE}"; + local PASS_FILE=; PASS_FILE=$(make_test_temp_file); echo "${PASSWORD}" > "${PASS_FILE}"; # Do not set GANTRY_AUTH_CONFIG_LABEL on the service. reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" # Do not set GANTRY_REGISTRY_CONFIG to login to the default configuration. @@ -124,7 +124,7 @@ Describe 'login' local RETURN_VALUE= run_gantry "${SUITE_NAME}" "${TEST_NAME}" RETURN_VALUE="${?}" - docker logout "${REGISTRY}" > /dev/null + docker logout "${REGISTRY}" 1>/dev/null rm "${USER_FILE}" rm "${PASS_FILE}" [ -d "${CONFIG}" ] && rm -r "${CONFIG}" && echo "${CONFIG} should not exist." >&2 && return 1 @@ -151,7 +151,7 @@ Describe 'login' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}.*--config.*" # Gantry adds --with-registry-auth for using the default configuration. The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" + The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_IMAGE_DIGEST_WARNING}.*${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}" @@ -176,7 +176,7 @@ Describe 'login' # When running with an Gantry image, docker buildx writes files to this folder which are owned by root. # Using a relative path, this the container will not write to the folder on the host. # So do not use an absolute path, otherwise we cannot remove this folder on the host. - AUTH_CONFIG="C$(unique_id)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_REGISTRY_CONFIGS_FILE() { local TEST_NAME="${1}" @@ -187,7 +187,7 @@ Describe 'login' local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; local CONFIGS_FILE= - CONFIGS_FILE=$(mktemp) + CONFIGS_FILE=$(make_test_temp_file) echo "# Test comments: CONFIG REGISTRY USERNAME PASSWORD" >> "${CONFIGS_FILE}" echo "${CONFIG} ${REGISTRY} ${USERNAME} ${PASSWORD}" >> "${CONFIGS_FILE}" docker_service_update --label-add "${GANTRY_AUTH_CONFIG_LABEL}=${CONFIG}" "${SERVICE_NAME}" @@ -229,7 +229,7 @@ Describe 'login' The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--config ${AUTH_CONFIG}.*${SERVICE_NAME}" # Gantry adds --with-registry-auth for finding GANTRY_AUTH_CONFIG_LABEL on the service. The stderr should satisfy spec_expect_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" + The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_IMAGE_DIGEST_WARNING}.*${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}" @@ -251,7 +251,7 @@ Describe 'login' TEST_NAME="test_login_external_config" IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}") SERVICE_NAME=$(get_test_service_name "${TEST_NAME}") - AUTH_CONFIG="$(mktemp -d)" + AUTH_CONFIG=$(get_config_name) TEST_REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 test_login_external_config() { local TEST_NAME="${1}" @@ -262,7 +262,7 @@ Describe 'login' local PASSWORD="${6}" check_login_input "${REGISTRY}" "${USERNAME}" "${PASSWORD}" || return 1; # Login outside Gantry. - echo "${PASSWORD}" | docker --config "${CONFIG}" login --username="${USERNAME}" --password-stdin "${REGISTRY}" > /dev/null 2>&1 + echo "${PASSWORD}" | docker --config "${CONFIG}" login --username="${USERNAME}" --password-stdin "${REGISTRY}" 1>/dev/null 2>/dev/null chmod 555 "${CONFIG}" # Do not set GANTRY_AUTH_CONFIG_LABEL on service. reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" @@ -272,7 +272,7 @@ Describe 'login' # We cannot remove DOCKER_CONFIG when it containes data from root. export GANTRY_MANIFEST_CMD="manifest" export GANTRY_MANIFEST_OPTIONS="--insecure" - # Do not set --with-registry-auth to trigger a warning IMAGE_DIGEST_WARNING. + # Do not set --with-registry-auth to trigger a warning FROM_DOCKER_IMAGE_DIGEST_WARNING. local RETURN_VALUE= run_gantry "${SUITE_NAME}" "${TEST_NAME}" RETURN_VALUE="${?}" @@ -301,7 +301,7 @@ Describe 'login' # No --with-registry-auth. The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS_WITH_REGISTRY_AUTH}.*" # Check the warning due to missing --with-registry-auth. - The stderr should satisfy spec_expect_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*${IMAGE_DIGEST_WARNING}" + The stderr should satisfy spec_expect_message "${FROM_DOCKER_IMAGE_DIGEST_WARNING}.*${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}" diff --git a/tests/gantry_notify_spec.sh b/tests/gantry_notify_spec.sh index d535cb0..ad24466 100644 --- a/tests/gantry_notify_spec.sh +++ b/tests/gantry_notify_spec.sh @@ -34,8 +34,8 @@ _notify_before_all() { initialize_all_tests "${SUITE_NAME}" pull_image_if_not_exist caronc/apprise pull_image_if_not_exist axllent/mailpit - docker_remove "${SERVICE_NAME_APPRISE}" 1>/dev/null 2>&1 - docker_remove "${SERVICE_NAME_MAILPIT}" 1>/dev/null 2>&1 + docker_remove "${SERVICE_NAME_APPRISE}" 1>/dev/null 2>/dev/null + docker_remove "${SERVICE_NAME_MAILPIT}" 1>/dev/null 2>/dev/null # Use docker_run to improve coverage on lib-common.sh. `docker run` can do the same thing. docker_run -d --restart=on-failure:10 --name="${SERVICE_NAME_APPRISE}" --network=host \ -e "APPRISE_STATELESS_URLS=mailto://localhost:${SMTP_PORT}?user=userid&pass=password" \ @@ -329,7 +329,8 @@ Describe 'notify' local SERVICE_NAME="${2}" local RETURN_VALUE=0 reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" - export GANTRY_UPDATE_OPTIONS="--bad-options-that-causes-error" + # Bad options will cause an update failure. + export GANTRY_UPDATE_OPTIONS="--incorrect-option" export GANTRY_NOTIFICATION_APPRISE_URL="http://localhost:${APPRISE_PORT}/notify" export GANTRY_NOTIFICATION_CONDITION="on-change" export GANTRY_NOTIFICATION_TITLE="TEST_TITLE" @@ -357,7 +358,8 @@ Describe 'notify' The stderr should satisfy spec_expect_no_message "${UPDATED}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_no_message "${NO_UPDATES}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${ROLLING_BACK}.*${SERVICE_NAME}" - The stderr should satisfy spec_expect_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}" + # Rollback should fail due to FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC + The stderr should satisfy spec_expect_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}.*${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" diff --git a/tests/gantry_rollback_spec.sh b/tests/gantry_rollback_spec.sh index ca2f9ea..e61a934 100644 --- a/tests/gantry_rollback_spec.sh +++ b/tests/gantry_rollback_spec.sh @@ -55,6 +55,7 @@ Describe 'rollback' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" The stderr should satisfy spec_expect_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 "${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" @@ -107,6 +108,7 @@ Describe 'rollback' The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--with-registry-auth.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${ROLLING_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" @@ -157,6 +159,7 @@ Describe 'rollback' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" 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 "${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" @@ -183,7 +186,7 @@ Describe 'rollback' # Assume service update won't be done within TIMEOUT second. export GANTRY_UPDATE_TIMEOUT_SECONDS="${TIMEOUT}" # label should override the global environment variable. - export GANTRY_ROLLBACK_OPTIONS="--insecure" + export GANTRY_ROLLBACK_OPTIONS="--incorrect-option" # Rollback would fail due to the incorrect option. # --with-registry-auth cannot be combined with --rollback. local LABEL_AND_VALUE="gantry.rollback.options=--with-registry-auth" @@ -212,6 +215,7 @@ Describe 'rollback' The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--with-registry-auth.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${ROLLING_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_no_message "${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" @@ -264,6 +268,7 @@ Describe 'rollback' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" 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 "${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" diff --git a/tests/gantry_service_single_spec.sh b/tests/gantry_service_single_spec.sh index 5e07452..9c0fd11 100644 --- a/tests/gantry_service_single_spec.sh +++ b/tests/gantry_service_single_spec.sh @@ -47,7 +47,6 @@ Describe 'service-single-service' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" The stderr should satisfy spec_expect_no_message "${SET_TIMEOUT_TO}" The stderr should satisfy spec_expect_no_message "${RETURN_VALUE_INDICATES_TIMEOUT}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${SERVICE_NAME}.*" The stderr should satisfy spec_expect_no_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}" @@ -94,7 +93,6 @@ Describe 'service-single-service' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" The stderr should satisfy spec_expect_no_message "${SET_TIMEOUT_TO}" The stderr should satisfy spec_expect_no_message "${RETURN_VALUE_INDICATES_TIMEOUT}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${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}" @@ -151,7 +149,6 @@ Describe 'service-single-service' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" The stderr should satisfy spec_expect_no_message "${SET_TIMEOUT_TO}" The stderr should satisfy spec_expect_no_message "${RETURN_VALUE_INDICATES_TIMEOUT}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${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}" @@ -203,7 +200,6 @@ Describe 'service-single-service' The stderr should satisfy spec_expect_no_message "${ADDING_OPTIONS}" The stderr should satisfy spec_expect_no_message "${SET_TIMEOUT_TO}" The stderr should satisfy spec_expect_no_message "${RETURN_VALUE_INDICATES_TIMEOUT}" - The stderr should satisfy spec_expect_no_message "${THERE_ARE_ADDITIONAL_MESSAGES}.*${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}" diff --git a/tests/gantry_update_options_spec.sh b/tests/gantry_update_options_spec.sh index 49b9495..f0c9e22 100644 --- a/tests/gantry_update_options_spec.sh +++ b/tests/gantry_update_options_spec.sh @@ -25,11 +25,11 @@ Describe 'update-options' SUITE_NAME="update-options" BeforeAll "initialize_all_tests ${SUITE_NAME}" AfterAll "finish_all_tests ${SUITE_NAME}" - Describe "test_update_UPDATE_OPTIONS" - TEST_NAME="test_update_UPDATE_OPTIONS" + Describe "test_update_UPDATE_OPTIONS_good" + TEST_NAME="test_update_UPDATE_OPTIONS_good" IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}") SERVICE_NAME=$(get_test_service_name "${TEST_NAME}") - test_update_UPDATE_OPTIONS() { + test_update_UPDATE_OPTIONS_good() { local TEST_NAME="${1}" local SERVICE_NAME="${2}" local LABEL="gantry.test" @@ -48,7 +48,7 @@ Describe 'update-options' BeforeEach "common_setup_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_update_UPDATE_OPTIONS "${TEST_NAME}" "${SERVICE_NAME}" + When run test_update_UPDATE_OPTIONS_good "${TEST_NAME}" "${SERVICE_NAME}" The status should be success The stdout should satisfy display_output # Check an observable difference before and after applying UPDATE_OPTIONS. @@ -140,6 +140,54 @@ Describe 'update-options' The stderr should satisfy spec_expect_message "${DONE_REMOVING_IMAGES}" End End + Describe "test_update_UPDATE_OPTIONS_bad" + TEST_NAME="test_update_UPDATE_OPTIONS_bad" + IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}") + SERVICE_NAME=$(get_test_service_name "${TEST_NAME}") + test_update_UPDATE_OPTIONS_bad() { + local TEST_NAME="${1}" + local SERVICE_NAME="${2}" + reset_gantry_env "${SUITE_NAME}" "${SERVICE_NAME}" + # Bad options will cause an update failure. + export GANTRY_UPDATE_OPTIONS="--incorrect-option" + run_gantry "${SUITE_NAME}" "${TEST_NAME}" + } + BeforeEach "common_setup_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_update_UPDATE_OPTIONS_bad "${TEST_NAME}" "${SERVICE_NAME}" + The status should be failure + 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}" + 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_HAS_NEWER_IMAGE}" + 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_no_message "${SET_TIMEOUT_TO}" + The stderr should satisfy spec_expect_no_message "${RETURN_VALUE_INDICATES_TIMEOUT}" + The stderr should satisfy spec_expect_no_message "${UPDATED}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_no_message "${NO_UPDATES}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_message "${ADDING_OPTIONS}.*--incorrect-option.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_message "${ROLLING_BACK}.*${SERVICE_NAME}" + # Rollback should fail due to FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC + The stderr should satisfy spec_expect_message "${FAILED_TO_ROLLBACK}.*${SERVICE_NAME}.*${FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC}" + The stderr should satisfy spec_expect_no_message "${ROLLED_BACK}.*${SERVICE_NAME}" + The stderr should satisfy spec_expect_message "${NO_SERVICES_UPDATED}" + The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_UPDATED}" + The stderr should satisfy spec_expect_message "${NUM_SERVICES_UPDATE_FAILED}" + The stderr should satisfy spec_expect_no_message "${NUM_SERVICES_ERRORS}" + The stderr should satisfy spec_expect_message "${NO_IMAGES_TO_REMOVE}" + The stderr should satisfy spec_expect_no_message "${REMOVING_NUM_IMAGES}" + The stderr should satisfy spec_expect_no_message "${SKIP_REMOVING_IMAGES}" + The stderr should satisfy spec_expect_no_message "${REMOVED_IMAGE}.*${IMAGE_WITH_TAG}" + The stderr should satisfy spec_expect_no_message "${FAILED_TO_REMOVE_IMAGE}.*${IMAGE_WITH_TAG}" + The stderr should satisfy spec_expect_no_message "${DONE_REMOVING_IMAGES}" + End + End Describe "test_update_UPDATE_TIMEOUT_SECONDS_not_a_number" TEST_NAME="test_update_UPDATE_TIMEOUT_SECONDS_not_a_number" IMAGE_WITH_TAG=$(get_image_with_tag "${SUITE_NAME}") diff --git a/tests/spec_gantry_test_helper.sh b/tests/spec_gantry_test_helper.sh index c9c927e..131a8a3 100644 --- a/tests/spec_gantry_test_helper.sh +++ b/tests/spec_gantry_test_helper.sh @@ -45,7 +45,6 @@ export ADDING_OPTIONS="Adding options" export ADDING_OPTIONS_WITH_REGISTRY_AUTH="Adding options.*--with-registry-auth" export SET_TIMEOUT_TO="Set timeout to" export RETURN_VALUE_INDICATES_TIMEOUT="The return value [0-9]+ indicates the job timed out." -export THERE_ARE_ADDITIONAL_MESSAGES="There are additional messages from updating" export NUM_SERVICES_SKIP_JOBS="Skip updating [0-9]+ service\(s\) due to they are job\(s\)" 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\)" @@ -54,6 +53,8 @@ export NO_UPDATES="No updates" export UPDATED="Updated" export ROLLING_BACK="Rolling back" export FAILED_TO_ROLLBACK="Failed to roll back" +# FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC is generated by a docker command. +export FROM_DOCKER_DOES_NOT_HAVE_A_PREVIOUS_SPEC="does not have a previous spec" export ROLLED_BACK="Rolled back" export NO_SERVICES_UPDATED="No services updated" export NUM_SERVICES_UPDATED="[0-9]+ service\(s\) updated" @@ -69,6 +70,8 @@ export DONE_REMOVING_IMAGES="Done removing images" export SCHEDULE_NEXT_UPDATE_AT="Schedule next update at" export SLEEP_SECONDS_BEFORE_NEXT_UPDATE="Sleep [0-9]+ seconds before next update" +export GANTRY_TEST_TEMP_DIR="gantry-test-tmp" + test_log() { echo "${GANTRY_LOG_LEVEL}" | grep -q -i "^NONE$" && return 0; echo "${GANTRY_LOG_LEVEL}" | grep -q -i "^ERROR$" && return 0; @@ -235,7 +238,7 @@ _next_available_port() { _get_docker_config_file() { local REGISTRY="${1:?}" REGISTRY=$(echo "${REGISTRY}" | tr ':' '-') - echo "/tmp/gantry-test-docker-config-${REGISTRY}" + echo "${GANTRY_TEST_TEMP_DIR}/gantry-test-docker-config-${REGISTRY}" } _get_docker_config_argument() { @@ -249,13 +252,9 @@ _get_docker_config_argument() { _login_test_registry() { local ENFORCE_LOGIN="${1}" - local REGISTRY="${2}" - local USERNAME="${3}" - local PASSWORD="${4}" - if ! _enforce_login_enabled "${ENFORCE_LOGIN}"; then - USERNAME="username" - PASSWORD="password" - fi + local REGISTRY="${2:?}" + local USERNAME="${3:?}" + local PASSWORD="${4:?}" echo "Logging in ${REGISTRY}." local CONFIG= CONFIG=$(_get_docker_config_file "${REGISTRY}") || return 1 @@ -267,17 +266,15 @@ _logout_test_registry() { local REGISTRY="${2}" local CONFIG= CONFIG=$(_get_docker_config_file "${REGISTRY}") || return 1 - if _enforce_login_enabled "${ENFORCE_LOGIN}"; then - echo "Logging out ${REGISTRY}." - docker --config "${CONFIG}" logout - fi + echo "Logging out ${REGISTRY}." + docker --config "${CONFIG}" logout [ -d "${CONFIG}" ] && rm -r "${CONFIG}" } _get_test_registry_file() { local SUITE_NAME="${1:?}" SUITE_NAME=$(echo "${SUITE_NAME}" | tr ' ' '-') - echo "/tmp/TEST_REGISTRY-${SUITE_NAME}" + echo "${GANTRY_TEST_TEMP_DIR}/gantry-test-registry-${SUITE_NAME}" } _remove_test_registry_file() { @@ -299,6 +296,7 @@ _store_test_registry() { echo "${TEST_REGISTRY}" > "${REGISTRY_FILE}" } + load_test_registry() { local SUITE_NAME="${1:?}" local REGISTRY_FILE= @@ -307,6 +305,20 @@ load_test_registry() { cat "${REGISTRY_FILE}" } +_get_test_registry_password_file() { + local SUITE_NAME="${1:?}" + local PASSWORD_FILE= + PASSWORD_FILE="$(_get_test_registry_file "${SUITE_NAME}")-password" || return 1 + readlink -f "${PASSWORD_FILE}" +} + +_remove_test_registry_password_file() { + local SUITE_NAME="${1:?}" + local PASSWORD_FILE= + PASSWORD_FILE=$(_get_test_registry_password_file "${SUITE_NAME}") || return 1 + [ -r "${PASSWORD_FILE}" ] && rm "${PASSWORD_FILE}" +} + _start_registry() { local SUITE_NAME="${1:?}" local ENFORCE_LOGIN="${2}" @@ -314,6 +326,7 @@ _start_registry() { SUITE_NAME=$(echo "${SUITE_NAME}" | tr ' ' '-') local SUITE_NAME_LENGTH="${#SUITE_NAME}" local REGISTRY_SERVICE_NAME="gantry-test-registry-${SUITE_NAME}" + REGISTRY_SERVICE_NAME=$(_sanitize_test_service_name "${REGISTRY_SERVICE_NAME}") local REGISTRY_BASE="localhost" local REGISTRY_PORT= REGISTRY_PORT=$(_get_initial_port "${SUITE_NAME_LENGTH}") @@ -334,8 +347,8 @@ _start_registry() { echo "_start_registry _next_available_port error: REGISTRY_PORT is empty." >&2 return 1 fi - docker container stop "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>&1; - docker container rm -f "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>&1; + docker container stop "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>/dev/null; + docker container rm -f "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>/dev/null; TEST_REGISTRY="${REGISTRY_BASE}:${REGISTRY_PORT}" echo "Suite \"${SUITE_NAME}\" starts registry ${TEST_REGISTRY} " local CID= @@ -348,7 +361,7 @@ _start_registry() { -e "REGISTRY_HTTP_ADDR=${TEST_REGISTRY}" \ -e "REGISTRY_HTTP_HOST=http://${TEST_REGISTRY}" \ --stop-timeout "${TIMEOUT_SECONDS}" \ - $(_add_htpasswd "${ENFORCE_LOGIN}" "${TEST_USERNAME}" "${TEST_PASSWORD}") \ + $(_add_htpasswd "${SUITE_NAME}" "${ENFORCE_LOGIN}" "${TEST_USERNAME}" "${TEST_PASSWORD}") \ "${REGISTRY_IMAGE}" 2>&1); then local STATUS= while [ "${STATUS}" != "running" ]; do @@ -356,6 +369,7 @@ _start_registry() { done break; fi + echo "docker container run: ${CID}"; if [ "${TRIES}" -ge "${MAX_RETRIES}" ]; then echo "_start_registry Reach MAX_RETRIES ${MAX_RETRIES}" >&2 return 1 @@ -384,11 +398,13 @@ _stop_registry() { local REGISTRY= REGISTRY=$(load_test_registry "${SUITE_NAME}") || return 1 echo "Removing registry ${REGISTRY} " - docker container stop "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>&1; - docker container rm -f "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>&1; - _logout_test_registry "${ENFORCE_LOGIN}" "${REGISTRY}" || return 1 - _remove_test_registry_file "${SUITE_NAME}" || return 1 - return 0 + docker container stop "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>/dev/null; + docker container rm -f "${REGISTRY_SERVICE_NAME}" 1>/dev/null 2>/dev/null; + local RETURN_VALUE=0 + _logout_test_registry "${ENFORCE_LOGIN}" "${REGISTRY}" || RETURN_VALUE=1 + _remove_test_registry_file "${SUITE_NAME}" || RETURN_VALUE=1 + _remove_test_registry_password_file "${SUITE_NAME}" || RETURN_VALUE=1 + return "${RETURN_VALUE}" } _get_test_service_image() { @@ -406,7 +422,7 @@ _build_and_push_gantry_image() { IMAGE="$(_get_gantry_image "${SUITE_NAME}")" || return 1 pull_image_if_not_exist "$(_get_test_service_image)" echo "Building gantry image ${IMAGE}" - timeout 120 docker build --quiet -t "${IMAGE}" . + docker build --quiet --tag "${IMAGE}" . echo "Pushing gantry image ${IMAGE}" # SC2046 (warning): Quote this to prevent word splitting. # shellcheck disable=SC2046 @@ -417,7 +433,7 @@ _remove_gantry_image() { local SUITE_NAME="${1:?}" IMAGE="$(_get_gantry_image "${SUITE_NAME}")" || return 1 echo "Removing gantry image ${IMAGE}" - docker image rm "${IMAGE}" + docker image rm -f "${IMAGE}" } initialize_all_tests() { @@ -430,6 +446,9 @@ initialize_all_tests() { echo "==============================" echo "== Starting suite ${SUITE_NAME}" echo "==============================" + mkdir -p "${GANTRY_TEST_TEMP_DIR}" + uname --all + docker_version _init_swarm _start_registry "${SUITE_NAME}" "${ENFORCE_LOGIN}" _build_and_push_gantry_image "${SUITE_NAME}" @@ -594,6 +613,21 @@ unique_id() { echo "${PID}-${TIME_STR}-${RANDOM_STR}" } +make_test_temp_file() { + local TEMP= + TEMP=$(mktemp -p "${GANTRY_TEST_TEMP_DIR}") + TEMP=$(readlink -f "${TEMP}") + echo "${TEMP}" +} + +get_config_name() { + local BASE="${1}" + local TEMP= + TEMP="${GANTRY_TEST_TEMP_DIR}/${BASE}C$(unique_id)" + TEMP=$(readlink -f "${TEMP}") + echo "${TEMP}" +} + build_test_image() { local IMAGE_WITH_TAG="${1}" local TASK_SECONDS="${2}" @@ -609,12 +643,12 @@ build_test_image() { EXIT_CMD="sleep ${EXIT_SECONDS};" fi local FILE= - FILE=$(mktemp) + 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}" - timeout 120 docker build --quiet --tag "${IMAGE_WITH_TAG}" --file "${FILE}" . + docker build --quiet --tag "${IMAGE_WITH_TAG}" --file "${FILE}" . rm "${FILE}" } @@ -636,7 +670,7 @@ prune_local_test_image() { } docker_service_update() { - docker service update --quiet "${@}" >/dev/null + docker service update --quiet "${@}" 1>/dev/null } wait_zero_running_tasks() { @@ -646,7 +680,7 @@ wait_zero_running_tasks() { local REPLICAS= local USED_SECONDS=0 local TRIES=0 - local MAX_RETRIES=120 + local MAX_RETRIES=60 echo "Wait until ${SERVICE_NAME} has zero running tasks." while [ "${NUM_RUNS}" -ne 0 ]; do if [ -n "${TIMEOUT_SECONDS}" ] && [ "${USED_SECONDS}" -ge "${TIMEOUT_SECONDS}" ]; then @@ -697,10 +731,10 @@ _location_constraints() { pull_image_if_not_exist() { local IMAGE="${1}" - if docker image inspect "${IMAGE}" > /dev/null 2>&1; then + if docker image inspect "${IMAGE}" 1>/dev/null 2>/dev/null; then return 0 fi - docker pull "${IMAGE}" > /dev/null + docker pull "${IMAGE}" 1>/dev/null } _enforce_login_enabled() { @@ -709,29 +743,30 @@ _enforce_login_enabled() { } _add_htpasswd() { - local ENFORCE_LOGIN="${1}" - local USER="${2}" - local PASS="${3}" + local SUITE_NAME="${1:?}" + local ENFORCE_LOGIN="${2}" + local USER="${3}" + local PASS="${4}" if ! _enforce_login_enabled "${ENFORCE_LOGIN}"; then return 0 fi local HTTPD_IMAGE="httpd:2" # https://distribution.github.io/distribution/about/deploying/#native-basic-auth - local PASSWD= - PASSWD="$(mktemp)" + local PASSWORD_FILE= + PASSWORD_FILE=$(_get_test_registry_password_file "${SUITE_NAME}") || return 1 pull_image_if_not_exist "${HTTPD_IMAGE}" - docker_run --entrypoint htpasswd "${HTTPD_IMAGE}" -Bbn "${USER}" "${PASS}" > "${PASSWD}" - echo "--mount type=bind,source=${PASSWD},target=${PASSWD} \ + docker_run --entrypoint htpasswd "${HTTPD_IMAGE}" -Bbn "${USER}" "${PASS}" > "${PASSWORD_FILE}" + echo "--mount type=bind,source=${PASSWORD_FILE},target=${PASSWORD_FILE} \ -e REGISTRY_AUTH=htpasswd \ -e REGISTRY_AUTH_HTPASSWD_REALM=RegistryRealm \ - -e REGISTRY_AUTH_HTPASSWD_PATH=${PASSWD} " + -e REGISTRY_AUTH_HTPASSWD_PATH=${PASSWORD_FILE} " } _wait_service_state() { local SERVICE_NAME="${1}"; local WANT_STATE="${2}"; local TIMEOUT_SECONDS="${3}"; - wait_service_state "${SERVICE_NAME}" "${WANT_STATE}" "${TIMEOUT_SECONDS}" 1>/dev/null 2>&1 + wait_service_state "${SERVICE_NAME}" "${WANT_STATE}" "${TIMEOUT_SECONDS}" 1>/dev/null 2>/dev/null } _sanitize_test_service_name() { @@ -767,7 +802,7 @@ start_replicated_service() { # * Add --rollback-monitor=1s: needs to exam the effect. # SC2046 (warning): Quote this to prevent word splitting. # shellcheck disable=SC2046 - timeout 120 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ + timeout 60 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ --name "${SERVICE_NAME}" \ --restart-condition "on-failure" \ --restart-max-attempts 5 \ @@ -779,7 +814,7 @@ start_replicated_service() { --mode=replicated \ --detach=true \ "${IMAGE_WITH_TAG}"; - _wait_service_state "${SERVICE_NAME}" "Running" 120 + _wait_service_state "${SERVICE_NAME}" "Running" 60 } start_multiple_replicated_services() { @@ -808,7 +843,7 @@ start_global_service() { # Do not add --detach, because we want to wait for the job finishes. # SC2046 (warning): Quote this to prevent word splitting. # shellcheck disable=SC2046 - timeout 120 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ + timeout 60 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ --name "${SERVICE_NAME}" \ --restart-condition "on-failure" \ --restart-max-attempts 5 \ @@ -831,7 +866,7 @@ _start_replicated_job() { # Always add --detach=true, do not wait for the job finishes. # SC2046 (warning): Quote this to prevent word splitting. # shellcheck disable=SC2046 - timeout 120 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ + timeout 60 docker $(_get_docker_config_argument "${IMAGE_WITH_TAG}") service create --quiet \ --name "${SERVICE_NAME}" \ --restart-condition "on-failure" \ --restart-max-attempts 5 \ @@ -842,7 +877,7 @@ _start_replicated_job() { --detach=true \ "${IMAGE_WITH_TAG}"; # wait until the job is running - _wait_service_state "${SERVICE_NAME}" "Running" 120 + _wait_service_state "${SERVICE_NAME}" "Running" 60 } stop_service() { @@ -917,7 +952,7 @@ _add_file_to_mount_options() { TARGET=$(readlink -f "${HOST_PATH}") local READONLY= READONLY=$(_get_file_readonly "${HOST_PATH}") - MOUNT_OPTIONS="${MOUNT_OPTIONS} --mount type=bind,source=${HOST_PATH},target=${TARGET},readonly=${READONLY}" + MOUNT_OPTIONS="${MOUNT_OPTIONS} --mount type=bind,source=${TARGET},target=${TARGET},readonly=${READONLY}" fi echo "${MOUNT_OPTIONS}" } @@ -933,7 +968,7 @@ stop_gantry_container() { local STACK="${1}" local SERVICE_NAME= SERVICE_NAME=$(_get_gantry_sut_name "${STACK}") - if ! docker service inspect --format '{{.ID}}' "${SERVICE_NAME}" >/dev/null 2>&1; then + if ! docker service inspect --format '{{.ID}}' "${SERVICE_NAME}" 1>/dev/null 2>/dev/null; then return 0 fi local RETURN_VALUE=0 @@ -947,12 +982,13 @@ stop_gantry_container() { } _run_gantry_container() { - local STACK="${1}" - local SUT_REPO_TAG="${2}" + local SUITE_NAME="${1}" + local STACK="${2}" + local SUT_REPO_TAG="${3}" pull_image_if_not_exist "${SUT_REPO_TAG}" local SERVICE_NAME= SERVICE_NAME=$(_get_gantry_sut_name "${STACK}") - docker service rm "${SERVICE_NAME}" >/dev/null 2>&1; + docker service rm "${SERVICE_NAME}" 1>/dev/null 2>/dev/null; local MOUNT_OPTIONS= MOUNT_OPTIONS=$(_add_file_to_mount_options "${MOUNT_OPTIONS}" "${GANTRY_TEST_HOST_TO_CONTAINER}") MOUNT_OPTIONS=$(_add_file_to_mount_options "${MOUNT_OPTIONS}" "${GANTRY_TEST_DOCKER_CONFIG}") @@ -1051,7 +1087,7 @@ run_gantry() { local SUT_REPO_TAG= SUT_REPO_TAG="$(_get_sut_image "${SUITE_NAME}")" if [ -n "${SUT_REPO_TAG}" ]; then - _run_gantry_container "${STACK}" "${SUT_REPO_TAG}" + _run_gantry_container "${SUITE_NAME}" "${STACK}" "${SUT_REPO_TAG}" RETURN_VALUE=$? else [ -n "${GANTRY_TEST_DOCKER_CONFIG}" ] && export DOCKER_CONFIG="${GANTRY_TEST_DOCKER_CONFIG}"