Skip to content

Commit

Permalink
Update User Mirror to 1.0.2 (#476)
Browse files Browse the repository at this point in the history
  • Loading branch information
AJGranowski authored Feb 2, 2025
1 parent 56a4bc8 commit 6e229e4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
name: Detect Changed Files
runs-on: ubuntu-latest
outputs:
run-ci: ${{ github.event_name == 'workflow_dispatch' || steps.ci-files-changed.outputs.paths_any_changed == 'true' }}
run-ci: ${{ github.event_name == 'workflow_dispatch' || steps.ci-files-changed.outputs.paths_any_modified == 'true' }}

steps:
- name: Harden Runner
Expand All @@ -36,6 +36,7 @@ jobs:
# https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/collaborating-on-repositories-with-code-quality-features/troubleshooting-required-status-checks#handling-skipped-but-required-checks
- name: CI Files Changed
id: ci-files-changed
if: ${{ github.event_name != 'workflow_dispatch' }}
uses: tj-actions/changed-files@d6e91a2266cdb9d62096cebf1e8546899c6aa18f # v45.0.6
with:
files_yaml: |
Expand Down
2 changes: 1 addition & 1 deletion images/node/entrypoint
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# License: MIT
set -eC;

VERSION='1.0.0-911dd14';
VERSION='1.0.2-5347de0';
exec 3>/dev/null;
exec 4>/dev/null;

Expand Down
118 changes: 104 additions & 14 deletions user-mirror
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
# Source: https://github.com/AJGranowski/docker-user-mirror
# License: MIT
set -eC;
VERSION='1.0.0-911dd14';
VERSION='1.0.2-5347de0';

# Create container(s) and return the container ID(s).
container_create() {
if [ "$COMMAND_TYPE" = 'compose' ]; then
# For compose commands, we just need to create the containers used in the command.
# TODO: Be selective when creating containers. https://github.com/AJGranowski/docker-user-mirror/issues/83
# For compose commands, we don't need to worry about preserving options when creating containers.
search_limit=3;
skip_counter=0;
for arg do
if [ "$is_file" = true ]; then
unset is_file;
Expand All @@ -17,45 +18,116 @@ container_create() {
else
compose_configuration_files="${compose_configuration_files} -f ${arg}";
fi
elif [ "$is_run_command" = true ]; then
case "$arg" in
-v|--volume)
echo 'Error: This script does not support compose CLI volume arguments.' >&2;
echo ' Rootless Docker will better serve your use case.' >&2;
echo ' https://docs.docker.com/engine/security/rootless/' >&2;
exit 1;
;;
esac
else
if [ "$search_limit" -le 0 ]; then
break;
fi

case "$arg" in
-f|--file) is_file=true;;
run) is_run_command=true;;
esac

case "$arg" in
-*) : $((search_limit += 2));;
esac

: $((search_limit -= 1));
: $((skip_counter += 1));
fi
done

$COMPOSE_ENGINE --progress quiet $compose_configuration_files create >/dev/null 2>&1;
$COMPOSE_ENGINE --progress quiet ps --status created --format '{{.ID}}';
create_all_containers=true;
for arg do
if [ "$skip_counter" -gt 0 ]; then
: $((skip_counter -= 1));
continue;
fi

if [ "$exit_loop" = true ]; then
break;
fi

items="$(docker compose config --services)";
while read service; do
if [ "$arg" = "$service" ]; then
unset create_all_containers;
$COMPOSE_ENGINE --progress quiet $compose_configuration_files create "$service" >/dev/null;
$COMPOSE_ENGINE ps -a --format "{{if eq .Service \"$service\"}}{{.ID}}{{end}}";

if [ "$is_run_command" = true ]; then
exit_loop=true;
fi

break;
fi
done <<EOF
$items
EOF
done

if [ "$create_all_containers" = true ]; then
$COMPOSE_ENGINE --progress quiet $compose_configuration_files create >/dev/null;
$COMPOSE_ENGINE ps --status created --format '{{.ID}}';
fi
elif [ "$COMMAND_TYPE" = 'container' ]; then
# For a non-compose commands, we can just change "exec" and "run" to "create", and then use that new command
# to create (but not start) a container for parsing. We're modifying the argument list since that's the only
# array supported in POSIX shell.
first_iteration=true;
index=0;
search_for_replace=true;
search_limit=3;
for arg do
if [ "$first_iteration" = true ]; then
unset first_iteration;
shift $#;
fi

if [ "$search_for_replace" = true ]; then
if [ "$search_limit" -le 0 ]; then
break;
fi

case "$arg" in
exec|run)
replaced_command="$arg";
replaced_command_index="$index";
set -- "$@" "create";
unset search_for_replace;
;;
create)
set -- "$@" "$arg";
unset search_for_replace;
;;
*) set -- "$@" "$arg";;
esac

case "$arg" in
-*) : $((search_limit += 2));;
esac

: $((index += 1));
: $((search_limit -= 1));
else
set -- "$@" "$arg";
fi

: $((index += 1));
done

if [ "$search_for_replace" = true ]; then
echo '[user-mirror]: Unable to create container.' >&2;
exit 1;
fi

"$@";

if [ -n "$replaced_command" ] && [ -n "$replaced_command_index" ]; then
Expand Down Expand Up @@ -136,24 +208,27 @@ prepare_environment() {
$($CONTAINER_ENGINE inspect --format "{{range .Mounts}}{{if .RW}}{{println \"${service_name}\"}}{{println .Destination}}{{end}}{{end}}" "$1")";

# Loop through bind mounts that have a non-empty mode (`create_host_path: true` sets this), and mkdir those source paths on the host.
items="$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{if and (eq .Type "bind") .Mode}}{{println .Source}}{{end}}{{end}}' "$1")";
while read create_path; do
if [ -z "$create_path" ]; then
break;
fi

mkdir -p "$create_path" >/dev/null 2>&1 || true;
done <<EOF
$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{if and (eq .Type "bind") .Mode}}{{println .Source}}{{end}}{{end}}' "$1")
$items
EOF
# Loop though every pair of bind mounts and all mounts to create nested directories and files on the host.
# (for example, a volume mount within a bind mount will need to be created on the host relative to the bind mount)
items="$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{if eq .Type "bind"}}{{println .Source}}{{println .Destination}}{{end}}{{end}}' "$1")";
while read edge_source; do
if [ -z "$edge_source" ]; then
break;
fi

read edge_destination;

items="$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{println .Source}}{{println .Destination}}{{end}}' "$1")";
while read node_source; do
if [ -z "$node_source" ]; then
break;
Expand All @@ -176,11 +251,11 @@ EOF
mkdir -p "$resolved_node_destination" >/dev/null 2>&1 || true;
fi
done <<EOF
$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{println .Source}}{{println .Destination}}{{end}}' "$1")
$items
EOF

done <<EOF
$($CONTAINER_ENGINE inspect --format '{{range .Mounts}}{{if eq .Type "bind"}}{{println .Source}}{{println .Destination}}{{end}}{{end}}' "$1")
$items
EOF
}

Expand Down Expand Up @@ -313,7 +388,12 @@ if [ -z "$COMPOSE_ENGINE" ] || [ -z "$CONTAINER_ENGINE" ]; then
fi

# Duck type the command
search_limit=2;
for arg do
if [ "$search_limit" -le 0 ]; then
break;
fi

case "$arg" in
compose|up)
COMMAND_TYPE='compose';
Expand All @@ -323,26 +403,36 @@ for arg do
COMMAND_TYPE='container';
break;
;;
-*) : $((search_limit += 2));;
esac

: $((search_limit -= 1));
done

if [ -z "$COMMAND_TYPE" ]; then
exec "$@";
exit 0;
echo '[user-mirror]: Unable to parse command. Aborting.' >&2;
exit 1;
fi

# If Docker and not rootless, then we have some work to do.
if [ "$CONTAINER_ENGINE" = "docker" ] && ! docker info -f '{{println .SecurityOptions}}' | grep 'rootless' 1>/dev/null; then
USER_MIRROR_HOST_USER="$(id -un):$(id -u):$(id -gn):$(id -g)";

# Loop through the containers created by this command.
items="$(container_create "$@")";
while read temp_container_id; do
if [ -z "$temp_container_id" ]; then
continue;
fi

prepare_environment "$temp_container_id"

# Cleanup.
$CONTAINER_ENGINE container rm "$temp_container_id" >/dev/null 2>&1;
if [ "$(docker inspect --format '{{.State.Status}}' "$temp_container_id")" = 'created' ]; then
$CONTAINER_ENGINE container rm "$temp_container_id" >/dev/null;
fi
done <<EOF
$(container_create "$@")
$items
EOF
else
USER_MIRROR_HOST_USER='root:0:root:0';
Expand Down

0 comments on commit 6e229e4

Please sign in to comment.