From 3ae01755f280f24dbc89d1de79fdb0a1ee588aae Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Fri, 29 Nov 2019 00:07:05 +0100 Subject: [PATCH] fix: management of ``compose.yml`` location We need to parse the command line and modify it accordingly if option ``-f`` or ``--file`` is given. Signed-off-by: Valentin Lab --- bin/compose | 300 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 280 insertions(+), 20 deletions(-) diff --git a/bin/compose b/bin/compose index e75519e..eaf5836 100755 --- a/bin/compose +++ b/bin/compose @@ -376,6 +376,225 @@ check_no_links_subdirs() { } +## requires docker_run_opts to be set +get_compose_file_opt() { + local compose_docker_image="$1" hash override + shift + image_id=$(docker_image_id "$compose_docker_image") + override=$(get_volume_opt "${docker_run_opts[@]}") || return 1 + if [ -n "$override" ]; then + if ! [ -f "$override" ]; then + err "Invalid override of 'compose-core' detected. File '$override' does not exist on host." + exit 1 + fi + hash=$( { p0 "$image_id"; cat "$override"; } | md5_compat) + else + hash=$(p0 "$image_id" | md5_compat) + fi + + _get_compose_file_opt "$hash" "$override" "$@" || exit 1 +} + + +_get_compose_file_opt() { + local hash_bin="$1" override="$2" \ + cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$(p0 "$@" | md5_compat)" + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + shift 2 + DC_MATCH_MULTI=$(get_compose_multi_opts_list "$hash_bin" "$override") || return 1 + DC_MATCH_SINGLE=$(get_compose_single_opts_list "$hash_bin" "$override") || return 1 + + while read-0 arg; do + case "$arg" in + "-f"|"--file") + read-0 value + e "$value" + return 0 + ;; + --*|-*) + if str_pattern_matches "$arg" $DC_MATCH_MULTI; then + read-0 value + opts+=("$arg" "$value") + shift + elif str_pattern_matches "$arg" $DC_MATCH_SINGLE; then + opts+=("$arg") + else + debug "Unknown option '$arg'. Didn't manage to pre-parse correctly options." + return 1 + fi + ;; + *) + return 1 + ;; + esac + done < <(cla.normalize "$@") | tee "$cache_file" +} + + +replace_compose_file_opt() { + local compose_docker_image="$1" hash override + shift + image_id=$(docker_image_id "$compose_docker_image") + override=$(get_volume_opt "${docker_run_opts[@]}") || return 1 + if [ -n "$override" ]; then + if ! [ -f "$override" ]; then + err "Invalid override of 'compose-core' detected. File '$override' does not exist on host." + exit 1 + fi + hash=$( { p0 "$image_id"; cat "$override"; } | md5_compat) + else + hash=$(p0 "$image_id" | md5_compat) + fi + _replace_compose_file_opt "$hash" "$override" "$@" || exit 1 +} + + +_replace_compose_file_opt() { + local hash_bin="$1" override="$2" \ + cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$(p0 "$@" | md5_compat)" + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + debug "Replacing '-f|--file' argument in command line." + shift 2 + DC_MATCH_MULTI=$(get_compose_multi_opts_list "$hash_bin" "$override") || return 1 + DC_MATCH_SINGLE=$(get_compose_single_opts_list "$hash_bin" "$override") || return 1 + + args=() + while read-0 arg; do + case "$arg" in + "-f"|"--file") + read-0 value + args+=("$arg" "${value##*/}") + ;; + --*|-*) + if str_pattern_matches "$arg" $DC_MATCH_MULTI; then + read-0 value + args+=("$arg" "$value") + shift + elif str_pattern_matches "$arg" $DC_MATCH_SINGLE; then + args+=("$arg") + else + err "Unknown option '$arg'. Didn't manage to pre-parse correctly options." + return 1 + fi + ;; + *) + args+=("$arg") + while read-0 arg; do + args+=("$arg") + done + ;; + esac + done < <(cla.normalize "$@") + p0 "${args[@]}" | tee "$cache_file" +} + + +get_compose_opts_list() { + local hash_bin="$1" override="$2" \ + cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$1" + + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + debug "Pre-Launching docker to retrieve command line argument definitions." + opts_list=() + if [ -n "$override" ]; then + opts_list+=("-v" "$override:/usr/local/bin/compose-core:ro") + fi + compose_opts_help=$(docker run "${opts_list[@]}" "$COMPOSE_DOCKER_IMAGE" --help 2>/dev/null) + + echo "$compose_opts_help" | + grep '^Options:' -A 20000 | + tail -n +2 | + { cat ; echo; } | + grep -E -m 1 "^\S*\$" -B 10000 | + head -n -1 | + grep -E "^\s+-" | + sed -r 's/\s+((((-[a-zA-Z]|--[a-zA-Z0-9-]+)( [A-Z=]+|=[^ ]+)?)(, )?)+)\s+.*$/\1/g' | + tee "$cache_file" || return 1 +} + + +multi_opts_filter() { + grep -E "$_MULTIOPTION_REGEX_LINE_FILTER" | + sed -r "s/^($_MULTIOPTION_REGEX)(\s|=).*$/\1/g" | + tr ',' "\n" | nspc +} + + +single_opts_filter() { + grep -E -v "$_MULTIOPTION_REGEX_LINE_FILTER" | + tr ',' "\n" | nspc +} + + +get_compose_multi_opts_list() { + local hash_bin="$1" override="$2" \ + cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$1" opts_list + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + + opts_list=$(get_compose_opts_list "$hash_bin" "$override") || return 1 + echo "$opts_list" | multi_opts_filter | tee "$cache_file" +} + + +get_compose_single_opts_list() { + local hash_bin="$1" override="$2" \ + cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$1" opts_list + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + opts_list=$(get_compose_opts_list "$hash_bin" "$override") || return 1 + echo "$opts_list" | single_opts_filter | tee "$cache_file" +} + + +get_volume_opt() { + local cache_file="$COMPOSE_CACHE/$FUNCNAME.cache.$(p0 "$@" | md5_compat)" + if [ -e "$cache_file" ]; then + cat "$cache_file" && + touch "$cache_file" || return 1 + return 0 + fi + + while [ "$#" != 0 ]; do + case "$1" in + "-v") + dst="${2#*:}" + dst="${dst%:*}" + if [ "$dst" == "/usr/local/bin/compose-core" ]; then + override="${2%%:*}" + debug "Override of compose-core found: $override" + fi + shift;; + "-e"|"-w") + shift;; + *) + : + ;; + esac + shift + done + { [ -n "$override" ] && echo "$override"; } | tee "$cache_file" +} + + get_tz() { if [ -n "$TZ" ]; then : @@ -448,24 +667,8 @@ mk_docker_run_options() { ~/.compose.conf ) - docker_run_opts=("-v" "/var/run/docker.sock:/var/run/docker.sock") - ## current dir - if parent=$(while true; do - [ -e "./compose.yml" ] && { - echo "$PWD" - exit 0 - } - [ "$PWD" == "/" ] && exit 1 - cd .. - done - ); then - docker_path=$COMPOSE_VAR/root/$(basename "$parent") - docker_run_opts+=("-v" "$parent:$docker_path:ro" \ - "-w" "$docker_path") - fi - ## ## Load config files @@ -517,6 +720,7 @@ mk_docker_run_options() { ;; esac + mkdir -p "$COMPOSE_CACHE" ## get TZ value and prepare TZ_PATH TZ=$(get_tz) || exit 1 @@ -584,6 +788,11 @@ mk_docker_run_options() { COMPOSE_DOCKER_IMAGE=${COMPOSE_DOCKER_IMAGE:-docker.0k.io/compose} docker_run_opts+=("-e" "COMPOSE_DOCKER_IMAGE=$COMPOSE_DOCKER_IMAGE") + if ! docker_has_image "$COMPOSE_DOCKER_IMAGE"; then + docker pull "$COMPOSE_DOCKER_IMAGE" || exit 1 + fi + + ## SSH config docker_run_opts+=( "-v" "$HOME/.ssh:/root/.ssh:ro" @@ -605,6 +814,48 @@ mk_docker_run_options() { esac done < <(list_compose_vars) + compose_file=$(get_compose_file_opt "$COMPOSE_DOCKER_IMAGE" "$@") || exit 1 + + if [ -z "$compose_file" ]; then + ## Find a compose.yml in parents + debug "No config file specified on command line arguments" + debug "Looking for 'compose.yml' in self and parents.." + if parent=$(while true; do + [ -e "./compose.yml" ] && { + echo "$PWD" + exit 0 + } + [ "$PWD" == "/" ] && exit 1 + cd .. + done + ); then + compose_file="$(realpath "$parent"/"compose.yml")" + debug " .. found '$compose_file'" + else + debug " .. not found." + fi + fi + + if [ -n "$compose_file" ]; then + if ! [ -f "$compose_file" ]; then + die "Specified compose file '$compose_file' not found." + fi + compose_file="$(realpath "$compose_file")" + parent_dir="${compose_file%/*}" + docker_path=/var/lib/compose/root/${parent_dir##*/}/${compose_file##*/} + docker_run_opts+=( + "-e" "COMPOSE_YML_FILE=${compose_file##*/}" + "-v" "${compose_file}:${docker_path}:ro" + "-w" "${docker_path%/*}" + ) + else + docker_path=/var/lib/compose/root + docker_run_opts+=( + "-w" "${docker_path}" + ) + fi + + clean_unused_sessions filename=$(mktemp -p /tmp/ -t launch_opts-XXXXXXXXXXXXXXXX) { p0 "${docker_run_opts[@]}" @@ -642,8 +893,7 @@ run() { docker_run_opts=() if [ -z "$COMPOSE_LAUNCHER_OPTS" ]; then - clean_unused_sessions - COMPOSE_LAUNCHER_OPTS="$(mk_docker_run_options)" + COMPOSE_LAUNCHER_OPTS="$(mk_docker_run_options "$@")" || return 1 fi while read-0 opt; do @@ -656,8 +906,18 @@ run() { else env= fi + if [[ "$vol" == "true" && "$opt" == *":/var/cache/compose" ]]; then + COMPOSE_CACHE=${opt%%:*} + elif [ "$opt" == "-v" ]; then + vol=true + else + vol= + fi done < <(cat "$COMPOSE_LAUNCHER_OPTS") + array_read-0 cmd_args < <(replace_compose_file_opt "$COMPOSE_DOCKER_IMAGE" "$@") + set -- "${cmd_args[@]}" + [ -t 0 ] && docker_run_opts+=("-i") [ -t 1 ] && docker_run_opts+=("-t") @@ -694,8 +954,8 @@ run() { ## -depends docker cat readlink sed realpath +depends docker cat readlink sed realpath tee sed grep tail ansi_color "${ansi_color:-tty}" -run "$@" +run "$@" \ No newline at end of file