diff --git a/oci_python_image/MODULE.bazel b/oci_python_image/MODULE.bazel index 44e86f54..426e670b 100644 --- a/oci_python_image/MODULE.bazel +++ b/oci_python_image/MODULE.bazel @@ -22,9 +22,20 @@ register_toolchains( oci = use_extension("@rules_oci//oci:extensions.bzl", "oci") oci.pull( - name = "distroless_python", + name = "docker_python", digest = "sha256:b48e216f7c4adcf24fecd7016f3b8ead76866a19571819f67f47c1ccaf899717", + # Shorthand for https://hub.docker.com/_/python image = "python", ) -use_repo(oci, "distroless_python") \ No newline at end of file +oci.pull( + name = "distroless_base", + digest = "sha256:ccaef5ee2f1850270d453fdf700a5392534f8d1a8ca2acda391fbb6a06b81c86", + image = "gcr.io/distroless/base", + platforms = [ + "linux/amd64", + "linux/arm64", + ], +) + +use_repo(oci, "distroless_base", "docker_python") \ No newline at end of file diff --git a/oci_python_image/README.md b/oci_python_image/README.md new file mode 100644 index 00000000..276c574b --- /dev/null +++ b/oci_python_image/README.md @@ -0,0 +1,9 @@ +# Example of Python plus rules_oci + +There are a couple ways to treat the interpreter: + +1. Choose a base image which includes it. This is the typical approach outside Bazel, and is + recommended when migrating from a Dockerfile or similar build system to avoid changing + multiple things at the same time. In `/MODULE.bazel` we use `oci.pull` to pull https://hub.docker.com/_/python and then in `use_python_base/BUILD.bazel` we choose that as the base image. Then, the Bazel-built Python application just falls through to the "system interpreter". +2. Propagate Bazel's "toolchain resolution" so that the same interpreter used by `bazel run` + will also be used when the Python application runs in a container. This avoids version skew in the Python version used. In `/MODULE.bazel` we use `oci.pull` to pull the Distroless base image (which is tiny). Then the `interpreter_as_layer/BUILD.bazel` file shows how we get that interpreter as a layer in the image. diff --git a/oci_python_image/interpreter_as_layer/BUILD.bazel b/oci_python_image/interpreter_as_layer/BUILD.bazel new file mode 100644 index 00000000..3364a2ad --- /dev/null +++ b/oci_python_image/interpreter_as_layer/BUILD.bazel @@ -0,0 +1,35 @@ +load(":current_py_toolchain.bzl", "current_py_toolchain") +load("@rules_oci//oci:defs.bzl", "oci_image") +load("@rules_pkg//pkg:pkg.bzl", "pkg_tar") + +current_py_toolchain( + name = "current_py_toolchain_runfiles", +) + +pkg_tar( + name = "py_interpreter_toolchain", + # If we just used @rules_python//python:current_py_toolchain then we would end up with platform + # specific paths here. The current_py_toolchain_runfiles remaps the paths to be platform independent + srcs = [":current_py_toolchain_runfiles"], + extension = "tar.gz", + package_dir = "/opt/python", + strip_prefix = ".", +) + +pkg_tar( + name = "python_aliases", + symlinks = { + "/usr/bin/python": "/usr/bin/fake_python", + "/usr/bin/python3": "/usr/bin/fake_python", + }, +) + +oci_image( + name = "py_base", + base = "@distroless_base", + tars = [ + ":python_aliases", + ":py_interpreter_toolchain", + ], + visibility = ["//visibility:public"], +) diff --git a/oci_python_image/interpreter_as_layer/current_py_toolchain.bzl b/oci_python_image/interpreter_as_layer/current_py_toolchain.bzl new file mode 100644 index 00000000..2055e152 --- /dev/null +++ b/oci_python_image/interpreter_as_layer/current_py_toolchain.bzl @@ -0,0 +1,35 @@ +load("@rules_pkg//:providers.bzl", "PackageFilegroupInfo", "PackageFilesInfo") + +def _strip_platform_specific(path): + parts = path.split("/") + return "/".join(parts[2:]) + +def _current_py_toolchain_impl(ctx): + default = ctx.toolchains["@rules_python//python:toolchain_type"].py3_runtime + file_map = {} + + files = depset(transitive = [default.files]) + + for file in files.to_list(): + file_map[_strip_platform_specific(file.path)] = file + + files = depset([], transitive = [files]) + + return [ + PackageFilegroupInfo( + pkg_dirs = [], + pkg_files = [ + [PackageFilesInfo( + dest_src_map = file_map, + attributes = {}, + ), ctx.label], + ], + ), + DefaultInfo(files = files), + ] + +current_py_toolchain = rule( + implementation = _current_py_toolchain_impl, + attrs = {}, + toolchains = ["@rules_python//python:toolchain_type"], +) diff --git a/oci_python_image/hello_world/BUILD.bazel b/oci_python_image/use_python_base/BUILD.bazel similarity index 98% rename from oci_python_image/hello_world/BUILD.bazel rename to oci_python_image/use_python_base/BUILD.bazel index 6b5d7bb4..d73cfd49 100644 --- a/oci_python_image/hello_world/BUILD.bazel +++ b/oci_python_image/use_python_base/BUILD.bazel @@ -31,7 +31,7 @@ py_image_layer( oci_image( name = "image", - base = "@distroless_python", + base = "@docker_python", entrypoint = ["/opt/hello_world/hello_world_bin"], tars = [":hello_world_layer"], ) diff --git a/oci_python_image/hello_world/__init__.py b/oci_python_image/use_python_base/__init__.py similarity index 100% rename from oci_python_image/hello_world/__init__.py rename to oci_python_image/use_python_base/__init__.py diff --git a/oci_python_image/hello_world/__main__.py b/oci_python_image/use_python_base/__main__.py similarity index 100% rename from oci_python_image/hello_world/__main__.py rename to oci_python_image/use_python_base/__main__.py diff --git a/oci_python_image/hello_world/app.py b/oci_python_image/use_python_base/app.py similarity index 100% rename from oci_python_image/hello_world/app.py rename to oci_python_image/use_python_base/app.py diff --git a/oci_python_image/hello_world/test.yaml b/oci_python_image/use_python_base/test.yaml similarity index 100% rename from oci_python_image/hello_world/test.yaml rename to oci_python_image/use_python_base/test.yaml