From 218da9acbed8771904a9f16bd7e66468f440f941 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 22 Jun 2025 13:57:01 -0400 Subject: [PATCH 1/5] Update `local_arg_printer` to use bzlmod --- examples/local_arg_printer/.bazelversion | 1 + examples/local_arg_printer/{BUILD => BUILD.bazel} | 0 examples/local_arg_printer/MODULE.bazel | 6 ++++++ examples/local_arg_printer/WORKSPACE | 13 ------------- examples/local_arg_printer/launcher.py | 2 +- 5 files changed, 8 insertions(+), 14 deletions(-) create mode 100644 examples/local_arg_printer/.bazelversion rename examples/local_arg_printer/{BUILD => BUILD.bazel} (100%) create mode 100644 examples/local_arg_printer/MODULE.bazel delete mode 100644 examples/local_arg_printer/WORKSPACE diff --git a/examples/local_arg_printer/.bazelversion b/examples/local_arg_printer/.bazelversion new file mode 100644 index 0000000..2b0aa21 --- /dev/null +++ b/examples/local_arg_printer/.bazelversion @@ -0,0 +1 @@ +8.2.1 diff --git a/examples/local_arg_printer/BUILD b/examples/local_arg_printer/BUILD.bazel similarity index 100% rename from examples/local_arg_printer/BUILD rename to examples/local_arg_printer/BUILD.bazel diff --git a/examples/local_arg_printer/MODULE.bazel b/examples/local_arg_printer/MODULE.bazel new file mode 100644 index 0000000..9da72e2 --- /dev/null +++ b/examples/local_arg_printer/MODULE.bazel @@ -0,0 +1,6 @@ +module( + name = "xmanager_local_arg_printer_example", + version = "1.0", +) + +bazel_dep(name = "rules_cc", version = "0.1.2") diff --git a/examples/local_arg_printer/WORKSPACE b/examples/local_arg_printer/WORKSPACE deleted file mode 100644 index 0c22255..0000000 --- a/examples/local_arg_printer/WORKSPACE +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2021 DeepMind Technologies Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/examples/local_arg_printer/launcher.py b/examples/local_arg_printer/launcher.py index d36f55d..4dba6a5 100644 --- a/examples/local_arg_printer/launcher.py +++ b/examples/local_arg_printer/launcher.py @@ -14,7 +14,7 @@ """XManager launcher that runs locally a binary built with Bazel. One must `cd` into xmanager/examples/local_arg_printer/ in order to run this -example because Bazel needs to locate the WORKSPACE file. +example because Bazel needs to locate the `MODULE.bazel` file. """ from typing import Sequence From 019f15d94853e0a9195437831e3cfc1ffbacda59 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 22 Jun 2025 14:14:02 -0400 Subject: [PATCH 2/5] Add example usage for `arg_printer.cc` --- examples/local_arg_printer/arg_printer.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/examples/local_arg_printer/arg_printer.cc b/examples/local_arg_printer/arg_printer.cc index ce5edb0..97ea5c8 100644 --- a/examples/local_arg_printer/arg_printer.cc +++ b/examples/local_arg_printer/arg_printer.cc @@ -12,6 +12,22 @@ // See the License for the specific language governing permissions and // limitations under the License. +// A simple C++ binary that prints its command-line arguments to a file. +// +// The output file path is read from the `OUTPUT_PATH` environment variable. +// The first line of the output file will contain the argument count, followed +// by each argument on a new line. The program then sleeps for 5 seconds to +// simulate a long-running job before exiting. +// Usage: +// ``` +// $ OUTPUT_PATH="/tmp/output.txt" bazel run //:arg_printer -- "arg1" "arg2" +// $ cat /tmp/output.txt +// 3 +// /home/user/.cache/bazel/_bazel_user/b2b20ce9d34fd5389bf343c4cc37f48f/execroot/_main/bazel-out/k8-fastbuild/bin/arg_printer +// arg1 +// arg2 +// ``` + #include #include #include From 33f9bec78095d0245e465fa5b3d49a1c3e5e5685 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 22 Jun 2025 16:50:40 -0400 Subject: [PATCH 3/5] Update `local_container_links` to use Bzlmod --- examples/local_container_links/.bazelversion | 1 + examples/local_container_links/BUILD.bazel | 52 ++++++++-------- examples/local_container_links/MODULE.bazel | 37 ++++++++++++ examples/local_container_links/README.md | 2 +- examples/local_container_links/WORKSPACE | 60 ------------------- .../local_container_links/requirements.in | 18 ++++++ .../local_container_links/requirements.txt | 31 +++++----- 7 files changed, 98 insertions(+), 103 deletions(-) create mode 100644 examples/local_container_links/.bazelversion create mode 100644 examples/local_container_links/MODULE.bazel delete mode 100644 examples/local_container_links/WORKSPACE create mode 100644 examples/local_container_links/requirements.in diff --git a/examples/local_container_links/.bazelversion b/examples/local_container_links/.bazelversion new file mode 100644 index 0000000..2b0aa21 --- /dev/null +++ b/examples/local_container_links/.bazelversion @@ -0,0 +1 @@ +8.2.1 diff --git a/examples/local_container_links/BUILD.bazel b/examples/local_container_links/BUILD.bazel index 7e05a15..4c9604e 100644 --- a/examples/local_container_links/BUILD.bazel +++ b/examples/local_container_links/BUILD.bazel @@ -1,36 +1,38 @@ -# Copyright 2021 DeepMind Technologies Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@io_bazel_rules_docker//container:container.bzl", "container_image") -load("@python_deps//:requirements.bzl", "requirement") -load("@subpar//:subpar.bzl", "par_binary") +load("@aspect_rules_py//py:defs.bzl", "py_image_layer") +load("@rules_python//python:defs.bzl", "py_binary") +load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load") +load("@rules_pkg//:pkg.bzl", "pkg_tar") licenses(["notice"]) -par_binary( +py_binary( name = "server", srcs = ["server.py"], deps = [ - requirement("absl-py"), - requirement("bottle"), - requirement("bottle-redis"), + "@pypi//absl_py", + "@pypi//bottle_redis", ], ) -container_image( +oci_image( name = "server_image", - base = "@io_docker_index_library_python//image", - entrypoint = ["/server.par"], - files = [":server.par"], + base = "@docker_io_python", + entrypoint = ["/app/server"], + tars = py_image_layer( + name = "server_layer", + binary = ":server", + root = "/app/", + ), +) + +oci_load( + name = "server_image.load", + image = ":server_image", + repo_tags = ["local_container_links_server:latest"], +) + +filegroup( + name = "server_image.tar", + srcs = [":server_image.load"], + output_group = "tarball", ) diff --git a/examples/local_container_links/MODULE.bazel b/examples/local_container_links/MODULE.bazel new file mode 100644 index 0000000..f2c6182 --- /dev/null +++ b/examples/local_container_links/MODULE.bazel @@ -0,0 +1,37 @@ +module( + name = "xmanager_local_container_links_bzlmod", + version = "1.0.0", +) + +bazel_dep(name = "aspect_rules_py", version = "1.6.0") +bazel_dep(name = "rules_python", version = "1.5.0-rc2") +bazel_dep(name = "rules_oci", version = "2.2.6") +bazel_dep(name = "rules_pkg", version = "1.1.0") + +python = use_extension("@rules_python//python/extensions:python.bzl", "python") + +# `bottle-redis` uses `inspect.getargspec`, which was deprecated in `python==3.11`. +# Thus, we need to use 3.10 for this example. This also forces us to use `redis<4`. +python.toolchain(python_version = "3.10") + +pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") +pip.parse( + hub_name = "pypi", + python_version = "3.10", + requirements_lock = "requirements.txt", +) +use_repo(pip, "pypi") + +oci = use_extension("@rules_oci//oci:extensions.bzl", "oci") + +oci.pull( + name = "docker_io_python", + digest = "sha256:034724ef64585eeb0e82385e9aabcbeabfe5f7cae2c2dcedb1da95114372b6d7", + image = "docker.io/library/python", + platforms = [ + "linux/amd64", + "linux/arm64/v8", + ], + tag = "3.10.18-slim", +) +use_repo(oci, "docker_io_python", "docker_io_python_linux_amd64", "docker_io_python_linux_arm64_v8") diff --git a/examples/local_container_links/README.md b/examples/local_container_links/README.md index 84f1a65..aa642a8 100644 --- a/examples/local_container_links/README.md +++ b/examples/local_container_links/README.md @@ -1,6 +1,6 @@ ## Description -launcher.py packages and executes (locally) two Docker containers: +`launcher.py` packages and executes (locally) two Docker containers: [Redis](https://hub.docker.com/_/redis), a key-value database, and a [Bottle](https://bottlepy.org/)-based HTTP server that listens on port 8080 and responds to requests at `/increment` by running diff --git a/examples/local_container_links/WORKSPACE b/examples/local_container_links/WORKSPACE deleted file mode 100644 index 4044a8b..0000000 --- a/examples/local_container_links/WORKSPACE +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2021 DeepMind Technologies Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "io_bazel_rules_docker", - sha256 = "59d5b42ac315e7eadffa944e86e90c2990110a1c8075f1cd145f487e999d22b3", - strip_prefix = "rules_docker-0.17.0", - urls = ["https://github.com/bazelbuild/rules_docker/releases/download/v0.17.0/rules_docker-v0.17.0.tar.gz"], -) - -http_archive( - name = "rules_python", - sha256 = "934c9ceb552e84577b0faf1e5a2f0450314985b4d8712b2b70717dc679fdc01b", - url = "https://github.com/bazelbuild/rules_python/releases/download/0.3.0/rules_python-0.3.0.tar.gz", -) - -git_repository( - name = "subpar", - remote = "https://github.com/google/subpar", - tag = "2.0.0", -) - -load("@rules_python//python:pip.bzl", "pip_install") - -pip_install( - name = "python_deps", - requirements = "//:requirements.txt", -) - -load("@io_bazel_rules_docker//repositories:repositories.bzl", container_repositories = "repositories") - -container_repositories() - -load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps") - -container_deps() - -load("@io_bazel_rules_docker//container:container.bzl", "container_pull") - -container_pull( - name = "io_docker_index_library_python", - digest = "sha256:11f3ccfbb8e809246f5993be99693966ffc99e1c7b632251fde27c0ce45b35f2", - registry = "index.docker.io", - repository = "library/python", - tag = "3.9.6", -) diff --git a/examples/local_container_links/requirements.in b/examples/local_container_links/requirements.in new file mode 100644 index 0000000..d2e35a3 --- /dev/null +++ b/examples/local_container_links/requirements.in @@ -0,0 +1,18 @@ +# Copyright 2021 DeepMind Technologies Limited +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +absl-py +bottle +bottle-redis +redis<4.0 diff --git a/examples/local_container_links/requirements.txt b/examples/local_container_links/requirements.txt index e777dd5..3c06b58 100644 --- a/examples/local_container_links/requirements.txt +++ b/examples/local_container_links/requirements.txt @@ -1,17 +1,14 @@ -# Copyright 2021 DeepMind Technologies Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -absl-py -bottle -bottle-redis +# This file was autogenerated by uv via the following command: +# uv pip compile ./requirements.in -o ./requirements.txt +absl-py==2.3.0 + # via -r ./requirements.in +bottle==0.13.4 + # via + # -r ./requirements.in + # bottle-redis +bottle-redis==0.2.3 + # via -r ./requirements.in +redis==3.5.3 + # via + # -r ./requirements.in + # bottle-redis From cdaf6ee7d2a9cfce9fe03d77dfde77cde8e221b9 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 22 Jun 2025 17:04:08 -0400 Subject: [PATCH 4/5] Migrate `local_container_links` to `python==3.13` This requires removing `bottle-redis` dependency, as it only supports `python<3.11`. Its functionality is quite trivial, however, and not much changes in `server.py` anyway. --- examples/local_container_links/BUILD.bazel | 3 +- examples/local_container_links/MODULE.bazel | 8 +-- .../local_container_links/requirements.in | 3 +- .../local_container_links/requirements.txt | 10 +--- examples/local_container_links/server.py | 57 ++++++++++++------- 5 files changed, 43 insertions(+), 38 deletions(-) diff --git a/examples/local_container_links/BUILD.bazel b/examples/local_container_links/BUILD.bazel index 4c9604e..05de918 100644 --- a/examples/local_container_links/BUILD.bazel +++ b/examples/local_container_links/BUILD.bazel @@ -10,7 +10,8 @@ py_binary( srcs = ["server.py"], deps = [ "@pypi//absl_py", - "@pypi//bottle_redis", + "@pypi//bottle", + "@pypi//redis", ], ) diff --git a/examples/local_container_links/MODULE.bazel b/examples/local_container_links/MODULE.bazel index f2c6182..2190708 100644 --- a/examples/local_container_links/MODULE.bazel +++ b/examples/local_container_links/MODULE.bazel @@ -10,14 +10,12 @@ bazel_dep(name = "rules_pkg", version = "1.1.0") python = use_extension("@rules_python//python/extensions:python.bzl", "python") -# `bottle-redis` uses `inspect.getargspec`, which was deprecated in `python==3.11`. -# Thus, we need to use 3.10 for this example. This also forces us to use `redis<4`. -python.toolchain(python_version = "3.10") +python.toolchain(python_version = "3.13") pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") pip.parse( hub_name = "pypi", - python_version = "3.10", + python_version = "3.13", requirements_lock = "requirements.txt", ) use_repo(pip, "pypi") @@ -32,6 +30,6 @@ oci.pull( "linux/amd64", "linux/arm64/v8", ], - tag = "3.10.18-slim", + tag = "3.13-slim", ) use_repo(oci, "docker_io_python", "docker_io_python_linux_amd64", "docker_io_python_linux_arm64_v8") diff --git a/examples/local_container_links/requirements.in b/examples/local_container_links/requirements.in index d2e35a3..b6b67be 100644 --- a/examples/local_container_links/requirements.in +++ b/examples/local_container_links/requirements.in @@ -14,5 +14,4 @@ absl-py bottle -bottle-redis -redis<4.0 +redis diff --git a/examples/local_container_links/requirements.txt b/examples/local_container_links/requirements.txt index 3c06b58..d2d6424 100644 --- a/examples/local_container_links/requirements.txt +++ b/examples/local_container_links/requirements.txt @@ -3,12 +3,6 @@ absl-py==2.3.0 # via -r ./requirements.in bottle==0.13.4 - # via - # -r ./requirements.in - # bottle-redis -bottle-redis==0.2.3 # via -r ./requirements.in -redis==3.5.3 - # via - # -r ./requirements.in - # bottle-redis +redis==6.2.0 + # via -r ./requirements.in diff --git a/examples/local_container_links/server.py b/examples/local_container_links/server.py index 33c49df..ee91025 100644 --- a/examples/local_container_links/server.py +++ b/examples/local_container_links/server.py @@ -1,16 +1,3 @@ -# Copyright 2021 DeepMind Technologies Limited -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. """An HTTP server incrementing a value in Redis.""" from typing import Sequence @@ -18,24 +5,50 @@ from absl import app from absl import flags import bottle -import bottle.ext.redis +import redis -redis_host = flags.DEFINE_string('redis_host', None, "Redis' host.") +redis_host = flags.DEFINE_string("redis_host", None, "Address to Redis server.") server = bottle.Bottle() +rdb = None -@server.route('/increment') -def increment(rdb): - return str(rdb.incr('counter')) +@server.route("/increment") +def increment(): + global rdb + if rdb is None: + return "Redis host not set." + counter = rdb.incr("counter") + return f"{counter=}" + + +@server.route("/") +def index(): + global rdb + if rdb is None: + return "Redis host not set." + counter = int(rdb.get("counter")) + return f"{counter=}\nIncrement it by visiting `/increment`." def main(argv: Sequence[str]) -> None: - del argv # Unused. + del argv + + global rdb + rdb = redis.Redis(host=redis_host.value, decode_responses=True) + + while True: + print("Waiting for Redis to be available...") + try: + rdb.ping() + break + except redis.exceptions.ConnectionError: + time.sleep(1) + + rdb.set("counter", 0) - server.install(bottle.ext.redis.RedisPlugin(host=redis_host.value)) - bottle.run(server, host='0.0.0.0', port=8080, debug=True) + bottle.run(server, host="0.0.0.0", port=8080, debug=False) -if __name__ == '__main__': +if __name__ == "__main__": app.run(main) From bb1ccc68b86e2efbe2b02bdff613da16a5731a03 Mon Sep 17 00:00:00 2001 From: Kristian Hartikainen Date: Sun, 22 Jun 2025 17:29:31 -0400 Subject: [PATCH 5/5] Migrate `local_container_links` to `flask` --- examples/local_container_links/BUILD.bazel | 2 +- examples/local_container_links/requirements.in | 2 +- .../local_container_links/requirements.txt | 17 ++++++++++++++++- examples/local_container_links/server.py | 18 +++++++----------- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/examples/local_container_links/BUILD.bazel b/examples/local_container_links/BUILD.bazel index 05de918..b9a073a 100644 --- a/examples/local_container_links/BUILD.bazel +++ b/examples/local_container_links/BUILD.bazel @@ -10,7 +10,7 @@ py_binary( srcs = ["server.py"], deps = [ "@pypi//absl_py", - "@pypi//bottle", + "@pypi//flask", "@pypi//redis", ], ) diff --git a/examples/local_container_links/requirements.in b/examples/local_container_links/requirements.in index b6b67be..bf7f45e 100644 --- a/examples/local_container_links/requirements.in +++ b/examples/local_container_links/requirements.in @@ -13,5 +13,5 @@ # limitations under the License. absl-py -bottle +flask redis diff --git a/examples/local_container_links/requirements.txt b/examples/local_container_links/requirements.txt index d2d6424..fbeb3e6 100644 --- a/examples/local_container_links/requirements.txt +++ b/examples/local_container_links/requirements.txt @@ -2,7 +2,22 @@ # uv pip compile ./requirements.in -o ./requirements.txt absl-py==2.3.0 # via -r ./requirements.in -bottle==0.13.4 +blinker==1.9.0 + # via flask +click==8.2.1 + # via flask +flask==3.1.1 # via -r ./requirements.in +itsdangerous==2.2.0 + # via flask +jinja2==3.1.6 + # via flask +markupsafe==3.0.2 + # via + # flask + # jinja2 + # werkzeug redis==6.2.0 # via -r ./requirements.in +werkzeug==3.1.3 + # via flask diff --git a/examples/local_container_links/server.py b/examples/local_container_links/server.py index ee91025..7b0f60e 100644 --- a/examples/local_container_links/server.py +++ b/examples/local_container_links/server.py @@ -1,32 +1,28 @@ """An HTTP server incrementing a value in Redis.""" +import time from typing import Sequence from absl import app from absl import flags -import bottle +from flask import Flask import redis redis_host = flags.DEFINE_string("redis_host", None, "Address to Redis server.") -server = bottle.Bottle() -rdb = None +server = Flask(__name__) @server.route("/increment") def increment(): - global rdb - if rdb is None: - return "Redis host not set." + rdb = server.config["RDB"] counter = rdb.incr("counter") return f"{counter=}" @server.route("/") def index(): - global rdb - if rdb is None: - return "Redis host not set." + rdb = server.config["RDB"] counter = int(rdb.get("counter")) return f"{counter=}\nIncrement it by visiting `/increment`." @@ -34,7 +30,6 @@ def index(): def main(argv: Sequence[str]) -> None: del argv - global rdb rdb = redis.Redis(host=redis_host.value, decode_responses=True) while True: @@ -45,9 +40,10 @@ def main(argv: Sequence[str]) -> None: except redis.exceptions.ConnectionError: time.sleep(1) + server.config["RDB"] = rdb rdb.set("counter", 0) - bottle.run(server, host="0.0.0.0", port=8080, debug=False) + server.run(host="0.0.0.0", port=8080, debug=False) if __name__ == "__main__":