Skip to content
This repository has been archived by the owner on Oct 2, 2023. It is now read-only.

Forward architecture from language rules to layers #2150

Closed
wants to merge 4 commits into from

Conversation

gibfahn
Copy link
Contributor

@gibfahn gibfahn commented Sep 1, 2022

Ideally, this would be able to be inferred from the base image but I don't think it is possible to propagate that information into the build transition. Explicitly setting the architecture seems like the simplest way to trigger the build transition and support non-x86_64 target platforms.

Opening for feedback first.

PR Checklist

Please check if your PR fulfills the following requirements:

  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

  • Feature

What is the current behavior?

Non-x86 target platforms aren't supported.

What is the new behavior?

Non-x86 target platforms are supported.

Does this PR introduce a breaking change?

  • No

Other information


Commits (oldest to newest)

8d69206 Forward architecture for py3_image


a9ee0fc Forward architecture for go_image


466a8de Forward architecture for java_image


130dea5 squash! don't set a default architecture

Respond to feedback from PR review.


go/image.bzl Outdated
@@ -89,7 +89,7 @@ STATIC_DEFAULT_BASE = select({
"//conditions:default": "@go_image_static//image",
})

def go_image(name, base = None, deps = [], layers = [], env = {}, binary = None, **kwargs):
def go_image(name, base = None, deps = [], layers = [], env = {}, binary = None, architecture = "x86_64", **kwargs):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing default is amd64 (set in container/image.bzl). Rather than setting a new default here and changing the behavior for existing users, set architecture = None

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, changed for all three.

