# repeat to shut down each running app
+dokku ps:stop APP # repeat to shut down each running app
cd ~/dokku
git pull --tags origin master
diff --git a/docs/getting-started/where-to-get-help.md b/docs/getting-started/where-to-get-help.md
index b0f6fb591..f96dac057 100644
--- a/docs/getting-started/where-to-get-help.md
+++ b/docs/getting-started/where-to-get-help.md
@@ -4,7 +4,7 @@ If you’re stuck, there are a number of places you can get help:
## The Official Dokku Website
-- [http://dokku.viewdocs.io/dokku/](/dokku/)
+- [http://dokku.viewdocs.io/dokku/](/docs/)
The Official Dokku website is always a great place to visit. It features links to oft-used developer tools, community plugins, and guides on using dokku.
diff --git a/docs/home.html b/docs/home.html
index 402a9e9b8..95396896d 100644
--- a/docs/home.html
+++ b/docs/home.html
@@ -7,33 +7,33 @@
-
+
Dokku - The smallest PaaS implementation you've ever seen
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
@@ -98,12 +98,12 @@
$
- wget https://raw.githubusercontent.com/dokku/dokku/v0.7.2/bootstrap.sh
+ wget https://raw.githubusercontent.com/dokku/dokku/v0.9.4/bootstrap.sh
$
- sudo DOKKU_TAG=v0.7.2 bash bootstrap.sh
+ sudo DOKKU_TAG=v0.9.4 bash bootstrap.sh
# go to your server's IP and follow the web installer
@@ -173,7 +173,7 @@
diff --git a/docs/template.html b/docs/template.html
index 7c69c23c8..84b26c22f 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -7,29 +7,28 @@
-
Dokku - The smallest PaaS implementation you've ever seen
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
diff --git a/dokku b/dokku
index 15a65e353..a8f4a3606 100755
--- a/dokku
+++ b/dokku
@@ -7,17 +7,9 @@ export DOKKU_ROOT=${DOKKU_ROOT:=~dokku}
[[ -d $DOKKU_ROOT/.dokkurc ]] && for f in $DOKKU_ROOT/.dokkurc/*; do source "$f"; done
[[ $DOKKU_TRACE ]] && set -x
-case "$(lsb_release -si)" in
- Arch)
- export DOKKU_DISTRO=${DOKKU_DISTRO:="arch"}
- ;;
- Debian)
- export DOKKU_DISTRO=${DOKKU_DISTRO:="debian"}
- ;;
- *)
- export DOKKU_DISTRO=${DOKKU_DISTRO:="ubuntu"}
- ;;
-esac
+export DOKKU_DISTRO
+# shellcheck disable=SC1091
+DOKKU_DISTRO=$(. /etc/os-release > /dev/null 2>&1 || true ; echo "$ID")
export DOKKU_IMAGE=${DOKKU_IMAGE:="gliderlabs/herokuish"}
export DOKKU_LIB_ROOT=${DOKKU_LIB_PATH:="/var/lib/dokku"}
@@ -43,10 +35,12 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
parse_args "$@"
args=("$@")
+skip_arg=false
if [[ "${args[0]}" =~ ^--.* ]]; then
for arg in "$@"; do
+ $skip_arg && skip_arg=false && continue
if [[ "$arg" == "--app" ]]; then
- shift 2
+ shift 2 && skip_arg=true
elif [[ "$arg" =~ ^--.* ]]; then
shift 1
else
@@ -62,8 +56,12 @@ if [[ $(id -un) != "dokku" ]] && [[ ! $1 =~ plugin:* ]] && [[ ! $1 == "ssh-keys:
exit $?
fi
-if [[ $(id -un) != "root" && $1 =~ ^plugin:.* ]] || [[ $(id -un) != "root" && $1 == "ssh-keys:add" ]]; then
- dokku_log_fail "This command must be run as root"
+if [[ $1 =~ ^plugin:.* && $1 != "plugin:help" && $1 != "plugin:list" ]] || [[ $1 == "ssh-keys:add" ]]; then
+ if [[ $(id -un) != "root" ]]; then
+ dokku_log_fail "This command must be run as root"
+ else
+ export SSH_USER=root
+ fi
fi
if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; then
diff --git a/plugins/00_dokku-standard/core-post-deploy b/plugins/00_dokku-standard/core-post-deploy
new file mode 100755
index 000000000..7b21978cb
--- /dev/null
+++ b/plugins/00_dokku-standard/core-post-deploy
@@ -0,0 +1,23 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+
+cleanup_container_state() {
+ declare desc="core post-deploy state cleanup"
+ local trigger="core-post-deploy cleanup_container_state"
+ local APP="$1"; local PROCTYPES="$(egrep -v "^#" "$DOKKU_ROOT/$APP/DOKKU_SCALE" | awk -F '=' '{ print $1 }' | xargs)"
+ local CONTAINER_FILES="$(find "$DOKKU_ROOT/$APP" -maxdepth 1 -name "CONTAINER.*" -printf "%f\n" 2>/dev/null | sort -t . -k 3 -n | xargs)"
+
+ local CONTAINER_FILE
+ for CONTAINER_FILE in $CONTAINER_FILES; do
+ local CONTAINER_TYPE="$(awk -F '.' '{ print $2 }' <<< "$CONTAINER_FILE")"
+ if [[ "$(is_val_in_list "$CONTAINER_TYPE" "$PROCTYPES" " ")" == "false" ]]; then
+ dokku_log_info1 "Container type ($CONTAINER_TYPE) is no longer defined. Removing state"
+ local IP_FILE="${CONTAINER_FILE//CONTAINER/IP}"
+ local PORT_FILE="${CONTAINER_FILE//CONTAINER/PORT}"
+ rm -f "$DOKKU_ROOT/$APP/$CONTAINER_FILE" "$DOKKU_ROOT/$APP/$IP_FILE" "$DOKKU_ROOT/$APP/$PORT_FILE"
+ fi
+ done
+}
+
+cleanup_container_state "$@"
diff --git a/plugins/00_dokku-standard/exec-app-json-scripts b/plugins/00_dokku-standard/exec-app-json-scripts
index b6a6870de..7bb3e5809 100755
--- a/plugins/00_dokku-standard/exec-app-json-scripts
+++ b/plugins/00_dokku-standard/exec-app-json-scripts
@@ -42,7 +42,7 @@ execute_script() {
COMMAND+=" rm -rf /tmp/cache ; "
COMMAND+=" ln -sf /cache /tmp/cache ; "
COMMAND+=" fi ; "
- COMMAND+=" $SCRIPT_CMD ;"
+ COMMAND+=" $SCRIPT_CMD || exit 1;"
COMMAND+=" if [[ -d '/cache' ]]; then "
COMMAND+=" echo removing installation cache... ; "
COMMAND+=" rm -f /tmp/cache ; "
diff --git a/plugins/00_dokku-standard/install b/plugins/00_dokku-standard/install
index 9ef25cf45..b129367b7 100755
--- a/plugins/00_dokku-standard/install
+++ b/plugins/00_dokku-standard/install
@@ -30,7 +30,8 @@ if [[ $(systemctl 2> /dev/null) =~ -\.mount ]]; then
cat< /etc/systemd/system/dokku-redeploy.service
[Unit]
Description=Dokku app redeploy service
-After=docker.target
+Requires=docker.service
+After=docker.service
[Service]
Type=simple
diff --git a/plugins/00_dokku-standard/plugin.toml b/plugins/00_dokku-standard/plugin.toml
index b0a1b8939..2f379d7d8 100644
--- a/plugins/00_dokku-standard/plugin.toml
+++ b/plugins/00_dokku-standard/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core standard plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/00_dokku-standard/post-deploy b/plugins/00_dokku-standard/post-deploy
index 7c765c3b3..50603555e 100755
--- a/plugins/00_dokku-standard/post-deploy
+++ b/plugins/00_dokku-standard/post-deploy
@@ -5,30 +5,11 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/00_dokku-standard/exec-app-json-scripts"
exec_app_json_scripts() {
declare desc="core app.json scripts execution"
- local trigger="pre-deploy app_json_scripts"
+ local trigger="post-deploy app_json_scripts"
local APP="$1"; local IMAGE_TAG="$4"; local PHASE_SCRIPT_KEY="postdeploy"
dokku_log_info1 "Attempting to run scripts.dokku.$PHASE_SCRIPT_KEY from app.json (if defined)"
execute_script "$APP" "$IMAGE_TAG" "$PHASE_SCRIPT_KEY"
}
-cleanup_container_state() {
- declare desc="core post-deploy state cleanup"
- local trigger="post-deploy cleanup_container_state"
- local APP="$1"; local PROCTYPES="$(egrep -v "^#" "$DOKKU_ROOT/$APP/DOKKU_SCALE" | awk -F '=' '{ print $1 }' | xargs)"
- local CONTAINER_FILES="$(find "$DOKKU_ROOT/$APP" -maxdepth 1 -name "CONTAINER.*" -printf "%f\n" 2>/dev/null | sort -t . -k 3 -n | xargs)"
-
- local CONTAINER_FILE
- for CONTAINER_FILE in $CONTAINER_FILES; do
- local CONTAINER_TYPE="$(awk -F '.' '{ print $2 }' <<< "$CONTAINER_FILE")"
- if [[ "$(is_val_in_list "$CONTAINER_TYPE" "$PROCTYPES" " ")" == "false" ]]; then
- dokku_log_info1 "Container type ($CONTAINER_TYPE) is no longer defined. Removing state"
- local IP_FILE="${CONTAINER_FILE//CONTAINER/IP}"
- local PORT_FILE="${CONTAINER_FILE//CONTAINER/PORT}"
- rm -f "$DOKKU_ROOT/$APP/$CONTAINER_FILE" "$DOKKU_ROOT/$APP/$IP_FILE" "$DOKKU_ROOT/$APP/$PORT_FILE"
- fi
- done
-}
-
exec_app_json_scripts "$@"
-cleanup_container_state "$@"
diff --git a/plugins/00_dokku-standard/pre-deploy b/plugins/00_dokku-standard/pre-deploy
index 928637b41..c78257140 100755
--- a/plugins/00_dokku-standard/pre-deploy
+++ b/plugins/00_dokku-standard/pre-deploy
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_CORE_AVAILABLE_PATH/config/functions"
source "$PLUGIN_CORE_AVAILABLE_PATH/00_dokku-standard/exec-app-json-scripts"
+source "$PLUGIN_AVAILABLE_PATH/config/functions"
exec_app_json_scripts() {
declare desc="core app.json scripts execution"
diff --git a/plugins/00_dokku-standard/subcommands/report b/plugins/00_dokku-standard/subcommands/report
index 1a155e744..036d61079 100755
--- a/plugins/00_dokku-standard/subcommands/report
+++ b/plugins/00_dokku-standard/subcommands/report
@@ -18,7 +18,7 @@ dokku_report_cmd() {
docker run --rm -ti gliderlabs/herokuish:latest herokuish version
dokku_log_info1 "dokku version: $(dokku version)"
dokku_log_info1 "dokku plugins: "
- dokku plugin
+ dokku plugin:list
}
dokku_report_cmd "$@"
diff --git a/plugins/00_dokku-standard/subcommands/run b/plugins/00_dokku-standard/subcommands/run
index 4a0d04d66..1c413de7c 100755
--- a/plugins/00_dokku-standard/subcommands/run
+++ b/plugins/00_dokku-standard/subcommands/run
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_CORE_AVAILABLE_PATH/ps/functions"
+source "$PLUGIN_AVAILABLE_PATH/ps/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"
dokku_run_cmd() {
diff --git a/plugins/20_events/core-post-deploy b/plugins/20_events/core-post-deploy
new file mode 120000
index 000000000..5178a749f
--- /dev/null
+++ b/plugins/20_events/core-post-deploy
@@ -0,0 +1 @@
+hook
\ No newline at end of file
diff --git a/plugins/20_events/install b/plugins/20_events/install
index 99be9dc24..cdc5cb497 100755
--- a/plugins/20_events/install
+++ b/plugins/20_events/install
@@ -10,9 +10,10 @@ flag_rsyslog_needs_restart=n
# This can be done unconditionally as mkdir -p
# exits gracefully if the path already exists
+# shellcheck disable=SC2174
mkdir -m 775 -p "$DOKKU_LOGS_DIR"
case "$DOKKU_DISTRO" in
- arch|debian)
+ arch|debian|centos)
chgrp dokku "$DOKKU_LOGS_DIR"
;;
*)
@@ -23,7 +24,7 @@ esac
if [[ ! -f "$DOKKU_EVENTS_LOGFILE" ]]; then
touch "$DOKKU_EVENTS_LOGFILE"
case "$DOKKU_DISTRO" in
- arch|debian)
+ arch|debian|centos)
chgrp dokku "$DOKKU_EVENTS_LOGFILE"
;;
*)
@@ -59,7 +60,7 @@ $DOKKU_LOGS_DIR/*.log {
EOF
if [[ "$DOKKU_DISTRO" = "debian" ]]; then
- sed -i '/\s*su syslog dokku/d; s/\(create [0-7][0-7][0-7]\) syslog dokku/\1 root dokku/g' $DOKKU_LOGROTATE_FILE
+ sed -i 's/ syslog dokku$/root dokku/g' $DOKKU_LOGROTATE_FILE
fi
flag_rsyslog_needs_restart=y
diff --git a/plugins/20_events/plugin.toml b/plugins/20_events/plugin.toml
index 8221c20a6..d88622762 100644
--- a/plugins/20_events/plugin.toml
+++ b/plugins/20_events/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core events logging plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/apps/commands b/plugins/apps/commands
index c46d255b1..84cd4b172 100755
--- a/plugins/apps/commands
+++ b/plugins/apps/commands
@@ -1,36 +1,11 @@
#!/usr/bin/env bash
[[ " help apps:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
+source "$PLUGIN_AVAILABLE_PATH/apps/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | apps:help)
- help_content_func () {
- declare desc="return apps plugin help content"
- cat<, Create a new app
- apps:destroy , Permanently destroy an app
- apps:rename , Rename an app
-help_content
- }
-
- if [[ $1 = "apps:help" ]] ; then
- echo -e 'Usage: dokku apps[:COMMAND]'
- echo ''
- echo 'List your apps.'
- echo ''
- echo 'Example:'
- echo ''
- echo '$ dokku apps'
- echo '=====> My Apps'
- echo 'example'
- echo 'example2'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
+ apps_help_cmd "$@"
;;
*)
diff --git a/plugins/apps/internal-functions b/plugins/apps/internal-functions
new file mode 100755
index 000000000..4de6de77e
--- /dev/null
+++ b/plugins/apps/internal-functions
@@ -0,0 +1,51 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+
+apps_help_content_func() {
+ declare desc="return apps plugin help content"
+ cat< , Clones an app
+ apps:create , Create a new app
+ apps:destroy , Permanently destroy an app
+ apps:list, List your apps
+ apps:rename , Rename an app
+ apps:report [] [], Display report about an app
+help_content
+}
+
+apps_help_cmd() {
+ if [[ $1 = "apps:help" ]] ; then
+ echo -e 'Usage: dokku apps[:COMMAND]'
+ echo ''
+ echo 'Manage Dokku apps'
+ echo ''
+ echo 'Example:'
+ echo ''
+ echo '$ dokku apps:list'
+ echo '=====> My Apps'
+ echo 'example'
+ echo 'example2'
+ echo ''
+ echo 'Additional commands:'
+ apps_help_content_func | sort | column -c2 -t -s,
+ elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
+ apps_help_content_func
+ else
+ cat< /dev/null
+ find ./* -not \( -name .cache \) | cpio -pvdmu "$DOKKU_ROOT/$NEW_APP"
+ popd > /dev/null
+
+ if [[ -d "$NEW_CACHE_DIR" ]] && ! rmdir "$NEW_CACHE_DIR"; then
+ docker run "$DOKKU_GLOBAL_RUN_ARGS" --rm -v "$NEW_CACHE_DIR:/cache" "dokku/$OLD_APP" chmod 777 -R /cache
+ fi
+ rm -rf "$NEW_CACHE_DIR"
+
+ [[ -f "$DOKKU_ROOT/$NEW_APP/URLS" ]] && sed -i -e "s/$OLD_APP/$NEW_APP/g" "$DOKKU_ROOT/$NEW_APP/URLS"
+ [[ -f "$DOKKU_ROOT/$NEW_APP/VHOST" ]] && sed -i -e "s/$OLD_APP/$NEW_APP/g" "$DOKKU_ROOT/$NEW_APP/VHOST"
+ [[ -f "$DOKKU_ROOT/$NEW_APP/hooks/pre-receive" ]] && sed -i -e "s/git-hook $OLD_APP/git-hook $NEW_APP/g" "$DOKKU_ROOT/$NEW_APP/hooks/pre-receive"
+ [[ "$SKIP_REBUILD" == "true" ]] || ps_rebuild "$NEW_APP"
+ plugn trigger post-app-clone "$OLD_APP" "$NEW_APP"
+ echo "Cloning $OLD_APP to $NEW_APP... done"
+}
+
+apps_clone_cmd "$@"
diff --git a/plugins/apps/subcommands/default b/plugins/apps/subcommands/default
index 47f5973a6..ac2c04ed3 100755
--- a/plugins/apps/subcommands/default
+++ b/plugins/apps/subcommands/default
@@ -1,16 +1,6 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
-source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/apps/internal-functions"
-apps_main_cmd() {
- declare desc="lists all apps"
- local cmd="apps"
- local app
-
- dokku_log_info2_quiet "My Apps"
- for app in $(dokku_apps); do
- echo "$app"
- done
-}
-
-apps_main_cmd "$@"
+dokku_log_warn "Deprecated: Please use apps:list"
+apps_list_cmd "$@"
diff --git a/plugins/apps/subcommands/list b/plugins/apps/subcommands/list
new file mode 100755
index 000000000..b9573fac2
--- /dev/null
+++ b/plugins/apps/subcommands/list
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_AVAILABLE_PATH/apps/internal-functions"
+
+apps_list_cmd "$@"
diff --git a/plugins/apps/subcommands/rename b/plugins/apps/subcommands/rename
index 7ad59b08d..34da3df31 100755
--- a/plugins/apps/subcommands/rename
+++ b/plugins/apps/subcommands/rename
@@ -13,11 +13,11 @@ apps_rename_cmd() {
local NEW_APP="$3"
local OLD_CACHE_DIR="$DOKKU_ROOT/$OLD_APP/cache"
- mkdir -p "$DOKKU_ROOT/$NEW_APP"
if [[ -d "$OLD_CACHE_DIR" ]] && ! rmdir "$OLD_CACHE_DIR"; then
docker run "$DOKKU_GLOBAL_RUN_ARGS" --rm -v "$OLD_CACHE_DIR:/cache" "dokku/$OLD_APP" chmod 777 -R /cache
fi
rm -rf "$OLD_CACHE_DIR"
+ apps_create "$NEW_APP"
cp -a "$DOKKU_ROOT/$OLD_APP/." "$DOKKU_ROOT/$NEW_APP"
DOKKU_APPS_FORCE_DELETE=1 apps_destroy "$OLD_APP"
[[ -f "$DOKKU_ROOT/$NEW_APP/URLS" ]] && sed -i -e "s/$OLD_APP/$NEW_APP/g" "$DOKKU_ROOT/$NEW_APP/URLS"
diff --git a/plugins/apps/subcommands/report b/plugins/apps/subcommands/report
new file mode 100755
index 000000000..10e48e54c
--- /dev/null
+++ b/plugins/apps/subcommands/report
@@ -0,0 +1,92 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/apps/functions"
+
+report_single_app() {
+ declare APP="$1" INFO_FLAG="$2"
+ local APP_DIR="$DOKKU_ROOT/$APP"
+ local use_echo;
+ if [[ "$INFO_FLAG" == "true" ]]; then
+ use_echo=true
+ INFO_FLAG=""
+ fi
+ verify_app_name "$APP"
+ local DOKKU_APP_CIDS=$(get_app_container_ids "$APP")
+ if [[ -n $DOKKU_APP_CIDS ]]; then
+ local cid
+ for CID in $DOKKU_APP_CIDS; do
+ local APP_CONTAINER_STATUS
+ local DOKKU_APP_CID
+ DOKKU_APP_CID=$CID
+ APP_CONTAINER_STATUS=$(docker inspect -f '{{.State.Status}}' "$CID")
+ done
+ fi
+ local flag_map=(
+ "--app-dir: $APP_DIR"
+ "--git-sha: $(GIT_DIR="$APP_DIR" git rev-parse --short HEAD 2> /dev/null || false)"
+ "--app-cid: ${DOKKU_APP_CID:0:12}"
+ "--status: $APP_CONTAINER_STATUS"
+ )
+
+ if [[ -z "$INFO_FLAG" ]]; then
+ dokku_log_info2 "$APP"
+ if (is_deployed "$APP"); then
+ for flag in "${flag_map[@]}"; do
+ key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"
+ dokku_log_verbose "$(printf "%-20s %-25s" "${key^}" "${flag#*: }")"
+ done
+ else
+ if [[ "$use_echo" ]]; then
+ echo "not deployed"
+ else
+ dokku_log_fail "not deployed"
+ fi
+ fi
+ else
+ local match=false; local value_exists=false
+ for flag in "${flag_map[@]}"; do
+ valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)"
+ if [[ "$flag" == "${INFO_FLAG}:"* ]]; then
+ value=${flag#*: }
+ size="${#value}"
+ if [[ "$size" -ne 0 ]]; then
+ echo "$value" && match=true && value_exists=true
+ else
+ match=true
+ fi
+ fi
+ done
+ if [[ "$match" == "true" ]]; then
+ [[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed"
+ else
+ dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}"
+ fi
+ fi
+}
+
+apps_report_cmd() {
+ declare desc="displays the app report for one or more apps"
+ local cmd="apps:report"
+ local INSTALLED_APPS=$(dokku_apps)
+ local APP="$2" INFO_FLAG="$3"
+
+ if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
+ INFO_FLAG="$APP"
+ APP=""
+ fi
+
+ if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then
+ INFO_FLAG="true"
+ fi
+
+ if [[ -z "$APP" ]]; then
+ for app in $INSTALLED_APPS; do
+ report_single_app "$app" "$INFO_FLAG" | tee || true
+ done
+ else
+ report_single_app "$APP" "$INFO_FLAG"
+ fi
+}
+
+apps_report_cmd "$@"
diff --git a/plugins/build-env/plugin.toml b/plugins/build-env/plugin.toml
index ade77a4a7..c71e37113 100644
--- a/plugins/build-env/plugin.toml
+++ b/plugins/build-env/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core build-env plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/build-env/pre-build-buildpack b/plugins/build-env/pre-build-buildpack
index 537cececf..57d32917d 100755
--- a/plugins/build-env/pre-build-buildpack
+++ b/plugins/build-env/pre-build-buildpack
@@ -9,8 +9,8 @@ build_env_pre_build_buildpack() {
local APP="$1"; local IMAGE=$(get_app_image_name "$APP"); local BUILD_ENV=""
verify_app_name "$APP"
- [[ -z $(config_get --global CURL_CONNECT_TIMEOUT) ]] && config_set --global CURL_CONNECT_TIMEOUT=5
- [[ -z $(config_get --global CURL_TIMEOUT) ]] && config_set --global CURL_TIMEOUT=30
+ [[ -z $(config_get --global CURL_CONNECT_TIMEOUT) ]] && config_set --global CURL_CONNECT_TIMEOUT=90
+ [[ -z $(config_get --global CURL_TIMEOUT) ]] && config_set --global CURL_TIMEOUT=60
if [[ -n $(config_export global) ]]; then
local BUILD_ENV+=$'\n'
diff --git a/plugins/certs/commands b/plugins/certs/commands
index c34b902c8..9e13305ab 100755
--- a/plugins/certs/commands
+++ b/plugins/certs/commands
@@ -1,34 +1,11 @@
#!/usr/bin/env bash
[[ " help certs:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
+source "$PLUGIN_AVAILABLE_PATH/certs/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | certs:help)
- help_content_func () {
- declare desc="return certs plugin help content"
- cat< CRT KEY, Add an ssl endpoint to an app. Can also import from a tarball on stdin
- certs:chain CRT [CRT ...], [NOT IMPLEMENTED] Print the ordered and complete chain for the given certificate
- certs:generate DOMAIN, Generate a key and certificate signing request (and self-signed certificate)
- certs:info , Show certificate information for an ssl endpoint
- certs:key CRT KEY [KEY ...], [NOT IMPLEMENTED] Print the correct key for the given certificate
- certs:remove , Remove an SSL Endpoint from an app
- certs:rollback , [NOT IMPLEMENTED] Rollback an SSL Endpoint for an app
- certs:update CRT KEY, Update an SSL Endpoint on an app. Can also import from a tarball on stdin
-help_content
- }
-
- if [[ $1 = "certs:help" ]] ; then
- echo -e 'Usage: dokku certs:COMMAND'
- echo ''
- echo 'Manage Dokku apps SSL (TLS) certs.'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
+ certs_help_cmd "$@"
;;
*)
diff --git a/plugins/certs/functions b/plugins/certs/functions
index b64a14a56..a130c993e 100755
--- a/plugins/certs/functions
+++ b/plugins/certs/functions
@@ -26,6 +26,6 @@ get_ssl_hostnames() {
else
local SSL_HOSTNAMES=$SSL_HOSTNAME
fi
- echo -e "$SSL_HOSTNAMES"
+ echo -e "$SSL_HOSTNAMES" | sort -u
return 0
}
diff --git a/plugins/certs/internal-functions b/plugins/certs/internal-functions
new file mode 100755
index 000000000..77f4f0c79
--- /dev/null
+++ b/plugins/certs/internal-functions
@@ -0,0 +1,71 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/certs/functions"
+
+certs_help_content_func() {
+ declare desc="return certs plugin help content"
+ cat<, [DEPRECATED] Alternative for certs:report
+ certs:add CRT KEY, Add an ssl endpoint to an app. Can also import from a tarball on stdin
+ certs:chain CRT [CRT ...], [NOT IMPLEMENTED] Print the ordered and complete chain for the given certificate
+ certs:generate DOMAIN, Generate a key and certificate signing request (and self-signed certificate)
+ certs:info , [DEPRECATED] Alternative for certs:report
+ certs:key CRT KEY [KEY ...], [NOT IMPLEMENTED] Print the correct key for the given certificate
+ certs:remove , Remove an SSL Endpoint from an app
+ certs:report [] [], Displays an ssl report for one or more apps
+ certs:rollback , [NOT IMPLEMENTED] Rollback an SSL Endpoint for an app
+ certs:update CRT KEY, Update an SSL Endpoint on an app. Can also import from a tarball on stdin
+help_content
+}
+
+certs_help_cmd() {
+ if [[ $1 = "certs:help" ]] ; then
+ echo -e 'Usage: dokku certs[:COMMAND]'
+ echo ''
+ echo 'Manage Dokku apps SSL (TLS) certs.'
+ echo ''
+ echo 'Additional commands:'
+ certs_help_content_func | sort | column -c2 -t -s,
+ elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
+ certs_help_content_func
+ else
+ cat< /dev/null || true; rm -rf $CERTS_SET_TMP_WORK_DIR > /dev/null' RETURN
tar xvf - <&0
- local CRT_FILE_SEARCH=$(find . -not -path '*/\.*' -type f -name "*.crt")
+ local CRT_FILE_SEARCH=$(find . -not -path '*/\.*' -type f | grep ".crt$")
local CRT_FILE_COUNT=$(printf "%s" "$CRT_FILE_SEARCH" | grep -c '^')
if [[ $CRT_FILE_COUNT -lt 1 ]]; then
dokku_log_fail "Tar archive is missing .crt file"
@@ -54,7 +54,7 @@ certs_set() {
local CRT_FILE=$CRT_FILE_SEARCH
fi
- local KEY_FILE_SEARCH=$(find . -not -path '*/\.*' -type f -name "*.key")
+ local KEY_FILE_SEARCH=$(find . -not -path '*/\.*' -type f | grep ".key$")
local KEY_FILE_COUNT=$(printf "%s" "$KEY_FILE_SEARCH" | grep -c '^')
if [[ $KEY_FILE_COUNT -lt 1 ]]; then
dokku_log_fail "Tar archive is missing .key file"
@@ -72,7 +72,6 @@ certs_set() {
cp "$KEY_FILE" "$APP_SSL_PATH/server.key"
chmod 750 "$APP_SSL_PATH"
chmod 640 "$APP_SSL_PATH/server.crt" "$APP_SSL_PATH/server.key"
- cd "$DOKKU_ROOT"
plugn trigger post-certs-update "$APP"
plugn trigger post-domains-update "$APP"
}
diff --git a/plugins/certs/subcommands/default b/plugins/certs/subcommands/default
new file mode 100755
index 000000000..9591bedae
--- /dev/null
+++ b/plugins/certs/subcommands/default
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_AVAILABLE_PATH/certs/internal-functions"
+
+dokku_log_warn "Deprecated: Please use certs:report"
+certs_info_cmd "$@"
diff --git a/plugins/certs/subcommands/generate b/plugins/certs/subcommands/generate
index daa2caed8..10349e9f9 100755
--- a/plugins/certs/subcommands/generate
+++ b/plugins/certs/subcommands/generate
@@ -22,14 +22,15 @@ certs_generate_cmd() {
mkdir -p "$APP_SSL_PATH"
dokku_log_info1 "Installing certificate and key..."
- mv -f "$CERTS_GENERATE_TMP_WORK_DIR/server.key" "$CERTS_GENERATE_TMP_WORK_DIR/server.crt" "$APP_SSL_PATH"
+ mv -f "$CERTS_GENERATE_TMP_WORK_DIR/server.crt" "$CERTS_GENERATE_TMP_WORK_DIR/server.csr" "$CERTS_GENERATE_TMP_WORK_DIR/server.key" "$APP_SSL_PATH"
chmod 750 "$APP_SSL_PATH"
- chmod 640 "$APP_SSL_PATH/server.key" "$APP_SSL_PATH/server.crt"
+ chmod 640 "$APP_SSL_PATH/server.crt" "$APP_SSL_PATH/server.csr" "$APP_SSL_PATH/server.key"
+ plugn trigger post-certs-update "$APP"
[[ -n "$DOMAIN" ]] && (domains_add "$APP" "$DOMAIN" || plugn trigger post-domains-update "$APP")
dokku_log_info1 "The following is a certificate signing request that can be used"
dokku_log_info1 "to generate an 'officially' signed SSL certificate for $APP at $DOMAIN"
dokku_log_info1 "by a CA of your choosing."
- cat server.csr
+ cat "$APP_SSL_PATH/server.csr"
else
dokku_log_info1 "$APP has an SSL endpoint already defined"
fi
diff --git a/plugins/certs/subcommands/info b/plugins/certs/subcommands/info
index cfda760d8..9591bedae 100755
--- a/plugins/certs/subcommands/info
+++ b/plugins/certs/subcommands/info
@@ -1,38 +1,6 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
-source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_AVAILABLE_PATH/certs/functions"
-
-certs_info_cmd() {
- declare desc="prints SSL certificate info for app"
- local cmd="certs:info"
- [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
- verify_app_name "$2"
- local APP="$2"; local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
-
- if [[ -n "$APP_SSL_PATH" ]]; then
- dokku_log_info1 "Fetching SSL Endpoint info for $APP..."
- dokku_log_info1 "Certificate details:"
- dokku_log_info2 "Common Name(s): "
-
- for domain in $(get_ssl_hostnames "$APP" | xargs); do
- dokku_log_info2 " $domain"
- done
-
- dokku_log_info2 "Expires At: $(openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not After :" | awk -F " : " '{ print $2 }')"
- dokku_log_info2 "Issuer: $(openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Issuer:" | xargs | sed -e "s/Issuer: //g")"
- dokku_log_info2 "Starts At: $(openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not Before:" | awk -F ": " '{ print $2 }')"
- dokku_log_info2 "Subject: $(openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -subject | sed -e "s:subject= ::g"| sed -e "s:^/::g" | sed -e "s:/:; :g")"
- local SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver "$APP_SSL_PATH/server.crt" | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)"
- if [[ "$SSL_VERIFY_OUTPUT" == "OK" ]]; then
- local SSL_SELF_SIGNED="verified by a certificate authority."
- else
- local SSL_SELF_SIGNED="self signed."
- fi
- dokku_log_info2 "SSL certificate is $SSL_SELF_SIGNED"
- else
- dokku_log_info1 "$APP does not have an SSL endpoint"
- fi
-}
+source "$PLUGIN_AVAILABLE_PATH/certs/internal-functions"
+dokku_log_warn "Deprecated: Please use certs:report"
certs_info_cmd "$@"
diff --git a/plugins/certs/subcommands/report b/plugins/certs/subcommands/report
new file mode 100755
index 000000000..9d8c7d976
--- /dev/null
+++ b/plugins/certs/subcommands/report
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/certs/functions"
+
+fn-ssl-enabled() {
+ declare APP="$1"
+ local SSL_ENABLED=false
+
+ if is_ssl_enabled "$APP"; then
+ SSL_ENABLED=true
+ fi
+ echo "$SSL_ENABLED"
+}
+
+fn-ssl-expires-at() {
+ declare APP="$1"
+ local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
+
+ if is_ssl_enabled "$APP"; then
+ openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not After :" | awk -F " : " '{ print $2 }'
+ fi
+}
+
+fn-ssl-hostnames() {
+ declare APP="$1"
+
+ if is_ssl_enabled "$APP"; then
+ get_ssl_hostnames "$APP" | xargs
+ fi
+}
+
+fn-ssl-issuer() {
+ declare APP="$1"
+ local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
+
+ if is_ssl_enabled "$APP"; then
+ openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Issuer:" | xargs | sed -e "s/Issuer: //g"
+ fi
+}
+
+fn-ssl-starts-at() {
+ declare APP="$1"
+ local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
+
+ if is_ssl_enabled "$APP"; then
+ openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -text | grep "Not Before:" | awk -F ": " '{ print $2 }'
+ fi
+}
+
+fn-ssl-subject() {
+ declare APP="$1"
+ local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
+
+ if is_ssl_enabled "$APP"; then
+ openssl x509 -in "$APP_SSL_PATH/server.crt" -noout -subject | sed -e "s:subject= ::g"| sed -e "s:^/::g" | sed -e "s:/:; :g"
+ fi
+}
+
+fn-ssl-verified() {
+ declare APP="$1"
+ local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"
+ local SSL_VERIFY_OUTPUT=false SSL_SELF_SIGNED="self signed"
+
+ if ! is_ssl_enabled "$APP"; then
+ return
+ fi
+
+ SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver "$APP_SSL_PATH/server.crt" | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)"
+ if [[ "$SSL_VERIFY_OUTPUT" == "OK" ]]; then
+ SSL_SELF_SIGNED="verified by a certificate authority"
+ fi
+
+ echo "$SSL_SELF_SIGNED"
+}
+
+report_single_app() {
+ declare APP="$1" INFO_FLAG="$2"
+ local APP_DIR="$DOKKU_ROOT/$APP"
+ if [[ "$INFO_FLAG" == "true" ]]; then
+ INFO_FLAG=""
+ fi
+ verify_app_name "$APP"
+ local flag_map=(
+ "--ssl-dir: $APP_DIR/tls"
+ "--ssl-enabled: $(fn-ssl-enabled "$APP")"
+ "--ssl-hostnames: $(fn-ssl-hostnames "$APP")"
+ "--ssl-expires-at: $(fn-ssl-expires-at "$APP")"
+ "--ssl-issuer: $(fn-ssl-issuer "$APP")"
+ "--ssl-starts-at: $(fn-ssl-starts-at "$APP")"
+ "--ssl-subject: $(fn-ssl-subject "$APP")"
+ "--ssl-verified: $(fn-ssl-verified "$APP")"
+ )
+
+ if [[ -z "$INFO_FLAG" ]]; then
+ dokku_log_info2 "$APP ssl information"
+ for flag in "${flag_map[@]}"; do
+ key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"
+ dokku_log_verbose "$(printf "%-20s %-25s" "${key^}" "${flag#*: }")"
+ done
+ else
+ local match=false; local value_exists=false
+ for flag in "${flag_map[@]}"; do
+ valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)"
+ if [[ "$flag" == "${INFO_FLAG}:"* ]]; then
+ value=${flag#*: }
+ size="${#value}"
+ if [[ "$size" -ne 0 ]]; then
+ echo "$value" && match=true && value_exists=true
+ else
+ match=true
+ fi
+ fi
+ done
+ if [[ "$match" == "true" ]]; then
+ [[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed"
+ else
+ dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}"
+ fi
+ fi
+}
+
+certs_report_cmd() {
+ declare desc="displays an ssl report for one or more apps"
+ local cmd="certs:report"
+ local INSTALLED_APPS=$(dokku_apps)
+ local APP="$2" INFO_FLAG="$3"
+
+ if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
+ INFO_FLAG="$APP"
+ APP=""
+ fi
+
+ if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then
+ INFO_FLAG="true"
+ fi
+
+ if [[ -z "$APP" ]]; then
+ for app in $INSTALLED_APPS; do
+ report_single_app "$app" "$INFO_FLAG" | tee || true
+ done
+ else
+ report_single_app "$APP" "$INFO_FLAG"
+ fi
+}
+
+certs_report_cmd "$@"
diff --git a/plugins/checks/check-deploy b/plugins/checks/check-deploy
index 7652331e4..7afe7a56b 100755
--- a/plugins/checks/check-deploy
+++ b/plugins/checks/check-deploy
@@ -74,7 +74,7 @@ checks_check_deploy() {
# after successfully copying the file. Thus, we suppress stderr.
# ref: https://github.com/dotcloud/docker/issues/3986
local CHECK_DEPLOY_TMP_WORK_DIR=$(mktemp -d /tmp/dokku_CHECKS.XXXXX)
- docker cp $DOKKU_APP_CONTAINER_ID:/app/CHECKS "$CHECK_DEPLOY_TMP_WORK_DIR" 2> /dev/null || true
+ docker cp "$DOKKU_APP_CONTAINER_ID:/app/CHECKS" "$CHECK_DEPLOY_TMP_WORK_DIR" 2> /dev/null || true
local FILENAME=${CHECK_DEPLOY_TMP_WORK_DIR}/CHECKS
@@ -107,8 +107,8 @@ checks_check_deploy() {
dokku_log_info1 "Waiting for $DOKKU_DEFAULT_CHECKS_WAIT seconds ..."
sleep "$DOKKU_DEFAULT_CHECKS_WAIT"
- ! (is_container_running $DOKKU_APP_CONTAINER_ID) && dokku_log_fail "App container failed to start!!"
- local container_restarts=$(docker inspect -f "{{ .RestartCount }}" $DOKKU_APP_CONTAINER_ID)
+ ! (is_container_running "$DOKKU_APP_CONTAINER_ID") && dokku_log_fail "App container failed to start!!"
+ local container_restarts="$(docker inspect -f "{{ .RestartCount }}" "$DOKKU_APP_CONTAINER_ID")"
if [[ $container_restarts -ne 0 ]]; then
docker stop "$DOKKU_APP_CONTAINER_ID" || true
dokku_log_fail "App container failed to start!!"
diff --git a/plugins/checks/commands b/plugins/checks/commands
index 2400b5ee0..0aed38a1b 100755
--- a/plugins/checks/commands
+++ b/plugins/checks/commands
@@ -1,30 +1,11 @@
#!/usr/bin/env bash
[[ " help checks:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
+source "$PLUGIN_AVAILABLE_PATH/checks/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | checks:help)
- help_content_func () {
- declare desc="return checks plugin help content"
- cat<, Show zero-downtime status
- checks:disable [process-type(s)], Disable zero-downtime deployment for all processes (or comma-separated process-type list) ***WARNING: this will cause downtime during deployments***
- checks:enable [process-type(s)], Enable zero-downtime deployment for all processes (or comma-separated process-type list)
- checks:run [process-type(s)], Runs zero-downtime checks for all processes (or comma-separated process-type list)
- checks:skip [process-type(s)], Skip zero-downtime checks for all processes (or comma-separated process-type list)
-help_content
- }
-
- if [[ $1 = "checks:help" ]] ; then
- echo -e 'Usage: dokku checks[:COMAND]'
- echo ''
- echo 'Manage zero-downtime settings.'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
+ checks_help_cmd "$@"
;;
*)
diff --git a/plugins/checks/internal-functions b/plugins/checks/internal-functions
new file mode 100755
index 000000000..d0ebad465
--- /dev/null
+++ b/plugins/checks/internal-functions
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+
+checks_help_content_func() {
+ declare desc="return checks plugin help content"
+ cat<, [DEPRECATED] Alternative for checks:report
+ checks:disable [process-type(s)], Disable zero-downtime deployment for all processes (or comma-separated process-type list) ***WARNING: this will cause downtime during deployments***
+ checks:enable [process-type(s)], Enable zero-downtime deployment for all processes (or comma-separated process-type list)
+ checks:report [] [], Displays a checks report for one or more apps
+ checks:run [process-type(s)], Runs zero-downtime checks for all processes (or comma-separated process-type list)
+ checks:skip [process-type(s)], Skip zero-downtime checks for all processes (or comma-separated process-type list)
+help_content
+}
+
+checks_help_cmd() {
+ if [[ $1 = "checks:help" ]] ; then
+ echo -e 'Usage: dokku checks[:COMMAND]'
+ echo ''
+ echo 'Manage zero-downtime settings.'
+ echo ''
+ echo 'Additional commands:'
+ checks_help_content_func | sort | column -c2 -t -s,
+ elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
+ checks_help_content_func
+ else
+ cat<&2
}
dokku_log_fail() {
@@ -142,7 +142,7 @@ verify_app_name() {
local APP="$1"
[[ ! -n "$APP" ]] && dokku_log_fail "(verify_app_name) APP must not be null"
if [[ ! "$APP" =~ ^[a-z].* && ! "$APP" =~ ^[0-9].* ]]; then
- [[ -d "$DOKKU_ROOT/$APP" ]] && rm -rf "$DOKKU_ROOT/$APP"
+ [[ -d "$DOKKU_ROOT/$APP" ]] && rm -rf "${DOKKU_ROOT:?}/$APP"
dokku_log_fail "App name must begin with lowercase alphanumeric character"
fi
[[ ! -d "$DOKKU_ROOT/$APP" ]] && dokku_log_fail "App $APP does not exist"
@@ -211,7 +211,7 @@ get_running_image_tag() {
verify_app_name "$APP"
local CIDS=( $(get_app_container_ids "$APP") )
- local RUNNING_IMAGE_TAG=$(docker inspect -f '{{ .Config.Image }}' ${CIDS[0]} 2>/dev/null | awk -F: '{ print $2 }' || echo '')
+ local RUNNING_IMAGE_TAG=$(docker inspect -f '{{ .Config.Image }}' "${CIDS[0]}" 2>/dev/null | awk -F: '{ print $2 }' || echo '')
echo "$RUNNING_IMAGE_TAG"
}
@@ -251,7 +251,12 @@ parse_args() {
declare desc="top-level cli arg parser"
local next_index=1; local skip=false; local args=("$@")
for arg in "$@"; do
- $skip && skip=false && continue
+ if [[ "$skip" == "true" ]]; then
+ next_index=$(( next_index + 1 ))
+ skip=false
+ continue
+ fi
+
case "$arg" in
--quiet)
export DOKKU_QUIET_OUTPUT=1
@@ -272,7 +277,7 @@ parse_args() {
export DOKKU_APP_NAME=${args[$next_index]}; skip=true
;;
esac
- local next_index=$(( next_index + 1 ))
+ next_index=$(( next_index + 1 ))
done
return 0
}
@@ -326,10 +331,11 @@ get_app_container_ids() {
get_app_running_container_ids() {
declare desc="return list of running docker container ids for given app and optional container_type"
local APP="$1" CONTAINER_TYPE="$2"
+ local CIDS
verify_app_name "$APP"
! (is_deployed "$APP") && dokku_log_fail "App $APP has not been deployed"
- local CIDS=$(get_app_container_ids "$APP" "$CONTAINER_TYPE")
+ CIDS=$(get_app_container_ids "$APP" "$CONTAINER_TYPE")
for CID in $CIDS; do
local APP_CONTAINER_STATUS=$(docker inspect -f '{{.State.Running}}' "$CID" 2>/dev/null || true)
@@ -362,7 +368,7 @@ get_cmd_from_procfile() {
if [[ -f $DOKKU_PROCFILE ]]; then
local line; local name; local command
- while read line || [[ -n "$line" ]]; do
+ while read -r line || [[ -n "$line" ]]; do
if [[ -z "$line" ]] || [[ "$line" == "#"* ]]; then
continue
fi
@@ -525,7 +531,7 @@ dokku_deploy_cmd() {
source "$PLUGIN_AVAILABLE_PATH/config/functions"
source "$PLUGIN_AVAILABLE_PATH/proxy/functions"
- [[ -z $1 ]] && dokku_log_fail "Please specify an app to deploy"
+ [[ -z $1 ]] && dokku_log_fail "Please specify an app to run the command on"
local APP="$1"; local IMAGE_TAG="$2"; local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG")
verify_app_name "$APP"
plugn trigger pre-deploy "$APP" "$IMAGE_TAG"
@@ -554,6 +560,8 @@ dokku_deploy_cmd() {
dokku_log_info2 "stopping $APP.$PROC_TYPE ($cid)"
# shellcheck disable=SC2086
docker stop $DOCKER_STOP_TIME_ARG "$cid" &> /dev/null
+ # remove cid from oldids to skip the old container finish processing
+ oldids="$(remove_val_from_list "$cid" "$oldids" " ")"
done
fi
@@ -651,6 +659,7 @@ dokku_deploy_cmd() {
done < "$DOKKU_SCALE_FILE"
dokku_log_info1 "Running post-deploy"
+ plugn trigger core-post-deploy "$APP" "$port" "$ipaddr" "$IMAGE_TAG"
plugn trigger post-deploy "$APP" "$port" "$ipaddr" "$IMAGE_TAG"
# kill the old container
@@ -694,6 +703,8 @@ release_and_deploy() {
source "$PLUGIN_AVAILABLE_PATH/config/functions"
local APP="$1"; local IMAGE_TAG="$2"; local IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG")
+ local DOKKU_DOCKERFILE_PORTS
+
verify_app_name "$APP"
if verify_image "$IMAGE"; then
@@ -701,7 +712,7 @@ release_and_deploy() {
local IMAGE_SOURCE_TYPE="herokuish"
else
local IMAGE_SOURCE_TYPE="dockerfile"
- local DOKKU_DOCKERFILE_PORTS=$(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)
+ DOKKU_DOCKERFILE_PORTS=$(config_get "$APP" DOKKU_DOCKERFILE_PORTS || true)
if [[ -z "$DOKKU_DOCKERFILE_PORTS" ]]; then
local DOCKER_IMAGE_PORTS=$(get_exposed_ports_from_image "$IMAGE")
[[ -n "$DOCKER_IMAGE_PORTS" ]] && config_set --no-restart "$APP" DOKKU_DOCKERFILE_PORTS="$DOCKER_IMAGE_PORTS"
diff --git a/plugins/common/glide.lock b/plugins/common/glide.lock
new file mode 100644
index 000000000..4f5cbc783
--- /dev/null
+++ b/plugins/common/glide.lock
@@ -0,0 +1,8 @@
+hash: e438ca8cf1bdca3e9c984bb1ee70c0d58be9da52cbe13a079a7c6cbc14210a37
+updated: 2017-01-03T17:17:27.973373328-08:00
+imports:
+- name: github.com/codegangsta/inject
+ version: 33e0aa1cb7c019ccc3fbe049a8262a6403d30504
+- name: github.com/codeskyblue/go-sh
+ version: ceb46ec4630a726eeed51d0a70066c9e1102c48f
+testImports: []
diff --git a/plugins/common/glide.yaml b/plugins/common/glide.yaml
new file mode 100644
index 000000000..2d8bf9366
--- /dev/null
+++ b/plugins/common/glide.yaml
@@ -0,0 +1,3 @@
+package: .
+import:
+- package: github.com/codeskyblue/go-sh
diff --git a/plugins/common/plugin.toml b/plugins/common/plugin.toml
index 9a1675ca7..98192de39 100644
--- a/plugins/common/plugin.toml
+++ b/plugins/common/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core common plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/.gitignore b/plugins/common/vendor/github.com/codegangsta/inject/.gitignore
new file mode 100644
index 000000000..df3df8a90
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/.gitignore
@@ -0,0 +1,2 @@
+inject
+inject.test
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/LICENSE b/plugins/common/vendor/github.com/codegangsta/inject/LICENSE
new file mode 100644
index 000000000..eb68a0e05
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Jeremy Saenz
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/README.md b/plugins/common/vendor/github.com/codegangsta/inject/README.md
new file mode 100644
index 000000000..679abe01a
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/README.md
@@ -0,0 +1,92 @@
+# inject
+--
+ import "github.com/codegangsta/inject"
+
+Package inject provides utilities for mapping and injecting dependencies in
+various ways.
+
+Language Translations:
+* [简体中文](translations/README_zh_cn.md)
+
+## Usage
+
+#### func InterfaceOf
+
+```go
+func InterfaceOf(value interface{}) reflect.Type
+```
+InterfaceOf dereferences a pointer to an Interface type. It panics if value is
+not an pointer to an interface.
+
+#### type Applicator
+
+```go
+type Applicator interface {
+ // Maps dependencies in the Type map to each field in the struct
+ // that is tagged with 'inject'. Returns an error if the injection
+ // fails.
+ Apply(interface{}) error
+}
+```
+
+Applicator represents an interface for mapping dependencies to a struct.
+
+#### type Injector
+
+```go
+type Injector interface {
+ Applicator
+ Invoker
+ TypeMapper
+ // SetParent sets the parent of the injector. If the injector cannot find a
+ // dependency in its Type map it will check its parent before returning an
+ // error.
+ SetParent(Injector)
+}
+```
+
+Injector represents an interface for mapping and injecting dependencies into
+structs and function arguments.
+
+#### func New
+
+```go
+func New() Injector
+```
+New returns a new Injector.
+
+#### type Invoker
+
+```go
+type Invoker interface {
+ // Invoke attempts to call the interface{} provided as a function,
+ // providing dependencies for function arguments based on Type. Returns
+ // a slice of reflect.Value representing the returned values of the function.
+ // Returns an error if the injection fails.
+ Invoke(interface{}) ([]reflect.Value, error)
+}
+```
+
+Invoker represents an interface for calling functions via reflection.
+
+#### type TypeMapper
+
+```go
+type TypeMapper interface {
+ // Maps the interface{} value based on its immediate type from reflect.TypeOf.
+ Map(interface{}) TypeMapper
+ // Maps the interface{} value based on the pointer of an Interface provided.
+ // This is really only useful for mapping a value as an interface, as interfaces
+ // cannot at this time be referenced directly without a pointer.
+ MapTo(interface{}, interface{}) TypeMapper
+ // Provides a possibility to directly insert a mapping based on type and value.
+ // This makes it possible to directly map type arguments not possible to instantiate
+ // with reflect like unidirectional channels.
+ Set(reflect.Type, reflect.Value) TypeMapper
+ // Returns the Value that is mapped to the current type. Returns a zeroed Value if
+ // the Type has not been mapped.
+ Get(reflect.Type) reflect.Value
+}
+```
+
+TypeMapper represents an interface for mapping interface{} values based on type.
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/inject.go b/plugins/common/vendor/github.com/codegangsta/inject/inject.go
new file mode 100644
index 000000000..3ff713c8a
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/inject.go
@@ -0,0 +1,187 @@
+// Package inject provides utilities for mapping and injecting dependencies in various ways.
+package inject
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// Injector represents an interface for mapping and injecting dependencies into structs
+// and function arguments.
+type Injector interface {
+ Applicator
+ Invoker
+ TypeMapper
+ // SetParent sets the parent of the injector. If the injector cannot find a
+ // dependency in its Type map it will check its parent before returning an
+ // error.
+ SetParent(Injector)
+}
+
+// Applicator represents an interface for mapping dependencies to a struct.
+type Applicator interface {
+ // Maps dependencies in the Type map to each field in the struct
+ // that is tagged with 'inject'. Returns an error if the injection
+ // fails.
+ Apply(interface{}) error
+}
+
+// Invoker represents an interface for calling functions via reflection.
+type Invoker interface {
+ // Invoke attempts to call the interface{} provided as a function,
+ // providing dependencies for function arguments based on Type. Returns
+ // a slice of reflect.Value representing the returned values of the function.
+ // Returns an error if the injection fails.
+ Invoke(interface{}) ([]reflect.Value, error)
+}
+
+// TypeMapper represents an interface for mapping interface{} values based on type.
+type TypeMapper interface {
+ // Maps the interface{} value based on its immediate type from reflect.TypeOf.
+ Map(interface{}) TypeMapper
+ // Maps the interface{} value based on the pointer of an Interface provided.
+ // This is really only useful for mapping a value as an interface, as interfaces
+ // cannot at this time be referenced directly without a pointer.
+ MapTo(interface{}, interface{}) TypeMapper
+ // Provides a possibility to directly insert a mapping based on type and value.
+ // This makes it possible to directly map type arguments not possible to instantiate
+ // with reflect like unidirectional channels.
+ Set(reflect.Type, reflect.Value) TypeMapper
+ // Returns the Value that is mapped to the current type. Returns a zeroed Value if
+ // the Type has not been mapped.
+ Get(reflect.Type) reflect.Value
+}
+
+type injector struct {
+ values map[reflect.Type]reflect.Value
+ parent Injector
+}
+
+// InterfaceOf dereferences a pointer to an Interface type.
+// It panics if value is not an pointer to an interface.
+func InterfaceOf(value interface{}) reflect.Type {
+ t := reflect.TypeOf(value)
+
+ for t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Interface {
+ panic("Called inject.InterfaceOf with a value that is not a pointer to an interface. (*MyInterface)(nil)")
+ }
+
+ return t
+}
+
+// New returns a new Injector.
+func New() Injector {
+ return &injector{
+ values: make(map[reflect.Type]reflect.Value),
+ }
+}
+
+// Invoke attempts to call the interface{} provided as a function,
+// providing dependencies for function arguments based on Type.
+// Returns a slice of reflect.Value representing the returned values of the function.
+// Returns an error if the injection fails.
+// It panics if f is not a function
+func (inj *injector) Invoke(f interface{}) ([]reflect.Value, error) {
+ t := reflect.TypeOf(f)
+
+ var in = make([]reflect.Value, t.NumIn()) //Panic if t is not kind of Func
+ for i := 0; i < t.NumIn(); i++ {
+ argType := t.In(i)
+ val := inj.Get(argType)
+ if !val.IsValid() {
+ return nil, fmt.Errorf("Value not found for type %v", argType)
+ }
+
+ in[i] = val
+ }
+
+ return reflect.ValueOf(f).Call(in), nil
+}
+
+// Maps dependencies in the Type map to each field in the struct
+// that is tagged with 'inject'.
+// Returns an error if the injection fails.
+func (inj *injector) Apply(val interface{}) error {
+ v := reflect.ValueOf(val)
+
+ for v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ if v.Kind() != reflect.Struct {
+ return nil // Should not panic here ?
+ }
+
+ t := v.Type()
+
+ for i := 0; i < v.NumField(); i++ {
+ f := v.Field(i)
+ structField := t.Field(i)
+ if f.CanSet() && (structField.Tag == "inject" || structField.Tag.Get("inject") != "") {
+ ft := f.Type()
+ v := inj.Get(ft)
+ if !v.IsValid() {
+ return fmt.Errorf("Value not found for type %v", ft)
+ }
+
+ f.Set(v)
+ }
+
+ }
+
+ return nil
+}
+
+// Maps the concrete value of val to its dynamic type using reflect.TypeOf,
+// It returns the TypeMapper registered in.
+func (i *injector) Map(val interface{}) TypeMapper {
+ i.values[reflect.TypeOf(val)] = reflect.ValueOf(val)
+ return i
+}
+
+func (i *injector) MapTo(val interface{}, ifacePtr interface{}) TypeMapper {
+ i.values[InterfaceOf(ifacePtr)] = reflect.ValueOf(val)
+ return i
+}
+
+// Maps the given reflect.Type to the given reflect.Value and returns
+// the Typemapper the mapping has been registered in.
+func (i *injector) Set(typ reflect.Type, val reflect.Value) TypeMapper {
+ i.values[typ] = val
+ return i
+}
+
+func (i *injector) Get(t reflect.Type) reflect.Value {
+ val := i.values[t]
+
+ if val.IsValid() {
+ return val
+ }
+
+ // no concrete types found, try to find implementors
+ // if t is an interface
+ if t.Kind() == reflect.Interface {
+ for k, v := range i.values {
+ if k.Implements(t) {
+ val = v
+ break
+ }
+ }
+ }
+
+ // Still no type found, try to look it up on the parent
+ if !val.IsValid() && i.parent != nil {
+ val = i.parent.Get(t)
+ }
+
+ return val
+
+}
+
+func (i *injector) SetParent(parent Injector) {
+ i.parent = parent
+}
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/inject_test.go b/plugins/common/vendor/github.com/codegangsta/inject/inject_test.go
new file mode 100644
index 000000000..eb94471d3
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/inject_test.go
@@ -0,0 +1,159 @@
+package inject_test
+
+import (
+ "fmt"
+ "github.com/codegangsta/inject"
+ "reflect"
+ "testing"
+)
+
+type SpecialString interface {
+}
+
+type TestStruct struct {
+ Dep1 string `inject:"t" json:"-"`
+ Dep2 SpecialString `inject`
+ Dep3 string
+}
+
+type Greeter struct {
+ Name string
+}
+
+func (g *Greeter) String() string {
+ return "Hello, My name is" + g.Name
+}
+
+/* Test Helpers */
+func expect(t *testing.T, a interface{}, b interface{}) {
+ if a != b {
+ t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
+ }
+}
+
+func refute(t *testing.T, a interface{}, b interface{}) {
+ if a == b {
+ t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
+ }
+}
+
+func Test_InjectorInvoke(t *testing.T) {
+ injector := inject.New()
+ expect(t, injector == nil, false)
+
+ dep := "some dependency"
+ injector.Map(dep)
+ dep2 := "another dep"
+ injector.MapTo(dep2, (*SpecialString)(nil))
+ dep3 := make(chan *SpecialString)
+ dep4 := make(chan *SpecialString)
+ typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem())
+ typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem())
+ injector.Set(typRecv, reflect.ValueOf(dep3))
+ injector.Set(typSend, reflect.ValueOf(dep4))
+
+ _, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) {
+ expect(t, d1, dep)
+ expect(t, d2, dep2)
+ expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem())
+ expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem())
+ expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir)
+ expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir)
+ })
+
+ expect(t, err, nil)
+}
+
+func Test_InjectorInvokeReturnValues(t *testing.T) {
+ injector := inject.New()
+ expect(t, injector == nil, false)
+
+ dep := "some dependency"
+ injector.Map(dep)
+ dep2 := "another dep"
+ injector.MapTo(dep2, (*SpecialString)(nil))
+
+ result, err := injector.Invoke(func(d1 string, d2 SpecialString) string {
+ expect(t, d1, dep)
+ expect(t, d2, dep2)
+ return "Hello world"
+ })
+
+ expect(t, result[0].String(), "Hello world")
+ expect(t, err, nil)
+}
+
+func Test_InjectorApply(t *testing.T) {
+ injector := inject.New()
+
+ injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil))
+
+ s := TestStruct{}
+ err := injector.Apply(&s)
+ expect(t, err, nil)
+
+ expect(t, s.Dep1, "a dep")
+ expect(t, s.Dep2, "another dep")
+ expect(t, s.Dep3, "")
+}
+
+func Test_InterfaceOf(t *testing.T) {
+ iType := inject.InterfaceOf((*SpecialString)(nil))
+ expect(t, iType.Kind(), reflect.Interface)
+
+ iType = inject.InterfaceOf((**SpecialString)(nil))
+ expect(t, iType.Kind(), reflect.Interface)
+
+ // Expecting nil
+ defer func() {
+ rec := recover()
+ refute(t, rec, nil)
+ }()
+ iType = inject.InterfaceOf((*testing.T)(nil))
+}
+
+func Test_InjectorSet(t *testing.T) {
+ injector := inject.New()
+ typ := reflect.TypeOf("string")
+ typSend := reflect.ChanOf(reflect.SendDir, typ)
+ typRecv := reflect.ChanOf(reflect.RecvDir, typ)
+
+ // instantiating unidirectional channels is not possible using reflect
+ // http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064
+ chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
+ chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
+
+ injector.Set(typSend, chanSend)
+ injector.Set(typRecv, chanRecv)
+
+ expect(t, injector.Get(typSend).IsValid(), true)
+ expect(t, injector.Get(typRecv).IsValid(), true)
+ expect(t, injector.Get(chanSend.Type()).IsValid(), false)
+}
+
+func Test_InjectorGet(t *testing.T) {
+ injector := inject.New()
+
+ injector.Map("some dependency")
+
+ expect(t, injector.Get(reflect.TypeOf("string")).IsValid(), true)
+ expect(t, injector.Get(reflect.TypeOf(11)).IsValid(), false)
+}
+
+func Test_InjectorSetParent(t *testing.T) {
+ injector := inject.New()
+ injector.MapTo("another dep", (*SpecialString)(nil))
+
+ injector2 := inject.New()
+ injector2.SetParent(injector)
+
+ expect(t, injector2.Get(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true)
+}
+
+func TestInjectImplementors(t *testing.T) {
+ injector := inject.New()
+ g := &Greeter{"Jeremy"}
+ injector.Map(g)
+
+ expect(t, injector.Get(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
+}
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/translations/README_zh_cn.md b/plugins/common/vendor/github.com/codegangsta/inject/translations/README_zh_cn.md
new file mode 100644
index 000000000..0ac3d3f55
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/translations/README_zh_cn.md
@@ -0,0 +1,85 @@
+# inject
+--
+ import "github.com/codegangsta/inject"
+
+inject包提供了多种对实体的映射和依赖注入方式。
+
+## 用法
+
+#### func InterfaceOf
+
+```go
+func InterfaceOf(value interface{}) reflect.Type
+```
+函数InterfaceOf返回指向接口类型的指针。如果传入的value值不是指向接口的指针,将抛出一个panic异常。
+
+#### type Applicator
+
+```go
+type Applicator interface {
+ // 在Type map中维持对结构体中每个域的引用并用'inject'来标记
+ // 如果注入失败将会返回一个error.
+ Apply(interface{}) error
+}
+```
+
+Applicator接口表示到结构体的依赖映射关系。
+
+#### type Injector
+
+```go
+type Injector interface {
+ Applicator
+ Invoker
+ TypeMapper
+ // SetParent用来设置父injector. 如果在当前injector的Type map中找不到依赖,
+ // 将会继续从它的父injector中找,直到返回error.
+ SetParent(Injector)
+}
+```
+
+Injector接口表示对结构体、函数参数的映射和依赖注入。
+
+#### func New
+
+```go
+func New() Injector
+```
+New创建并返回一个Injector.
+
+#### type Invoker
+
+```go
+type Invoker interface {
+ // Invoke尝试将interface{}作为一个函数来调用,并基于Type为函数提供参数。
+ // 它将返回reflect.Value的切片,其中存放原函数的返回值。
+ // 如果注入失败则返回error.
+ Invoke(interface{}) ([]reflect.Value, error)
+}
+```
+
+Invoker接口表示通过反射进行函数调用。
+
+#### type TypeMapper
+
+```go
+type TypeMapper interface {
+ // 基于调用reflect.TypeOf得到的类型映射interface{}的值。
+ Map(interface{}) TypeMapper
+ // 基于提供的接口的指针映射interface{}的值。
+ // 该函数仅用来将一个值映射为接口,因为接口无法不通过指针而直接引用到。
+ MapTo(interface{}, interface{}) TypeMapper
+ // 为直接插入基于类型和值的map提供一种可能性。
+ // 它使得这一类直接映射成为可能:无法通过反射直接实例化的类型参数,如单向管道。
+ Set(reflect.Type, reflect.Value) TypeMapper
+ // 返回映射到当前类型的Value. 如果Type没被映射,将返回对应的零值。
+ Get(reflect.Type) reflect.Value
+}
+```
+
+TypeMapper接口用来表示基于类型到接口值的映射。
+
+
+## 译者
+
+张强 (qqbunny@yeah.net)
\ No newline at end of file
diff --git a/plugins/common/vendor/github.com/codegangsta/inject/update_readme.sh b/plugins/common/vendor/github.com/codegangsta/inject/update_readme.sh
new file mode 100644
index 000000000..497f9a577
--- /dev/null
+++ b/plugins/common/vendor/github.com/codegangsta/inject/update_readme.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+go get github.com/robertkrimen/godocdown/godocdown
+godocdown > README.md
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/LICENSE b/plugins/common/vendor/github.com/codeskyblue/go-sh/LICENSE
new file mode 100644
index 000000000..e06d20818
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/LICENSE
@@ -0,0 +1,202 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/OLD_README.md b/plugins/common/vendor/github.com/codeskyblue/go-sh/OLD_README.md
new file mode 100644
index 000000000..7e899402d
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/OLD_README.md
@@ -0,0 +1,69 @@
+## OLD README
+First give you a full example, I will explain every command below.
+
+ session := sh.NewSession()
+ session.Env["PATH"] = "/usr/bin:/bin"
+ session.Stdout = os.Stdout
+ session.Stderr = os.Stderr
+ session.Alias("ll", "ls", "-l")
+ session.ShowCMD = true // enable for debug
+ var err error
+ err = session.Call("ll", "/")
+ if err != nil {
+ log.Fatal(err)
+ }
+ ret, err := session.Capture("pwd", sh.Dir("/home")) # wraper of session.Call
+ if err != nil {
+ log.Fatal(err)
+ }
+ # ret is "/home\n"
+ fmt.Println(ret)
+
+create a new Session
+
+ session := sh.NewSession()
+
+use alias like this
+
+ session.Alias("ll", "ls", "-l") # like alias ll='ls -l'
+
+set current env like this
+
+ session.Env["BUILD_ID"] = "123" # like export BUILD_ID=123
+
+set current directory
+
+ session.Set(sh.Dir("/")) # like cd /
+
+pipe is also supported
+
+ session.Command("echo", "hello\tworld").Command("cut", "-f2")
+ // output should be "world"
+ session.Run()
+
+test, the build in command support
+
+ session.Test("d", "dir") // test dir
+ session.Test("f", "file) // test regular file
+
+with `Alias Env Set Call Capture Command` a shell scripts can be easily converted into golang program. below is a shell script.
+
+ #!/bin/bash -
+ #
+ export PATH=/usr/bin:/bin
+ alias ll='ls -l'
+ cd /usr
+ if test -d "local"
+ then
+ ll local | awk '{print $1, $NF}'
+ fi
+
+convert to golang, will be
+
+ s := sh.NewSession()
+ s.Env["PATH"] = "/usr/bin:/bin"
+ s.Set(sh.Dir("/usr"))
+ s.Alias("ll", "ls", "-l")
+ if s.Test("d", "local") {
+ s.Command("ll", "local").Command("awk", "{print $1, $NF}").Run()
+ }
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/README.md b/plugins/common/vendor/github.com/codeskyblue/go-sh/README.md
new file mode 100644
index 000000000..28614708d
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/README.md
@@ -0,0 +1,85 @@
+## go-sh
+[](https://app.wercker.com/project/bykey/009acbd4f00ccc6de7e2554e12a50d84)
+[](http://gowalker.org/github.com/codeskyblue/go-sh)
+
+*If you depend on the old api, see tag: v.0.1*
+
+install: `go get github.com/codeskyblue/go-sh`
+
+Pipe Example:
+
+ package main
+
+ import "github.com/codeskyblue/go-sh"
+
+ func main() {
+ sh.Command("echo", "hello\tworld").Command("cut", "-f2").Run()
+ }
+
+Because I like os/exec, `go-sh` is very much modelled after it. However, `go-sh` provides a better experience.
+
+These are some of its features:
+
+* keep the variable environment (e.g. export)
+* alias support (e.g. alias in shell)
+* remember current dir
+* pipe command
+* shell build-in commands echo & test
+* timeout support
+
+Examples are important:
+
+ sh: echo hello
+ go: sh.Command("echo", "hello").Run()
+
+ sh: export BUILD_ID=123
+ go: s = sh.NewSession().SetEnv("BUILD_ID", "123")
+
+ sh: alias ll='ls -l'
+ go: s = sh.NewSession().Alias('ll', 'ls', '-l')
+
+ sh: (cd /; pwd)
+ go: sh.Command("pwd", sh.Dir("/")).Run()
+
+ sh: test -d data || mkdir data
+ go: if ! sh.Test("dir", "data") { sh.Command("mkdir", "data").Run() }
+
+ sh: cat first second | awk '{print $1}'
+ go: sh.Command("cat", "first", "second").Command("awk", "{print $1}").Run()
+
+ sh: count=$(echo "one two three" | wc -w)
+ go: count, err := sh.Echo("one two three").Command("wc", "-w").Output()
+
+ sh(in ubuntu): timeout 1s sleep 3
+ go: c := sh.Command("sleep", "3"); c.Start(); c.WaitTimeout(time.Second) # default SIGKILL
+ go: out, err := sh.Command("sleep", "3").SetTimeout(time.Second).Output() # set session timeout and get output)
+
+ sh: echo hello | cat
+ go: out, err := sh.Command("cat").SetInput("hello").Output()
+
+ sh: cat # read from stdin
+ go: out, err := sh.Command("cat").SetStdin(os.Stdin).Output()
+
+If you need to keep env and dir, it is better to create a session
+
+ session := sh.NewSession()
+ session.SetEnv("BUILD_ID", "123")
+ session.SetDir("/")
+ # then call cmd
+ session.Command("echo", "hello").Run()
+ # set ShowCMD to true for easily debug
+ session.ShowCMD = true
+
+for more information, it better to see docs.
+[](http://gowalker.org/github.com/codeskyblue/go-sh)
+
+### contribute
+If you love this project, starring it will encourage the coder. Pull requests are welcome.
+
+support the author: [alipay](https://me.alipay.com/goskyblue)
+
+### thanks
+this project is based on . thanks for the author.
+
+# the reason to use Go shell
+Sometimes we need to write shell scripts, but shell scripts are not good at working cross platform, Go, on the other hand, is good at that. Is there a good way to use Go to write shell like scripts? Using go-sh we can do this now.
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/example/example1.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/example1.go
new file mode 100644
index 000000000..cdc7f6c39
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/example1.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/codeskyblue/go-sh"
+)
+
+func main() {
+ sh.Command("echo", "hello").Run()
+ out, err := sh.Command("echo", "hello").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println("output is", string(out))
+
+ var a int
+ sh.Command("echo", "2").UnmarshalJSON(&a)
+ fmt.Println("a =", a)
+
+ s := sh.NewSession()
+ s.Alias("hi", "echo", "hi")
+ s.Command("hi", "boy").Run()
+
+ fmt.Print("pwd = ")
+ s.Command("pwd", sh.Dir("/")).Run()
+
+ if !sh.Test("dir", "data") {
+ sh.Command("echo", "mkdir", "data").Run()
+ }
+
+ sh.Command("echo", "hello", "world").
+ Command("awk", `{print "second arg is "$2}`).Run()
+ s.ShowCMD = true
+ s.Command("echo", "hello", "world").
+ Command("awk", `{print "second arg is "$2}`).Run()
+
+ s.SetEnv("BUILD_ID", "123").Command("bash", "-c", "echo $BUILD_ID").Run()
+ s.Command("bash", "-c", "echo current shell is $SHELL").Run()
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/example/less/less.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/less/less.go
new file mode 100644
index 000000000..ffced2e81
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/less/less.go
@@ -0,0 +1,7 @@
+package main
+
+import "github.com/codeskyblue/go-sh"
+
+func main() {
+ sh.Command("less", "less.go").Run()
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/example/tail/tailf.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/tail/tailf.go
new file mode 100644
index 000000000..b7ecd3bed
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/tail/tailf.go
@@ -0,0 +1,17 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "github.com/codeskyblue/go-sh"
+)
+
+func main() {
+ flag.Parse()
+ if flag.NArg() != 1 {
+ fmt.Println("Usage: PROGRAM ")
+ return
+ }
+ sh.Command("tail", "-f", flag.Arg(0)).Run()
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/example/timeout/timeout.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/timeout/timeout.go
new file mode 100644
index 000000000..852908a47
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/example/timeout/timeout.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "fmt"
+ "time"
+
+ sh "github.com/codeskyblue/go-sh"
+)
+
+func main() {
+ c := sh.Command("sleep", "3")
+ c.Start()
+ err := c.WaitTimeout(time.Second * 1)
+ if err != nil {
+ fmt.Printf("timeout should happend: %v\n", err)
+ }
+ // timeout should be a session
+ out, err := sh.Command("sleep", "2").SetTimeout(time.Second).Output()
+ fmt.Printf("output:(%s), err(%v)\n", string(out), err)
+
+ out, err = sh.Command("echo", "hello").SetTimeout(time.Second).Output()
+ fmt.Printf("output:(%s), err(%v)\n", string(out), err)
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/example_test.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/example_test.go
new file mode 100644
index 000000000..5abfed64d
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/example_test.go
@@ -0,0 +1,28 @@
+package sh_test
+
+import (
+ "fmt"
+
+ "github.com/codeskyblue/go-sh"
+)
+
+func ExampleCommand() {
+ out, err := sh.Command("echo", "hello").Output()
+ fmt.Println(string(out), err)
+}
+
+func ExampleCommandPipe() {
+ out, err := sh.Command("echo", "-n", "hi").Command("wc", "-c").Output()
+ fmt.Println(string(out), err)
+}
+
+func ExampleCommandSetDir() {
+ out, err := sh.Command("pwd", sh.Dir("/")).Output()
+ fmt.Println(string(out), err)
+}
+
+func ExampleTest() {
+ if sh.Test("dir", "mydir") {
+ fmt.Println("mydir exists")
+ }
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe.go
new file mode 100644
index 000000000..c35f4e2a0
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe.go
@@ -0,0 +1,148 @@
+package sh
+
+import (
+ "bytes"
+ "encoding/json"
+ "encoding/xml"
+ "errors"
+ "io"
+ "os"
+ "strings"
+ "syscall"
+ "time"
+)
+
+var ErrExecTimeout = errors.New("execute timeout")
+
+// unmarshal shell output to decode json
+func (s *Session) UnmarshalJSON(data interface{}) (err error) {
+ bufrw := bytes.NewBuffer(nil)
+ s.Stdout = bufrw
+ if err = s.Run(); err != nil {
+ return
+ }
+ return json.NewDecoder(bufrw).Decode(data)
+}
+
+// unmarshal command output into xml
+func (s *Session) UnmarshalXML(data interface{}) (err error) {
+ bufrw := bytes.NewBuffer(nil)
+ s.Stdout = bufrw
+ if err = s.Run(); err != nil {
+ return
+ }
+ return xml.NewDecoder(bufrw).Decode(data)
+}
+
+// start command
+func (s *Session) Start() (err error) {
+ s.started = true
+ var rd *io.PipeReader
+ var wr *io.PipeWriter
+ var length = len(s.cmds)
+ if s.ShowCMD {
+ var cmds = make([]string, 0, 4)
+ for _, cmd := range s.cmds {
+ cmds = append(cmds, strings.Join(cmd.Args, " "))
+ }
+ s.writePrompt(strings.Join(cmds, " | "))
+ }
+ for index, cmd := range s.cmds {
+ if index == 0 {
+ cmd.Stdin = s.Stdin
+ } else {
+ cmd.Stdin = rd
+ }
+ if index != length {
+ rd, wr = io.Pipe() // create pipe
+ cmd.Stdout = wr
+ cmd.Stderr = os.Stderr
+ }
+ if index == length-1 {
+ cmd.Stdout = s.Stdout
+ cmd.Stderr = s.Stderr
+ }
+ err = cmd.Start()
+ if err != nil {
+ return
+ }
+ }
+ return
+}
+
+// Should be call after Start()
+// only catch the last command error
+func (s *Session) Wait() (err error) {
+ for _, cmd := range s.cmds {
+ err = cmd.Wait()
+ wr, ok := cmd.Stdout.(*io.PipeWriter)
+ if ok {
+ wr.Close()
+ }
+ }
+ return err
+}
+
+func (s *Session) Kill(sig os.Signal) {
+ for _, cmd := range s.cmds {
+ if cmd.Process != nil {
+ cmd.Process.Signal(sig)
+ }
+ }
+}
+
+func (s *Session) WaitTimeout(timeout time.Duration) (err error) {
+ select {
+ case <-time.After(timeout):
+ s.Kill(syscall.SIGKILL)
+ return ErrExecTimeout
+ case err = <-Go(s.Wait):
+ return err
+ }
+}
+
+func Go(f func() error) chan error {
+ ch := make(chan error)
+ go func() {
+ ch <- f()
+ }()
+ return ch
+}
+
+func (s *Session) Run() (err error) {
+ if err = s.Start(); err != nil {
+ return
+ }
+ if s.timeout != time.Duration(0) {
+ return s.WaitTimeout(s.timeout)
+ }
+ return s.Wait()
+}
+
+func (s *Session) Output() (out []byte, err error) {
+ oldout := s.Stdout
+ defer func() {
+ s.Stdout = oldout
+ }()
+ stdout := bytes.NewBuffer(nil)
+ s.Stdout = stdout
+ err = s.Run()
+ out = stdout.Bytes()
+ return
+}
+
+func (s *Session) CombinedOutput() (out []byte, err error) {
+ oldout := s.Stdout
+ olderr := s.Stderr
+ defer func() {
+ s.Stdout = oldout
+ s.Stderr = olderr
+ }()
+ stdout := bytes.NewBuffer(nil)
+ s.Stdout = stdout
+ s.Stderr = stdout
+
+ err = s.Run()
+ out = stdout.Bytes()
+ return
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe_test.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe_test.go
new file mode 100644
index 000000000..8d0e22c0c
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/pipe_test.go
@@ -0,0 +1,126 @@
+package sh
+
+import (
+ "encoding/xml"
+ "io"
+ "os"
+ "os/exec"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestUnmarshalJSON(t *testing.T) {
+ var a int
+ s := NewSession()
+ s.ShowCMD = true
+ err := s.Command("echo", []string{"1"}).UnmarshalJSON(&a)
+ if err != nil {
+ t.Error(err)
+ }
+ if a != 1 {
+ t.Errorf("expect a tobe 1, but got %d", a)
+ }
+}
+
+func TestUnmarshalXML(t *testing.T) {
+ s := NewSession()
+ xmlSample := `
+`
+ type server struct {
+ XMLName xml.Name `xml:"server"`
+ Version string `xml:"version,attr"`
+ }
+ data := &server{}
+ s.Command("echo", xmlSample).UnmarshalXML(data)
+ if data.Version != "1" {
+ t.Error(data)
+ }
+}
+
+func TestPipe(t *testing.T) {
+ s := NewSession()
+ s.ShowCMD = true
+ s.Call("echo", "hello")
+ err := s.Command("echo", "hi").Command("cat", "-n").Start()
+ if err != nil {
+ t.Error(err)
+ }
+ err = s.Wait()
+ if err != nil {
+ t.Error(err)
+ }
+ out, err := s.Command("echo", []string{"hello"}).Output()
+ if err != nil {
+ t.Error(err)
+ }
+ if string(out) != "hello\n" {
+ t.Error("capture wrong output:", out)
+ }
+ s.Command("echo", []string{"hello\tworld"}).Command("cut", []string{"-f2"}).Run()
+}
+
+func TestPipeCommand(t *testing.T) {
+ c1 := exec.Command("echo", "good")
+ rd, wr := io.Pipe()
+ c1.Stdout = wr
+ c2 := exec.Command("cat", "-n")
+ c2.Stdout = os.Stdout
+ c2.Stdin = rd
+ c1.Start()
+ c2.Start()
+
+ c1.Wait()
+ wc, ok := c1.Stdout.(io.WriteCloser)
+ if ok {
+ wc.Close()
+ }
+ c2.Wait()
+}
+
+func TestPipeInput(t *testing.T) {
+ s := NewSession()
+ s.ShowCMD = true
+ s.SetInput("first line\nsecond line\n")
+ out, err := s.Command("grep", "second").Output()
+ if err != nil {
+ t.Error(err)
+ }
+ if string(out) != "second line\n" {
+ t.Error("capture wrong output:", out)
+ }
+}
+
+func TestTimeout(t *testing.T) {
+ s := NewSession()
+ err := s.Command("sleep", "2").Start()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = s.WaitTimeout(time.Second)
+ if err != ErrExecTimeout {
+ t.Fatal(err)
+ }
+}
+
+func TestSetTimeout(t *testing.T) {
+ s := NewSession()
+ s.SetTimeout(time.Second)
+ defer s.SetTimeout(0)
+ err := s.Command("sleep", "2").Run()
+ if err != ErrExecTimeout {
+ t.Fatal(err)
+ }
+}
+
+func TestCombinedOutput(t *testing.T) {
+ s := NewSession()
+ bytes, err := s.Command("sh", "-c", "echo stderr >&2 ; echo stdout").CombinedOutput()
+ if err != nil {
+ t.Error(err)
+ }
+ stringOutput := string(bytes)
+ if !(strings.Contains(stringOutput, "stdout") && strings.Contains(stringOutput, "stderr")) {
+ t.Errorf("expect output from both output streams, got '%s'", strings.TrimSpace(stringOutput))
+ }
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/sh.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/sh.go
new file mode 100644
index 000000000..15f1b85d5
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/sh.go
@@ -0,0 +1,200 @@
+/*
+Package go-sh is intented to make shell call with golang more easily.
+Some usage is more similar to os/exec, eg: Run(), Output(), Command(name, args...)
+
+But with these similar function, pipe is added in and this package also got shell-session support.
+
+Why I love golang so much, because the usage of golang is simple, but the power is unlimited. I want to make this pakcage got the sample style like golang.
+
+ // just like os/exec
+ sh.Command("echo", "hello").Run()
+
+ // support pipe
+ sh.Command("echo", "hello").Command("wc", "-c").Run()
+
+ // create a session to store dir and env
+ sh.NewSession().SetDir("/").Command("pwd")
+
+ // shell buildin command - "test"
+ sh.Test("dir", "mydir")
+
+ // like shell call: (cd /; pwd)
+ sh.Command("pwd", sh.Dir("/")) same with sh.Command(sh.Dir("/"), "pwd")
+
+ // output to json and xml easily
+ v := map[string] int {}
+ err = sh.Command("echo", `{"number": 1}`).UnmarshalJSON(&v)
+*/
+package sh
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "reflect"
+ "strings"
+ "time"
+
+ "github.com/codegangsta/inject"
+)
+
+type Dir string
+
+type Session struct {
+ inj inject.Injector
+ alias map[string][]string
+ cmds []*exec.Cmd
+ dir Dir
+ started bool
+ Env map[string]string
+ Stdin io.Reader
+ Stdout io.Writer
+ Stderr io.Writer
+ ShowCMD bool // enable for debug
+ timeout time.Duration
+}
+
+func (s *Session) writePrompt(args ...interface{}) {
+ var ps1 = fmt.Sprintf("[golang-sh]$")
+ args = append([]interface{}{ps1}, args...)
+ fmt.Fprintln(s.Stderr, args...)
+}
+
+func NewSession() *Session {
+ env := make(map[string]string)
+ for _, key := range []string{"PATH"} {
+ env[key] = os.Getenv(key)
+ }
+ s := &Session{
+ inj: inject.New(),
+ alias: make(map[string][]string),
+ dir: Dir(""),
+ Stdin: strings.NewReader(""),
+ Stdout: os.Stdout,
+ Stderr: os.Stderr,
+ Env: env,
+ }
+ return s
+}
+
+func InteractiveSession() *Session {
+ s := NewSession()
+ s.SetStdin(os.Stdin)
+ return s
+}
+
+func Command(name string, a ...interface{}) *Session {
+ s := NewSession()
+ return s.Command(name, a...)
+}
+
+func Echo(in string) *Session {
+ s := NewSession()
+ return s.SetInput(in)
+}
+
+func (s *Session) Alias(alias, cmd string, args ...string) {
+ v := []string{cmd}
+ v = append(v, args...)
+ s.alias[alias] = v
+}
+
+func (s *Session) Command(name string, a ...interface{}) *Session {
+ var args = make([]string, 0)
+ var sType = reflect.TypeOf("")
+
+ // init cmd, args, dir, envs
+ // if not init, program may panic
+ s.inj.Map(name).Map(args).Map(s.dir).Map(map[string]string{})
+ for _, v := range a {
+ switch reflect.TypeOf(v) {
+ case sType:
+ args = append(args, v.(string))
+ default:
+ s.inj.Map(v)
+ }
+ }
+ if len(args) != 0 {
+ s.inj.Map(args)
+ }
+ s.inj.Invoke(s.appendCmd)
+ return s
+}
+
+// combine Command and Run
+func (s *Session) Call(name string, a ...interface{}) error {
+ return s.Command(name, a...).Run()
+}
+
+/*
+func (s *Session) Exec(cmd string, args ...string) error {
+ return s.Call(cmd, args)
+}
+*/
+
+func (s *Session) SetEnv(key, value string) *Session {
+ s.Env[key] = value
+ return s
+}
+
+func (s *Session) SetDir(dir string) *Session {
+ s.dir = Dir(dir)
+ return s
+}
+
+func (s *Session) SetInput(in string) *Session {
+ s.Stdin = strings.NewReader(in)
+ return s
+}
+
+func (s *Session) SetStdin(r io.Reader) *Session {
+ s.Stdin = r
+ return s
+}
+
+func (s *Session) SetTimeout(d time.Duration) *Session {
+ s.timeout = d
+ return s
+}
+
+func newEnviron(env map[string]string, inherit bool) []string { //map[string]string {
+ environ := make([]string, 0, len(env))
+ if inherit {
+ for _, line := range os.Environ() {
+ for k, _ := range env {
+ if strings.HasPrefix(line, k+"=") {
+ goto CONTINUE
+ }
+ }
+ environ = append(environ, line)
+ CONTINUE:
+ }
+ }
+ for k, v := range env {
+ environ = append(environ, k+"="+v)
+ }
+ return environ
+}
+
+func (s *Session) appendCmd(cmd string, args []string, cwd Dir, env map[string]string) {
+ if s.started {
+ s.started = false
+ s.cmds = make([]*exec.Cmd, 0)
+ }
+ for k, v := range s.Env {
+ if _, ok := env[k]; !ok {
+ env[k] = v
+ }
+ }
+ environ := newEnviron(s.Env, true) // true: inherit sys-env
+ v, ok := s.alias[cmd]
+ if ok {
+ cmd = v[0]
+ args = append(v[1:], args...)
+ }
+ c := exec.Command(cmd, args...)
+ c.Env = environ
+ c.Dir = string(cwd)
+ s.cmds = append(s.cmds, c)
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/sh_test.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/sh_test.go
new file mode 100644
index 000000000..be5c27998
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/sh_test.go
@@ -0,0 +1,106 @@
+package sh
+
+import (
+ "fmt"
+ "log"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestAlias(t *testing.T) {
+ s := NewSession()
+ s.Alias("gr", "echo", "hi")
+ out, err := s.Command("gr", "sky").Output()
+ if err != nil {
+ t.Error(err)
+ }
+ if string(out) != "hi sky\n" {
+ t.Errorf("expect 'hi sky' but got:%s", string(out))
+ }
+}
+
+func ExampleSession_Command() {
+ s := NewSession()
+ out, err := s.Command("echo", "hello").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(out))
+ // Output: hello
+}
+
+func ExampleSession_Command_pipe() {
+ s := NewSession()
+ out, err := s.Command("echo", "hello", "world").Command("awk", "{print $2}").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(out))
+ // Output: world
+}
+
+func ExampleSession_Alias() {
+ s := NewSession()
+ s.Alias("alias_echo_hello", "echo", "hello")
+ out, err := s.Command("alias_echo_hello", "world").Output()
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(out))
+ // Output: hello world
+}
+
+func TestEcho(t *testing.T) {
+ out, err := Echo("one two three").Command("wc", "-w").Output()
+ if err != nil {
+ t.Error(err)
+ }
+ if strings.TrimSpace(string(out)) != "3" {
+ t.Errorf("expect '3' but got:%s", string(out))
+ }
+}
+
+func TestSession(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Log("ignore test on windows")
+ return
+ }
+ session := NewSession()
+ session.ShowCMD = true
+ err := session.Call("pwd")
+ if err != nil {
+ t.Error(err)
+ }
+ out, err := session.SetDir("/").Command("pwd").Output()
+ if err != nil {
+ t.Error(err)
+ }
+ if string(out) != "/\n" {
+ t.Errorf("expect /, but got %s", string(out))
+ }
+}
+
+/*
+ #!/bin/bash -
+ #
+ export PATH=/usr/bin:/bin
+ alias ll='ls -l'
+ cd /usr
+ if test -d "local"
+ then
+ ll local | awk '{print $1, $NF}' | grep bin
+ fi
+*/
+func Example(t *testing.T) {
+ s := NewSession()
+ //s.ShowCMD = true
+ s.Env["PATH"] = "/usr/bin:/bin"
+ s.SetDir("/bin")
+ s.Alias("ll", "ls", "-l")
+
+ if s.Test("d", "local") {
+ //s.Command("ll", []string{"local"}).Command("awk", []string{"{print $1, $NF}"}).Command("grep", []string{"bin"}).Run()
+ s.Command("ll", "local").Command("awk", "{print $1, $NF}").Command("grep", "bin").Run()
+ }
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/test.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/test.go
new file mode 100644
index 000000000..b9d310527
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/test.go
@@ -0,0 +1,64 @@
+package sh
+
+import (
+ "os"
+ "path/filepath"
+)
+
+func filetest(name string, modemask os.FileMode) (match bool, err error) {
+ fi, err := os.Stat(name)
+ if err != nil {
+ return
+ }
+ match = (fi.Mode() & modemask) == modemask
+ return
+}
+
+func (s *Session) pwd() string {
+ dir := string(s.dir)
+ if dir == "" {
+ dir, _ = os.Getwd()
+ }
+ return dir
+}
+
+func (s *Session) abspath(name string) string {
+ if filepath.IsAbs(name) {
+ return name
+ }
+ return filepath.Join(s.pwd(), name)
+}
+
+func init() {
+ //log.SetFlags(log.Lshortfile | log.LstdFlags)
+}
+
+// expression can be dir, file, link
+func (s *Session) Test(expression string, argument string) bool {
+ var err error
+ var fi os.FileInfo
+ fi, err = os.Lstat(s.abspath(argument))
+ switch expression {
+ case "d", "dir":
+ return err == nil && fi.IsDir()
+ case "f", "file":
+ return err == nil && fi.Mode().IsRegular()
+ case "x", "executable":
+ /*
+ fmt.Println(expression, argument)
+ if err == nil {
+ fmt.Println(fi.Mode())
+ }
+ */
+ return err == nil && fi.Mode()&os.FileMode(0100) != 0
+ case "L", "link":
+ return err == nil && fi.Mode()&os.ModeSymlink != 0
+ }
+ return false
+}
+
+// expression can be d,dir, f,file, link
+func Test(exp string, arg string) bool {
+ s := NewSession()
+ return s.Test(exp, arg)
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/test_test.go b/plugins/common/vendor/github.com/codeskyblue/go-sh/test_test.go
new file mode 100644
index 000000000..15225aa9a
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/test_test.go
@@ -0,0 +1,58 @@
+package sh_test
+
+import (
+ "testing"
+
+ "github.com/codeskyblue/go-sh"
+)
+
+var s = sh.NewSession()
+
+type T struct{ *testing.T }
+
+func NewT(t *testing.T) *T {
+ return &T{t}
+}
+
+func (t *T) checkTest(exp string, arg string, result bool) {
+ r := s.Test(exp, arg)
+ if r != result {
+ t.Errorf("test -%s %s, %v != %v", exp, arg, r, result)
+ }
+}
+
+func TestTest(i *testing.T) {
+ t := NewT(i)
+ t.checkTest("d", "../go-sh", true)
+ t.checkTest("d", "./yymm", false)
+
+ // file test
+ t.checkTest("f", "testdata/hello.txt", true)
+ t.checkTest("f", "testdata/xxxxx", false)
+ t.checkTest("f", "testdata/yymm", false)
+
+ // link test
+ t.checkTest("link", "testdata/linkfile", true)
+ t.checkTest("link", "testdata/xxxxxlinkfile", false)
+ t.checkTest("link", "testdata/hello.txt", false)
+
+ // executable test
+ t.checkTest("x", "testdata/executable", true)
+ t.checkTest("x", "testdata/xxxxx", false)
+ t.checkTest("x", "testdata/hello.txt", false)
+}
+
+func ExampleShellTest(t *testing.T) {
+ // test -L
+ sh.Test("link", "testdata/linkfile")
+ sh.Test("L", "testdata/linkfile")
+ // test -f
+ sh.Test("file", "testdata/file")
+ sh.Test("f", "testdata/file")
+ // test -x
+ sh.Test("executable", "testdata/binfile")
+ sh.Test("x", "testdata/binfile")
+ // test -d
+ sh.Test("dir", "testdata/dir")
+ sh.Test("d", "testdata/dir")
+}
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/executable b/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/executable
new file mode 100755
index 000000000..e69de29bb
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/hello.txt b/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/hello.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/linkfile b/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/linkfile
new file mode 120000
index 000000000..a5162f80d
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/testdata/linkfile
@@ -0,0 +1 @@
+hello.txt
\ No newline at end of file
diff --git a/plugins/common/vendor/github.com/codeskyblue/go-sh/wercker.yml b/plugins/common/vendor/github.com/codeskyblue/go-sh/wercker.yml
new file mode 100644
index 000000000..72c47020f
--- /dev/null
+++ b/plugins/common/vendor/github.com/codeskyblue/go-sh/wercker.yml
@@ -0,0 +1,28 @@
+box: wercker/golang
+# Build definition
+build:
+ # The steps that will be executed on build
+ steps:
+ # Sets the go workspace and places you package
+ # at the right place in the workspace tree
+ - setup-go-workspace
+
+ # Gets the dependencies
+ - script:
+ name: go get
+ code: |
+ cd $WERCKER_SOURCE_DIR
+ go version
+ go get -t .
+
+ # Build the project
+ - script:
+ name: go build
+ code: |
+ go build .
+
+ # Test the project
+ - script:
+ name: go test
+ code: |
+ go test -v ./...
diff --git a/plugins/config/functions b/plugins/config/functions
index c97ff7c57..908f6a7af 100755
--- a/plugins/config/functions
+++ b/plugins/config/functions
@@ -150,7 +150,7 @@ config_get() {
config_create "$ENV_FILE"
if [[ ! -s $ENV_FILE ]]; then
- return 0
+ return 1
fi
local KEY="${*: -1}"
diff --git a/plugins/config/plugin.toml b/plugins/config/plugin.toml
index 4f87dd4ea..46139556c 100644
--- a/plugins/config/plugin.toml
+++ b/plugins/config/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core config plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/docker-options/commands b/plugins/docker-options/commands
index f3fd00ccc..da85da9c8 100755
--- a/plugins/docker-options/commands
+++ b/plugins/docker-options/commands
@@ -7,9 +7,10 @@ case "$1" in
help_content_func () {
declare desc="return docker-options plugin help content"
cat< [phase(s)], Display app's Docker options for all phases (or comma separated phase list)
+ docker-options [phase(s)], [DEPRECATED] Alternative for docker-options:report
docker-options:add OPTION, Add Docker option to app for phase (comma separated phase list)
docker-options:remove OPTION, Remove Docker option from app for phase (comma separated phase list)
+ docker-options:report [] [], Displays a docker options report for one or more apps
help_content
}
diff --git a/plugins/docker-options/functions b/plugins/docker-options/functions
index d49ba305d..fe95dbf0c 100755
--- a/plugins/docker-options/functions
+++ b/plugins/docker-options/functions
@@ -4,12 +4,19 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
AVAILABLE_PHASES=(build deploy run)
+fn-get-phase-file-path() {
+ declare desc="return docker-options config file path for specified phase"
+ local APP="$1" PHASE="$2"
+ local PHASE_FILE="${DOKKU_ROOT}/${APP}/DOCKER_OPTIONS_${PHASE^^}"
+ echo "$PHASE_FILE"
+}
+
get_phase_file_path() {
declare desc="return docker-options config file path for specified phase"
local phase_file_prefix="DOCKER_OPTIONS_"
local phase=$1
- [[ "$DOKKU_ROOT" && "$APP" && "$phase_file_prefix" && "$phase" ]] || dokku_log_fail "Error: phase_file_path is incomplete."
- echo "${DOKKU_ROOT}/${APP}/${phase_file_prefix}${phase^^}"
+ [[ "$APP" && "$phase" ]] || dokku_log_fail "Error: phase_file_path is incomplete."
+ fn-get-phase-file-path "$APP" "$phase"
}
get_phases() {
diff --git a/plugins/docker-options/plugin.toml b/plugins/docker-options/plugin.toml
index a802b0fc4..2373b0f26 100644
--- a/plugins/docker-options/plugin.toml
+++ b/plugins/docker-options/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core docker-options plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/docker-options/subcommands/add b/plugins/docker-options/subcommands/add
index b66576539..8a5242243 100755
--- a/plugins/docker-options/subcommands/add
+++ b/plugins/docker-options/subcommands/add
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_CORE_AVAILABLE_PATH/docker-options/functions"
+source "$PLUGIN_AVAILABLE_PATH/docker-options/functions"
docker_options_add_cmd() {
declare desc="Add a docker option to application"
diff --git a/plugins/docker-options/subcommands/default b/plugins/docker-options/subcommands/default
index 25d4580d5..4cfaa6114 100755
--- a/plugins/docker-options/subcommands/default
+++ b/plugins/docker-options/subcommands/default
@@ -1,11 +1,12 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_CORE_AVAILABLE_PATH/docker-options/functions"
+source "$PLUGIN_AVAILABLE_PATH/docker-options/functions"
docker_options_main_cmd() {
declare desc="Display applications docker options"
local cmd="docker-options"
+ dokku_log_warn "Deprecated: Please use docker-options:report"
verify_app_name "$2" && local APP="$2"
read -ra passed_phases <<< "$(get_phases "$3")"
diff --git a/plugins/docker-options/subcommands/remove b/plugins/docker-options/subcommands/remove
index f232083ef..8677039ed 100755
--- a/plugins/docker-options/subcommands/remove
+++ b/plugins/docker-options/subcommands/remove
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-source "$PLUGIN_CORE_AVAILABLE_PATH/docker-options/functions"
+source "$PLUGIN_AVAILABLE_PATH/docker-options/functions"
docker_options_remove_cmd() {
declare desc="Remove a docker option from application"
@@ -10,6 +10,7 @@ docker_options_remove_cmd() {
verify_app_name "$2" && local APP="$2"
read -ra passed_phases <<< "$(get_phases "$3")"
shift 3 # everything else passed is the docker option
+ # shellcheck disable=SC2154
[[ -z ${passed_docker_option="$@"} ]] && dokku_log_fail "Please specify docker options to add to the phase"
remove_passed_docker_option passed_phases[@] "${passed_docker_option[@]}"
}
diff --git a/plugins/docker-options/subcommands/report b/plugins/docker-options/subcommands/report
new file mode 100755
index 000000000..e13618364
--- /dev/null
+++ b/plugins/docker-options/subcommands/report
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/docker-options/functions"
+
+fn-docker-options() {
+ declare APP="$1" PHASE="$2"
+ local PHASE_FILE="$(fn-get-phase-file-path "$APP" "$PHASE")"
+ if [[ -r "$PHASE_FILE" ]]; then
+ tr '\n' ' ' < "$PHASE_FILE"
+ fi
+}
+
+report_single_app() {
+ declare APP="$1" INFO_FLAG="$2"
+ local APP_DIR="$DOKKU_ROOT/$APP"
+ if [[ "$INFO_FLAG" == "true" ]]; then
+ INFO_FLAG=""
+ fi
+ verify_app_name "$APP"
+ local flag_map=(
+ "--docker-options-build: $(fn-docker-options "$APP" build)"
+ "--docker-options-deploy: $(fn-docker-options "$APP" deploy)"
+ "--docker-options-run: $(fn-docker-options "$APP" run)"
+ )
+
+ if [[ -z "$INFO_FLAG" ]]; then
+ dokku_log_info2 "$APP docker options information"
+ for flag in "${flag_map[@]}"; do
+ key="$(echo "${flag#--}" | cut -f1 -d' ' | tr - ' ')"
+ dokku_log_verbose "$(printf "%-20s %-25s" "${key^}" "${flag#*: }")"
+ done
+ else
+ local match=false; local value_exists=false
+ for flag in "${flag_map[@]}"; do
+ valid_flags="${valid_flags} $(echo "$flag" | cut -d':' -f1)"
+ if [[ "$flag" == "${INFO_FLAG}:"* ]]; then
+ value=${flag#*: }
+ size="${#value}"
+ if [[ "$size" -ne 0 ]]; then
+ echo "$value" && match=true && value_exists=true
+ else
+ match=true
+ fi
+ fi
+ done
+ if [[ "$match" == "true" ]]; then
+ [[ "$value_exists" == "true" ]] || dokku_log_fail "not deployed"
+ else
+ dokku_log_fail "Invalid flag passed, valid flags:${valid_flags}"
+ fi
+ fi
+}
+
+docker_options_report_cmd() {
+ declare desc="displays a docker options report for one or more apps"
+ local cmd="docker-options:report"
+ local INSTALLED_APPS=$(dokku_apps)
+ local APP="$2" INFO_FLAG="$3"
+
+ if [[ -n "$APP" ]] && [[ "$APP" == --* ]]; then
+ INFO_FLAG="$APP"
+ APP=""
+ fi
+
+ if [[ -z "$APP" ]] && [[ -z "$INFO_FLAG" ]]; then
+ INFO_FLAG="true"
+ fi
+
+ if [[ -z "$APP" ]]; then
+ for app in $INSTALLED_APPS; do
+ report_single_app "$app" "$INFO_FLAG" | tee || true
+ done
+ else
+ report_single_app "$APP" "$INFO_FLAG"
+ fi
+}
+
+docker_options_report_cmd "$@"
diff --git a/plugins/domains/commands b/plugins/domains/commands
index 7504678f7..1ed2fde7f 100755
--- a/plugins/domains/commands
+++ b/plugins/domains/commands
@@ -1,33 +1,11 @@
#!/usr/bin/env bash
[[ " help domains:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
+source "$PLUGIN_AVAILABLE_PATH/domains/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | domains:help)
- help_content_func () {
- declare desc="return domains plugin help content"
- cat<], List domains
- domains:add [ ...], Add domains to app
- domains:add-global [ ...], Add global domain names
- domains:clear , Clear all domains for app
- domains:disable , Disable VHOST support
- domains:enable , Enable VHOST support
- domains:remove [ ...], Remove domains from app
- domains:remove-global [ ...], Remove global domain names
-help_content
- }
-
- if [[ $1 = "domains:help" ]] ; then
- echo -e 'Usage: dokku domains[:COMMAND]'
- echo ''
- echo 'Manage vhost domains used by the Dokku proxy.'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
+ domains_help_cmd "$@"
;;
*)
diff --git a/plugins/domains/functions b/plugins/domains/functions
index 2cc3a78a4..a4171dbd0 100755
--- a/plugins/domains/functions
+++ b/plugins/domains/functions
@@ -85,7 +85,7 @@ domains_add() {
fi
done
- if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]];then
+ if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]]; then
domains_enable "$APP" --no-restart
fi
@@ -98,6 +98,7 @@ domains_remove() {
local APP="$1"; local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
shift 1
+ touch "$APP_VHOST_PATH"
for DOMAIN in "$@"; do
sed -i "\|^$DOMAIN\$|d" "$APP_VHOST_PATH"
dokku_log_info1 "Removed $DOMAIN from $APP"
@@ -105,12 +106,36 @@ domains_remove() {
plugn trigger post-domains-update "$APP" "remove" "$@"
}
+domains_set() {
+ declare desc="set list of domains for app"
+ verify_app_name "$1"
+ local APP="$1"; local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
+
+ shift 1
+ touch "$APP_VHOST_PATH"
+ for DOMAIN in "$@"; do
+ if ! (is_valid_hostname "$DOMAIN"); then
+ dokku_log_fail "$DOMAIN is invalid. exiting..."
+ fi
+ done
+
+ echo "$*" | tr " " "\n" > "$APP_VHOST_PATH"
+ dokku_log_info1 "Set $* for $APP"
+
+ if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]]; then
+ domains_enable "$APP" --no-restart
+ fi
+
+ plugn trigger post-domains-update "$APP" "set" "$@"
+}
+
domains_disable() {
declare desc="disable domains/VHOST support"
verify_app_name "$1"
local APP="$1"; local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
- if [[ "$(is_app_vhost_enabled "$APP")" == "true" ]];then
+ touch "$APP_VHOST_PATH"
+ if [[ "$(is_app_vhost_enabled "$APP")" == "true" ]]; then
disable_app_vhost "$APP"
else
dokku_log_info1 "domains (VHOST) support is already disabled for app ($APP)"
@@ -123,7 +148,8 @@ domains_enable() {
local APP="$1"; local APP_VHOST_PATH="$DOKKU_ROOT/$APP/VHOST"
local DEFAULT_VHOSTS="$(get_default_vhosts "$APP")"
- if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]];then
+ touch "$APP_VHOST_PATH"
+ if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]]; then
if [[ -n "$DEFAULT_VHOSTS" ]]; then
echo "$DEFAULT_VHOSTS" > "$APP_VHOST_PATH"
fi
@@ -138,6 +164,7 @@ domains_add_global() {
declare desc="add list of global domains"
local GLOBAL_VHOST_PATH="$DOKKU_ROOT/VHOST"
+ touch "$GLOBAL_VHOST_PATH"
for DOMAIN in "$@"; do
if ! (is_valid_hostname "$DOMAIN"); then
dokku_log_fail "$DOMAIN is invalid. exiting..."
@@ -148,7 +175,7 @@ domains_add_global() {
if [[ $(egrep -w "^$DOMAIN$" "$GLOBAL_VHOST_PATH" > /dev/null 2>&1; echo $?) -eq 0 ]]; then
dokku_log_info1 "Skipping: $DOMAIN already added"
else
- echo "$DOMAIN" >> "$GLOBAL_VHOST_PATH"
+ sed -i -e "\$a\\$DOMAIN" "$GLOBAL_VHOST_PATH"
dokku_log_info1 "Added $DOMAIN"
fi
done
@@ -158,8 +185,24 @@ domains_remove_global() {
declare desc="remove list of domains"
local GLOBAL_VHOST_PATH="$DOKKU_ROOT/VHOST"
+ touch "$GLOBAL_VHOST_PATH"
for DOMAIN in "$@"; do
sed -i "\|^$DOMAIN\$|d" "$GLOBAL_VHOST_PATH"
dokku_log_info1 "Removed $DOMAIN"
done
}
+
+domains_set_global() {
+ declare desc="set list of global domains"
+ local GLOBAL_VHOST_PATH="$DOKKU_ROOT/VHOST"
+
+ touch "$GLOBAL_VHOST_PATH"
+ for DOMAIN in "$@"; do
+ if ! (is_valid_hostname "$DOMAIN"); then
+ dokku_log_fail "$DOMAIN is invalid. exiting..."
+ fi
+ done
+
+ echo "$*" | tr " " "\n" > "$GLOBAL_VHOST_PATH"
+ dokku_log_info1 "Set $*"
+}
diff --git a/plugins/domains/internal-functions b/plugins/domains/internal-functions
new file mode 100755
index 000000000..fb4ab8fce
--- /dev/null
+++ b/plugins/domains/internal-functions
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+
+domains_help_content_func () {
+ declare desc="return domains plugin help content"
+ cat<], [DEPRECATED] Alternative for domains:report
+ domains:add [ ...], Add domains to app
+ domains:add-global [ ...], Add global domain names
+ domains:clear , Clear all domains for app
+ domains:disable , Disable VHOST support
+ domains:enable , Enable VHOST support
+ domains:remove [ ...], Remove domains from app
+ domains:remove-global [ ...], Remove global domain names
+ domains:report [] [], Displays a domains report for one or more apps
+ domains:set [ ...], Set domains for app
+ domains:set-global [ ...], Set global domain names
+help_content
+}
+
+domains_help_cmd() {
+ if [[ $1 = "domains:help" ]] ; then
+ echo -e 'Usage: dokku domains[:COMMAND]'
+ echo ''
+ echo 'Manage vhost domains used by the Dokku proxy.'
+ echo ''
+ echo 'Additional commands:'
+ domains_help_content_func | sort | column -c2 -t -s,
+ elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
+ domains_help_content_func
+ else
+ cat< [ ...]"
+
+ shift 1
+ domains_set "$@"
+}
+
+domains_set_cmd "$@"
diff --git a/plugins/domains/subcommands/set-global b/plugins/domains/subcommands/set-global
new file mode 100755
index 000000000..665990104
--- /dev/null
+++ b/plugins/domains/subcommands/set-global
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
+source "$PLUGIN_AVAILABLE_PATH/domains/functions"
+
+domains_set_global_cmd() {
+ declare desc="set global domain names via command line"
+ local cmd="domains:set-global"
+ [[ -z $2 ]] && dokku_log_fail "Please specify a domain name. Usage: dokku $1 [ ...]"
+
+ shift 1
+ domains_set_global "$@"
+}
+
+domains_set_global_cmd "$@"
diff --git a/plugins/enter/plugin.toml b/plugins/enter/plugin.toml
index 71417684a..fe9bd0aa5 100644
--- a/plugins/enter/plugin.toml
+++ b/plugins/enter/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core enter plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/enter/subcommands/default b/plugins/enter/subcommands/default
index 9d2a23da1..b53ea0a71 100755
--- a/plugins/enter/subcommands/default
+++ b/plugins/enter/subcommands/default
@@ -12,7 +12,7 @@ enter_default_cmd() {
if [[ -z "$3" ]]; then
if [[ ${#AVAILABLE_CONTAINER_TYPES[@]} -gt 1 ]]; then
dokku_log_warn "No container type specified."
- dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[@]}"
+ dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[*]}"
else
CONTAINER_TYPE="${AVAILABLE_CONTAINER_TYPES[0]}"
fi
@@ -22,11 +22,11 @@ enter_default_cmd() {
local DOKKU_APP_CIDS=( $(get_app_container_ids "$APP") )
if [[ ! -n "$4" ]]; then
dokku_log_warn "No container id specified."
- dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[@]}"
+ dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[*]}"
fi
if ! (printf -- '%s\n' "${DOKKU_APP_CIDS[@]}" | grep -q -e "^$4"); then
dokku_log_warn "Invalid container id for app"
- dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[@]}"
+ dokku_log_fail "Available ids for app ($APP): ${DOKKU_APP_CIDS[*]}"
fi
local ID=$(printf -- '%s\n' "${DOKKU_APP_CIDS[@]}" | grep -e "^$4")
shift 4
@@ -35,7 +35,7 @@ enter_default_cmd() {
local ID=${DOKKU_APP_CIDS[0]}
if [[ ! -n $ID ]]; then
dokku_log_warn "No containers found for type '$CONTAINER_TYPE'"
- dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[@]}"
+ dokku_log_fail "Available types for app ($APP): ${AVAILABLE_CONTAINER_TYPES[*]}"
fi
if [[ $3 ]]; then
shift 3
diff --git a/plugins/git/functions b/plugins/git/functions
index 33158d646..40d5cac2d 100755
--- a/plugins/git/functions
+++ b/plugins/git/functions
@@ -4,6 +4,23 @@ source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/apps/functions"
source "$PLUGIN_AVAILABLE_PATH/config/functions"
+use_git_worktree() {
+ declare desc="detects whether to use git worktree"
+ local GIT_VERSION MAJOR_VERSION MINOR_VERSION
+
+ GIT_VERSION=$(git --version | awk '{split($0,a," "); print a[3]}')
+ MAJOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[1]}')
+ MINOR_VERSION=$(echo "$GIT_VERSION" | awk '{split($0,a,"."); print a[2]}')
+
+ if [[ "$MAJOR_VERSION" -ge "3" ]]; then
+ return 0
+ elif [[ "$MAJOR_VERSION" -eq "2" ]] && [[ "$MINOR_VERSION" -ge "11" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
git_build_app_repo() {
declare desc="builds local git app repo for app"
verify_app_name "$1"
@@ -13,24 +30,37 @@ git_build_app_repo() {
local GIT_BUILD_APP_REPO_TMP_WORK_DIR=$(mktemp -d "/tmp/dokku_git.XXXX")
trap 'rm -rf "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null' RETURN INT TERM EXIT
- # git clone - this method creates a new git repository and adds the primary
- # repo as a remote, then does a fetch depth=1 to avoid cloning
- # the entire repo
local TMP_TAG="dokku/$REV"
chmod 755 "$GIT_BUILD_APP_REPO_TMP_WORK_DIR"
unset GIT_DIR GIT_WORK_TREE
- pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
[[ ! -d "$DOKKU_ROOT/$APP" ]] && apps_create "$APP"
- GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
- GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &> /dev/null
- git init &> /dev/null
- git config advice.detachedHead false
- git remote add origin "$DOKKU_ROOT/$APP" &> /dev/null
- git fetch --depth=1 origin "refs/tags/$TMP_TAG" &> /dev/null
- git reset --hard FETCH_HEAD &> /dev/null
- git submodule update --init --recursive &> /dev/null
- GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
- find -name .git -prune -exec rm -rf {} \; > /dev/null
+
+ if use_git_worktree; then
+ # git worktree - this method uses git worktree which was introduced in git 2.5
+ git worktree add "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" > /dev/null
+ pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
+ else
+ # git clone - this method creates a new git repository and adds the primary
+ # repo as a remote, then does a fetch depth=1 to avoid cloning
+ # the entire repo.
+ # Not working for git >= 2.11.0 due to changes introduced
+ # in this merge:
+ # https://github.com/git/git/commit/25ab004c53cdcfea485e5bf437aeaa74df47196d
+ pushd "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" > /dev/null
+ GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
+ GIT_DIR="$DOKKU_ROOT/$APP" git tag "$TMP_TAG" "$REV" &> /dev/null
+ git init &> /dev/null
+ git config advice.detachedHead false
+ git remote add origin "$DOKKU_ROOT/$APP" &> /dev/null
+ git fetch --depth=1 origin "refs/tags/$TMP_TAG" &> /dev/null
+ git reset --hard FETCH_HEAD &> /dev/null
+ GIT_DIR="$DOKKU_ROOT/$APP" git tag -d "$TMP_TAG" &> /dev/null || true
+ fi
+
+ suppress_output git submodule update --init --recursive
+ find . -name .git -prune -exec rm -rf {} \; > /dev/null
+
+ plugn trigger post-extract "$APP" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV" | sed -u "s/^/"$'\e[1G'"/"
if [[ -f Dockerfile ]] && [[ "$([[ -f .env ]] && grep -q BUILDPACK_URL .env; echo $?)" != "0" ]] && [[ ! -f ".buildpacks" ]] && [[ -z $(config_get "$APP" BUILDPACK_URL || true) ]]; then
plugn trigger pre-receive-app "$APP" "dockerfile" "$GIT_BUILD_APP_REPO_TMP_WORK_DIR" "$REV"
@@ -41,16 +71,53 @@ git_build_app_repo() {
fi
}
+suppress_output() {
+ declare desc="suppress all output from a given command unless there is an error"
+ local TMP_COMMAND_OUTPUT
+ TMP_COMMAND_OUTPUT=$(mktemp "/tmp/${FUNCNAME[0]}.XXXX")
+ trap 'rm -rf "$TMP_COMMAND_OUTPUT" > /dev/null' RETURN INT TERM EXIT
+
+ "$@" > "$TMP_COMMAND_OUTPUT" 2>&1 || {
+ local exit_code="$?"
+ cat "$TMP_COMMAND_OUTPUT"
+ return "$exit_code"
+ }
+ return 0
+}
+
+git_deploy_branch() {
+ declare desc="retrieve the deploy branch for a given application"
+ local cmd="git-hook"
+ local APP="$1"
+
+ local DOKKU_DEPLOY_BRANCH="$(config_get "$APP" DOKKU_DEPLOY_BRANCH || true)"
+ local DOKKU_GLOBAL_DEPLOY_BRANCH="$(config_get --global DOKKU_DEPLOY_BRANCH || true)"
+ if [[ -n "$DOKKU_DEPLOY_BRANCH" ]]; then
+ echo "$DOKKU_DEPLOY_BRANCH"
+ elif [[ -n "$DOKKU_GLOBAL_DEPLOY_BRANCH" ]]; then
+ echo "$DOKKU_GLOBAL_DEPLOY_BRANCH"
+ else
+ echo "master"
+ fi
+}
+
git_hook_cmd() {
declare desc="kick off receive-app trigger from git prereceive hook"
local cmd="git-hook"
- local APP="$2"
+ local APP="$2"
+ local DOKKU_DEPLOY_BRANCH="$(git_deploy_branch "$APP")"
+
+ if ! git check-ref-format --branch "$DOKKU_DEPLOY_BRANCH" > /dev/null 2>&1; then
+ echo $'\e[1G\e[K'"-----> WARNING: Invalid branch name '$DOKKU_DEPLOY_BRANCH' specified via DOKKU_DEPLOY_BRANCH."
+ echo $'\e[1G\e[K'"-----> For more details, please see the man page for 'git-check-ref-format.'"
+ return
+ fi
local oldrev newrev refname
while read -r oldrev newrev refname; do
# 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 [[ $refname = "refs/heads/master" ]]; then
+ if [[ $refname = "refs/heads/${DOKKU_DEPLOY_BRANCH}" ]]; then
# broken out into plugin so we might support other methods to receive an app
# shellcheck disable=SC2086
plugn trigger receive-app $APP $newrev
diff --git a/plugins/git/plugin.toml b/plugins/git/plugin.toml
index e045d49d2..c3a8c857d 100644
--- a/plugins/git/plugin.toml
+++ b/plugins/git/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core git plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/logs/plugin.toml b/plugins/logs/plugin.toml
index 8913ebada..a780f0ef7 100644
--- a/plugins/logs/plugin.toml
+++ b/plugins/logs/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core logs plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/logs/subcommands/default b/plugins/logs/subcommands/default
index e97480c6a..14532f31d 100755
--- a/plugins/logs/subcommands/default
+++ b/plugins/logs/subcommands/default
@@ -17,7 +17,8 @@ logs_default_cmd() {
shift 2;
local TEMP=$(getopt -o htqn:p: --long help,tail,quiet,num:,ps: -n 'dokku logs' -- "$@")
- if [[ $? != 0 ]]; then usage >&2 ; exit 1 ; fi
+ local EXIT_CODE="$?"
+ if [[ "$EXIT_CODE" != 0 ]]; then usage >&2 ; exit 1 ; fi
eval set -- "$TEMP"
local DOKKU_LOGS_LINE_NUMBERS="100"
diff --git a/plugins/named-containers/post-deploy b/plugins/named-containers/core-post-deploy
similarity index 92%
rename from plugins/named-containers/post-deploy
rename to plugins/named-containers/core-post-deploy
index 2489b71ce..2d84b28c3 100755
--- a/plugins/named-containers/post-deploy
+++ b/plugins/named-containers/core-post-deploy
@@ -2,9 +2,9 @@
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
-named_containers_post_deploy() {
+named_containers_core_post_deploy() {
declare desc="names deployed app container is consistent manner"
- local trigger="named_containers_post_deploy"
+ local trigger="named_containers_core_post_deploy"
local APP="$1"; local APP_ROOT="$DOKKU_ROOT/$APP"
local container
@@ -38,4 +38,4 @@ named_containers_post_deploy() {
shopt -u nullglob
}
-named_containers_post_deploy "$@"
+named_containers_core_post_deploy "$@"
diff --git a/plugins/named-containers/plugin.toml b/plugins/named-containers/plugin.toml
index 6488244b6..e18894aa9 100644
--- a/plugins/named-containers/plugin.toml
+++ b/plugins/named-containers/plugin.toml
@@ -1,4 +1,4 @@
[plugin]
description = "dokku core named containers plugin"
-version = "0.7.2"
+version = "0.9.4"
[plugin.config]
diff --git a/plugins/nginx-vhosts/commands b/plugins/nginx-vhosts/commands
index f7911fc25..3dbfc98d8 100755
--- a/plugins/nginx-vhosts/commands
+++ b/plugins/nginx-vhosts/commands
@@ -1,33 +1,15 @@
#!/usr/bin/env bash
[[ " help nginx:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
+source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/internal-functions"
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
case "$1" in
help | nginx:help)
- help_content_func () {
- declare desc="return nginx plugin help content"
- cat<, (Re)builds nginx config for given app
- nginx:access-logs [-t], Show the nginx access logs for an application (-t follows)
- nginx:error-logs [-t], Show the nginx error logs for an application (-t follows)
-help_content
- }
-
- if [[ $1 = "nginx:help" ]] ; then
- echo -e 'Usage: dokku nginx[:COMMAND]'
- echo ''
- echo 'Interact with Dokku'"'"'s Nginx proxy.'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
- ;;
+ nginx_vhosts_help_cmd "$@"
+ ;;
*)
exit "$DOKKU_NOT_IMPLEMENTED_EXIT"
- ;;
+ ;;
esac
diff --git a/plugins/nginx-vhosts/post-deploy b/plugins/nginx-vhosts/core-post-deploy
similarity index 81%
rename from plugins/nginx-vhosts/post-deploy
rename to plugins/nginx-vhosts/core-post-deploy
index f716b0715..7bb7db877 100755
--- a/plugins/nginx-vhosts/post-deploy
+++ b/plugins/nginx-vhosts/core-post-deploy
@@ -5,9 +5,9 @@ source "$PLUGIN_AVAILABLE_PATH/domains/functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions"
source "$PLUGIN_AVAILABLE_PATH/proxy/functions"
-nginx_post_deploy() {
- declare desc="nginx-vhosts post-deploy plugin trigger"
- local trigger="nginx_post_deploy"
+nginx_core_post_deploy() {
+ declare desc="nginx-vhosts core-post-deploy plugin trigger"
+ local trigger="nginx_core_post_deploy"
local APP="$1"
if [[ -f "$DOKKU_ROOT/$APP/IP.web.1" ]] && [[ -f "$DOKKU_ROOT/$APP/PORT.web.1" ]]; then
if [[ "$(is_app_vhost_enabled "$APP")" == "false" ]]; then
@@ -21,5 +21,5 @@ nginx_post_deploy() {
}
if [[ "$(get_app_proxy_type "$1")" == "nginx" ]]; then
- nginx_post_deploy "$@"
+ nginx_core_post_deploy "$@"
fi
diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions
index 044c2d0dd..4778d266d 100755
--- a/plugins/nginx-vhosts/functions
+++ b/plugins/nginx-vhosts/functions
@@ -39,9 +39,9 @@ restart_nginx() {
sudo /sbin/service nginx reload > /dev/null
;;
- arch)
+ arch|centos)
sudo /usr/bin/systemctl reload nginx
- ;;
+ ;;
esac
}
@@ -81,14 +81,13 @@ configure_nginx_ports() {
config_set --no-restart "$APP" DOKKU_NGINX_PORT="$NGINX_PORT"
fi
if [[ -z "$DOKKU_NGINX_SSL_PORT" ]]; then
- if (is_ssl_enabled "$APP") && [[ -z "$RAW_TCP_PORTS" ]]; then
- if [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then
+ if (is_ssl_enabled "$APP"); then
+ local NGINX_SSL_PORT=443
+ if [[ -z "$RAW_TCP_PORTS" ]] && [[ "$IS_APP_VHOST_ENABLED" == "false" ]]; then
dokku_log_info1 "no nginx ssl port set. setting to random open high port"
- local NGINX_SSL_PORT=$(get_available_port)
- else
- local NGINX_SSL_PORT=443
+ NGINX_SSL_PORT=$(get_available_port)
fi
- config_set --no-restart "$APP" DOKKU_NGINX_SSL_PORT="$NGINX_SSL_PORT"
+ config_set --no-restart "$APP" DOKKU_NGINX_SSL_PORT="$NGINX_SSL_PORT"
fi
fi
if [[ -z "$DOKKU_PROXY_PORT_MAP" ]]; then
@@ -148,7 +147,7 @@ get_custom_nginx_template() {
}
is_spdy_enabled() {
- declare desc="detects whether the installed nginx version has spdy or http2 support"
+ declare desc="detects whether the installed nginx version has spdy support"
local NGINX_VERSION="$1"
local MAJOR_VERSION MINOR_VERSION PATCH_VERSION
local HAS_SUPPORT=true
@@ -174,6 +173,33 @@ is_spdy_enabled() {
echo $HAS_SUPPORT
}
+is_http2_enabled() {
+ declare desc="detects whether the installed nginx version has http2 support"
+ local NGINX_VERSION="$1"
+ local MAJOR_VERSION MINOR_VERSION PATCH_VERSION
+ local HAS_SUPPORT=false
+
+ if ! which nginx > /dev/null 2>&1; then
+ echo $HAS_SUPPORT
+ return
+ fi
+
+ MAJOR_VERSION=$(echo "$NGINX_VERSION" | awk '{split($0,a,"."); print a[1]}')
+ MINOR_VERSION=$(echo "$NGINX_VERSION" | awk '{split($0,a,"."); print a[2]}')
+ PATCH_VERSION=$(echo "$NGINX_VERSION" | awk '{split($0,a,"."); print a[3]}')
+ if [[ "$MAJOR_VERSION" -ge "2" ]]; then
+ HAS_SUPPORT=true
+ elif [[ "$MAJOR_VERSION" -eq "1" ]]; then
+ if [[ "$MINOR_VERSION" -eq "11" ]] && [[ "$PATCH_VERSION" -ge "5" ]]; then
+ HAS_SUPPORT=true
+ elif [[ "$MINOR_VERSION" -ge "12" ]]; then
+ HAS_SUPPORT=true
+ fi
+ fi
+
+ echo $HAS_SUPPORT
+}
+
nginx_build_config() {
declare desc="build nginx config to proxy app containers using sigil"
local APP="$1"; verify_app_name "$APP"
@@ -245,11 +271,19 @@ nginx_build_config() {
else
local SSL_VHOSTS=$(< "$DOKKU_ROOT/HOSTNAME")
fi
- local SSL_SERVER_NAME=$(echo "$SSL_VHOSTS" | xargs)
+ local SSL_SERVER_NAME
+ local host
+ for host in $SSL_VHOSTS; do
+ # SSL_SERVER_NAME should only contain items not in NOSSL_SERVER_NAME
+ if [[ ! $NOSSL_SERVER_NAME =~ (^|[[:space:]])$host($|[[:space:]]) ]]; then
+ SSL_SERVER_NAME="${host}${SSL_SERVER_NAME:+ $SSL_SERVER_NAME}"
+ fi
+ done
fi
local NGINX_VERSION="$(nginx -v 2>&1 | cut -d'/' -f 2)"
local SPDY_SUPPORTED="$(is_spdy_enabled "$NGINX_VERSION")"
+ local HTTP2_SUPPORTED="$(is_http2_enabled "$NGINX_VERSION")"
eval "$(config_export app "$APP")"
local SIGIL_PARAMS=(-f $NGINX_TEMPLATE APP="$APP" DOKKU_ROOT="$DOKKU_ROOT"
@@ -257,6 +291,7 @@ nginx_build_config() {
DOKKU_APP_LISTENERS="$DOKKU_APP_LISTENERS"
PASSED_LISTEN_IP_PORT="$PASSED_LISTEN_IP_PORT"
SPDY_SUPPORTED="$SPDY_SUPPORTED"
+ HTTP2_SUPPORTED="$HTTP2_SUPPORTED"
DOKKU_APP_LISTEN_PORT="$DOKKU_APP_LISTEN_PORT" DOKKU_APP_LISTEN_IP="$DOKKU_APP_LISTEN_IP"
APP_SSL_PATH="$APP_SSL_PATH" SSL_INUSE="$SSL_INUSE" SSL_SERVER_NAME="$SSL_SERVER_NAME"
NGINX_PORT="$NGINX_PORT" NGINX_SSL_PORT="$NGINX_SSL_PORT" RAW_TCP_PORTS="$RAW_TCP_PORTS"
diff --git a/plugins/nginx-vhosts/install b/plugins/nginx-vhosts/install
index e08840d2d..6057a8720 100755
--- a/plugins/nginx-vhosts/install
+++ b/plugins/nginx-vhosts/install
@@ -17,12 +17,19 @@ case "$DOKKU_DISTRO" in
arch)
echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
;;
+
+ centos)
+ echo "%dokku ALL=(ALL) NOPASSWD:/usr/bin/systemctl reload nginx, /usr/sbin/nginx -t" > /etc/sudoers.d/dokku-nginx
+ echo "Defaults:dokku !requiretty" >> /etc/sudoers.d/dokku-nginx
+ ;;
esac
chmod 0440 /etc/sudoers.d/dokku-nginx
# if dokku.conf has not been created, create it
if [[ ! -f /etc/nginx/conf.d/dokku.conf ]]; then
+ mkdir -p /etc/nginx/conf.d
+ chown root:root /etc/nginx/conf.d
cat< /etc/nginx/conf.d/dokku.conf
include $DOKKU_ROOT/*/nginx.conf;
@@ -66,10 +73,8 @@ case "$DOKKU_DISTRO" in
"$NGINX_INIT" nginx start || "$NGINX_INIT" nginx reload
;;
- arch)
+ arch|centos)
NGINX_INIT="/usr/bin/systemctl"
"$NGINX_INIT" start nginx || "$NGINX_INIT" reload nginx
;;
esac
-
-
diff --git a/plugins/nginx-vhosts/internal-functions b/plugins/nginx-vhosts/internal-functions
new file mode 100755
index 000000000..93d929bb6
--- /dev/null
+++ b/plugins/nginx-vhosts/internal-functions
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+
+nginx_vhosts_help_content_func() {
+ declare desc="return nginx plugin help content"
+ cat<, (Re)builds nginx config for given app
+ nginx:access-logs [-t], Show the nginx access logs for an application (-t follows)
+ nginx:error-logs [-t], Show the nginx error logs for an application (-t follows)
+help_content
+}
+
+nginx_vhosts_help_cmd() {
+ if [[ $1 = "nginx:help" ]] ; then
+ echo -e 'Usage: dokku nginx[:COMMAND]'
+ echo ''
+ echo 'Interact with Dokku'"'"'s Nginx proxy.'
+ echo ''
+ echo 'Additional commands:'
+ nginx_vhosts_help_content_func | sort | column -c2 -t -s,
+ elif [[ $(ps -o command= $PPID) == *"--all"* ]]; then
+ nginx_vhosts_help_content_func
+ else
+ cat<, Show proxy settings for app
- proxy:enable , Enable proxy for app
- proxy:disable , Disable proxy for app
- proxy:ports , List proxy port mappings for app
- proxy:ports-clear , Clear all proxy port mappings for app
- proxy:ports-add :: [::...], Set proxy port mappings for app
- proxy:ports-remove [|::...], Unset proxy port mappings for app
- proxy:set , Set proxy type for app
-help_content
- }
-
- if [[ $1 = "proxy:help" ]] ; then
- echo -e 'Usage: dokku proxy[:COMMAND]'
- echo ''
- echo 'Control the proxy used by dokku, per app.'
- echo ''
- echo 'Additional commands:'
- help_content_func | sort | column -c2 -t -s,
- else
- help_content_func
- fi
+ proxy_help_cmd "$@"
;;
*)
diff --git a/plugins/proxy/functions b/plugins/proxy/functions
index d7d3b1a2f..5202a21ef 100755
--- a/plugins/proxy/functions
+++ b/plugins/proxy/functions
@@ -24,10 +24,16 @@ get_app_proxy_type() {
echo "$APP_PROXY_TYPE"
}
+get_app_proxy_port_map() {
+ declare desc="return a list of configured port mappings"
+ local APP="$1"; local PROXY_PORT_MAP="$(config_get "$APP" DOKKU_PROXY_PORT_MAP || true)"
+ echo "$PROXY_PORT_MAP"
+}
+
list_app_proxy_ports() {
declare desc="list proxy port mappings for an app"
local APP="$1"; verify_app_name "$APP"
- local PROXY_PORT_MAP="$(config_get "$APP" DOKKU_PROXY_PORT_MAP || true)"
+ local PROXY_PORT_MAP="$(get_app_proxy_port_map "$APP")"
if [[ -n "$PROXY_PORT_MAP" ]]; then
dokku_log_info1_quiet "Port mappings for $APP"
@@ -45,7 +51,7 @@ filter_app_proxy_ports() {
declare desc="list proxy port mappings for an app that start with a particular scheme and a host port"
local APP="$1"; verify_app_name "$APP"
local SCHEME="$2" HOST_PORT="$3"
- local PROXY_PORT_MAP="$(config_get "$APP" DOKKU_PROXY_PORT_MAP || true)"
+ local PROXY_PORT_MAP="$(get_app_proxy_port_map "$APP")"
if [[ -n "$PROXY_PORT_MAP" ]]; then
local port_map
@@ -62,7 +68,7 @@ filter_app_proxy_ports() {
add_proxy_ports() {
declare desc="add proxy port mappings from an app"
local APP="$1"
- local PROXY_PORT_MAP="$(config_get "$APP" DOKKU_PROXY_PORT_MAP || true)"
+ local PROXY_PORT_MAP="$(get_app_proxy_port_map "$APP")"
shift 1
local INPUT_PORTS="$*"
@@ -78,7 +84,7 @@ add_proxy_ports() {
remove_proxy_ports() {
declare desc="remove specific proxy port mappings from an app"
local APP="$1"
- local PROXY_PORT_MAP="$(config_get "$APP" DOKKU_PROXY_PORT_MAP || true)"
+ local PROXY_PORT_MAP="$(get_app_proxy_port_map "$APP")"
local RE_PORT='^[0-9]+$'
shift 1
diff --git a/plugins/proxy/internal-functions b/plugins/proxy/internal-functions
new file mode 100755
index 000000000..3ff81c845
--- /dev/null
+++ b/plugins/proxy/internal-functions
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
+
+proxy_help_content_func() {
+ declare desc="return proxy plugin help content"
+ cat<, [DEPRECATED] Show proxy settings for app
+ proxy:enable , Enable proxy for app
+ proxy:disable , Disable proxy for app
+ proxy:ports