Skip to content

Commit d426b5d

Browse files
Malewaresbernauer
andauthored
Multi-Architecture Operator pipeline (#378)
* Typos * Removing old action to install cargo-edit * Adding new preflight check * Yamlllint * Ignore DS_Store * Adding actionlint.yaml for buildjet custom runners * Cleaning up for consitency * Nits from sbernauer Co-authored-by: Sebastian Bernauer <[email protected]> --------- Co-authored-by: Sebastian Bernauer <[email protected]>
1 parent bd0e4dd commit d426b5d

File tree

4 files changed

+105
-20
lines changed

4 files changed

+105
-20
lines changed

template/.actionlint.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
self-hosted-runner:
3+
# BuildJet machines we are using
4+
labels:
5+
- buildjet-2vcpu-ubuntu-2204-arm
6+
- buildjet-4vcpu-ubuntu-2204-arm

template/.github/workflows/build.yml.j2

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ jobs:
220220
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
221221
with:
222222
submodules: recursive
223-
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # tag=v5.1.0
223+
- uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
224224
with:
225225
python-version: '3.12'
226226
- name: Install jinja2-cli
@@ -298,11 +298,14 @@ jobs:
298298
run: echo All tests have passed!
299299

300300
package_and_publish:
301-
name: Package Charts, Build Docker Image and publish them
301+
name: Package Charts, Build Docker Image and publish them - ${{ matrix.runner }}
302302
needs:
303303
- tests_passed
304304
- select_helm_repo
305-
runs-on: ubuntu-latest
305+
strategy:
306+
matrix:
307+
runner: ["ubuntu-latest", "buildjet-2vcpu-ubuntu-2204-arm"]
308+
runs-on: ${{ matrix.runner }}
306309
permissions:
307310
id-token: write
308311
env:
@@ -324,7 +327,7 @@ jobs:
324327
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
325328
with:
326329
submodules: recursive
327-
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # tag=v26
330+
- uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26
328331
- uses: dtolnay/rust-toolchain@d8352f6b1d2e870bc5716e7a6d9b65c4cc244a1a
329332
with:
330333
toolchain: ${{ env.RUST_TOOLCHAIN_VERSION }}
@@ -340,15 +343,25 @@ jobs:
340343
if: ${{ github.event_name == 'pull_request' }}
341344
run: cargo set-version --offline --workspace 0.0.0-pr${{ github.event.pull_request.number }}
342345

343-
# Recreate charts and publish charts and docker image. The "-e" is needed as we want to override the
344-
# default value in the makefile if called from this action, but not otherwise (i.e. when called locally).
345-
# This is needed for the HELM_REPO variable.
346+
# Recreate charts and publish charts and docker image. The "-e" is needed as we want to override the
347+
# default value in the makefile if called from this action, but not otherwise (i.e. when called locally).
348+
# This is needed for the HELM_REPO variable.
346349
- name: Install cosign
347-
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # tag=v3.5.0
350+
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
348351
- name: Install syft
349352
uses: anchore/sbom-action/download-syft@7ccf588e3cf3cc2611714c2eeae48550fbc17552 # v0.15.11
350353
- name: Build Docker image and Helm chart
351-
run: make -e build-ci
354+
run: |
355+
# Installing helm on BuildJet only
356+
if [ "$(arch)" = "aarch64" ]; then
357+
curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
358+
sudo apt-get -y install apt-transport-https --yes
359+
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
360+
sudo apt-get -y update
361+
sudo apt-get -y install helm
362+
fi
363+
364+
make -e build
352365
- name: Publish Docker image and Helm chart
353366
if: ${{ !github.event.pull_request.head.repo.fork }}
354367
run: make -e publish
@@ -358,6 +371,43 @@ jobs:
358371
if: ${{ !github.event.pull_request.head.repo.fork }}
359372
run: echo "IMAGE_TAG=$(make -e print-docker-tag)" >> $GITHUB_OUTPUT
360373

374+
create_manifest_list:
375+
name: Build and publish manifest list
376+
needs:
377+
- package_and_publish
378+
runs-on: ubuntu-latest
379+
permissions:
380+
id-token: write
381+
env:
382+
NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }}
383+
OCI_REGISTRY_SDP_PASSWORD: ${{ secrets.HARBOR_ROBOT_SDP_GITHUB_ACTION_BUILD_SECRET }}
384+
OCI_REGISTRY_SDP_USERNAME: "robot$sdp+github-action-build"
385+
OCI_REGISTRY_SDP_CHARTS_PASSWORD: ${{ secrets.HARBOR_ROBOT_SDP_CHARTS_GITHUB_ACTION_BUILD_SECRET }}
386+
OCI_REGISTRY_SDP_CHARTS_USERNAME: "robot$sdp-charts+github-action-build"
387+
steps:
388+
- name: Install cosign
389+
uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 # v3.5.0
390+
- name: Checkout
391+
uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
392+
with:
393+
submodules: recursive
394+
# This step checks if the current run was triggered by a push to a pr (or a pr being created).
395+
# If this is the case it changes the version of this project in all Cargo.toml files to include the suffix
396+
# "-pr<prnumber>" so that the published artifacts can be linked to this PR.
397+
- uses: stackabletech/cargo-install-action@main
398+
with:
399+
crate: cargo-edit
400+
bin: cargo-set-version
401+
- name: Update version if PR
402+
if: ${{ github.event_name == 'pull_request' }}
403+
run: cargo set-version --offline --workspace 0.0.0-pr${{ github.event.pull_request.number }}
404+
- name: Build manifest list
405+
run: |
406+
# Creating manifest list
407+
make -e docker-manifest-list-build
408+
# Pushing and signing manifest list
409+
make -e docker-manifest-list-publish
410+
361411
openshift_preflight:
362412
name: Run the OpenShift Preflight check on the published images
363413
if: ${{ !github.event.pull_request.head.repo.fork }}
@@ -376,4 +426,4 @@ jobs:
376426
ARCH_FOR_PREFLIGHT="$(arch | sed -e 's#x86_64#amd64#' | sed -e 's#aarch64#arm64#')"
377427
./preflight-linux-amd64 check container "$IMAGE_TAG" --platform "${ARCH_FOR_PREFLIGHT}" > preflight.out
378428
- name: "Passed?"
379-
run: '[ "$(./preflight-linux-amd64 check container "$IMAGE_TAG" --platform "${ARCH_FOR_PREFLIGHT}" | jq -r .passed)" == true ]'
429+
run: '[ "$(cat preflight.out | jq -r .passed)" == true ]'