java/image.bzl Outdated
@@ -275,6 +275,7 @@ def java_image(
env = {},
jvm_flags = [],
classpath_as_file = None,
architecture = "x86_64",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, leave the default unset

@@ -73,7 +73,7 @@ DEFAULT_BASE = select({
"//conditions:default": "@py3_image_base//image",
})

def py3_image(name, base = None, deps = [], layers = [], env = {}, **kwargs):
def py3_image(name, base = None, deps = [], layers = [], env = {}, architecture = "x86_64", **kwargs):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, leave the default unset

@sfc-gh-kleonhard
Copy link

This change looks great. architecture is defaulted to amd64 in container/image.bzl with no way to control it. Thanks for sending this PR

@sfc-gh-shsharma
Copy link

I need this change. I was banging my head trying to figure out why I couldn't use go_image to build an arm64 container. I've demonstrated the fix in this repo: https://github.com/shadanan/go-bazel-gazelle-docker-template

Agree with Kyle. We should leave the default for architecture unset.

@gibfahn
Copy link
Contributor Author

gibfahn commented Oct 3, 2022

This change looks great. architecture is defaulted to amd64 in container/image.bzl with no way to control it. Thanks for sending this PR

Updated, please take a look.

@mvgijssel
Copy link

mvgijssel commented Oct 26, 2022

Looking forward to this change, because I too was caught off guard about only being able to build amd64 images! This would make an already amazing library even better 😍

@sfc-gh-kleonhard
Copy link

There's a pretty simple workaround for Go that ought to apply to other languages as well - Inline the implementation of go_image code.

Here's a completely untested example:

load("@io_bazel_rules_go//go:def.bzl", "go_binary")
load("@io_bazel_rules_docker//go:image.bzl", "go_image", "DEFAULT_BASE")
load("//lang:image.bzl", "app_layer")

go_library(
    name = "my_binary_lib",
    srcs = ["main.go"],
    deps = [],
)

go_binary(
  name= "my_binary",
  embed = [":my_binary_lib"],
)

app_layer(
    name = "my_image_arm64,
    // Customize as needed. e.g. base = "@alpine_linux//image"
    base = DEFAULT_BASE,
    binary = ":my_binary",
    architecture = "arm64",
)

@sfc-gh-shsharma
Copy link

While I'm waiting for this to merge, I basically did what @sfc-gh-kleonhard suggested. I create a file called /go/image.bzl with the following contents:

load("@io_bazel_rules_go//go:def.bzl", "go_binary")
load("@io_bazel_rules_docker//container:container.bzl", "container_pull")
load("@io_bazel_rules_docker//lang:image.bzl", "app_layer")
load("@io_bazel_rules_docker//repositories:go_repositories.bzl", _go_deps = "go_deps")

# Load the resolved digests.
load("@io_bazel_rules_docker//go:go.bzl", BASE_DIGESTS = "DIGESTS")
load("@io_bazel_rules_docker//go:static.bzl", STATIC_DIGESTS = "DIGESTS")

def repositories():
    """Import the dependencies of the go_image rule.

    Call the core "go_deps" function to reduce boilerplate. This is
    idempotent if folks call it themselves.
    """
    _go_deps()

    excludes = native.existing_rules().keys()
    if "go_image_base" not in excludes:
        container_pull(
            name = "go_image_base",
            registry = "gcr.io",
            repository = "distroless/base",
            digest = BASE_DIGESTS["latest"],
        )
    if "go_debug_image_base" not in excludes:
        container_pull(
            name = "go_debug_image_base",
            registry = "gcr.io",
            repository = "distroless/base",
            digest = BASE_DIGESTS["debug"],
        )
    if "go_image_static" not in excludes:
        container_pull(
            name = "go_image_static",
            registry = "gcr.io",
            repository = "distroless/static",
            digest = STATIC_DIGESTS["latest"],
        )
    if "go_debug_image_static" not in excludes:
        container_pull(
            name = "go_debug_image_static",
            registry = "gcr.io",
            repository = "distroless/static",
            digest = STATIC_DIGESTS["debug"],
        )

DEFAULT_BASE = select({
    "@io_bazel_rules_docker//:debug": "@go_debug_image_base//image",
    "@io_bazel_rules_docker//:fastbuild": "@go_image_base//image",
    "@io_bazel_rules_docker//:optimized": "@go_image_base//image",
    "//conditions:default": "@go_image_base//image",
})

STATIC_DEFAULT_BASE = select({
    "@io_bazel_rules_docker//:debug": "@go_debug_image_static//image",
    "@io_bazel_rules_docker//:fastbuild": "@go_image_static//image",
    "@io_bazel_rules_docker//:optimized": "@go_image_static//image",
    "//conditions:default": "@go_image_static//image",
})

def go_image(name, base = None, deps = [], layers = [], env = {}, binary = None, architecture = "x86_64", **kwargs):
    """Constructs a container image wrapping a go_binary target.

    Args:
        name: Name of the go_image target.
        base: Base image to use to build the go_image.
        deps: Dependencies of the go image target.
        layers: Augments "deps" with dependencies that should be put into their own layers.
        env: Environment variables for the go_image.
        binary: An alternative binary target to use instead of generating one.
        architecture: The target architecture, defaults to `x86_64`.
        **kwargs: See go_binary.
    """
    if layers:
        print("go_image does not benefit from layers=[], got: %s" % layers)

    if not binary:
        binary = name + ".binary"
        go_binary(name = binary, deps = deps + layers, **kwargs)
    elif deps:
        fail("kwarg does nothing when binary is specified", "deps")

    if not base:
        base = STATIC_DEFAULT_BASE if kwargs.get("pure") == "on" else DEFAULT_BASE

    tags = kwargs.get("tags", None)
    for index, dep in enumerate(layers):
        base = app_layer(name = "%s.%d" % (name, index), base = base, dep = dep, tags = tags, architecture = architecture)
        base = app_layer(name = "%s.%d-symlinks" % (name, index), base = base, dep = dep, binary = binary, tags = tags, architecture = architecture)

    visibility = kwargs.get("visibility", None)
    restricted_to = kwargs.get("restricted_to", None)
    compatible_with = kwargs.get("compatible_with", None)
    app_layer(
        name = name,
        base = base,
        env = env,
        binary = binary,
        visibility = visibility,
        tags = tags,
        args = kwargs.get("args"),
        data = kwargs.get("data"),
        testonly = kwargs.get("testonly"),
        restricted_to = restricted_to,
        compatible_with = compatible_with,
        architecture = architecture,
    )

And in my BUILD.bazel files, I have this load statement:

load("//go:image.bzl", "go_image")

That way, when this PR gets merged, I can just delete the image.bzl file and replace the load statement with the go_image from this rules repo.

@github-actions
Copy link

github-actions bot commented May 8, 2023

This Pull Request has been automatically marked as stale because it has not had any activity for 180 days. It will be closed if no further activity occurs in 30 days.
Collaborators can add an assignee to keep this open indefinitely. Thanks for your contributions to rules_docker!

@github-actions github-actions bot added the Can Close? Will close in 30 days unless there is a comment indicating why not label May 8, 2023
@github-actions
Copy link

github-actions bot commented Jun 7, 2023

This PR was automatically closed because it went 30 days without a reply since it was labeled "Can Close?"

@github-actions github-actions bot closed this Jun 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Can Close? Will close in 30 days unless there is a comment indicating why not
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants