Skip to content

Commit

Permalink
Support for docker run args for docker image targets (#1957)
Browse files Browse the repository at this point in the history
This adds parsing of command-line arguments being passed to 'bazel run'
when executing an image target.  The "--" delimiter is used to separate
arguments for "docker run" from arguments to the container.

This makes the following style of calls possible:

```
bazel run //:my_cool_image -- -p 8080:8080 -v /tmp:/tmp -- --my-cool-flag
```

which is translated into

```
docker run -i --rm -p 8080:8080 -v /tmp:/tmp bazel/my_cool_image:image --my-cool-flag
```
  • Loading branch information
psigen authored Dec 17, 2021
1 parent 8a4f73f commit 40cc8e8
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 16 deletions.
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,20 @@ You can load this into your local Docker client by running:
`bazel run my/image:helloworld`.

For the `lang_image` targets, this will also **run** the
container to maximize compatibility with `lang_binary` rules. You can suppress
this behavior by passing the single flag: `bazel run :foo -- --norun`
container using `docker run` to maximize compatibility with `lang_binary` rules.

Arguments to this command are forwarded to docker, meaning the command

```bash
bazel run my/image:helloworld -- -p 8080:80 -- arg0

This comment has been minimized.

Copy link
@chasezheng

chasezheng Jul 2, 2022

maybe just
bazel run my/image:helloworld -- --docker_args="-p 8080:80" --container_args="arg0"

```

performs the following steps:
* load the `my/image:helloworld` target into your local Docker client
* start a container using this image where `arg0` is passed to the image entrypoint
* port forward 8080 on the host to port 80 on the container, as per `docker run` documentation

You can suppress this behavior by passing the single flag: `bazel run :foo -- --norun`

Alternatively, you can build a `docker load` compatible bundle with:
`bazel build my/image:helloworld.tar`. This will produce the file:
Expand Down
34 changes: 32 additions & 2 deletions container/incremental_load.sh.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,41 @@ function read_variables() {
# An optional "docker run" statement for invoking a loaded container.
# This is not executed if the single argument --norun is passed or
# no run_statements are generated (in which case, 'run' is 'False').
if [[ "a$*" != "a--norun" && "%{run}" == "True" ]]; then
if [[ "%{run}" == "True" ]]; then
docker_args=()
container_args=()

# Search remaining params looking for docker and container args.
#
# It is assumed that they will follow the pattern:
# [dockerargs...] -- [container args...]
#
# "--norun" is treated as a "virtual" additional parameter to
# "docker run", since it cannot conflict with any "docker run"
# arguments. If "--norun" needs to be passed to the container,
# it can be safely placed after "--".

This comment has been minimized.

Copy link
@chasezheng

chasezheng Jul 1, 2022

this logic creates a bug where bazel run //tests/container/python3:py3_image gets an error

while test $# -gt 0

This comment has been minimized.

Copy link
@chasezheng

chasezheng Jul 2, 2022

given the complexity of $@ I suggest changing this script to only take keyward args like --key=val, and passing container_args explicitly as incremental_load.sh --container_args=...

do
case "$1" in
--norun) # norun as a "docker run" option means exit
exit
;;
--) # divider between docker and container args
shift
container_args=("$@")
break
;;
*) # potential "docker run" option
docker_args+=("$1")
shift
;;
esac
done

# Once we've loaded the images for all layers, we no longer need the temporary files on disk.
# We can clean up before we exec docker, since the exit handler will no longer run.
cleanup

# This generated and injected by docker_*.
exec %{run_statements}
exec %{run_statement} "${docker_args[@]}" "%{run_tag}" "${container_args[@]}"
fi
22 changes: 10 additions & 12 deletions container/layer_tools.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -220,18 +220,21 @@ def incremental_load(

toolchain_info = ctx.toolchains["@io_bazel_rules_docker//toolchains/docker:toolchain_type"].info

# Default to interactively launching the container,
# and cleaning up when it exits.

run_flags = run_flags or "-i --rm"

if len(images) > 1 and run:
fail("Bazel run does not currently support execution of " +
"multiple containers (only loading).")

# Default to interactively launching the container, and cleaning up when
# it exits. These template variables are unused if "run" is not set, so
# it is harmless to always define them as a function of the first image.
run_flags = run_flags or "-i --rm"
run_statement = "\"${DOCKER}\" ${DOCKER_FLAGS} run %s" % run_flags
run_tag = images.keys()[0]
if stamp:
run_tag = run_tag.replace("{", "${")

load_statements = []
tag_statements = []
run_statements = []

# TODO(mattmoor): Consider adding cleanup_statements.
for tag in images:
Expand Down Expand Up @@ -271,19 +274,14 @@ def incremental_load(
_get_runfile_path(ctx, image["config_digest"]),
),
)
if run:
# Args are embedded into the image, so omitted here.
run_statements.append(
"\"${DOCKER}\" ${DOCKER_FLAGS} run %s %s \"$@\"" % (run_flags, tag_reference),
)

ctx.actions.expand_template(
template = ctx.file.incremental_load_template,
substitutions = {
"%{docker_flags}": " ".join(toolchain_info.docker_flags),
"%{docker_tool_path}": docker_path(toolchain_info),
"%{load_statements}": "\n".join(load_statements),
"%{run_statements}": "\n".join(run_statements),
"%{run_statement}": run_statement,
"%{run}": str(run),
# If this rule involves stamp variables than load them as bash
# variables, and turn references to them into bash variable
Expand Down

0 comments on commit 40cc8e8

Please sign in to comment.