Merge pull request #9131 from dokku/fix-source-image-on-failed-deploy
Keep the git source-image property in sync and harden the deploy lock
This commit is contained in:
@@ -170,6 +170,8 @@ By default, when running `git:sync` with a git branch reference, Dokku will auto
|
|||||||
dokku git:sync --skip-deploy-branch node-js-app https://github.com/heroku/node-js-getting-started.git main
|
dokku git:sync --skip-deploy-branch node-js-app https://github.com/heroku/node-js-getting-started.git main
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The `git:sync` command holds the app's deploy lock while fetching, and will fail if a deploy is already in progress. When the fetched code changes the repository or a build is triggered, the `source-image` property set by [image deploys](/docs/deployment/methods/image.md) is cleared. As the synced code is kept even when a build fails, the property is not restored on build failure.
|
||||||
|
|
||||||
### Initializing from private repositories
|
### Initializing from private repositories
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ dokku git:from-image --build-dir path/to/build node-js-app domy-registrykku/node
|
|||||||
|
|
||||||
See the [dockerfile documentation](/docs/deployment/builders/dockerfiles.md) to learn about the different ways to configure Dockerfile-based deploys.
|
See the [dockerfile documentation](/docs/deployment/builders/dockerfiles.md) to learn about the different ways to configure Dockerfile-based deploys.
|
||||||
|
|
||||||
|
The image used for a `git:from-image` or `git:load-image` deploy is stored in the `source-image` git property, which can be viewed via `dokku git:report node-js-app --git-source-image`. This property is only changed while the app's deploy lock is held. If the deploy fails, the deploy branch is rolled back and the property is restored to its previous value, so it always refers to the image the repository was last successfully deployed from. Deploying via other methods - such as `git push`, `git:from-archive`, or `git:sync` - clears the property.
|
||||||
|
|
||||||
## Initializing an app repository from a remote image without a registry
|
## Initializing an app repository from a remote image without a registry
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
|
|||||||
@@ -137,8 +137,13 @@ func CommandLock(appName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the file is not truncated as it may hold the build id of an in-progress deploy
|
||||||
lockPath := getLockPath(appName)
|
lockPath := getLockPath(appName)
|
||||||
if _, err := os.Create(lockPath); err != nil {
|
lockFile, err := os.OpenFile(lockPath, os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("Unable to create deploy lock")
|
||||||
|
}
|
||||||
|
if err := lockFile.Close(); err != nil {
|
||||||
return errors.New("Unable to create deploy lock")
|
return errors.New("Unable to create deploy lock")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -980,11 +980,17 @@ dokku_discard_build_capture() {
|
|||||||
unset DOKKU_BUILD_ID DOKKU_BUILD_LOG_FILE DOKKU_BUILD_SOURCE
|
unset DOKKU_BUILD_ID DOKKU_BUILD_LOG_FILE DOKKU_BUILD_SOURCE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app_deploy_lock_file() {
|
||||||
|
declare desc="outputs the path to the deploy lock file for an app"
|
||||||
|
declare APP="$1"
|
||||||
|
echo "$DOKKU_LIB_ROOT/data/apps/$APP/.deploy.lock"
|
||||||
|
}
|
||||||
|
|
||||||
acquire_app_deploy_lock() {
|
acquire_app_deploy_lock() {
|
||||||
declare desc="acquire advisory lock for use in deploys"
|
declare desc="acquire advisory lock for use in deploys"
|
||||||
local APP="$1"
|
local APP="$1"
|
||||||
local LOCK_TYPE="${2:-waiting}"
|
local LOCK_TYPE="${2:-waiting}"
|
||||||
local APP_DEPLOY_LOCK_FILE="$DOKKU_LIB_ROOT/data/apps/$APP/.deploy.lock"
|
local APP_DEPLOY_LOCK_FILE="$(app_deploy_lock_file "$APP")"
|
||||||
local LOCK_WAITING_MSG="$APP currently has a deploy lock in place. Waiting..."
|
local LOCK_WAITING_MSG="$APP currently has a deploy lock in place. Waiting..."
|
||||||
local LOCK_FAILED_MSG="$APP currently has a deploy lock in place. Exiting..."
|
local LOCK_FAILED_MSG="$APP currently has a deploy lock in place. Exiting..."
|
||||||
|
|
||||||
@@ -1003,7 +1009,7 @@ release_app_deploy_lock() {
|
|||||||
declare desc="release advisory lock used in deploys"
|
declare desc="release advisory lock used in deploys"
|
||||||
local APP="$1"
|
local APP="$1"
|
||||||
local EXIT_CODE="${2:-0}"
|
local EXIT_CODE="${2:-0}"
|
||||||
local APP_DEPLOY_LOCK_FILE="$DOKKU_LIB_ROOT/data/apps/$APP/.deploy.lock"
|
local APP_DEPLOY_LOCK_FILE="$(app_deploy_lock_file "$APP")"
|
||||||
|
|
||||||
if [[ -n "$DOKKU_BUILD_ID" ]]; then
|
if [[ -n "$DOKKU_BUILD_ID" ]]; then
|
||||||
plugn trigger builds-record-finalize "$APP" "$DOKKU_BUILD_ID" "$EXIT_CODE" || true
|
plugn trigger builds-record-finalize "$APP" "$DOKKU_BUILD_ID" "$EXIT_CODE" || true
|
||||||
@@ -1018,24 +1024,35 @@ acquire_advisory_lock() {
|
|||||||
local LOCK_FD="200"
|
local LOCK_FD="200"
|
||||||
local SHOW_MSG=true
|
local SHOW_MSG=true
|
||||||
|
|
||||||
eval "exec $LOCK_FD>$LOCK_FILE"
|
while true; do
|
||||||
if [[ "$LOCK_TYPE" == "waiting" ]]; then
|
# opened for appending so a failed attempt does not truncate the build id
|
||||||
while [[ $(
|
# written by the current lock holder
|
||||||
flock -n "$LOCK_FD" &>/dev/null
|
eval "exec $LOCK_FD>>$LOCK_FILE"
|
||||||
echo $?
|
if [[ "$LOCK_TYPE" == "waiting" ]]; then
|
||||||
) -ne 0 ]]; do
|
while [[ $(
|
||||||
if [[ "$SHOW_MSG" == "true" ]]; then
|
flock -n "$LOCK_FD" &>/dev/null
|
||||||
echo "$LOCK_WAITING_MSG"
|
echo $?
|
||||||
SHOW_MSG=false
|
) -ne 0 ]]; do
|
||||||
|
if [[ "$SHOW_MSG" == "true" ]]; then
|
||||||
|
echo "$LOCK_WAITING_MSG"
|
||||||
|
SHOW_MSG=false
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
else
|
||||||
|
if ! flock -n "$LOCK_FD" &>/dev/null; then
|
||||||
|
dokku_log_warn "$LOCK_FAILED_MSG"
|
||||||
|
dokku_log_fail "Run 'apps:unlock' to release the existing deploy lock"
|
||||||
fi
|
fi
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
else
|
|
||||||
if ! flock -n "$LOCK_FD" &>/dev/null; then
|
|
||||||
dokku_log_warn "$LOCK_FAILED_MSG"
|
|
||||||
dokku_log_fail "Run 'apps:unlock' to release the existing deploy lock"
|
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
|
# the previous holder removes the lock file on release, so the file locked
|
||||||
|
# above may no longer be the one at the lock path
|
||||||
|
if [[ "/proc/self/fd/$LOCK_FD" -ef "$LOCK_FILE" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
eval "exec $LOCK_FD>&-"
|
||||||
|
done
|
||||||
echo "${DOKKU_BUILD_ID:-$DOKKU_PID}" >"$LOCK_FILE"
|
echo "${DOKKU_BUILD_ID:-$DOKKU_PID}" >"$LOCK_FILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1044,7 +1061,13 @@ release_advisory_lock() {
|
|||||||
local LOCK_FILE="$1"
|
local LOCK_FILE="$1"
|
||||||
local LOCK_FD="200"
|
local LOCK_FD="200"
|
||||||
|
|
||||||
flock -u "$LOCK_FD" && rm -f "$LOCK_FILE" &>/dev/null
|
# the file is removed before unlocking so a waiter that locks it afterwards
|
||||||
|
# can tell that its lock is stale. It is only removed if it is still the file
|
||||||
|
# this process locked, as apps:unlock may have handed the path to another deploy
|
||||||
|
if [[ "/proc/self/fd/$LOCK_FD" -ef "$LOCK_FILE" ]]; then
|
||||||
|
rm -f "$LOCK_FILE" &>/dev/null
|
||||||
|
fi
|
||||||
|
flock -u "$LOCK_FD"
|
||||||
}
|
}
|
||||||
|
|
||||||
suppress_output() {
|
suppress_output() {
|
||||||
|
|||||||
@@ -88,12 +88,12 @@ cmd-git-hook() {
|
|||||||
# Only run this script for the master branch. You can remove this
|
# Only run this script for the master branch. You can remove this
|
||||||
# if block if you wish to run it for others as well.
|
# if block if you wish to run it for others as well.
|
||||||
if [[ $refname == "refs/heads/${DOKKU_DEPLOY_BRANCH}" ]] || [[ $refname == "refs/tags/${DOKKU_DEPLOY_BRANCH}" ]]; then
|
if [[ $refname == "refs/heads/${DOKKU_DEPLOY_BRANCH}" ]] || [[ $refname == "refs/tags/${DOKKU_DEPLOY_BRANCH}" ]]; then
|
||||||
git_receive_app "$APP" "$newrev"
|
DOKKU_GIT_SOURCE_IMAGE="" git_receive_app "$APP" "$newrev"
|
||||||
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
||||||
else
|
else
|
||||||
# broken out into plugin so we might support other methods to receive an app
|
# broken out into plugin so we might support other methods to receive an app
|
||||||
if [[ $(find "$PLUGIN_PATH"/enabled/*/receive-branch 2>/dev/null | wc -l) != 1 ]]; then
|
if [[ $(find "$PLUGIN_PATH"/enabled/*/receive-branch 2>/dev/null | wc -l) != 1 ]]; then
|
||||||
plugn trigger receive-branch "$APP" "$newrev" "$refname"
|
DOKKU_GIT_SOURCE_IMAGE="" plugn trigger receive-branch "$APP" "$newrev" "$refname"
|
||||||
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
||||||
elif [[ -z "$(fn-git-computed-deploy-branch "$APP" "")" ]]; then
|
elif [[ -z "$(fn-git-computed-deploy-branch "$APP" "")" ]]; then
|
||||||
local deploy_branch="${refname/refs\/heads\//}"
|
local deploy_branch="${refname/refs\/heads\//}"
|
||||||
@@ -101,7 +101,7 @@ cmd-git-hook() {
|
|||||||
|
|
||||||
echo $'\e[1G\e[K'"-----> Set ${deploy_branch} as deploy-branch"
|
echo $'\e[1G\e[K'"-----> Set ${deploy_branch} as deploy-branch"
|
||||||
fn-plugin-property-write "git" "$APP" "deploy-branch" "${deploy_branch}"
|
fn-plugin-property-write "git" "$APP" "deploy-branch" "${deploy_branch}"
|
||||||
git_receive_app "$APP" "$newrev"
|
DOKKU_GIT_SOURCE_IMAGE="" git_receive_app "$APP" "$newrev"
|
||||||
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
plugn trigger deploy-source-set "$APP" "git-push" "$newrev"
|
||||||
else
|
else
|
||||||
echo $'\e[1G\e[K'"-----> WARNING: deploy did not complete, you must push to ${DOKKU_DEPLOY_BRANCH}."
|
echo $'\e[1G\e[K'"-----> WARNING: deploy did not complete, you must push to ${DOKKU_DEPLOY_BRANCH}."
|
||||||
@@ -152,12 +152,34 @@ git_receive_app() {
|
|||||||
else
|
else
|
||||||
local exit_code=0
|
local exit_code=0
|
||||||
acquire_app_deploy_lock "$APP" "exclusive"
|
acquire_app_deploy_lock "$APP" "exclusive"
|
||||||
git_build "$APP" "$REV" || exit_code="$?"
|
fn-git-build-with-lock-held "$APP" "$REV" || exit_code="$?"
|
||||||
release_app_deploy_lock "$APP" "$exit_code"
|
release_app_deploy_lock "$APP" "$exit_code"
|
||||||
return "$exit_code"
|
return "$exit_code"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn-git-build-with-lock-held() {
|
||||||
|
declare desc="builds an app at a revision, expecting the caller to hold the app deploy lock"
|
||||||
|
declare APP="$1" REV="$2"
|
||||||
|
local exit_code=0 has_source_image=false source_image previous_source_image
|
||||||
|
|
||||||
|
# DOKKU_GIT_SOURCE_IMAGE is only applied while the deploy lock is held. An
|
||||||
|
# empty value clears the property, while an unset variable leaves it as-is.
|
||||||
|
if [[ -n "${DOKKU_GIT_SOURCE_IMAGE+x}" ]]; then
|
||||||
|
has_source_image=true
|
||||||
|
source_image="$DOKKU_GIT_SOURCE_IMAGE"
|
||||||
|
unset DOKKU_GIT_SOURCE_IMAGE
|
||||||
|
previous_source_image="$(fn-git-source-image "$APP")"
|
||||||
|
fn-plugin-property-write "git" "$APP" "source-image" "$source_image"
|
||||||
|
fi
|
||||||
|
|
||||||
|
(git_build "$APP" "$REV") || exit_code="$?"
|
||||||
|
if [[ "$exit_code" -ne 0 ]] && [[ "$has_source_image" == "true" ]]; then
|
||||||
|
fn-plugin-property-write "git" "$APP" "source-image" "$previous_source_image"
|
||||||
|
fi
|
||||||
|
return "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
cmd-git-upload-pack() {
|
cmd-git-upload-pack() {
|
||||||
declare desc="executes git-upload-pack"
|
declare desc="executes git-upload-pack"
|
||||||
declare cmd="git-upload-pack"
|
declare cmd="git-upload-pack"
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ trigger-git-git-from-archive() {
|
|||||||
popd &>/dev/null || pushd "/tmp" >/dev/null
|
popd &>/dev/null || pushd "/tmp" >/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
plugn trigger git-from-directory "$APP" "$TMP_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
DOKKU_GIT_SOURCE_IMAGE="" plugn trigger git-from-directory "$APP" "$TMP_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger-git-git-from-archive "$@"
|
trigger-git-git-from-archive "$@"
|
||||||
|
|||||||
@@ -5,20 +5,19 @@ source "$PLUGIN_AVAILABLE_PATH/git/internal-functions"
|
|||||||
set -eo pipefail
|
set -eo pipefail
|
||||||
[[ $DOKKU_TRACE ]] && set -x
|
[[ $DOKKU_TRACE ]] && set -x
|
||||||
|
|
||||||
trigger-git-git-from-directory() {
|
fn-git-from-directory-with-lock-held() {
|
||||||
declare desc="updates a repository from a directory"
|
declare desc="updates a repository from a directory and builds it, expecting the caller to hold the app deploy lock"
|
||||||
declare trigger="git-from-directory"
|
declare APP="$1" SOURCECODE_WORK_DIR="$2" USER_NAME="$3" USER_EMAIL="$4"
|
||||||
declare APP="$1" SOURCECODE_WORK_DIR="$2" USER_NAME="${3:-Dokku}" USER_EMAIL="${4:-automated@dokku.sh}"
|
|
||||||
|
|
||||||
local APP_ROOT="$DOKKU_ROOT/$APP"
|
local APP_ROOT="$DOKKU_ROOT/$APP"
|
||||||
local DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
local DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
||||||
local REV
|
local REV PREVIOUS_REV
|
||||||
local TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
|
local TMP_WORK_DIR=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
|
||||||
local TMP_WORK_DIR_2=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
|
local TMP_WORK_DIR_2=$(mktemp -d "/tmp/dokku-${DOKKU_PID}-${FUNCNAME[0]}.XXXXXX")
|
||||||
trap "rm -rf '$TMP_WORK_DIR' '$TMP_WORK_DIR_2' >/dev/null" RETURN INT TERM EXIT
|
trap "rm -rf '$TMP_WORK_DIR' '$TMP_WORK_DIR_2' >/dev/null" RETURN INT TERM EXIT
|
||||||
|
|
||||||
local has_code=false
|
local has_code=false
|
||||||
if fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" &>/dev/null; then
|
if PREVIOUS_REV="$(fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" 2>/dev/null)"; then
|
||||||
has_code=true
|
has_code=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -60,18 +59,39 @@ trigger-git-git-from-directory() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf "$TMP_WORK_DIR" "$TMP_WORK_DIR_2" >/dev/null
|
rm -rf "$TMP_WORK_DIR" "$TMP_WORK_DIR_2" >/dev/null
|
||||||
if git_receive_app "$APP" "$REV"; then
|
if fn-git-build-with-lock-held "$APP" "$REV"; then
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
local exit_code=$?
|
local exit_code=$?
|
||||||
local DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
local DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
||||||
|
# only roll back the deploy branch if it still points at the commit created above
|
||||||
if [[ "$has_code" == "false" ]]; then
|
if [[ "$has_code" == "false" ]]; then
|
||||||
fn-git-cmd "$APP_ROOT" update-ref -d HEAD
|
fn-git-cmd "$APP_ROOT" update-ref -d "refs/heads/$DOKKU_DEPLOY_BRANCH" "$REV" || dokku_log_warn "Unable to roll back the $DOKKU_DEPLOY_BRANCH branch"
|
||||||
else
|
else
|
||||||
fn-git-cmd "$APP_ROOT" update-ref "refs/heads/$DOKKU_DEPLOY_BRANCH" "$DOKKU_DEPLOY_BRANCH^"
|
fn-git-cmd "$APP_ROOT" update-ref "refs/heads/$DOKKU_DEPLOY_BRANCH" "$PREVIOUS_REV" "$REV" || dokku_log_warn "Unable to roll back the $DOKKU_DEPLOY_BRANCH branch"
|
||||||
fi
|
fi
|
||||||
return "$exit_code"
|
return "$exit_code"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger-git-git-from-directory() {
|
||||||
|
declare desc="updates a repository from a directory"
|
||||||
|
declare trigger="git-from-directory"
|
||||||
|
declare APP="$1" SOURCECODE_WORK_DIR="$2" USER_NAME="${3:-Dokku}" USER_EMAIL="${4:-automated@dokku.sh}"
|
||||||
|
local exit_code=0
|
||||||
|
|
||||||
|
# the lock is held from before the deploy branch is updated until after it is
|
||||||
|
# rolled back, and the subshell ensures it is released on any failure
|
||||||
|
acquire_app_deploy_lock "$APP" "exclusive"
|
||||||
|
set +e
|
||||||
|
(
|
||||||
|
set -e
|
||||||
|
fn-git-from-directory-with-lock-held "$APP" "$SOURCECODE_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
||||||
|
)
|
||||||
|
exit_code="$?"
|
||||||
|
set -e
|
||||||
|
release_app_deploy_lock "$APP" "$exit_code"
|
||||||
|
return "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
trigger-git-git-from-directory "$@"
|
trigger-git-git-from-directory "$@"
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ trigger-git-git-from-image() {
|
|||||||
dokku_log_info1 "Image exists on host, skipping pull"
|
dokku_log_info1 "Image exists on host, skipping pull"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
fn-plugin-property-write "git" "$APP" "source-image" "$DOCKER_IMAGE"
|
DOKKU_GIT_SOURCE_IMAGE="$DOCKER_IMAGE" plugn trigger git-from-directory "$APP" "$TMP_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
||||||
plugn trigger git-from-directory "$APP" "$TMP_WORK_DIR" "$USER_NAME" "$USER_EMAIL"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trigger-git-git-from-image "$@"
|
trigger-git-git-from-image "$@"
|
||||||
|
|||||||
@@ -279,33 +279,59 @@ cmd-git-sync() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local APP_ROOT="$DOKKU_ROOT/$APP"
|
local APP_ROOT="$DOKKU_ROOT/$APP"
|
||||||
DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
local APP_DEPLOY_LOCK_FILE="$(app_deploy_lock_file "$APP")"
|
||||||
CURRENT_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" 2>/dev/null || true)"
|
local fetch_exit_code=0
|
||||||
|
|
||||||
dokku_setup_build_capture "$APP" "git:sync"
|
dokku_setup_build_capture "$APP" "git:sync"
|
||||||
|
|
||||||
if ! fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" &>/dev/null; then
|
# acquire_app_deploy_lock is not used as releasing it would finalize the
|
||||||
dokku_log_info1_quiet "Cloning $APP from $GIT_REMOTE#$GIT_REF"
|
# build record before the build starts
|
||||||
fn-git-clone "$APP" "$GIT_REMOTE" "$GIT_REF" "$SKIP_DEPLOY_BRANCH"
|
acquire_advisory_lock "$APP_DEPLOY_LOCK_FILE" "exclusive" "$APP currently has a deploy lock in place. Waiting..." "$APP currently has a deploy lock in place. Exiting..."
|
||||||
else
|
|
||||||
dokku_log_verbose "Fetching remote code for $APP from $GIT_REMOTE#$GIT_REF"
|
DOKKU_DEPLOY_BRANCH="$(fn-git-computed-deploy-branch "$APP")"
|
||||||
fn-git-fetch "$APP" "$GIT_REMOTE" "$GIT_REF"
|
CURRENT_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
# the subshell keeps set -e active and ensures the lock is released on failure
|
||||||
|
set +e
|
||||||
|
(
|
||||||
|
set -e
|
||||||
|
if ! fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" &>/dev/null; then
|
||||||
|
dokku_log_info1_quiet "Cloning $APP from $GIT_REMOTE#$GIT_REF"
|
||||||
|
fn-git-clone "$APP" "$GIT_REMOTE" "$GIT_REF" "$SKIP_DEPLOY_BRANCH"
|
||||||
|
else
|
||||||
|
dokku_log_verbose "Fetching remote code for $APP from $GIT_REMOTE#$GIT_REF"
|
||||||
|
fn-git-fetch "$APP" "$GIT_REMOTE" "$GIT_REF"
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
fetch_exit_code="$?"
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ "$fetch_exit_code" -ne 0 ]]; then
|
||||||
|
release_advisory_lock "$APP_DEPLOY_LOCK_FILE"
|
||||||
|
return "$fetch_exit_code"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
UPDATED_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" 2>/dev/null || true)"
|
UPDATED_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$DOKKU_DEPLOY_BRANCH" 2>/dev/null || true)"
|
||||||
local SHOULD_BUILD=false
|
local SHOULD_BUILD=false
|
||||||
if [[ "$FLAG" == "--build" ]]; then
|
if [[ "$FLAG" == "--build" ]]; then
|
||||||
SHOULD_BUILD=true
|
SHOULD_BUILD=true
|
||||||
elif [[ "$FLAG" == "--build-if-changes" ]]; then
|
elif [[ "$FLAG" == "--build-if-changes" ]] && [[ "$CURRENT_REF" != "$UPDATED_REF" ]]; then
|
||||||
if [[ "$CURRENT_REF" == "$UPDATED_REF" ]]; then
|
|
||||||
dokku_log_verbose "Skipping build as no changes were detected"
|
|
||||||
dokku_discard_build_capture "$APP"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
SHOULD_BUILD=true
|
SHOULD_BUILD=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# the synced code is kept even if the build fails, so the source-image is
|
||||||
|
# cleared rather than restored on failure
|
||||||
|
if [[ "$CURRENT_REF" != "$UPDATED_REF" ]] || [[ "$SHOULD_BUILD" == "true" ]]; then
|
||||||
|
fn-plugin-property-write "git" "$APP" "source-image"
|
||||||
|
fi
|
||||||
|
release_advisory_lock "$APP_DEPLOY_LOCK_FILE"
|
||||||
|
|
||||||
|
if [[ "$FLAG" == "--build-if-changes" ]] && [[ "$SHOULD_BUILD" == "false" ]]; then
|
||||||
|
dokku_log_verbose "Skipping build as no changes were detected"
|
||||||
|
dokku_discard_build_capture "$APP"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$SHOULD_BUILD" == "true" ]]; then
|
if [[ "$SHOULD_BUILD" == "true" ]]; then
|
||||||
if [[ -n "$GIT_REF" ]]; then
|
if [[ -n "$GIT_REF" ]]; then
|
||||||
GIT_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$GIT_REF")"
|
GIT_REF="$(fn-git-cmd "$APP_ROOT" rev-parse "$GIT_REF")"
|
||||||
|
|||||||
@@ -402,6 +402,36 @@ teardown() {
|
|||||||
destroy_app
|
destroy_app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "(apps) apps:lock does not truncate an existing lock file" {
|
||||||
|
run create_app
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "echo holder-id >$DOKKU_LIB_ROOT/data/apps/$TEST_APP/.deploy.lock && chown dokku:dokku $DOKKU_LIB_ROOT/data/apps/$TEST_APP/.deploy.lock"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:lock $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "cat $DOKKU_LIB_ROOT/data/apps/$TEST_APP/.deploy.lock"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "holder-id"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:unlock $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
destroy_app
|
||||||
|
}
|
||||||
|
|
||||||
@test "(apps:report) emits new stripped JSON keys alongside legacy" {
|
@test "(apps:report) emits new stripped JSON keys alongside legacy" {
|
||||||
create_app
|
create_app
|
||||||
|
|
||||||
|
|||||||
109
tests/unit/deploy-lock.bats
Normal file
109
tests/unit/deploy-lock.bats
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
global_setup
|
||||||
|
LOCK_DIR="$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX")"
|
||||||
|
LOCK_FILE="$LOCK_DIR/test.lock"
|
||||||
|
LOCK_PIDS=()
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown() {
|
||||||
|
local pid
|
||||||
|
touch "$LOCK_DIR/release"
|
||||||
|
for pid in "${LOCK_PIDS[@]}"; do
|
||||||
|
kill "$pid" &>/dev/null || true
|
||||||
|
done
|
||||||
|
rm -rf "$LOCK_DIR"
|
||||||
|
global_teardown
|
||||||
|
}
|
||||||
|
|
||||||
|
lock_script() {
|
||||||
|
declare BUILD_ID="$1" SCRIPT="$2"
|
||||||
|
echo "source '$PLUGIN_CORE_AVAILABLE_PATH/common/functions'; export DOKKU_BUILD_ID='$BUILD_ID'; $SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_lock_holder() {
|
||||||
|
declare BUILD_ID="$1"
|
||||||
|
/bin/bash -c "$(lock_script "$BUILD_ID" "acquire_advisory_lock '$LOCK_FILE' exclusive 'waiting' 'lock held'; while [[ ! -f '$LOCK_DIR/release' ]]; do sleep 0.2; done; release_advisory_lock '$LOCK_FILE'")" >/dev/null 2>&1 3>&- &
|
||||||
|
LOCK_PIDS+=("$!")
|
||||||
|
wait_for "[[ \"\$(cat '$LOCK_FILE' 2>/dev/null)\" == '$BUILD_ID' ]]"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for() {
|
||||||
|
declare CONDITION="$1"
|
||||||
|
local i
|
||||||
|
for i in $(seq 1 50); do
|
||||||
|
if eval "$CONDITION"; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.2
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "timed out waiting for: $CONDITION"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(deploy-lock) a failed acquire does not truncate the lock holder's build id" {
|
||||||
|
start_lock_holder "holder-a"
|
||||||
|
|
||||||
|
run /bin/bash -c "$(lock_script "other" "acquire_advisory_lock '$LOCK_FILE' exclusive 'waiting' 'lock held'")"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
assert_output_contains "lock held"
|
||||||
|
|
||||||
|
run /bin/bash -c "cat '$LOCK_FILE'"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "holder-a"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(deploy-lock) a waiter does not keep a stale lock after the holder removes the lock file" {
|
||||||
|
start_lock_holder "holder-a"
|
||||||
|
|
||||||
|
/bin/bash -c "$(lock_script "holder-b" "acquire_advisory_lock '$LOCK_FILE' waiting 'waiting' 'lock held'; touch '$LOCK_DIR/b.acquired'; exec sleep 60")" >"$LOCK_DIR/b.log" 2>&1 3>&- &
|
||||||
|
local B_PID="$!"
|
||||||
|
LOCK_PIDS+=("$B_PID")
|
||||||
|
wait_for "grep -q waiting '$LOCK_DIR/b.log'"
|
||||||
|
|
||||||
|
touch "$LOCK_DIR/release"
|
||||||
|
wait_for "[[ -f '$LOCK_DIR/b.acquired' ]]"
|
||||||
|
|
||||||
|
run /bin/bash -c "[[ /proc/$B_PID/fd/200 -ef '$LOCK_FILE' ]]"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "cat '$LOCK_FILE'"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "holder-b"
|
||||||
|
|
||||||
|
run /bin/bash -c "$(lock_script "holder-c" "acquire_advisory_lock '$LOCK_FILE' exclusive 'waiting' 'lock held'")"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
assert_output_contains "lock held"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(deploy-lock) release only removes the lock file it holds" {
|
||||||
|
start_lock_holder "holder-a"
|
||||||
|
|
||||||
|
run /bin/bash -c "rm -f '$LOCK_FILE' && echo 'replacement' >'$LOCK_FILE'"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
touch "$LOCK_DIR/release"
|
||||||
|
wait_for "! kill -0 ${LOCK_PIDS[0]} 2>/dev/null"
|
||||||
|
|
||||||
|
run /bin/bash -c "cat '$LOCK_FILE'"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "replacement"
|
||||||
|
}
|
||||||
@@ -10,11 +10,88 @@ setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
teardown() {
|
teardown() {
|
||||||
|
release_deploy_lock
|
||||||
|
docker image rm "dokku-test/$TEST_APP:second-procfile" "dokku-test/$TEST_APP:default-procfile" &>/dev/null || true
|
||||||
rm -f /home/dokku/.ssh/id_rsa.pub || true
|
rm -f /home/dokku/.ssh/id_rsa.pub || true
|
||||||
destroy_app
|
destroy_app
|
||||||
global_teardown
|
global_teardown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build_python_image() {
|
||||||
|
declare TAG="$1" PROCFILE="$2"
|
||||||
|
local TMP_IMAGE_DIR exit_code=0
|
||||||
|
TMP_IMAGE_DIR="$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX")"
|
||||||
|
cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/python/." "$TMP_IMAGE_DIR"
|
||||||
|
if [[ -n "$PROCFILE" ]]; then
|
||||||
|
cp "$TMP_IMAGE_DIR/$PROCFILE" "$TMP_IMAGE_DIR/Procfile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker image build -t "dokku-test/$TEST_APP:$TAG" -f "$TMP_IMAGE_DIR/alt.Dockerfile" "$TMP_IMAGE_DIR" || exit_code="$?"
|
||||||
|
rm -rf "$TMP_IMAGE_DIR"
|
||||||
|
return "$exit_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy_second_procfile_image() {
|
||||||
|
run build_python_image second-procfile second.Procfile
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku-test/$TEST_APP:second-procfile"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger procfile-get-command $TEST_APP web 5001"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "second.Procfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
hold_deploy_lock() {
|
||||||
|
DEPLOY_LOCK_FILE="$DOKKU_LIB_ROOT/data/apps/$TEST_APP/.deploy.lock"
|
||||||
|
DEPLOY_LOCK_HOLDER_PID_FILE="$(mktemp "/tmp/${DOKKU_DOMAIN}.XXXXX")"
|
||||||
|
chown dokku:dokku "$DEPLOY_LOCK_HOLDER_PID_FILE"
|
||||||
|
|
||||||
|
# the holder writes a build id like a deploy would, then becomes the sleep
|
||||||
|
# process so that killing its pid releases the lock
|
||||||
|
sudo -u dokku bash -c "exec 200>>'$DEPLOY_LOCK_FILE'; flock -n 200 || exit 1; echo test-lock-holder >'$DEPLOY_LOCK_FILE'; echo \$\$ >'$DEPLOY_LOCK_HOLDER_PID_FILE'; exec sleep 120" >/dev/null 2>&1 3>&- &
|
||||||
|
|
||||||
|
local i
|
||||||
|
for i in $(seq 1 20); do
|
||||||
|
if [[ -s "$DEPLOY_LOCK_HOLDER_PID_FILE" ]] && ! flock -n "$DEPLOY_LOCK_FILE" true; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "unable to acquire the deploy lock for $TEST_APP"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
release_deploy_lock() {
|
||||||
|
if [[ -z "$DEPLOY_LOCK_HOLDER_PID_FILE" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local holder_pid i
|
||||||
|
if [[ -s "$DEPLOY_LOCK_HOLDER_PID_FILE" ]]; then
|
||||||
|
holder_pid="$(cat "$DEPLOY_LOCK_HOLDER_PID_FILE")"
|
||||||
|
kill "$holder_pid" &>/dev/null || true
|
||||||
|
for i in $(seq 1 20); do
|
||||||
|
kill -0 "$holder_pid" &>/dev/null || break
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
rm -f "$DEPLOY_LOCK_FILE" "$DEPLOY_LOCK_HOLDER_PID_FILE"
|
||||||
|
DEPLOY_LOCK_HOLDER_PID_FILE=""
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy_branch_rev() {
|
||||||
|
sudo -u dokku git -C "/home/dokku/$TEST_APP" rev-parse master
|
||||||
|
}
|
||||||
|
|
||||||
@test "(git) git:from-image [missing]" {
|
@test "(git) git:from-image [missing]" {
|
||||||
run /bin/bash -c "dokku git:from-image $TEST_APP dokku/python:missing-tag"
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku/python:missing-tag"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
@@ -50,11 +127,21 @@ teardown() {
|
|||||||
assert_output_contains "Image exists on host, skipping pull"
|
assert_output_contains "Image exists on host, skipping pull"
|
||||||
assert_output_contains "No changes detected, skipping git commit"
|
assert_output_contains "No changes detected, skipping git commit"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:locked $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
|
||||||
run /bin/bash -c "dokku git:from-image --force $TEST_APP linuxserver/foldingathome:7.6.21"
|
run /bin/bash -c "dokku git:from-image --force $TEST_APP linuxserver/foldingathome:7.6.21"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_output_contains "Force pulling image"
|
assert_output_contains "Force pulling image"
|
||||||
assert_output_contains "No changes detected, skipping git commit"
|
assert_output_contains "No changes detected, skipping git commit"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:locked $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "(git) git:from-image [normal-custom-branch]" {
|
@test "(git) git:from-image [normal-custom-branch]" {
|
||||||
@@ -141,6 +228,17 @@ teardown() {
|
|||||||
assert_failure
|
assert_failure
|
||||||
assert_output "fatal: this operation must be run in a work tree"
|
assert_output "fatal: this operation must be run in a work tree"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:locked $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -151,6 +249,12 @@ teardown() {
|
|||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_success
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:latest"
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -161,6 +265,17 @@ teardown() {
|
|||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_failure
|
assert_failure
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku apps:locked $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:latest"
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -172,6 +287,12 @@ teardown() {
|
|||||||
assert_success
|
assert_success
|
||||||
assert_output_contains "No changes detected, skipping git commit" 0
|
assert_output_contains "No changes detected, skipping git commit" 0
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:v2"
|
||||||
|
|
||||||
run /bin/bash -c "docker image rm dokku-test/$TEST_APP:latest dokku-test/$TEST_APP:v2"
|
run /bin/bash -c "docker image rm dokku-test/$TEST_APP:latest dokku-test/$TEST_APP:v2"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -249,3 +370,235 @@ EOF
|
|||||||
assert_success
|
assert_success
|
||||||
assert_output_contains "linuxserver/foldingathome:7.6.21"
|
assert_output_contains "linuxserver/foldingathome:7.6.21"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "(git) git:from-image source-image after failed deploy and ps:rebuild" {
|
||||||
|
deploy_second_procfile_image
|
||||||
|
|
||||||
|
run build_python_image default-procfile
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku-test/$TEST_APP:default-procfile"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku ps:rebuild $TEST_APP"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger procfile-get-command $TEST_APP web 5001"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "second.Procfile"
|
||||||
|
assert_output_contains "first.Procfile" 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(git) git:from-image then git:from-archive ignores the previous source-image" {
|
||||||
|
deploy_second_procfile_image
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "tar -C ${BATS_TEST_DIRNAME}/../../tests/apps -cf - python | dokku git:from-archive $TEST_APP --"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "tar -C ${BATS_TEST_DIRNAME}/../../tests/apps -cf - python | dokku git:from-archive $TEST_APP --"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger procfile-get-command $TEST_APP web 5001"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "first.Procfile"
|
||||||
|
assert_output_contains "second.Procfile" 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(git) git:from-image then git push ignores the previous source-image" {
|
||||||
|
local TMP=$(mktemp -d "/tmp/${DOKKU_DOMAIN}.XXXXX")
|
||||||
|
trap 'rm -rf "$TMP"' INT TERM
|
||||||
|
|
||||||
|
deploy_second_procfile_image
|
||||||
|
|
||||||
|
rmdir "$TMP" && cp -r "${BATS_TEST_DIRNAME}/../../tests/apps/python" "$TMP"
|
||||||
|
run /bin/bash -c "cd $TMP && git init -q && git checkout -q -b master && git config user.email 'robot@example.com' && git config user.name 'Test Robot' && git add . && git commit -q -m 'initial commit' && git push --force dokku@${DOKKU_DOMAIN}:$TEST_APP master:master"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
rm -rf "$TMP"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku plugin:trigger procfile-get-command $TEST_APP web 5001"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "first.Procfile"
|
||||||
|
assert_output_contains "second.Procfile" 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(git) git:from-image then git:sync clears the source-image" {
|
||||||
|
deploy_second_procfile_image
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:sync $TEST_APP https://github.com/dokku/smoke-test-app.git 2.0.0"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku-test/$TEST_APP:second-procfile"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:sync --build $TEST_APP https://github.com/dokku/smoke-test-app.git 2.0.0"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "(git) source-image is unchanged when the deploy lock is held" {
|
||||||
|
deploy_second_procfile_image
|
||||||
|
|
||||||
|
run build_python_image default-procfile
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
|
||||||
|
local DEPLOY_REV
|
||||||
|
DEPLOY_REV="$(deploy_branch_rev)"
|
||||||
|
echo "deploy branch rev: $DEPLOY_REV"
|
||||||
|
|
||||||
|
hold_deploy_lock
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku-test/$TEST_APP:default-procfile"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
assert_output_contains "currently has a deploy lock in place"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run deploy_branch_rev
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "$DEPLOY_REV"
|
||||||
|
|
||||||
|
run /bin/bash -c "cat $DEPLOY_LOCK_FILE"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "test-lock-holder"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:sync $TEST_APP https://github.com/dokku/smoke-test-app.git 2.0.0"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_failure
|
||||||
|
assert_output_contains "currently has a deploy lock in place"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:second-procfile"
|
||||||
|
|
||||||
|
run deploy_branch_rev
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "$DEPLOY_REV"
|
||||||
|
|
||||||
|
run /bin/bash -c "cat $DEPLOY_LOCK_FILE"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "test-lock-holder"
|
||||||
|
|
||||||
|
release_deploy_lock
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:from-image $TEST_APP dokku-test/$TEST_APP:default-procfile"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output_contains "No changes detected, skipping git commit" 0
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:default-procfile"
|
||||||
|
}
|
||||||
|
|||||||
@@ -154,6 +154,12 @@ teardown() {
|
|||||||
assert_failure
|
assert_failure
|
||||||
assert_output "fatal: this operation must be run in a work tree"
|
assert_output "fatal: this operation must be run in a work tree"
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output ""
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -164,6 +170,12 @@ teardown() {
|
|||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_success
|
assert_success
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:latest"
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=true"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -174,6 +186,12 @@ teardown() {
|
|||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_failure
|
assert_failure
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:latest"
|
||||||
|
|
||||||
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
run /bin/bash -c "dokku config:set --no-restart $TEST_APP FAIL_ON_STARTUP=false"
|
||||||
echo "output: $output"
|
echo "output: $output"
|
||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
@@ -184,6 +202,12 @@ teardown() {
|
|||||||
echo "status: $status"
|
echo "status: $status"
|
||||||
assert_success
|
assert_success
|
||||||
assert_output_contains "No changes detected, skipping git commit" 0
|
assert_output_contains "No changes detected, skipping git commit" 0
|
||||||
|
|
||||||
|
run /bin/bash -c "dokku git:report $TEST_APP --git-source-image"
|
||||||
|
echo "output: $output"
|
||||||
|
echo "status: $status"
|
||||||
|
assert_success
|
||||||
|
assert_output "dokku-test/$TEST_APP:v2"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "(git) git:load-image [onbuild]" {
|
@test "(git) git:load-image [onbuild]" {
|
||||||
|
|||||||
Reference in New Issue
Block a user