From 384a0b5e163cc1aebbad7f27aee74cba38f29ad5 Mon Sep 17 00:00:00 2001 From: Akos Eros Date: Thu, 16 Oct 2025 12:06:21 +0200 Subject: [PATCH 1/4] chore: support for arm64 and multiarch manifest Dockerfile changes needed to support the multiarch build. Change the usage of hack/build.sh script and invoke the directly make build target from dockerfile. --- .github/workflows/build-push.yaml | 159 ++++++++++++++++++++++++++++++ Dockerfile | 24 ++--- Makefile | 28 ++++++ hack/build.sh | 5 - 4 files changed, 194 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/build-push.yaml diff --git a/.github/workflows/build-push.yaml b/.github/workflows/build-push.yaml new file mode 100644 index 000000000..2221d7bba --- /dev/null +++ b/.github/workflows/build-push.yaml @@ -0,0 +1,159 @@ +name: Build (amd64 and arm64) and push to quay registries +on: + push: + branches: ["main"] + tags: ['[0-9]+.[0-9]+.[0-9]+'] + pull_request: + branches: ["main"] + + workflow_dispatch: + +permissions: + contents: read + +env: + REGISTRY: localhost + NAME: patterns-operator + TAG: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || (github.ref_name == 'main' && 'latest' || github.ref_name) }} + +jobs: + build-container: + strategy: + matrix: + include: + - targetarch: amd64 + runner: ubuntu-latest + - targetarch: arm64 + runner: ubuntu-24.04-arm + + runs-on: ${{ matrix.runner }} + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Build container and save tarball + env: + TARGETARCH: ${{ matrix.targetarch }} + OPERATOR_IMG: ${{ env.NAME }}:${{ env.TAG }} + KEY: ${{ secrets.API_KEY }} + run: | + export APIFILE="internal/controller/apikey.txt" + trap "rm -f ${APIFILE}" SIGINT EXIT + + if [ -z "${KEY}" ]; then + echo "Key is empty" + echo '' > "${APIFILE}" + else + echo "Key is set" + echo "${KEY}" > "${APIFILE}" + fi + make "podman-build-${TARGETARCH}" + buildah push "${OPERATOR_IMG}-${TARGETARCH}" "docker-archive:/tmp/image-${TARGETARCH}.tar:${OPERATOR_IMG}-${TARGETARCH}" + + - name: Upload image artifact + uses: actions/upload-artifact@v4 + with: + name: image-${{ matrix.targetarch }}-${{ github.run_id }} + path: /tmp/image-${{ matrix.targetarch }}.tar + retention-days: 1 + + pre-push-check: + needs: [build-container] + if: github.event_name != 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + persist-credentials: false + + # We use an env due to https://docs.zizmor.sh/audits/#remediation_18 + - name: Check that tag version corresponds to metadata version + run: |- + VERSION=$(yq -r '.spec.version' bundle/manifests/patterns-operator.clusterserviceversion.yaml) + if [ "${VERSION}" != "${TAG}" ]; then + echo "Version in metadata ${VERSION} whereas tag is different: ${TAG}" + exit 1 + fi + + push-multiarch-manifest: + needs: [pre-push-check] + if: github.event_name != 'pull_request' + strategy: + matrix: + include: + - upload_registry: quay.io/validatedpatterns + legacy: false + - upload_registry: quay.io/hybridcloudpatterns + legacy: true + runs-on: ubuntu-latest + permissions: + contents: read + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + persist-credentials: false + + - name: Download AMD64 image + uses: actions/download-artifact@v5 + with: + name: image-amd64-${{ github.run_id }} + path: /tmp + + - name: Download ARM64 image + uses: actions/download-artifact@v5 + with: + name: image-arm64-${{ github.run_id }} + path: /tmp + + - name: Load tarballs into local containers-storage + run: | + buildah pull docker-archive:/tmp/image-amd64.tar + buildah pull docker-archive:/tmp/image-arm64.tar + + - name: Log into Quay + env: + USERNAME: ${{ matrix.legacy && secrets.LEGACY_QUAY_USERNAME || secrets.QUAY_USERNAME }} + PASSWORD: ${{ matrix.legacy && secrets.LEGACY_QUAY_PASSWORD || secrets.QUAY_PASSWORD }} + run: | + buildah login -u "${USERNAME}" -p "${PASSWORD}" quay.io + + # The compressed manifest in Quay has a different digest than the local so we + # need to use skopeo to retrieve the correct digest for signing + - name: Create manifest and push to Quay + id: manifest-push + env: + UPLOADREGISTRY: ${{ matrix.upload_registry }} + OPERATOR_IMG: ${{ env.NAME }}:${{ env.TAG }} + run: | + make buildah-manifest + buildah manifest add --arch=amd64 "${REGISTRY}/${OPERATOR_IMG}" "${REGISTRY}/${OPERATOR_IMG}-amd64" + buildah manifest add --arch=arm64 "${REGISTRY}/${OPERATOR_IMG}" "${REGISTRY}/${OPERATOR_IMG}-arm64" + make buildah-push + DIGEST=$(skopeo inspect --format "{{.Digest}}" "docker://${UPLOADREGISTRY}/${OPERATOR_IMG}") + echo "digest=$DIGEST" >> "$GITHUB_OUTPUT" + + - name: Install cosign + uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 + with: + cosign-release: "v2.2.4" + + # Cosign expects the docker config.json for registry authentication so we must + # copy it from buildah + - name: Sign the published Docker image + env: + DIGEST: ${{ steps.manifest-push.outputs.digest }} + UPLOADREGISTRY: ${{ matrix.upload_registry }} + OPERATOR_IMG: ${{ env.NAME }}:${{ env.TAG }} + run: | + cat "${XDG_RUNTIME_DIR}/containers/auth.json" > ~/.docker/config.json + cosign sign --yes "${UPLOADREGISTRY}/${OPERATOR_IMG}@${DIGEST}" diff --git a/Dockerfile b/Dockerfile index d832f375d..663c86114 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,6 @@ -FROM registry.access.redhat.com/ubi9/ubi-minimal:latest AS builder -RUN microdnf install git-core jq tar -y && microdnf clean all +ARG GO_VERSION=1.24.6 +FROM registry.access.redhat.com/ubi9/go-toolset:${GO_VERSION} AS builder +ARG GOARCH=amd64 # Build the manager binary @@ -9,18 +10,6 @@ WORKDIR /workspace COPY go.mod go.mod COPY go.sum go.sum -# use latest Go z release -ENV GOTOOLCHAIN=auto -ENV GO_INSTALL_DIR=/golang - -# Ensure correct Go version -RUN export GO_VERSION=$(grep -E "go [[:digit:]]\.[[:digit:]][[:digit:]]" go.mod | awk '{print $2}') && \ - export GO_FILENAME=$(curl -sL 'https://go.dev/dl/?mode=json&include=all' | jq -r "[.[] | select(.version | startswith(\"go${GO_VERSION}\"))][0].files[] | select(.os == \"linux\" and .arch == \"amd64\") | .filename") && \ - curl -sL -o go.tar.gz "https://golang.org/dl/${GO_FILENAME}" && \ - mkdir -p ${GO_INSTALL_DIR} && \ - tar -C ${GO_INSTALL_DIR} -xzf go.tar.gz && \ - rm go.tar.gz && ln -sf ${GO_INSTALL_DIR}/go/bin/go /usr/bin/go - # Copy the go sources COPY vendor/ vendor/ COPY cmd/main.go cmd/main.go @@ -29,11 +18,12 @@ COPY version/ version/ COPY internal/controller/ internal/controller/ COPY hack/ hack/ COPY .git/ .git/ -RUN mkdir /licenses -COPY LICENSE /licenses +COPY LICENSE /licenses/ +COPY Makefile . # Build -RUN --mount=type=secret,id=apikey hack/build.sh +USER root +RUN --mount=type=secret,id=apikey GOARCH=${GOARCH} make build # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/Makefile b/Makefile index b21b9870f..5f9f847b8 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,8 @@ VERSION ?= 0.0.4 OPERATOR_NAME ?= patterns GOFLAGS=-mod=vendor GOLANGCI_VERSION ?= 2.5.0 +REGISTRY ?= localhost +UPLOADREGISTRY ?= quay.io/validatedpatterns # CI uses a non-writable home dir, make sure .cache is writable ifeq ("${HOME}", "/") @@ -65,6 +67,7 @@ BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:v$(VERSION) # Image URL to use all building/pushing image targets IMG ?= $(IMAGE_TAG_BASE):$(VERSION) +OPERATOR_IMG ?= $(OPERATOR_NAME)-operator:$(VERSION) # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.30 @@ -148,6 +151,31 @@ clean: ## Remove build artifacts and downloaded tools run: apikey manifests generate fmt vet ## Run a controller from your host. GOOS=${GOOS} GOARCH=${GOARCH} hack/build.sh run + +##@ Conatiner-related tasks +.PHONY: buildah-manifest +buildah-manifest: ## creates the buildah manifest for multi-arch images + # The rm is needed due to bug https://www.github.com/containers/podman/issues/19757 + buildah manifest rm "${REGISTRY}/${OPERATOR_IMG}" || /bin/true + buildah manifest create "${REGISTRY}/${OPERATOR_IMG}" + +.PHONY: podman-build-amd64 +podman-build-amd64: buildah-manifest ## build the container in amd64 + @echo "Building the operator amd64" + buildah build --secret id=apikey,src=$(APIKEYFILE) --platform linux/amd64 --format docker -f Dockerfile -t "${OPERATOR_IMG}-amd64" + buildah manifest add --arch=amd64 "${REGISTRY}/${OPERATOR_IMG}" "${REGISTRY}/${OPERATOR_IMG}-amd64" + +.PHONY: podman-build-arm64 +podman-build-arm64: buildah-manifest ## build the container in arm64 + @echo "Building the operator arm64" + buildah build --secret id=apikey,src=$(APIKEYFILE) --platform linux/arm64 --build-arg GOARCH="arm64" --format docker -f Dockerfile -t "${OPERATOR_IMG}-arm64" + buildah manifest add --arch=arm64 "${REGISTRY}/${OPERATOR_IMG}" "${REGISTRY}/${OPERATOR_IMG}-arm64" + +.PHONY: buildah-push +buildah-push: ## Uploads the container to quay.io/validatedpatterns/${CONTAINER} + @echo "Uploading the ${REGISTRY}/${OPERATOR_IMG} container to ${UPLOADREGISTRY}/${OPERATOR_IMG}" + buildah manifest push --all "${REGISTRY}/${OPERATOR_IMG}" "docker://${UPLOADREGISTRY}/${OPERATOR_IMG}" + .PHONY: docker-build docker-build: apikey ## Build docker image with the manager. docker build --secret id=apikey,src=$(APIKEYFILE) --platform $(CONTAINER_OS)/$(CONTAINER_PLATFORM) -t ${IMG} . diff --git a/hack/build.sh b/hack/build.sh index 80f0a97c2..fa95c0f3a 100755 --- a/hack/build.sh +++ b/hack/build.sh @@ -1,11 +1,6 @@ #!/bin/bash set -ex -# GOOS and GOARCH will be set if calling from make. Dockerfile calls this script -# directly without calling make so the default values need to be set here also. -[[ -z "$GOOS" ]] && GOOS=linux -[[ -z "$GOARCH" ]] && GOARCH=amd64 - GIT_VERSION=$(git describe --always --tags || true) VERSION=${CI_UPSTREAM_VERSION:-${GIT_VERSION}} GIT_COMMIT=$(git rev-list -1 HEAD || true) From 03d77b8d2a6682e1159fa076f1d549e68ab05c38 Mon Sep 17 00:00:00 2001 From: Akos Eros Date: Fri, 17 Oct 2025 14:31:45 +0200 Subject: [PATCH 2/4] chore: remove deprecated github workflows --- .github/workflows/docker-build.yml | 33 -------------- .github/workflows/push-built-tag.yml | 68 ---------------------------- 2 files changed, 101 deletions(-) delete mode 100644 .github/workflows/docker-build.yml delete mode 100644 .github/workflows/push-built-tag.yml diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml deleted file mode 100644 index 31876a551..000000000 --- a/.github/workflows/docker-build.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "Docker Build" -on: [push, pull_request] - -permissions: - contents: read - -jobs: - docker-build: - name: Docker Build - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v5 - with: - persist-credentials: false - - - name: Docker Build - env: - KEY: ${{ secrets.API_KEY }} - shell: bash - run: | - set -e - export APIFILE="internal/controller/apikey.txt" - trap "rm -f ${APIFILE}" SIGINT EXIT - - if [ -z "${KEY}" ]; then - echo "Key is empty" - echo '' > "${APIFILE}" - else - echo "Key is set" - echo "${KEY}" > "${APIFILE}" - fi - make USE_IMAGE_DIGESTS=true docker-build diff --git a/.github/workflows/push-built-tag.yml b/.github/workflows/push-built-tag.yml deleted file mode 100644 index abca30b6c..000000000 --- a/.github/workflows/push-built-tag.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: vp-patterns/update-quay-image -on: - push: - tags: - - '[0-9]+.[0-9]+.[0-9]+' - -permissions: - contents: read - -jobs: - build_and_push_image: - name: Build and push pattern-operator image to quay.io - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v5 - with: - persist-credentials: false - - # We use an env due to https://docs.zizmor.sh/audits/#remediation_18 - - name: Check that tag version corresponds to metadata version - run: |- - VERSION=$(yq -r '.spec.version' bundle/manifests/patterns-operator.clusterserviceversion.yaml) - TAG="${ TAG_ENV }" - if [ "${VERSION}" != "${TAG}" ]; then - echo "Version in metadata ${VERSION} whereas tag is different: ${TAG}" - exit 1 - fi - env: - TAG_ENV: ${{ github.ref_name }} - - - name: Check that both QUAY_ROBOT_USER and QUAY_ROBOT_TOKEN exist - run: |- - failed=0 - if [ -z "${{ secrets.QUAY_ROBOT_USER }}" ]; then - echo "QUAY_ROBOT_USER secret missing" - failed=1 - fi - if [ -z "${{ secrets.QUAY_ROBOT_TOKEN }}" ]; then - echo "QUAY_ROBOT_TOKEN secret missing" - failed=1 - fi - if [ ${failed} -eq 1 ]; then - echo "Erroring out due to missing secrets" - exit 1 - fi - - - name: Build container and push it to quay.io - env: - KEY: ${{ secrets.API_KEY }} - run: |- - set -e - export VERSION=$(yq -r '.spec.version' bundle/manifests/patterns-operator.clusterserviceversion.yaml) - export REPO="quay.io/hybridcloudpatterns/patterns-operator" - export APIFILE="internal/controller/apikey.txt" - trap "rm -f ${APIFILE}" SIGINT EXIT - - if [ -z "${KEY}" ]; then - echo "Key is empty" - echo '' > "${APIFILE}" - else - echo "Key is set" - echo "${KEY}" > "${APIFILE}" - fi - make USE_IMAGE_DIGESTS=true docker-build - echo "${{ secrets.QUAY_ROBOT_TOKEN }}" | docker login -u="${{ secrets.QUAY_ROBOT_USER }}" --password-stdin quay.io - docker images - docker push "${REPO}:${VERSION}" From 0ce70a71a67d0d0077d4f56a936b18edba81722a Mon Sep 17 00:00:00 2001 From: Akos Eros Date: Tue, 21 Oct 2025 14:44:53 +0200 Subject: [PATCH 3/4] chore: change deprecated make targets to buildah targets --- Makefile | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 5f9f847b8..f997c8ae3 100644 --- a/Makefile +++ b/Makefile @@ -154,7 +154,7 @@ run: apikey manifests generate fmt vet ## Run a controller from your host. ##@ Conatiner-related tasks .PHONY: buildah-manifest -buildah-manifest: ## creates the buildah manifest for multi-arch images +buildah-manifest: apikey ## creates the buildah manifest for multi-arch images # The rm is needed due to bug https://www.github.com/containers/podman/issues/19757 buildah manifest rm "${REGISTRY}/${OPERATOR_IMG}" || /bin/true buildah manifest create "${REGISTRY}/${OPERATOR_IMG}" @@ -172,22 +172,21 @@ podman-build-arm64: buildah-manifest ## build the container in arm64 buildah manifest add --arch=arm64 "${REGISTRY}/${OPERATOR_IMG}" "${REGISTRY}/${OPERATOR_IMG}-arm64" .PHONY: buildah-push -buildah-push: ## Uploads the container to quay.io/validatedpatterns/${CONTAINER} +buildah-push: ## Uploads the container to quay.io/validatedpatterns/${OPERATOR_IMG} @echo "Uploading the ${REGISTRY}/${OPERATOR_IMG} container to ${UPLOADREGISTRY}/${OPERATOR_IMG}" buildah manifest push --all "${REGISTRY}/${OPERATOR_IMG}" "docker://${UPLOADREGISTRY}/${OPERATOR_IMG}" -.PHONY: docker-build -docker-build: apikey ## Build docker image with the manager. - docker build --secret id=apikey,src=$(APIKEYFILE) --platform $(CONTAINER_OS)/$(CONTAINER_PLATFORM) -t ${IMG} . - -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - docker push ${IMG} - .PHONY: golangci-lint golangci-lint: apikey ## Run golangci-lint locally podman run --pull=newer --rm -v $(PWD):/app:rw,z -w /app golangci/golangci-lint:v$(GOLANGCI_VERSION) golangci-lint run -v +##@ Legacy docker tasks +.PHONY: docker-build +docker-build: apikey podman-build-amd64 ## Build docker image with the manager. + +.PHONY: docker-push +docker-push: buildah-push ## Push docker image with the manager. + ##@ Deployment ifndef ignore-not-found From 30dcdb4777c30ac88c4135a51abae3ebfd44bf5a Mon Sep 17 00:00:00 2001 From: Akos Eros Date: Tue, 21 Oct 2025 14:59:58 +0200 Subject: [PATCH 4/4] chore: add arm support in csv (bundle) --- .../bases/patterns-operator.clusterserviceversion.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/manifests/bases/patterns-operator.clusterserviceversion.yaml b/config/manifests/bases/patterns-operator.clusterserviceversion.yaml index 8c41c37be..f006d0198 100644 --- a/config/manifests/bases/patterns-operator.clusterserviceversion.yaml +++ b/config/manifests/bases/patterns-operator.clusterserviceversion.yaml @@ -20,6 +20,9 @@ metadata: operators.openshift.io/must-gather-image: quay.io/validatedpatterns/must-gather:latest repository: https://github.com/validatedpatterns/patterns-operator support: validatedpatterns@googlegroups.com + labels: + operatorframework.io/arch.amd64: supported + operatorframework.io/arch.arm64: supported name: patterns-operator.v0.0.0 namespace: placeholder spec: