Skip to content

Commit

Permalink
Fix container id discovery on environments like GitHub Actions (#4)
Browse files Browse the repository at this point in the history
And also creates a workflow for testing changes.
  • Loading branch information
felipecrs committed Jun 17, 2023
1 parent 979b4cb commit 8584e65
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 46 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: ci

on:
push:
branches: [main]
pull_request:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
test:
strategy:
fail-fast: false
matrix:
docker-version: ["18.09", "19.03", "20.10", "23", "24", latest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Show host Docker information
run: |
docker version
docker info
- name: Run tests with Docker ${{ matrix.docker-version }}
run: |
if [[ '${{ runner.debug }}' == 1 ]]; then
export DEBUG=true
export DOND_SHIM_DEBUG=true
fi
scripts/test.sh '${{ matrix.docker-version }}'
result:
needs: test
runs-on: ubuntu-latest
steps:
- run: |
echo "All tests passed!"
65 changes: 45 additions & 20 deletions docker
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@ function echo_error() {

function error() {
echo_error "${@}"
uncaught_error=false
exit 1
}

# Ensure the user knows that the error was originated from the shim
trap 'echo_error "Uncaught error at line ${LINENO}"' ERR
function handle_error_trap() {
if [[ "${uncaught_error:-true}" == true ]]; then
echo_error "Uncaught error at line ${LINENO}"
fi
}

trap handle_error_trap ERR

# Finds all docker options that take a value from the --help output and
# stores them in the docker_options_with_value array.
Expand Down Expand Up @@ -59,29 +66,50 @@ function run_docker() {
}

# Gets the current/parent container id on the host.
function get_container_id() {
local cpuset_output
cpuset_output=$(head -1 /proc/self/cpuset)
basename "${cpuset_output}"
function set_container_id() {
local result

local mount_info_lines=()
readarray -t mount_info_lines </proc/self/mountinfo
for line in "${mount_info_lines[@]}"; do
if [[ "${line}" =~ /([a-z0-9]{12,128})/resolv.conf" " ]]; then
result="${BASH_REMATCH[1]}"
fi
done
unset mount_info_lines

# Sanity check
if [[ "${result}" =~ ^[a-z0-9]{12,128}$ ]]; then
readonly container_id="${result}"
else
error "Could not get parent container id"
fi
}

# Gets the root directory of the current/parent container on the host
# filesystem.
function get_container_root_on_host() {
local container_id="${1}"
function set_container_root_on_host() {
local result

if [[ -z "${mock_container_root_on_host}" ]]; then
"${docker_path}" inspect --format '{{.GraphDriver.Data.MergedDir}}' "${container_id}"
result="$(
"${docker_path}" inspect --format '{{.GraphDriver.Data.MergedDir}}' "${container_id}"
)"
else
result="${mock_container_root_on_host}"
fi

# Sanity check
if [[ "${result}" =~ ^(/[^/]+)+$ ]]; then
readonly container_root_on_host="${result}"
else
echo "${mock_container_root_on_host}"
error "Could not get parent container root on host"
fi
}

# Reads the mounts of the current/parent container and stores them in the
# parent_container_mounts array.
function set_parent_container_mounts() {
local -r container_id="${1}"

local docker_output
docker_output=$(
"${docker_path}" inspect \
Expand All @@ -90,6 +118,7 @@ function set_parent_container_mounts() {
)

readarray -t parent_container_mounts <<<"${docker_output}"
readonly parent_container_mounts
}

# Performs the necessary transformations to the volume/mount argument.
Expand Down Expand Up @@ -126,10 +155,9 @@ function fix_volume_arg() {

# Fetch data only once and if needed
if [[ "${container_data_fetched}" == false ]]; then
container_id="$(get_container_id)"
container_root_dir="$(get_container_root_on_host "${container_id}")"
set_parent_container_mounts "${container_id}"
readonly container_id container_root_dir parent_container_mounts
set_container_id
set_container_root_on_host
set_parent_container_mounts
container_data_fetched=true
fi

Expand Down Expand Up @@ -176,7 +204,7 @@ function fix_volume_arg() {
# we mount relative to the container root directory on the host
# filesystem.
if [[ -z "${fixed_source}" ]]; then
fixed_source="${container_root_dir}${source}"
fixed_source="${container_root_on_host}${source}"
fi

if [[ "${arg_type}" == "volume" ]]; then
Expand All @@ -187,10 +215,7 @@ function fix_volume_arg() {
fi
}

script_path="$(
cd -- "$(dirname "$0")" >/dev/null 2>&1
pwd -P
)"
script_path="$(realpath "$0")"
readonly script_path

# Parse supported environment variables
Expand Down
59 changes: 33 additions & 26 deletions test.sh → scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,61 +18,68 @@ trap "exit 130" INT

# Set docker versions from args or use default
if [[ $# -eq 0 ]]; then
docker_versions=("18.09" "19.03" "20.10" "23" "24" latest)
docker_versions=(latest)
else
docker_versions=("$@")
fi
readonly docker_versions

readonly docker_args=(docker run --rm --env DOND_SHIM_DEBUG --volume /var/run/docker.sock:/var/run/docker.sock)

# Find fixtures directory
script_path=$(realpath "$0")
script_dir=$(dirname "${script_path}")
fixtures_dir="$(realpath "${script_dir}/../tests/fixtures")"
readonly fixtures_dir
unset script_path script_dir

for docker_version in "${docker_versions[@]}"; do
echo "Testing with docker version: ${docker_version}"

image_id="$(docker build --target test --build-arg "DOCKER_VERSION=${docker_version}" --quiet .)"

echo "Do not change global options or after the image"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/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$"
grep --quiet "^docker.orig --host test run --volume ${fixtures_dir}:/wd alpine --volume /wd:/wd$"

echo "Same as above, but retaining read only mode"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/wd" "${image_id}" \
docker --host test run --volume /wd:/wd:ro alpine --volume /wd:/wd |
grep --quiet "^docker.orig --host test run --volume ${PWD}:/wd:ro alpine --volume /wd:/wd$"
grep --quiet "^docker.orig --host test run --volume ${fixtures_dir}:/wd:ro alpine --volume /wd:/wd$"

echo "Same as above but with --mount"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/wd" "${image_id}" \
docker --host test run --volume /wd:/wd:ro --mount=type=bind,source=/wd,readonly,destination=/wd2 alpine --volume /wd:/wd |
grep --quiet "^docker.orig --host test run --volume ${PWD}:/wd:ro --mount=type=bind,source=${PWD},readonly,destination=/wd2 alpine --volume /wd:/wd$"
grep --quiet "^docker.orig --host test run --volume ${fixtures_dir}:/wd:ro --mount=type=bind,source=${fixtures_dir},readonly,destination=/wd2 alpine --volume /wd:/wd$"

echo "Same as above (without --mount), but retaining read only mode on auto added volume"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --env DOND_SHIM_MOCK_CONTAINER_ROOT_ON_HOST=/container-root --volume "${PWD}:/wd" --volume "${PWD}/testfile:/test/testfile" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --env DOND_SHIM_MOCK_CONTAINER_ROOT_ON_HOST=/container-root --volume "${fixtures_dir}:/wd" --volume "${fixtures_dir}/testfile:/test/testfile" "${image_id}" \
docker --host test run --volume /wd:/wd:ro --volume /test:/test:ro alpine --volume /wd:/wd |
grep --quiet "^docker.orig --host test run --volume ${PWD}:/wd:ro --volume /container-root/test:/test:ro --volume ${PWD}/testfile:/test/testfile:ro alpine --volume /wd:/wd$"
grep --quiet "^docker.orig --host test run --volume ${fixtures_dir}:/wd:ro --volume /container-root/test:/test:ro --volume ${fixtures_dir}/testfile:/test/testfile:ro alpine --volume /wd:/wd$"

echo "Same as above (with --mount src and target, dst), but retaining read only mode on auto added volume"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --env DOND_SHIM_MOCK_CONTAINER_ROOT_ON_HOST=/container-root --volume "${PWD}:/wd" --volume "${PWD}/testfile:/test/testfile" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --env DOND_SHIM_MOCK_CONTAINER_ROOT_ON_HOST=/container-root --volume "${fixtures_dir}:/wd" --volume "${fixtures_dir}/testfile:/test/testfile" "${image_id}" \
docker --host test run --mount type=bind,src=/wd,target=/wd,readonly --mount type=bind,source=/test,dst=/test,readonly alpine --mount type=bind,source=/wd,destination=/wd,readonly |
grep --quiet "^docker.orig --host test run --mount type=bind,src=${PWD},target=/wd,readonly --mount type=bind,source=/container-root/test,dst=/test,readonly --mount type=bind,source=${PWD}/testfile,dst=/test/testfile,readonly alpine --mount type=bind,source=/wd,destination=/wd,readonly$"
grep --quiet "^docker.orig --host test run --mount type=bind,src=${fixtures_dir},target=/wd,readonly --mount type=bind,source=/container-root/test,dst=/test,readonly --mount type=bind,source=${fixtures_dir}/testfile,dst=/test/testfile,readonly alpine --mount type=bind,source=/wd,destination=/wd,readonly$"

echo "Same but for container run"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/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$"
grep --quiet "^docker.orig --host test container run --volume ${fixtures_dir}:/wd alpine --volume /wd:/wd$"

echo "Same but for create"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/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$"
grep --quiet "^docker.orig --host test create --volume ${fixtures_dir}:/wd alpine --volume /wd:/wd$"

echo "Same but container create"
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${PWD}:/wd" "${image_id}" \
"${docker_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/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$"
grep --quiet "^docker.orig --host test container create --volume ${fixtures_dir}:/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_args[@]}" --env DOND_SHIM_PRINT_COMMAND=true --volume "${fixtures_dir}:/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$"

Expand All @@ -89,34 +96,34 @@ for docker_version in "${docker_versions[@]}"; do
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_args[@]}" --volume "${fixtures_dir}:/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_args[@]}" --volume "${fixtures_dir}:/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_args[@]}" --volume "${fixtures_dir}/testfile:/test/testfile" "${image_id}" \
docker run --rm --volume /test:/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null

echo "With --mount"
"${docker_args[@]}" --volume "${PWD}/testfile:/test/testfile" "${image_id}" \
"${docker_args[@]}" --volume "${fixtures_dir}/testfile:/test/testfile" "${image_id}" \
docker run --rm --mount type=bind,source=/test,destination=/wd ubuntu:latest grep "^test$" /wd/testfile >/dev/null

echo "With --mount shuffling order"
"${docker_args[@]}" --volume "${PWD}/testfile:/test/testfile" "${image_id}" \
"${docker_args[@]}" --volume "${fixtures_dir}/testfile:/test/testfile" "${image_id}" \
docker run --rm --mount destination=/wd,source=/test,type=bind 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_args[@]}" --volume "${fixtures_dir}/testfile:/test/testfile" --volume "${fixtures_dir}/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

echo "Same test as above but with a read only volume"
"${docker_args[@]}" --volume "${PWD}/testfile:/test/testfile" --volume "${PWD}/testfile:/test/testfile2" "${image_id}" \
"${docker_args[@]}" --volume "${fixtures_dir}/testfile:/test/testfile" --volume "${fixtures_dir}/testfile:/test/testfile2" "${image_id}" \
docker run --rm --volume /test:/wd:ro ubuntu:latest bash -c 'grep "^test$" /wd/testfile && grep "^test$" /wd/testfile2 && grep "^test$" /wd/only-inside-container' >/dev/null

echo "Same as above but with a volume that matches the parent first"
"${docker_args[@]}" --volume "${PWD}:/folder" --volume "${PWD}/testfile:/test/testfile" --volume "${PWD}/testfile:/test/testfile2" "${image_id}" \
"${docker_args[@]}" --volume "${fixtures_dir}:/folder" --volume "${fixtures_dir}/testfile:/test/testfile" --volume "${fixtures_dir}/testfile:/test/testfile2" "${image_id}" \
docker run --rm --volume /folder:/test --volume /test:/wd:ro ubuntu:latest bash -c 'grep "^test$" /wd/testfile && grep "^test$" /wd/testfile2 && grep "^test$" /wd/only-inside-container' >/dev/null
done
File renamed without changes.

0 comments on commit 8584e65

Please sign in to comment.