From 73e85b7df52d7ff102e3bcdf0ce7c0a14066725f Mon Sep 17 00:00:00 2001 From: Alex Quick Date: Mon, 30 Jan 2017 22:20:39 -0500 Subject: [PATCH] [config] preliminary Dockerfile support --- .../00_dokku-standard/exec-app-json-scripts | 7 +++-- plugins/00_dokku-standard/pre-deploy | 8 +++--- plugins/00_dokku-standard/subcommands/run | 5 +++- plugins/common/functions | 17 ++++++++---- plugins/config/docker-args-deploy | 27 +++++++++++++++---- plugins/config/functions | 11 ++++++++ plugins/config/src/configenv/configenv.go | 5 ++-- .../config/src/subcommands/export/export.go | 8 ++++++ plugins/config/src/subcommands/get/get.go | 8 +++++- tests/apps/dockerfile/check_deploy | 7 ++--- 10 files changed, 79 insertions(+), 24 deletions(-) diff --git a/plugins/00_dokku-standard/exec-app-json-scripts b/plugins/00_dokku-standard/exec-app-json-scripts index 7bb3e5809..e90457b2c 100755 --- a/plugins/00_dokku-standard/exec-app-json-scripts +++ b/plugins/00_dokku-standard/exec-app-json-scripts @@ -55,8 +55,11 @@ execute_script() { # strip --restart args from DOCKER_ARGS local DOCKER_ARGS=$(sed -e "s/--restart=[[:graph:]]\+[[:blank:]]\?//g" <<< "$DOCKER_ARGS") - # shellcheck disable=SC2086 - local id=$(docker run "$DOKKU_GLOBAL_RUN_ARGS" -e DOKKU_TRACE="$DOKKU_TRACE" --label=dokku_phase_script="${PHASE_SCRIPT_KEY}" -d -v "$CACHE_DIR:/cache" $DOCKER_ARGS "$IMAGE" /bin/bash -c "$COMMAND") + # eval args as array to respect escapes + declare -a ARG_ARRAY + eval ARG_ARRAY=($DOCKER_ARGS) + + local id=$(docker run "$DOKKU_GLOBAL_RUN_ARGS" -e DOKKU_TRACE="$DOKKU_TRACE" --label=dokku_phase_script="${PHASE_SCRIPT_KEY}" -d -v "$CACHE_DIR:/cache" "${ARG_ARRAY[@]}" "$IMAGE" /bin/bash -c "$COMMAND") if test "$(docker wait "$id")" -eq 0; then dokku_container_log_verbose_quiet "$id" if [[ "$PHASE_SCRIPT_KEY" != "postdeploy" ]];then diff --git a/plugins/00_dokku-standard/pre-deploy b/plugins/00_dokku-standard/pre-deploy index c78257140..ec8b1ba30 100755 --- a/plugins/00_dokku-standard/pre-deploy +++ b/plugins/00_dokku-standard/pre-deploy @@ -15,8 +15,9 @@ exec_app_json_scripts() { app_user_pre_deploy_trigger() { local desc="app user pre-deploy trigger" - local APP="$1"; local IMAGE_TAG="$2"; local IMAGE; local DOCKER_ARGS + local APP="$1"; local IMAGE_TAG="$2"; local IMAGE; local DOCKER ARGS local DOKKU_APP_TYPE DOKKU_APP_USER APP_PATHS CONTAINER_PATHS + declare -a ARG_ARRAY IMAGE=$(get_app_image_name "$APP" "$IMAGE_TAG") @@ -27,12 +28,13 @@ app_user_pre_deploy_trigger() { if [[ -n "$APP_PATHS" ]]; then CONTAINER_PATHS=$(echo "$APP_PATHS" | awk -F ':' '{ print $2 }' | xargs) - DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG" | xargs) + DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG") + eval ARG_ARRAY=($DOCKER_ARGS) fi if [[ "$DOKKU_APP_TYPE" == "herokuish" ]] && [[ -n "$CONTAINER_PATHS" ]]; then # shellcheck disable=SC2086 - docker run $DOKKU_GLOBAL_RUN_ARGS $DOCKER_ARGS $IMAGE /bin/bash -c "find $CONTAINER_PATHS -not -user $DOKKU_APP_USER -print0 | xargs -0 -r chown -R $DOKKU_APP_USER" || true + docker run $DOKKU_GLOBAL_RUN_ARGS "${ARG_ARRAY[@]}" $IMAGE /bin/bash -c "find $CONTAINER_PATHS -not -user $DOKKU_APP_USER -print0 | xargs -0 -r chown -R $DOKKU_APP_USER" || true fi } diff --git a/plugins/00_dokku-standard/subcommands/run b/plugins/00_dokku-standard/subcommands/run index 1a844d8fe..9f5f6aa41 100755 --- a/plugins/00_dokku-standard/subcommands/run +++ b/plugins/00_dokku-standard/subcommands/run @@ -22,6 +22,9 @@ dokku_run_cmd() { local DOCKER_ARGS=$(: | plugn trigger docker-args-run "$APP" "$IMAGE_TAG") [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " + declare -a ARG_ARRAY + eval ARG_ARRAY=($DOCKER_ARGS) + local DOKKU_RUN_OPTS=" -e DYNO=run.$RANDOM" if [[ "$DOKKU_RM_CONTAINER" ]]; then DOKKU_RUN_OPTS+=" --rm" @@ -44,7 +47,7 @@ dokku_run_cmd() { fi # shellcheck disable=SC2086 - docker run $DOKKU_GLOBAL_RUN_ARGS $DOKKU_RUN_OPTS $DOCKER_ARGS $IMAGE $EXEC_CMD "$@" + docker run $DOKKU_GLOBAL_RUN_ARGS $DOKKU_RUN_OPTS "${ARG_ARRAY[@]}" $IMAGE $EXEC_CMD "$@" } dokku_run_cmd "$@" diff --git a/plugins/common/functions b/plugins/common/functions index c9f28250a..eb306ebf7 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -458,8 +458,10 @@ dokku_build() { local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " + declare -a ARG_ARRAY + eval ARG_ARRAY=($DOCKER_ARGS) # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build) + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache "${ARG_ARRAY[@]}" $IMAGE /build) docker attach "$id" test "$(docker wait "$id")" -eq 0 docker commit "$id" "$IMAGE" > /dev/null @@ -483,9 +485,11 @@ dokku_build() { local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") # strip --volume and -v args from DOCKER_ARGS local DOCKER_ARGS=$(sed -e "s/--volume=[[:graph:]]\+[[:blank:]]\?//g" -e "s/-v[[:blank:]]\?[[:graph:]]\+[[:blank:]]\?//g" <<< "$DOCKER_ARGS") + declare -a ARG_ARRAY + eval ARG_ARRAY=($DOCKER_ARGS) # shellcheck disable=SC2086 - docker build $DOCKER_ARGS $DOKKU_DOCKER_BUILD_OPTS -t $IMAGE . + docker build "${ARG_ARRAY[@]}" $DOKKU_DOCKER_BUILD_OPTS -t $IMAGE . plugn trigger post-build-dockerfile "$APP" ;; @@ -583,6 +587,9 @@ dokku_deploy_cmd() { DOCKER_ARGS+=" -e DYNO=$PROC_TYPE.$CONTAINER_INDEX " [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " + declare -a ARG_ARRAY + eval ARG_ARRAY=($DOCKER_ARGS) + [[ -n "$DOKKU_HEROKUISH" ]] && local START_CMD="/start $PROC_TYPE" if [[ -z "$DOKKU_HEROKUISH" ]]; then @@ -609,7 +616,7 @@ dokku_deploy_cmd() { fi if [[ "$DOKKU_IS_APP_PROXY_ENABLED" == "true" ]]; then # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d -e PORT=$port "${ARG_ARRAY[@]}" $IMAGE $START_CMD) local ipaddr=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$id") # Docker < 1.9 compatibility if [[ -z $ipaddr ]]; then @@ -617,13 +624,13 @@ dokku_deploy_cmd() { fi else # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOKKU_DOCKER_PORT_ARGS -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOKKU_DOCKER_PORT_ARGS -e PORT=$port "${ARG_ARRAY[@]}" $IMAGE $START_CMD) local port=$(docker port "$id" "$port" | sed 's/[0-9.]*://') local ipaddr=127.0.0.1 fi else # shellcheck disable=SC2086 - local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d $DOCKER_ARGS $IMAGE $START_CMD) + local id=$(docker run $DOKKU_GLOBAL_RUN_ARGS -d "${ARG_ARRAY[@]}" $IMAGE $START_CMD) fi kill_new() { diff --git a/plugins/config/docker-args-deploy b/plugins/config/docker-args-deploy index cd89cc14d..366d2dd7d 100755 --- a/plugins/config/docker-args-deploy +++ b/plugins/config/docker-args-deploy @@ -7,15 +7,32 @@ config_docker_args() { declare desc="config docker-args plugin trigger" local trigger="$0 config_docker_args" local STDIN=$(cat); local APP="$1"; local IMAGE_TAG="$2"; local IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG") - local DOCKERFILE_ENV_FILE="$DOKKU_ROOT/$APP/DOCKERFILE_ENV_FILE" verify_app_name "$APP" if ! is_image_herokuish_based "$IMAGE"; then - > "$DOCKERFILE_ENV_FILE" - config_export global | sed -e "s:^export ::g" -e "s:=':=:g" -e "s:'$::g" > "$DOCKERFILE_ENV_FILE" - config_export app "$APP" | sed -e "s:^export ::g" -e "s:=':=:g" -e "s:'$::g" >> "$DOCKERFILE_ENV_FILE" + shopt -s extglob + local ENV_ARGS + declare -A ENV_ARGS + for key in $(config_keys global); do + VAR="$(config_get --global "$key")" + printf -vVAR '%q' "${VAR%%*( )}" + ENV_ARGS[$key]="$VAR" + done + for key in $(config_keys app "$APP"); do + VAR="$(config_get "$APP" "$key")" + printf -vVAR '%q' "${VAR%%*( )}" + ENV_ARGS[$key]="$VAR" + done + shopt -u extglob + local ALL_ENV_ARGS + declare -a ALL_ENV_ARGS + for key in "${!ENV_ARGS[@]}"; do + local ARG="$key=${ENV_ARGS[$key]}" + ALL_ENV_ARGS+=("--env") + ALL_ENV_ARGS+=("$ARG") + done - echo -n "$STDIN --env-file=$DOCKERFILE_ENV_FILE" + echo -n "$STDIN ${ALL_ENV_ARGS[@]}" else echo -n "$STDIN" fi diff --git a/plugins/config/functions b/plugins/config/functions index 0a60f5fec..0dda7d76e 100755 --- a/plugins/config/functions +++ b/plugins/config/functions @@ -13,6 +13,17 @@ config_export() { return 0 } +config_keys() { + declare desc="returns keys for app or global" + local CONFIG_TYPE="$1" + local APP="$2" + if [[ $CONFIG_TYPE == "global" ]]; then + APP="--global" + fi + $PLUGIN_AVAILABLE_PATH/config/subcommands/export config:export --keys "$APP" + return 0 +} + config_get() { declare desc="get value of given config var" $PLUGIN_AVAILABLE_PATH/config/subcommands/get config:get "$@" diff --git a/plugins/config/src/configenv/configenv.go b/plugins/config/src/configenv/configenv.go index 65ae6884a..7fdf53d7d 100644 --- a/plugins/config/src/configenv/configenv.go +++ b/plugins/config/src/configenv/configenv.go @@ -37,12 +37,13 @@ func (e *Env) stringWithEntryPrefix(prefix string) string { sort.Strings(keys) entries := make([]string, len(keys)) for i, k := range keys { - entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, singleQuoteEscape(e.env[k])) + entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, SingleQuoteEscape(e.env[k])) } return strings.Join(entries, "\n") } -func singleQuoteEscape(value string) string { // so that 'esc'apped' -> 'esc'\''aped' +//SingleQuoteEscape escapes the value as if it were shell-quoted in single quotes +func SingleQuoteEscape(value string) string { // so that 'esc'apped' -> 'esc'\''aped' return strings.Replace(value, "'", "'\\''", -1) } diff --git a/plugins/config/src/subcommands/export/export.go b/plugins/config/src/subcommands/export/export.go index 52feae877..9253dc88f 100644 --- a/plugins/config/src/subcommands/export/export.go +++ b/plugins/config/src/subcommands/export/export.go @@ -13,6 +13,7 @@ import ( func main() { args := flag.NewFlagSet("config:export", flag.ExitOnError) global := args.Bool("global", false, "--global: use the global environment") + keys := args.Bool("keys", false, "--keys: export keys only") envfile := args.Bool("envfile", false, "--envfile: export as envfile rather than bash exports") args.Parse(os.Args[2:]) @@ -32,6 +33,13 @@ func main() { if err != nil { common.LogFail(err.Error()) } + if *keys { + for _, k := range env.Keys() { + fmt.Println(k) + } + return + } + if *envfile { fmt.Println(env.EnvfileString()) } else { diff --git a/plugins/config/src/subcommands/get/get.go b/plugins/config/src/subcommands/get/get.go index 3c57eea71..321ed856b 100644 --- a/plugins/config/src/subcommands/get/get.go +++ b/plugins/config/src/subcommands/get/get.go @@ -13,6 +13,7 @@ import ( func main() { args := flag.NewFlagSet("config:get", flag.ExitOnError) global := args.Bool("global", false, "--global: use the global environment") + quoted := args.Bool("quoted", false, "--quoted: get the value quoted") args.Parse(os.Args[2:]) appName := args.Arg(0) nextArg := 0 @@ -35,5 +36,10 @@ func main() { common.LogFail(fmt.Sprintf("Unexpected argument(s): %v", args.Args()[nextArg+1:])) } key := args.Arg(nextArg) - fmt.Printf("%s", env.GetDefault(key, "")) + value := env.GetDefault(key, "") + if *quoted { + fmt.Printf("'%s'", configenv.SingleQuoteEscape(value)) + } else { + fmt.Printf("%s", value) + } } diff --git a/tests/apps/dockerfile/check_deploy b/tests/apps/dockerfile/check_deploy index 6c9b953ce..4fa457760 100755 --- a/tests/apps/dockerfile/check_deploy +++ b/tests/apps/dockerfile/check_deploy @@ -4,15 +4,12 @@ set -e; test_url () { path="$1" expected_output="$2" - url="$base_url$path" - output="$(curl -s -S "$url:3000")" - echo "$output" + url="${base_url}:3000${path}" + output="$(curl -s -S "$url")" test "$output" == "$expected_output" } base_url="$1" test_url / "Hello World!" -test_url /env/FROM_APP_JSON "'foo -bar'" test_url /env/HELLO "Hello 'world' " \ No newline at end of file