template/.gitignore.j2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ tilt_options.json
1717
.direnv/
1818
.direnvrc
1919
.envrc
20+
21+
.DS_Store

template/Makefile.j2

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
TAG := $(shell git rev-parse --short HEAD)
1313
OPERATOR_NAME := {[ operator.name }]
1414
VERSION := $(shell cargo metadata --format-version 1 | jq -r '.packages[] | select(.name=="stackable-${OPERATOR_NAME}") | .version')
15+
ARCH := $(shell arch | sed -e 's#x86_64#amd64#' | sed -e 's#aarch64#arm64#')
1516

1617
DOCKER_REPO := docker.stackable.tech
1718
ORGANIZATION := stackable
@@ -30,17 +31,17 @@ render-readme:
3031

3132
## Docker related targets
3233
docker-build:
33-
docker build --force-rm --build-arg VERSION=${VERSION} -t "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}" -f docker/Dockerfile .
34-
docker tag "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}" "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}"
34+
docker build --force-rm --build-arg VERSION=${VERSION} -t "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-${ARCH}" -f docker/Dockerfile .
35+
docker tag "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-${ARCH}" "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-${ARCH}"
3536

3637
docker-publish:
3738
# Push to Nexus
3839
echo "${NEXUS_PASSWORD}" | docker login --username github --password-stdin "${DOCKER_REPO}"
3940
DOCKER_OUTPUT=$$(docker push --all-tags "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}");\
4041
# Obtain the digest of the pushed image from the output of `docker push`, because signing by tag is deprecated and will be removed from cosign in the future\
41-
REPO_DIGEST_OF_IMAGE=$$(echo "$$DOCKER_OUTPUT" | awk '/^${VERSION}: digest: sha256:[0-9a-f]{64} size: [0-9]+$$/ { print $$3 }');\
42+
REPO_DIGEST_OF_IMAGE=$$(echo "$$DOCKER_OUTPUT" | awk '/^${VERSION}-${ARCH}: digest: sha256:[0-9a-f]{64} size: [0-9]+$$/ { print $$3 }');\
4243
if [ -z "$$REPO_DIGEST_OF_IMAGE" ]; then\
43-
echo 'Could not find repo digest for container image: ${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}';\
44+
echo 'Could not find repo digest for container image: ${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-${ARCH}';\
4445
exit 1;\
4546
fi;\
4647
# This generates a signature and publishes it to the registry, next to the image\
@@ -51,8 +52,8 @@ docker-publish:
5152
# Determine the PURL for the container image\
5253
PURL="pkg:docker/${ORGANIZATION}/${OPERATOR_NAME}@$$REPO_DIGEST_OF_IMAGE?repository_url=${DOCKER_REPO}";\
5354
# Get metadata from the image\
54-
IMAGE_DESCRIPTION=$$(docker inspect --format='{{.Config.Labels.description}}' "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}");\
55-
IMAGE_NAME=$$(docker inspect --format='{{.Config.Labels.name}}' "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}");\
55+
IMAGE_DESCRIPTION=$$(docker inspect --format='{{.Config.Labels.description}}' "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-${ARCH}");\
56+
IMAGE_NAME=$$(docker inspect --format='{{.Config.Labels.name}}' "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-${ARCH}");\
5657
# Merge the SBOM with the metadata for the operator\
5758
jq -s '{"metadata":{"component":{"description":"'"$$IMAGE_NAME. $$IMAGE_DESCRIPTION"'","supplier":{"name":"Stackable GmbH","url":["https://stackable.tech/"]},"author":"Stackable GmbH","purl":"'"$$PURL"'","publisher":"Stackable GmbH"}}} * .[0]' sbom.json > sbom.merged.json;\
5859
# Attest the SBOM to the image\
@@ -63,9 +64,9 @@ docker-publish:
6364
docker login --username '${value OCI_REGISTRY_SDP_USERNAME}' --password '${OCI_REGISTRY_SDP_PASSWORD}' '${OCI_REGISTRY_HOSTNAME}'
6465
DOCKER_OUTPUT=$$(docker push --all-tags '${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}');\
6566
# Obtain the digest of the pushed image from the output of `docker push`, because signing by tag is deprecated and will be removed from cosign in the future\
66-
REPO_DIGEST_OF_IMAGE=$$(echo "$$DOCKER_OUTPUT" | awk '/^${VERSION}: digest: sha256:[0-9a-f]{64} size: [0-9]+$$/ { print $$3 }');\
67+
REPO_DIGEST_OF_IMAGE=$$(echo "$$DOCKER_OUTPUT" | awk '/^${VERSION}-${ARCH}: digest: sha256:[0-9a-f]{64} size: [0-9]+$$/ { print $$3 }');\
6768
if [ -z "$$REPO_DIGEST_OF_IMAGE" ]; then\
68-
echo 'Could not find repo digest for container image: ${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}';\
69+
echo 'Could not find repo digest for container image: ${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-${ARCH}';\
6970
exit 1;\
7071
fi;\
7172
# This generates a signature and publishes it to the registry, next to the image\
@@ -76,13 +77,39 @@ docker-publish:
7677
# Determine the PURL for the container image\
7778
PURL="pkg:docker/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}@$$REPO_DIGEST_OF_IMAGE?repository_url=${OCI_REGISTRY_HOSTNAME}";\
7879
# Get metadata from the image\
79-
IMAGE_DESCRIPTION=$$(docker inspect --format='{{.Config.Labels.description}}' "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}");\
80-
IMAGE_NAME=$$(docker inspect --format='{{.Config.Labels.name}}' "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}");\
80+
IMAGE_DESCRIPTION=$$(docker inspect --format='{{.Config.Labels.description}}' "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-${ARCH}");\
81+
IMAGE_NAME=$$(docker inspect --format='{{.Config.Labels.name}}' "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-${ARCH}");\
8182
# Merge the SBOM with the metadata for the operator\
8283
jq -s '{"metadata":{"component":{"description":"'"$$IMAGE_NAME. $$IMAGE_DESCRIPTION"'","supplier":{"name":"Stackable GmbH","url":["https://stackable.tech/"]},"author":"Stackable GmbH","purl":"'"$$PURL"'","publisher":"Stackable GmbH"}}} * .[0]' sbom.json > sbom.merged.json;\
8384
# Attest the SBOM to the image\
8485
cosign attest -y --predicate sbom.merged.json --type cyclonedx "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}@$$REPO_DIGEST_OF_IMAGE"
8586

87+
# This assumes "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-amd64 and "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-arm64 are build and pushed
88+
docker-manifest-list-build:
89+
docker manifest create "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}" --amend "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-amd64" --amend "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}-arm64"
90+
docker manifest create "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}" --amend "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-amd64" --amend "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}-arm64"
91+
92+
docker-manifest-list-publish:
93+
# Push to Nexus
94+
echo "${NEXUS_PASSWORD}" | docker login --username github --password-stdin "${DOCKER_REPO}"
95+
# `docker manifest push` directly returns the digest of the manifest list
96+
# As it is an experimental feature, this might change in the future
97+
# Further reading: https://docs.docker.com/reference/cli/docker/manifest/push/
98+
DIGEST_NEXUS=$$(docker manifest push "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}");\
99+
# Refer to image via its digest (oci.stackable.tech/sdp/airflow@sha256:0a1b2c...)\
100+
# This generates a signature and publishes it to the registry, next to the image\
101+
# Uses the keyless signing flow with Github Actions as identity provider\
102+
cosign sign -y "${DOCKER_REPO}/${ORGANIZATION}/${OPERATOR_NAME}:${VERSION}@$$DIGEST_NEXUS"
103+
104+
# Push to Harbor
105+
# We need to use "value" here to prevent the variable from being recursively expanded by make (username contains a dollar sign, since it's a Harbor bot)
106+
docker login --username '${value OCI_REGISTRY_SDP_USERNAME}' --password '${OCI_REGISTRY_SDP_PASSWORD}' '${OCI_REGISTRY_HOSTNAME}'
107+
DIGEST_HARBOR=$$(docker manifest push "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}");\
108+
# Refer to image via its digest (oci.stackable.tech/sdp/airflow@sha256:0a1b2c...);\
109+
# This generates a signature and publishes it to the registry, next to the image\
110+
# Uses the keyless signing flow with Github Actions as identity provider\
111+
cosign sign -y "${OCI_REGISTRY_HOSTNAME}/${OCI_REGISTRY_PROJECT_IMAGES}/${OPERATOR_NAME}:${VERSION}@$$DIGEST_HARBOR"
112+
86113
# TODO remove if not used/needed
87114
docker: docker-build docker-publish
88115

0 commit comments

Comments
 (0)