diff --git a/Makefile b/Makefile index df52b6e3..47a596b7 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,16 @@ REGISTRY ?= quay.io/kubevirt IMAGE_TAG ?= latest IMAGE_GIT_TAG ?= $(shell git describe --abbrev=8 --tags) +PLATFORMS ?= linux/amd64,linux/s390x +# Set the platforms for building a multi-platform supported image. +# Example: +# PLATFORMS ?= linux/amd64,linux/arm64,linux/s390x +# Alternatively, you can export the PLATFORMS variable like this: +# export PLATFORMS=linux/arm64,linux/s390x,linux/amd64 +ARCH := $(shell uname -m | sed 's/x86_64/amd64/') +DOCKER_BUILDER ?= marker-docker-builder +MARKER_IMAGE_TAGGED_1 := ${REGISTRY}/bridge-marker:${IMAGE_TAG} +MARKER_IMAGE_TAGGED_2 := ${REGISTRY}/bridge-marker:${IMAGE_GIT_TAG} BIN_DIR = $(CURDIR)/build/_output/bin/ export GOPROXY=direct @@ -40,15 +50,22 @@ functest: $(GINKGO) marker: $(GO) hack/version.sh > $(BIN_DIR)/.version - CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GO) build -o $(BIN_DIR)/marker github.com/kubevirt/bridge-marker/cmd/marker docker-build: marker - $(OCI_BIN) build -t ${REGISTRY}/bridge-marker:${IMAGE_TAG} ./build +ifeq ($(OCI_BIN),podman) + $(MAKE) build-multiarch-marker-podman +else ifeq ($(OCI_BIN),docker) + $(MAKE) build-multiarch-marker-docker +else + $(error Unsupported OCI_BIN value: $(OCI_BIN)) +endif docker-push: - $(OCI_BIN) push ${TLS_SETTING} ${REGISTRY}/bridge-marker:${IMAGE_TAG} - $(OCI_BIN) tag ${REGISTRY}/bridge-marker:${IMAGE_TAG} ${REGISTRY}/bridge-marker:${IMAGE_GIT_TAG} - $(OCI_BIN) push ${TLS_SETTING} ${REGISTRY}/bridge-marker:${IMAGE_GIT_TAG} +ifeq ($(OCI_BIN),podman) + podman manifest push ${TLS_SETTING} ${MARKER_IMAGE_TAGGED_1} ${MARKER_IMAGE_TAGGED_1} + podman tag ${MARKER_IMAGE_TAGGED_1} ${MARKER_IMAGE_TAGGED_2} + podman manifest push ${TLS_SETTING} ${MARKER_IMAGE_TAGGED_2} ${MARKER_IMAGE_TAGGED_2} +endif manifests: ./hack/build-manifests.sh @@ -69,4 +86,10 @@ vendor: $(GO) tools: $(GO) ./hack/install-tools.sh -.PHONY: build format docker-build docker-push manifests cluster-up cluster-down cluster-sync vendor marker tools +build-multiarch-marker-docker: + ARCH=$(ARCH) PLATFORMS=$(PLATFORMS) MARKER_IMAGE_TAGGED_1=$(MARKER_IMAGE_TAGGED_1) MARKER_IMAGE_TAGGED_2=$(MARKER_IMAGE_TAGGED_2) DOCKER_BUILDER=$(DOCKER_BUILDER) ./hack/build-marker-docker.sh + +build-multiarch-marker-podman: + ARCH=$(ARCH) PLATFORMS=$(PLATFORMS) MARKER_IMAGE_TAGGED_1=$(MARKER_IMAGE_TAGGED_1) MARKER_IMAGE_TAGGED_2=$(MARKER_IMAGE_TAGGED_2) ./hack/build-marker-podman.sh + +.PHONY: build build-multiarch-marker-docker build-multiarch-marker-podman format docker-build docker-push manifests cluster-up cluster-down cluster-sync vendor marker tools diff --git a/build/Dockerfile b/build/Dockerfile index e5aeda4e..ef0780e8 100644 --- a/build/Dockerfile +++ b/build/Dockerfile @@ -1,4 +1,21 @@ -FROM registry.access.redhat.com/ubi8/ubi-minimal -COPY _output/bin/marker /marker -COPY _output/bin/.version /.version +ARG BUILD_ARCH=amd64 +FROM --platform=linux/${BUILD_ARCH} quay.io/centos/centos:stream9 AS builder +RUN dnf install -y tar gzip jq && dnf clean all +RUN ARCH=$(uname -m | sed 's/x86_64/amd64/') && \ + GO_VERSION=$(curl -L -s "https://go.dev/dl/?mode=json" | jq -r '.[0].version') && \ + curl -L "https://go.dev/dl/${GO_VERSION}.linux-${ARCH}.tar.gz" -o go.tar.gz && \ + tar -C /usr/local -xzf go.tar.gz && \ + rm go.tar.gz +ENV PATH=$PATH:/usr/local/go/bin +WORKDIR /go/src/bridge-marker +COPY . . + +ARG TARGETOS +ARG TARGETARCH + +RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o build/_output/bin/marker github.com/kubevirt/bridge-marker/cmd/marker + +FROM --platform=linux/${TARGETARCH} registry.access.redhat.com/ubi8/ubi-minimal AS final +COPY --from=builder /go/src/bridge-marker/build/_output/bin/marker /marker +COPY --from=builder /go/src/bridge-marker/build/_output/bin/.version /.version ENTRYPOINT [ "/marker"] diff --git a/hack/build-marker-docker.sh b/hack/build-marker-docker.sh new file mode 100755 index 00000000..9e39bd4a --- /dev/null +++ b/hack/build-marker-docker.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -z "$ARCH" ] || [ -z "$PLATFORMS" ] || [ -z "$MARKER_IMAGE_TAGGED_1" ] || [ -z "$MARKER_IMAGE_TAGGED_2" ]; then + echo "Error: ARCH, PLATFORMS, MARKER_IMAGE_TAGGED_1, and MARKER_IMAGE_TAGGED_2 must be set." + exit 1 +fi + +IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS" + +BUILD_ARGS="--no-cache --build-arg BUILD_ARCH=$ARCH -f build/Dockerfile -t $MARKER_IMAGE_TAGGED_1 -t $MARKER_IMAGE_TAGGED_2 . --push" + +if [ ${#PLATFORM_LIST[@]} -eq 1 ]; then + docker build --platform "$PLATFORMS" $BUILD_ARGS +else + ./hack/init-buildx.sh "$DOCKER_BUILDER" + docker buildx build --platform "$PLATFORMS" $BUILD_ARGS +fi diff --git a/hack/build-marker-podman.sh b/hack/build-marker-podman.sh new file mode 100755 index 00000000..51a7992e --- /dev/null +++ b/hack/build-marker-podman.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +if [ -z "$ARCH" ] || [ -z "$PLATFORMS" ] || [ -z "$MARKER_IMAGE_TAGGED_1" ]; then + echo "Error: ARCH, PLATFORMS, and MARKER_IMAGE_TAGGED_1 must be set." + exit 1 +fi + +IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS" + +# Remove any existing manifests and images +podman manifest rm "${MARKER_IMAGE_TAGGED_1}" || true +podman manifest rm "${MARKER_IMAGE_TAGGED_2}" || true +podman rmi "${MARKER_IMAGE_TAGGED_1}" || true +podman rmi "${MARKER_IMAGE_TAGGED_2}" || true + +podman manifest create "${MARKER_IMAGE_TAGGED_1}" + +for platform in "${PLATFORM_LIST[@]}"; do + podman build \ + --no-cache \ + --build-arg BUILD_ARCH="$ARCH" \ + --platform "$platform" \ + --manifest "${MARKER_IMAGE_TAGGED_1}" \ + -f build/Dockerfile . +done diff --git a/hack/init-buildx.sh b/hack/init-buildx.sh new file mode 100755 index 00000000..fd444a20 --- /dev/null +++ b/hack/init-buildx.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +check_buildx() { + export DOCKER_CLI_EXPERIMENTAL=enabled + + if ! docker buildx > /dev/null 2>&1; then + mkdir -p ~/.docker/cli-plugins + BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | jq -r .tag_name) + curl -L https://github.com/docker/buildx/releases/download/"${BUILDX_VERSION}"/buildx-"${BUILDX_VERSION}".linux-amd64 --output ~/.docker/cli-plugins/docker-buildx + chmod a+x ~/.docker/cli-plugins/docker-buildx + fi +} + +create_or_use_buildx_builder() { + local builder_name=$1 + if [ -z "$builder_name" ]; then + echo "Error: Builder name is required." + exit 1 + fi + + check_buildx + + current_builder="$(docker buildx inspect "${builder_name}")" + + if ! grep -q "^Driver: docker$" <<<"${current_builder}" && \ + grep -q "linux/amd64" <<<"${current_builder}" && \ + grep -q "linux/arm64" <<<"${current_builder}" && \ + grep -q "linux/s390x" <<<"${current_builder}"; then + echo "The current builder already has multi-architecture support (amd64, arm64, s390x)." + echo "Skipping setup as the builder is already configured correctly." + exit 0 + fi + + # Check if the builder already exists by parsing the output of `docker buildx ls` + # We check if the builder_name appears in the list of active builders + existing_builder=$(docker buildx ls | grep -w "$builder_name" | awk '{print $1}') + + if [ -n "$existing_builder" ]; then + echo "Builder '$builder_name' already exists." + echo "Using existing builder '$builder_name'." + docker buildx use "$builder_name" + else + echo "Creating a new Docker Buildx builder: $builder_name" + docker buildx create --driver-opt network=host --use --name "$builder_name" + echo "The new builder '$builder_name' has been created and set as active." + fi +} + +if [ $# -eq 1 ]; then + create_or_use_buildx_builder "$1" +else + echo "Usage: $0 " + echo "Example: $0 mybuilder" + exit 1 +fi diff --git a/hack/install-go.sh b/hack/install-go.sh index 13b4fc44..588c0eca 100755 --- a/hack/install-go.sh +++ b/hack/install-go.sh @@ -2,7 +2,8 @@ destination=$1 version=$(grep "^go " go.mod |awk '{print $2}') -tarball=go$version.linux-amd64.tar.gz +arch=$(uname -m | sed 's/x86_64/amd64/') +tarball=go$version.linux-$arch.tar.gz url=https://dl.google.com/go/ mkdir -p $destination