Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow setting pure, static, etc. in go_cross_binary rule #4131

Open
fishy opened this issue Oct 3, 2024 · 5 comments
Open

Allow setting pure, static, etc. in go_cross_binary rule #4131

fishy opened this issue Oct 3, 2024 · 5 comments

Comments

@fishy
Copy link
Contributor

fishy commented Oct 3, 2024

What version of rules_go are you using?

0.50.1

What version of gazelle are you using?

0.39.0

What version of Bazel are you using?

7.3.2

Does this issue reproduce with the latest releases of all the above?

Yes

What operating system and processor architecture are you using?

linux/amd64

Any other potentially useful information about your toolchain?

What did you do?

We currently cross build docker image for linux/amd64 by passing in the follow args to the command line:

build:docker --@rules_go//go/config:pure --platforms=//:amd64_v1
run:docker --@rules_go//go/config:pure --platforms=//:amd64_v1

Because of bazel-contrib/rules_oci#706, and also just that passing in platform args to the command line is just brittle, we are moving away to use go_cross_binary to cross build.

Basically we now have BUILD.bazel like this:

go_binary(
    name = "foo_native",
    embed = [":foo_lib"],
    pure = "on",
    visibility = ["//visibility:public"],
)

go_cross_binary(
    name = "foo",
    platform = "//:amd64_v1",
    target = ":foo_native",
    visibility = ["//visibility:public"],
)

pkg_tar(
    name = "bin",
    srcs = [
        ":foo",
    ],
    mode = "0755",
    package_dir = "/bin",
)

oci_image(
    name = "docker",
    base = "@distroless",
    tars = [
        ":bin",
    ],
    visibility = ["//visibility:public"],
)

Since we use distroless, pure mode is very important for us (without it we would have to use the version of distroless with libc instead), but it's not currently allowed on go_cross_binary, so we have to set it on go_binary instead. It would be great if we can set it on go_cross_binary.

Additionally (this could be its own issue), it would be great if we can have an alias/rename attribute allowed in go_cross_binary rule. Currently the binary name always come from the rule name, and since we are not allowed to have duplicate rule names, we cannot use it to generate same binary name with different platform cross binary rules (for example, to build multi-arch oci image). Basically ideally we could be able to do something like this instead:

go_binary(
    name = "foo",
    embed = [":foo_lib"],
    pure = "on",
    visibility = ["//visibility:public"],
)

go_cross_binary(
    name = "foo_linux_amd64",
    platform = "//:linux_amd64",
    target = ":foo",
    rename = "foo",
    visibility = ["//visibility:public"],
)

go_cross_binary(
    name = "foo_linux_arm64",
    platform = "//:linux_arm64",
    target = ":foo",
    rename = "foo",
    visibility = ["//visibility:public"],
)

# then have pkg_tar and oci_image rules to build multi-arch image

What did you expect to see?

What did you see instead?

@fmeum
Copy link
Member

fmeum commented Oct 3, 2024

Could you try whether you can get the basename you want by using a name such as linux_amd64/foo?

I have the feeling that go_cross_binary was a mistake as it's complex, but still isn't flexible enough (as we can tell from this issue). Could you give https://github.com/fmeum/with_cfg.bzl a try and see whether that works for you?

@dzbarsky
Copy link
Contributor

dzbarsky commented Oct 4, 2024

We use platform_transition_filegroup on go_binary to build multi arch OCI image index, exactly as you describe. I think go_cross_binary is not needed

@fishy
Copy link
Contributor Author

fishy commented Oct 4, 2024

@fmeum it looks like using a name like linux_amd64/foo works as expected

@dzbarsky can you give an example of how platform_transition_filegroup works? the doc I can find doesn't say much, I also don't understand the example I found: the name of the platform_transition_filegroup, transitioned_image, is not referenced by anything else in that build file, so I don't understand how it's used or triggered. also it doesn't look like this can resolve the pure issue either, it can still only change the platform.

@fishy
Copy link
Contributor Author

fishy commented Oct 4, 2024

but I do have a fear that the behavior of naming it linux_amd64/foo could change in the future. currently it packs bin/foo into the tar, but maybe one day someone think that's a bug and it should be packed as bin/linux_amd64/foo instead?

@DavidZbarsky-at
Copy link

DavidZbarsky-at commented Oct 4, 2024

We do this:

def native_binary_container_image(
        name,
        binary,
        cmd = None,
        workdir = None,
        tags = [],
        testonly = False,
        strip_prefix = None,
        add_prefix = "bin",
        visibility = None):
    common_kwargs = {
        "testonly": testonly,
        "visibility": visibility,
    }

    oci_layer_tar(
        name = name,
        srcs = [binary],
        strip_prefix = strip_prefix or native.package_name(),
        add_prefix = add_prefix,
    )

    binary_name = binary.replace(native.package_name(), "").replace(":", "/")

    oci_image(
        name = name + "_host",
        base = "@distroless_base_image",
        entrypoint = ["/" + add_prefix + "/" + binary_name],
        cmd = cmd,
        workdir = workdir,
        tars = [name + "_tar"],
        tags = tags + ["manual"],
        **common_kwargs
    )

    multiarch_image(
        name = name,
        host_image = name + "_host",
        tags = tags,
        **common_kwargs
    )
def multiarch_image(
        name,
        host_image,
        **kwargs):
    linux_amd64_target_name = name + "_linux_amd64"
    linux_arm64_target_name = name + "_linux_arm64"

    platform_transition_filegroup(
        name = linux_amd64_target_name,
        srcs = [host_image],
        target_platform = "//build_tools/platforms:linux_amd64",
        **kwargs
    )

    platform_transition_filegroup(
        name = linux_arm64_target_name,
        srcs = [host_image],
        target_platform = "//build_tools/platforms:linux_arm64",
        **kwargs
    )

    oci_image_index(
        name = name,
        images = [
            linux_amd64_target_name,
            linux_arm64_target_name,
        ],
        **kwargs
    )

BUILD usage:

go_binary(
    name = "otel_collector",
    out = "otel_collector",
    embed = [":otel_collector_lib"],
    visibility = ["//visibility:public"],
)

# Load into docker with `bazel run //services/observability/otel_collector:image_docker`
native_binary_container_image(
    name = "image",
    binary = "otel_collector",
)

So with this setup you can either tag pure on the go_binary directly, or if you don't want that, you could use with_cfg as @fmeum suggests to transition to a pure binary, and then transition platforms after. Example:

pure_go_binary, __ = with_cfg(go_binary).set(
    Label("@io_bazel_rules_go//go/config:pure"),
    True,
).build()

...
load(.. pure_go_binary)

pure_go_binary(
    name = "otel_collector",
    out = "otel_collector",
    embed = [":otel_collector_lib"],
    visibility = ["//visibility:public"],
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants