diff --git a/kubernetes/tekton/create_manifest.sh b/kubernetes/tekton/create_manifest.sh index 340934a3a..4ad20b992 100755 --- a/kubernetes/tekton/create_manifest.sh +++ b/kubernetes/tekton/create_manifest.sh @@ -604,6 +604,20 @@ cp "${OPERATOR_FILE}" "${OPERATOR_YAML}" # shellcheck disable=SC2016 perl -0pi -e 's/^([ \t]*)- name: IMAGE_ADDONS_OC\n([ \t]*)value: image-registry\.openshift-image-registry\.svc:5000\/openshift\/cli:latest/${1}# - name: IMAGE_ADDONS_OC\n${2}# value: image-registry.openshift-image-registry.svc:5000\/openshift\/cli:latest/m' "${OPERATOR_YAML}" +# Local policy: pin the console-plugin image to our locally built release +# (the upstream release-v1.24.x tag is corrupted by a webpack caching bug; +# see PRs for the console-plugin repo). Pin by digest so future upstream +# tag re-publishes cannot silently break the console again. Idempotent: +# removes any existing IMAGE_PIPELINES_CONSOLE_PLUGIN entries then appends. +IMAGE_VALUE="registry.redhat.io/openshift-pipelines/pipelines-console-plugin-rhel9@sha256:b087e18744c18135e4a0fc2f1c1c6e3114db02419c9933d632e67b455301cccb" +# 1) Remove any existing IMAGE_PIPELINES_CONSOLE_PLUGIN entries (idempotent) +perl -0pi -e 's/((- [ \t]*name: IMAGE_PIPELINES_CONSOLE_PLUGIN\n[ \t]*value: .*?\n))//mg' "${OPERATOR_YAML}" +# 2) Insert the pinned image after IMAGE_ADDONS_PARAM_MAVEN_IMAGE value +# (pass image via ENV to avoid perl interpreting @ as an array ref) +export IMAGE_PIPELINES_CONSOLE_PLUGIN="${IMAGE_VALUE}" +perl -0pi -e 's~(IMAGE_ADDONS_PARAM_MAVEN_IMAGE\n([ \t]+value: .*?\n))~$1 - name: IMAGE_PIPELINES_CONSOLE_PLUGIN\n value: $ENV{IMAGE_PIPELINES_CONSOLE_PLUGIN}\n~m' "${OPERATOR_YAML}" +unset IMAGE_PIPELINES_CONSOLE_PLUGIN + # Local policy: restore the rationale comment above the rbac-wildcards exemption # on the tekton-operator ClusterRole. yq sets the annotation value but cannot # emit the explanatory comment, so it is re-inserted here (idempotent: it only diff --git a/kubernetes/tekton/overlays/operator/operator.yaml b/kubernetes/tekton/overlays/operator/operator.yaml index aa581f9a0..f8cd2f9b2 100644 --- a/kubernetes/tekton/overlays/operator/operator.yaml +++ b/kubernetes/tekton/overlays/operator/operator.yaml @@ -1028,6 +1028,9 @@ spec: value: registry.redhat.io/source-to-image/source-to-image-rhel9@sha256:718e59d9aacacbaa16be3ae247bdb8582dc4fd9cbe0c5314dfdfc8f22e4f3578 - name: IMAGE_ADDONS_PARAM_MAVEN_IMAGE value: registry.redhat.io/ubi9/openjdk-17@sha256:bda04343db9f920886f81a214d539673b954ab1fe71b6917d8e7b5107231ef6a + - name: IMAGE_PIPELINES_CONSOLE_PLUGIN + value: registry.redhat.io/openshift-pipelines/pipelines-console-plugin-rhel9@sha256:b087e18744c18135e4a0fc2f1c1c6e3114db02419c9933d632e67b455301cccb + # - name: IMAGE_ADDONS_OC # value: image-registry.openshift-image-registry.svc:5000/openshift/cli:latest image: ghcr.io/tektoncd/operator/operator-1d69a75f22dd094880847eac907fb2c1:v0.81.0@sha256:8817ebccf9a39e9f500a800a35efe0f51dd2d8fb4764a83cebd75af2403b23ff diff --git a/tekton/tasks/buildah/0.7.1/README.md b/tekton/tasks/buildah/0.8.0/README.md similarity index 58% rename from tekton/tasks/buildah/0.7.1/README.md rename to tekton/tasks/buildah/0.8.0/README.md index 4d0b786c5..c440cb93b 100644 --- a/tekton/tasks/buildah/0.7.1/README.md +++ b/tekton/tasks/buildah/0.8.0/README.md @@ -14,20 +14,19 @@ to assemble a container image, then pushes that image to a container registry. ## Install the Task ```bash -kubectl apply -f https://tekton-hub-api-openshift-pipelines.apps.okd.homelab.arthurvardevanyan.com/v1/resource/homelab/task/buildah/0.7.1/raw +kubectl apply -f https://tekton-hub-api-openshift-pipelines.apps.okd.homelab.arthurvardevanyan.com/v1/resource/homelab/task/buildah/0.8.0/raw ``` ## Parameters - **IMAGE**: The name (reference) of the image to build. -- **BUILDER_IMAGE:**: The name of the image containing the Buildah tool. See - note below. (_default:_ quay.io/buildah/stable:v1.23.3) +- **BUILDER_IMAGE:**: The name of the image containing the Buildah tool. (_default:_ quay.io/buildah/stable:v1.23.3) - **DOCKERFILE**: The path to the `Dockerfile` to execute (_default:_ `./Dockerfile`) - **CONTEXT**: Path to the directory to use as context (_default:_ `.`) -- **TLSVERIFY**: Verify the TLS on the registry endpoint (for push/pull to a - non-TLS registry) (_default:_ `true`) +- **TLSVERIFY**: Verify the TLS on the registry endpoint for push/pull to a + non-TLS registry (_default:_ `true`) - **FORMAT**: The format of the built container, oci or docker (_default:_ `oci`) - **BUILD_EXTRA_ARGS**: Extra parameters passed for the build command when @@ -35,6 +34,17 @@ kubectl apply -f https://tekton-hub-api-openshift-pipelines.apps.okd.homelab.art - **PUSH_EXTRA_ARGS**: Extra parameters passed for the push command when pushing images. (_default:_ `""`) - **SKIP_PUSH**: Skip pushing the built image (_default:_ `false`) +- **IMAGE_REPOSITORIES**: Comma-separated list of image repositories to push + the manifest to (e.g. `ghcr.io/ArthurVardevanyan/k8s-gitops-ci`). When + set, the task builds multi-arch (per `PLATFORMS`), assembles a manifest + list, and pushes the complete manifest to each repository. When empty, + falls back to single-arch build-and-push (backward-compatible). +- **PLATFORMS**: Comma-separated list of platforms to build for (e.g. + `linux/amd64,linux/arm64`). Ignored when `IMAGE_REPOSITORIES` is empty. +- **IMAGE_TAG_SUFFIX**: Suffix appended to each per-arch intermediate image + name to avoid collisions during multi-arch builds (e.g. `${ARCH}`). + Ignored in single-arch mode. Defaults to empty (buildah auto-generates + unique tags). ## Workspaces @@ -45,7 +55,45 @@ kubectl apply -f https://tekton-hub-api-openshift-pipelines.apps.okd.homelab.art ## Platforms -The Task can be run on `linux/amd64`, `linux/s390x`, `linux/arm64` and `linux/ppc64le` platforms. +The Task can be run on `linux/amd64`, `linux/s390x`, `linux/arm64` and +`linux/ppc64le` platforms. It supports multi-arch builds when +`IMAGE_REPOSITORIES` is set — it builds each platform separately, assembles +a manifest list with `buildah manifest`, and pushes the complete manifest +(all of the per-arch images + the manifest list) to each `IMAGE_REPOSITORY` +in a single `buildah manifest push` operation. + +## Multi-Arch Usage + +When `IMAGE_REPOSITORIES` is set, the task builds a multi-arch manifest +list: + +```yaml +apiVersion: tekton.dev/v1 +kind: TaskRun +metadata: + name: buildah-build-multiarch +spec: + taskRef: + name: buildah + params: + - name: IMAGE + value: ghcr.io/ArthurVardevanyan/k8s-gitops-ci:latest + - name: IMAGE_REPOSITORIES + value: "ghcr.io/ArthurVardevanyan/k8s-gitops-ci" + - name: PLATFORMS + value: "linux/amd64,linux/arm64" + - name: IMAGE_TAG_SUFFIX + value: "-${ARCH}" + workspaces: + - name: source + persistentVolumeClaim: + claimName: my-source +``` + +This builds both `linux/amd64` and `linux/arm64` images, creates a manifest +list tagged `ghcr.io/ArthurVardevanyan/k8s-gitops-ci:latest`, and pushes the +complete multi-arch manifest to the registry. The intermediate per-arch +images are cleaned up after the push. ## Usage diff --git a/tekton/tasks/buildah/0.8.0/buildah.yaml b/tekton/tasks/buildah/0.8.0/buildah.yaml new file mode 100644 index 000000000..7bc201c7a --- /dev/null +++ b/tekton/tasks/buildah/0.8.0/buildah.yaml @@ -0,0 +1,287 @@ +--- +apiVersion: tekton.dev/v1 +kind: Task +metadata: + name: buildah + namespace: homelab + labels: + app.kubernetes.io/version: "0.8.0" + annotations: + argocd.argoproj.io/sync-wave: "2" + argocd.argoproj.io/sync-options: SkipDryRunOnMissingResource=true + tekton.dev/categories: Image Build + tekton.dev/pipelines.minVersion: "0.17.0" + tekton.dev/tags: image-build + tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le,linux/arm64" + tekton.dev/displayName: buildah +spec: + description: >- + Buildah task builds source into a container image and + then pushes it to a container registry. + + Buildah Task builds source into a container image using Project Atomic's + Buildah build tool. It uses Buildah's support for building from + Dockerfiles, using its buildah bud command. This command executes the + directives in the Dockerfile to assemble a container image, then pushes + that image to a container registry. + + Supports multi-arch image builds via manifest lists when IMAGE_REPOS is + set — builds each platform separately, assembles a manifest list with + `buildah manifest`, and pushes the complete manifest (all arch images + + the manifest list) to each IMAGE_REPOSITORY in one `buildah manifest push`. + + params: + - name: IMAGE + description: Reference of the image buildah will produce. + - name: BUILDER_IMAGE + description: The location of the buildah builder image. + default: quay.io/buildah/stable:v1.43.1-immutable@sha256:fc649e1899c4e09a8e19d6fbdbda178d2008fa4186d5a280211f8ffa59e77e32 + - name: STORAGE_DRIVER + description: "Buildah storage driver (default: overlay with fuse-overlayfs as mount_program for improved performance)" + default: overlay + - name: DOCKERFILE + description: Path to the Dockerfile to build. + default: ./Dockerfile + - name: CONTEXT + description: Path to the directory to use as context. + default: . + - name: TLSVERIFY + description: Verify the TLS on the registry endpoint (for push/pull to a non-TLS registry) + default: "true" + - name: FORMAT + description: The format of the built container, oci or docker + default: "oci" + - name: BUILD_EXTRA_ARGS + description: Extra parameters passed for the build command when building images. + default: "" + - name: PUSH_EXTRA_ARGS + description: Extra parameters passed for the push command when pushing images. + type: string + default: "" + - name: SKIP_PUSH + description: Skip pushing the built image + default: "false" + - name: IMAGE_REPOSITORIES + description: "Comma-separated list of image repositories to push the manifest to. When set, builds multi-arch (PLATFORMS) and pushes a manifest list; when empty, falls back to single-arch build-and-push (backward-compatible)." + default: "" + - name: PLATFORMS + description: "Comma-separated list of platforms to build for (e.g. linux/amd64,linux/arm64). Ignored when IMAGE_REPOSITORIES is empty (single-arch fallback)." + default: "linux/amd64,linux/arm64" + - name: IMAGE_TAG_SUFFIX + description: "Suffix appended to each per-arch intermediate image name to avoid collisions during multi-arch builds (e.g. ${ARCH}). Ignored in single-arch mode." + default: "" + workspaces: + - name: source + - name: sslcertdir + optional: true + - name: dockerconfig + description: >- + An optional workspace that allows providing a .docker/config.json file + for Buildah to access the container registry. + The file should be placed at the root of the Workspace with name config.json. + optional: true + results: + - name: IMAGE_DIGEST + description: Digest of the image just built. + - name: IMAGE_URL + description: Image repository where the built image would be pushed to + steps: + - name: build-and-push + computeResources: + requests: + memory: 500Mi + ephemeral-storage: 5Gi + cpu: 1000m + limits: + cpu: "4000m" + ephemeral-storage: 75Gi + memory: 5Gi + image: $(params.BUILDER_IMAGE) + workingDir: $(workspaces.source.path) + script: | + mkdir -p /home/build/.local/share/containers + mkdir -p /home/build/rundir/libpod + rm -rf $(workspaces.source.path)/lost+found + + [ "$(workspaces.sslcertdir.bound)" = "true" ] && CERT_DIR_FLAG="--cert-dir=$(workspaces.sslcertdir.path)" + [ "$(workspaces.dockerconfig.bound)" = "true" ] && DOCKER_CONFIG="$(workspaces.dockerconfig.path)" && export DOCKER_CONFIG + + # Resolve the IMAGE param into a shell variable so string operations + # like ${IMAGE%:*} work correctly. + IMAGE="$(params.IMAGE)" + + # Detect if multi-arch mode is enabled (IMAGE_REPOSITORIES is non-empty) + MULTI_ARCH=false + if [ -n "$(params.IMAGE_REPOSITORIES)" ]; then + MULTI_ARCH=true + fi + + if [ "${MULTI_ARCH}" = "true" ]; then + # ── Multi-arch mode ────────────────────────────────────────────── + # 1. Parse platforms and images into parallel arrays + IFS=',' read -ra PLATFORMS <<< "$(params.PLATFORMS)" + IMAGES=() + for i in "${!PLATFORMS[@]}"; do + PLATFORM="${PLATFORMS[$i]}" + ARCH="${PLATFORM##*/}" + # Extract image name (without tag), use :ARCH as the tag + # No placeholder trickery — ARCH is already available in this loop + IMAGE_NAME="${IMAGE%:*}" + # IMAGE has a tag (e.g. "registry.example.com/repo/image:0.1.0") + # -> IMAGE_NAME = "registry.example.com/repo/image" + # If IMAGE has no tag, IMAGE_NAME == IMAGE which is fine + IMAGES+=("${IMAGE_NAME}:${ARCH}") + done + + # 2. Build each architecture + for i in "${!PLATFORMS[@]}"; do + PLATFORM="${PLATFORMS[$i]}" + IMAGE="${IMAGES[$i]}" + ARCH="${PLATFORM##*/}" + echo "Building ${IMAGE} for ${PLATFORM}..." + buildah ${CERT_DIR_FLAG} "--storage-driver=$(params.STORAGE_DRIVER)" bud \ + --arch="${ARCH}" $(params.BUILD_EXTRA_ARGS) \ + "--format=$(params.FORMAT)" "--tls-verify=$(params.TLSVERIFY)" \ + -f "$(params.DOCKERFILE)" -t "${IMAGE}" "$(params.CONTEXT)" + done + + # 3. Create and populate manifest list + MANIFEST_IMAGE="${IMAGES[0]%%:*}" + MANIFEST_TAG="${IMAGES[0]##*:}" + echo "Creating manifest ${MANIFEST_IMAGE}:${MANIFEST_TAG}..." + buildah manifest create "${MANIFEST_IMAGE}:${MANIFEST_TAG}" + for i in "${!PLATFORMS[@]}"; do + PLATFORM="${PLATFORMS[$i]}" + IMAGE="${IMAGES[$i]}" + ARCH="${PLATFORM##*/}" + echo "Adding ${IMAGE} (${ARCH}) to manifest..." + buildah manifest add "${MANIFEST_IMAGE}:${MANIFEST_TAG}" "docker://${IMAGE}" + done + + # 4. Push manifest to all repositories + RETRIES=5 + IFS=',' read -ra REPOS <<< "$(params.IMAGE_REPOSITORIES)" + for repo in "${REPOS[@]}"; do + echo "Pushing manifest to ${repo}..." + for i in $(seq 1 $RETRIES); do + if buildah ${CERT_DIR_FLAG} "--storage-driver=$(params.STORAGE_DRIVER)" push \ + $(params.PUSH_EXTRA_ARGS) "--tls-verify=$(params.TLSVERIFY)" \ + --digestfile /tmp/image-digest \ + "manifests:${MANIFEST_IMAGE}:${MANIFEST_TAG}" "docker://${repo}"; then + break + fi + [ "$i" -eq "$RETRIES" ] && echo "Push failed after $RETRIES attempts" >&2 && exit 1 + echo "Push attempt $i/$RETRIES failed, retrying..." >&2 + sleep 5 + done + tee "$(results.IMAGE_DIGEST.path)" < /tmp/image-digest + printf '%s' "${repo}:${MANIFEST_TAG}" | tee "$(results.IMAGE_URL.path)" + done + + # 5. Cleanup intermediate images + for i in "${!PLATFORMS[@]}"; do + IMAGE="${IMAGES[$i]}" + buildah rm "${IMAGE}" 2>/dev/null || true + done + + else + # ── Single-arch mode (backward-compatible) ─────────────────────── + # shellcheck disable=SC2046,SC2086 + buildah ${CERT_DIR_FLAG} "--storage-driver=$(params.STORAGE_DRIVER)" bud $(params.BUILD_EXTRA_ARGS) \ + "--format=$(params.FORMAT)" "--tls-verify=$(params.TLSVERIFY)" \ + -f "$(params.DOCKERFILE)" -t "$(params.IMAGE)" "$(params.CONTEXT)" + [ "$(params.SKIP_PUSH)" = "true" ] && echo "Push skipped" && exit 0 + # push the image with retries (registry 500s are transient) + RETRIES=5 + for i in $(seq 1 $RETRIES); do + if buildah ${CERT_DIR_FLAG} "--storage-driver=$(params.STORAGE_DRIVER)" push $(params.PUSH_EXTRA_ARGS) \ + "--tls-verify=$(params.TLSVERIFY)" --digestfile /tmp/image-digest "$(params.IMAGE)" \ + "docker://$(params.IMAGE)"; then + break + fi + [ "$i" -eq "$RETRIES" ] && echo "Push failed after $RETRIES attempts" >&2 && exit 1 + echo "Push attempt $i/$RETRIES failed, retrying..." >&2 + sleep 5 + done + tee "$(results.IMAGE_DIGEST.path)" < /tmp/image-digest + printf '%s' "$(params.IMAGE)" | tee "$(results.IMAGE_URL.path)" + fi + volumeMounts: + - name: varlibcontainers + mountPath: /var/lib/containers + - name: tmp + mountPath: /tmp + - name: var-tmp + mountPath: /var/tmp + - name: home + mountPath: /home + - name: root + mountPath: /root + - name: run + mountPath: /var/run + - name: cache + mountPath: /var/cache + - name: networks # TODO Circle Back to This. + mountPath: /etc/containers/networks + securityContext: + # runAsNonRoot: true + runAsUser: 0 #1000 # 65532 + privileged: false + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + # seccompProfile: + # type: RuntimeDefault + capabilities: + add: + - SETFCAP + drop: + - MKNOD + - KILL + # - SETUID + # - SETGID + # - ALL + volumes: + - name: varlibcontainers + emptyDir: + sizeLimit: 75Gi + # ephemeral: + # volumeClaimTemplate: + # metadata: + # creationTimestamp: null + # spec: + # accessModes: ["ReadWriteOnce"] + # storageClassName: "rook-ceph-block-ci" + # resources: + # requests: + # storage: 30Gi + - name: home + emptyDir: + sizeLimit: 1Mi + - name: tmp + emptyDir: + sizeLimit: 2Mi + - name: var-tmp + emptyDir: + sizeLimit: 30Gi + # ephemeral: + # volumeClaimTemplate: + # metadata: + # creationTimestamp: null + # spec: + # accessModes: ["ReadWriteOnce"] + # storageClassName: "rook-ceph-block-ci" + # resources: + # requests: + # storage: 30Gi + - name: root + emptyDir: + sizeLimit: 1Mi + - name: run + emptyDir: + sizeLimit: 1Mi + - name: cache + emptyDir: + sizeLimit: 1Mi + - name: networks # TODO Circle Back to This. + emptyDir: + sizeLimit: 1Mi