From 6ec3fc4de55b3df91468ac1f2adc68c271a4f8f6 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Wed, 14 Jun 2023 22:46:26 -0300 Subject: [PATCH] Avoid parsing global or image-specific args Among other things. --- Dockerfile | 19 +++++++-- docker | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++-- test.sh | 85 ++++++++++++++++++++++++++++++++-------- 3 files changed, 192 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index b19ad1f..4bcff9d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,19 @@ +ARG DOCKER_VERSION="latest" +FROM docker:${DOCKER_VERSION} AS test + +# Install APK deps +RUN apk add --no-cache bash + +# Install dond-shim +ARG DOCKER_PATH="/usr/local/bin/docker" +RUN mv -f "${DOCKER_PATH}" "${DOCKER_PATH}.orig" +COPY docker "${DOCKER_PATH}" + +# Create fixtures +RUN mkdir -p /test && \ + echo test | tee /test/only-inside-container + +# Set default stage FROM felipecrs/fixdockergid:latest USER root @@ -7,6 +23,3 @@ RUN mv -f "${DOCKER_PATH}" "${DOCKER_PATH}.orig" COPY docker "${DOCKER_PATH}" USER rootless - -# Create fixtures -RUN echo test | tee /home/rootless/only-inside-container diff --git a/docker b/docker index fca86ec..6929f87 100755 --- a/docker +++ b/docker @@ -2,6 +2,10 @@ set -euo pipefail +if [[ "${DOND_SHIM_DEBUG:-false}" == true ]]; then + set -x +fi + function get_container_id() { local cpuset_output cpuset_output=$(head -1 /proc/self/cpuset) @@ -74,14 +78,107 @@ function fix_volume_arg() { volume_arg="${source}:${destination}" } +function get_docker_options_with_value() { + local -n target_array="${1}" + shift 1 + local command=("${@}") + + local help_output + help_output="$(docker.orig "${command[@]}" --help)" + + readarray -t help_lines <<<"${help_output}" + target_array=() + for line in "${help_lines[@]}"; do + # second group is the short option (optional) + # third group is the long option + if [[ "${line}" =~ ^[[:space:]]+((-[a-zA-Z]),[[:space:]])?(--[a-z0-9-]+)[[:space:]][a-z]+[[:space:]]+.*$ ]]; then + if [[ -n "${BASH_REMATCH[2]}" ]]; then + target_array+=("${BASH_REMATCH[2]}") + fi + target_array+=("${BASH_REMATCH[3]}") + fi + done +} + +options_with_value=() +get_docker_options_with_value options_with_value + original_args=("$@") -fixed_args=() -# TODO: only piggyback on docker run, container run, create and container create +skip_next=false +parsing="global" +global_args=() +found_first_global_positional=false +command_args=() +image_args=() +for i in "${!original_args[@]}"; do + arg="${original_args[${i}]}" + + if [[ "${parsing}" == "global" ]]; then + global_args+=("${arg}") + + if [[ "${skip_next}" == true ]]; then + skip_next=false + continue + fi + + if [[ "${arg}" == "-"* ]]; then + if [[ "${found_first_global_positional}" == false ]]; then + for option_with_value in "${options_with_value[@]}"; do + if [[ "${arg}" == "${option_with_value}" ]]; then + skip_next=true + continue + fi + done + fi + continue + fi + + if [[ "${arg}" == "run" || "${arg}" == "create" ]]; then + options_with_value=() + get_docker_options_with_value options_with_value "${arg}" + parsing="command" + elif [[ "${arg}" == "container" ]]; then + found_first_global_positional=true + true + else + # Skip if command is not run, create, container run, or container create + if [[ "${DOND_SHIM_PRINT_COMMAND:-false}" == true ]]; then + echo docker.orig "${original_args[@]}" + exit 0 + else + exec docker.orig "${original_args[@]}" + fi + fi + elif [[ "${parsing}" == "command" ]]; then + command_args+=("${arg}") + + if [[ "${skip_next}" == true ]]; then + skip_next=false + continue + fi + + if [[ "${arg}" == "-"* ]]; then + for option_with_value in "${options_with_value[@]}"; do + if [[ "${arg}" == "${option_with_value}" ]]; then + skip_next=true + continue + fi + done + continue + fi + + # If this is reached, then the first positional argument has been found + parsing="image" + elif [[ "${parsing}" == "image" ]]; then + image_args+=("${arg}") + fi +done +fixed_args=() extra_args=() fix_next_arg=false -for arg in "${original_args[@]}"; do +for arg in "${command_args[@]}"; do if [[ "${fix_next_arg}" == true ]]; then fix_next_arg=false volume_arg="${arg}" @@ -100,4 +197,10 @@ for arg in "${original_args[@]}"; do extra_args=() done -exec docker.orig "${fixed_args[@]}" +final_command=(docker.orig "${global_args[@]}" "${fixed_args[@]}" "${image_args[@]}") + +if [[ "${DOND_SHIM_PRINT_COMMAND:-false}" == true ]]; then + echo "${final_command[@]}" +else + exec "${final_command[@]}" +fi diff --git a/test.sh b/test.sh index 783802c..05b39e7 100755 --- a/test.sh +++ b/test.sh @@ -1,25 +1,78 @@ #!/bin/bash -set -euxo pipefail +set -euo pipefail -image_id="$(docker build --quiet .)" +if [[ "${DEBUG:-false}" == true ]]; then + set -x + export DOND_SHIM_DEBUG=true +fi -# shellcheck disable=SC2312 -docker_args=(docker run --rm --user "$(id -u):$(id -g)" --volume /var/run/docker.sock:/var/run/docker.sock) +# Set docker versions from args or use defaults +if [[ $# -eq 0 ]]; then + docker_versions=("18.09" "19.03" "20.10" "23" "24" latest) +else + docker_versions=("$@") +fi -# Check if docker on docker is working -"${docker_args[@]}" "${image_id}" docker version >/dev/null +for docker_version in "${docker_versions[@]}"; do + echo "Testing with docker version: ${docker_version}" -# Check if mounting an volume from the container gets fixed -"${docker_args[@]}" "${image_id}" docker run --rm --volume /home/rootless/only-inside-container:/only-inside-container ubuntu:latest grep "^test$" /only-inside-container >/dev/null -"${docker_args[@]}" "${image_id}" docker run --rm --volume=/home/rootless/only-inside-container:/only-inside-container ubuntu:latest grep "^test$" /only-inside-container >/dev/null + image_id="$(docker build --target test --build-arg "DOCKER_VERSION=${docker_version}" --quiet .)" -# Check if mounting a volume which is already a volume gets fixed -"${docker_args[@]}" --volume "${PWD}:/wd" "${image_id}" docker run --rm --volume /wd:/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null + # shellcheck disable=SC2312 + docker_args=(docker run --rm --env DOND_SHIM_DEBUG --entrypoint= --volume /var/run/docker.sock:/var/run/docker.sock) -# Same as above but for a file within the volume -"${docker_args[@]}" --volume "${PWD}:/wd" "${image_id}" docker run --rm --volume /wd/testfile:/wd/testfile ubuntu:latest grep "^test$" /wd/testfile >/dev/null + echo "Do not change global options or after the image" + "${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \ + docker --host test run --volume /wd:/wd alpine --volume /wd:/wd | + grep --quiet "^docker.orig --host test run --volume ${PWD}:/wd alpine --volume /wd:/wd$" -# Volumes inside volumes -"${docker_args[@]}" --volume "${PWD}/testfile:/home/rootless/testfile" "${image_id}" docker run --rm --volume /home/rootless:/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null -"${docker_args[@]}" --volume "${PWD}/testfile:/home/rootless/testfile" --volume "${PWD}/testfile:/home/rootless/testfile2" "${image_id}" docker run --rm --volume /home/rootless:/wd ubuntu:latest bash -c 'grep "^test$" /wd/testfile && grep "^test$" /wd/testfile2 && grep "^test$" /wd/only-inside-container' >/dev/null + echo "Same but for container run" + "${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \ + docker --host test container run --volume /wd:/wd alpine --volume /wd:/wd | + grep --quiet "^docker.orig --host test container run --volume ${PWD}:/wd alpine --volume /wd:/wd$" + + echo "Same but for create" + "${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \ + docker --host test create --volume /wd:/wd alpine --volume /wd:/wd | + grep --quiet "^docker.orig --host test create --volume ${PWD}:/wd alpine --volume /wd:/wd$" + + echo "Same but container create" + "${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \ + docker --host test container create --volume /wd:/wd alpine --volume /wd:/wd | + grep --quiet "^docker.orig --host test container create --volume ${PWD}:/wd alpine --volume /wd:/wd$" + + echo "Do not do anything for other commands" + "${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \ + docker --host test whatever --volume /wd:/wd alpine --volume /wd:/wd | + grep --quiet "^docker.orig --host test whatever --volume /wd:/wd alpine --volume /wd:/wd$" + + echo "Check if docker on docker is working" + "${docker_args[@]}" "${image_id}" \ + docker version >/dev/null + + echo "Check if mounting an volume from the container gets fixed" + "${docker_args[@]}" "${image_id}" \ + docker run --rm --volume /test/only-inside-container:/only-inside-container ubuntu:latest grep "^test$" /only-inside-container >/dev/null + + echo "Same but with equals sign" + "${docker_args[@]}" "${image_id}" \ + docker run --rm --volume=/test/only-inside-container:/only-inside-container ubuntu:latest grep "^test$" /only-inside-container >/dev/null + + echo "Check if mounting a volume which is already a volume gets fixed" + "${docker_args[@]}" --volume "${PWD}:/wd" "${image_id}" \ + docker run --rm --volume /wd:/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null + + echo "Same as above but for a file within the volume" + "${docker_args[@]}" --volume "${PWD}:/wd" "${image_id}" \ + docker run --rm --volume /wd/testfile:/wd/testfile ubuntu:latest grep "^test$" /wd/testfile >/dev/null + + echo "Check if mounting a volume which contains another volume adds all proper volumes" + "${docker_args[@]}" --volume "${PWD}/testfile:/test/testfile" "${image_id}" \ + docker run --rm --volume /test:/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null + + echo "Same as above but for multiple files under different volumes" + "${docker_args[@]}" --volume "${PWD}/testfile:/test/testfile" --volume "${PWD}/testfile:/test/testfile2" "${image_id}" \ + docker run --rm --volume /test:/wd ubuntu:latest bash -c 'grep "^test$" /wd/testfile && grep "^test$" /wd/testfile2 && grep "^test$" /wd/only-inside-container' >/dev/null + +done