diff --git a/Makefile b/Makefile index d14dbea2..3267c287 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,18 @@ OCI_BIN ?= docker # tools GITHUB_RELEASE ?= $(GOBIN)/github-release +PLATFORM_LIST ?= linux/amd64,linux/s390x,linux/arm64 +ARCH := $(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') +PLATFORMS ?= linux/${ARCH} +PLATFORMS := $(if $(filter all,$(PLATFORMS)),$(PLATFORM_LIST),$(PLATFORMS)) +# 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 +# or export PLATFORMS=all to automatically include all supported platforms. +DOCKER_BUILDER ?= macvtap-docker-builder +MACVTAP_IMAGE_TAGGED := ${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} # Make does not offer a recursive wildcard function, so here's one: rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) @@ -30,7 +42,7 @@ go_sources=$(call rwildcard,cmd/,*.go) $(call rwildcard,pkg/,*.go) $(call rwildc # Configure Go export GOOS=linux -export GOARCH=amd64 +export GOARCH=$(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') export CGO_ENABLED=0 export GO111MODULE=on export GOFLAGS=-mod=vendor @@ -69,17 +81,21 @@ vet: $(go_sources) $(GO) $(GO) vet ./pkg/... ./cmd/... ./tests/... docker-build: - $(OCI_BIN) build -t ${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} -f ./cmd/Dockerfile . +ifeq ($(OCI_BIN),podman) + $(MAKE) build-multiarch-macvtap-podman +else ifeq ($(OCI_BIN),docker) + $(MAKE) build-multiarch-macvtap-docker +else + $(error Unsupported OCI_BIN value: $(OCI_BIN)) +endif docker-push: ifeq ($(OCI_BIN),podman) - $(OCI_BIN) push --tls-verify=false ${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} -else - $(OCI_BIN) push ${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} + podman manifest push --tls-verify=false ${MACVTAP_IMAGE_TAGGED} ${MACVTAP_IMAGE_TAGGED} endif docker-tag-latest: - $(OCI_BIN) tag ${IMAGE_REGISTRY}/${IMAGE_NAME}:latest ${IMAGE_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG} + $(OCI_BIN) tag ${IMAGE_REGISTRY}/${IMAGE_NAME}:latest ${MACVTAP_IMAGE_TAGGED} cluster-up: ./cluster/up.sh @@ -122,8 +138,16 @@ release: docker-build docker-push release: $(GITHUB_RELEASE) TAG=$(IMAGE_TAG) GITHUB_RELEASE=$(GITHUB_RELEASE) DESCRIPTION=./version/description ./hack/release.sh +build-multiarch-macvtap-docker: + PLATFORMS=$(PLATFORMS) MACVTAP_IMAGE_TAGGED=$(MACVTAP_IMAGE_TAGGED) DOCKER_BUILDER=$(DOCKER_BUILDER) ./hack/build-macvtap-docker.sh + +build-multiarch-macvtap-podman: + PLATFORMS=$(PLATFORMS) MACVTAP_IMAGE_TAGGED=$(MACVTAP_IMAGE_TAGGED) ./hack/build-macvtap-podman.sh + .PHONY: \ all \ + build-multiarch-macvtap-docker \ + build-multiarch-macvtap-podman \ check \ cluster-up \ cluster-down \ diff --git a/automation/publish.sh b/automation/publish.sh index ee9bb8fd..3d9d319b 100755 --- a/automation/publish.sh +++ b/automation/publish.sh @@ -11,6 +11,7 @@ source automation/check-patch.setup.sh cd ${TMP_PROJECT_PATH} +export PLATFORMS=all IMAGE_TAG=${IMAGE_TAG:-$(git log -1 --pretty=%h)-$(date +%s)} make docker-build make docker-tag-latest diff --git a/cmd/Dockerfile b/cmd/Dockerfile index 50c52c2e..d9cdc7a9 100644 --- a/cmd/Dockerfile +++ b/cmd/Dockerfile @@ -1,12 +1,20 @@ # Multi-stage dockerfile building a container image with both binaries included -FROM quay.io/projectquay/golang:1.20 as builder +FROM --platform=$BUILDPLATFORM quay.io/projectquay/golang:1.20 AS builder ENV GOPATH=/go WORKDIR /go/src/github.com/kubevirt/macvtap-cni +ARG TARGETOS +ARG TARGETARCH +ENV TARGETOS=${TARGETOS:-linux} +ENV TARGETARCH=${TARGETARCH:-amd64} + +ENV GOOS=${TARGETOS} +ENV GOARCH=${TARGETARCH} + COPY . . RUN GOOS=linux CGO_ENABLED=0 go build -o /macvtap-deviceplugin github.com/kubevirt/macvtap-cni/cmd/deviceplugin RUN GOOS=linux CGO_ENABLED=0 go build -o /macvtap-cni github.com/kubevirt/macvtap-cni/cmd/cni -FROM registry.access.redhat.com/ubi8/ubi-minimal +FROM --platform=linux/${TARGETARCH} registry.access.redhat.com/ubi8/ubi-minimal COPY --from=builder /macvtap-deviceplugin /macvtap-deviceplugin COPY --from=builder /macvtap-cni /macvtap-cni diff --git a/hack/build-macvtap-docker.sh b/hack/build-macvtap-docker.sh new file mode 100755 index 00000000..cdacb140 --- /dev/null +++ b/hack/build-macvtap-docker.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +if [ -z "$PLATFORMS" ] || [ -z "$MACVTAP_IMAGE_TAGGED" ]; then + echo "Error: PLATFORMS, and MACVTAP_IMAGE_TAGGED must be set." + exit 1 +fi + +IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS" + +BUILD_ARGS="-f cmd/Dockerfile -t $MACVTAP_IMAGE_TAGGED . --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 + docker buildx rm "$DOCKER_BUILDER" 2>/dev/null || echo "Builder ${DOCKER_BUILDER} not found or already removed, skipping." +fi diff --git a/hack/build-macvtap-podman.sh b/hack/build-macvtap-podman.sh new file mode 100755 index 00000000..2bdfd3f9 --- /dev/null +++ b/hack/build-macvtap-podman.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +if [ -z "$PLATFORMS" ] || [ -z "$MACVTAP_IMAGE_TAGGED" ]; then + echo "Error: PLATFORMS, and MACVTAP_IMAGE_TAGGED must be set." + exit 1 +fi + +IFS=',' read -r -a PLATFORM_LIST <<< "$PLATFORMS" + +podman manifest rm "${MACVTAP_IMAGE_TAGGED}" 2>/dev/null || true +podman manifest rm "${MARKER_IMAGE_GIT_TAGGED}" 2>/dev/null || true +podman rmi "${MACVTAP_IMAGE_TAGGED}" 2>/dev/null || true +podman rmi "${MARKER_IMAGE_GIT_TAGGED}" 2>/dev/null || true + +podman manifest create "${MACVTAP_IMAGE_TAGGED}" + +for platform in "${PLATFORM_LIST[@]}"; do + podman build \ + --platform "$platform" \ + --manifest "${MACVTAP_IMAGE_TAGGED}" \ + -f cmd/Dockerfile . +done diff --git a/hack/init-buildx.sh b/hack/init-buildx.sh new file mode 100755 index 00000000..16cb71c8 --- /dev/null +++ b/hack/init-buildx.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env 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) + ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') + curl -L https://github.com/docker/buildx/releases/download/"${BUILDX_VERSION}"/buildx-"${BUILDX_VERSION}".linux-"${ARCH}" --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}" 2>/dev/null)" || echo "Builder '${builder_name}' not found" + + 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