diff --git a/.github/actions/build-okd/action.yaml b/.github/actions/build-okd/action.yaml new file mode 100644 index 00000000..74feb6c7 --- /dev/null +++ b/.github/actions/build-okd/action.yaml @@ -0,0 +1,114 @@ +name: build-okd-images +description: Reusable action to build OKD images + +inputs: + ushift-branch: + description: MicroShift branch from https://github.com/openshift/microshift/branches + required: true + type: string + okd-version-tag: + description: OKD version tag from https://quay.io/repository/okd/scos-release?tab=tags + required: true + type: string + bootc-image-url: + description: Base Bootc image URL used in `make image` command + required: false + default: quay.io/centos-bootc/centos-bootc + type: string + bootc-image-tag: + description: Base Bootc image tag used in `make image` command + required: false + default: stream9 + type: string + target-arch: + description: Target architecture for the OKD images + required: true + type: string + target-registry: + description: Target registry for the OKD images + required: true + type: string + token: + description: Token for the GitHub Container Registry + required: true + type: string + +runs: + using: "composite" + steps: + - name: Detect the CPU architecture + id: detect-cpu-arch + uses: ./.github/actions/arch + + - name: Collect debug information before the build + if: always() + uses: ./.github/actions/debug-info + + - name: Prepare the build and run environment + uses: ./.github/actions/prebuild + + - name: Login to GitHub Container Registry + uses: ./.github/actions/podman-login + with: + token: ${{ inputs.token }} + + - name: Build OKD images + shell: bash + run: | + set -euo pipefail + + cd ${GITHUB_WORKSPACE}/ + TARGET_REGISTRY="${{ inputs.target-registry }}" ./src/okd/build_images.sh \ + "${{ inputs.okd-version-tag }}" \ + "${{ inputs.ushift-branch }}" \ + "${{ inputs.target-arch }}" + + - name: Build MicroShift RPMs + shell: bash + run: | + # See https://github.com/microshift-io/microshift/blob/main/docs/build.md + # for more information about the build process. + + # Run the RPM build process. + cd ${GITHUB_WORKSPACE}/ + make rpm \ + USHIFT_BRANCH="${{ inputs.ushift-branch }}" \ + OKD_VERSION_TAG="${{ inputs.okd-version-tag }}" \ + OKD_RELEASE_IMAGE="${{ inputs.target-registry }}/okd-release-${{ steps.detect-cpu-arch.outputs.go_arch }}" \ + RPM_OUTDIR=/mnt/rpms + + - name: Build MicroShift bootc container image + shell: bash + run: | + # See https://github.com/microshift-io/microshift/blob/main/docs/build.md + # for more information about the build process. + + # Run the container image build process. + cd ${GITHUB_WORKSPACE}/ + + make image \ + BOOTC_IMAGE_URL="${{ inputs.bootc-image-url }}" \ + BOOTC_IMAGE_TAG="${{ inputs.bootc-image-tag }}" \ + + - name: Run a test to verify that MicroShift is functioning properly + shell: bash + run: | + # See https://github.com/microshift-io/microshift/blob/main/docs/run.md + # for more information about the run process. + + # Run the MicroShift container + make run + + # Start-stop test with readiness check + make run-ready + make run-healthy + make stop + + # Uncomment this to enable tmate-debug on failure + # - name: Pause and open tmate debug session + # if: failure() + # uses: ./.github/actions/tmate-debug + + - name: Collect debug information after the build + if: always() + uses: ./.github/actions/debug-info diff --git a/.github/actions/build/action.yaml b/.github/actions/build/action.yaml index 70a697f7..db45a396 100644 --- a/.github/actions/build/action.yaml +++ b/.github/actions/build/action.yaml @@ -67,8 +67,8 @@ runs: # Run the RPM build process. cd ${GITHUB_WORKSPACE}/ make rpm \ - USHIFT_BRANCH=${{ inputs.ushift-branch }} \ - OKD_VERSION_TAG=${{ inputs.okd-version-tag }} \ + USHIFT_BRANCH="${{ inputs.ushift-branch }}" \ + OKD_VERSION_TAG="${{ inputs.okd-version-tag }}" \ RPM_OUTDIR=/mnt/rpms - name: Build MicroShift bootc container image @@ -87,8 +87,8 @@ runs: [ "${{ inputs.ovnk-networking }}" = "1" ] && make_opts+=("WITH_KINDNET=0") make image \ - BOOTC_IMAGE_URL=${{ inputs.bootc-image-url }} \ - BOOTC_IMAGE_TAG=${{ inputs.bootc-image-tag }} \ + BOOTC_IMAGE_URL="${{ inputs.bootc-image-url }}" \ + BOOTC_IMAGE_TAG="${{ inputs.bootc-image-tag }}" \ ${make_opts[@]} - name: Run a test to verify that MicroShift is functioning properly diff --git a/.github/actions/podman-login/action.yaml b/.github/actions/podman-login/action.yaml new file mode 100644 index 00000000..946f71bb --- /dev/null +++ b/.github/actions/podman-login/action.yaml @@ -0,0 +1,29 @@ +name: podman-login +description: Reusable action to login to GitHub Container Registry + +inputs: + token: + description: Token for the registry + required: true + type: string + +runs: + using: "composite" + steps: + - name: Login to GitHub Container Registry + uses: redhat-actions/podman-login@v1 + with: + registry: ghcr.io/${{ github.repository_owner }} + username: ${{ github.actor }} + password: ${{ inputs.token }} + auth_file_path: /tmp/ghcr-auth.json + + - name: Run podman login command + shell: bash + run: | + set -euo pipefail + + sudo chmod 644 /tmp/ghcr-auth.json + # Login both for the current and elevated users + podman login --authfile /tmp/ghcr-auth.json ghcr.io/${{ github.repository_owner }} + sudo podman login --authfile /tmp/ghcr-auth.json ghcr.io/${{ github.repository_owner }} diff --git a/.github/actions/prebuild/action.yaml b/.github/actions/prebuild/action.yaml index 70feeadb..21432501 100644 --- a/.github/actions/prebuild/action.yaml +++ b/.github/actions/prebuild/action.yaml @@ -23,6 +23,15 @@ runs: sudo apt-get update -y -q sudo apt-get install -y -q make lvm2 podman jq curl + # Install the latest OpenShift client from the OpenShift Mirror + ocp_client="openshift-client-linux-amd64-rhel9.tar.gz" + if [ "$(uname -m)" == "aarch64" ] ; then + ocp_client="openshift-client-linux-arm64-rhel9.tar.gz" + fi + curl -L https://mirror.openshift.com/pub/openshift-v4/clients/ocp/latest/${ocp_client} -o /tmp/${ocp_client} + sudo tar xvf /tmp/${ocp_client} -C /usr/bin + rm -f /tmp/${ocp_client} + # Redirect the container build directories to /mnt/ to avoid running out of disk space. sudo mv /var/tmp /var/tmp.orig sudo mv /var/lib/containers /mnt/containers diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 10cae9d7..72444ac3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -9,9 +9,13 @@ on: description: MicroShift branch from https://github.com/openshift/microshift/branches type: string okd-version-tag: - default: "4.19.0-okd-scos.19" + default: "4.21.0-okd-scos.ec.5" description: OKD version tag from https://quay.io/repository/okd/scos-release?tab=tags type: string + okd-target-registry: + default: ghcr.io/microshift-io/okd + description: Target registry for the OKD release images for ARM + type: string build: type: choice description: Types of artifacts to build @@ -20,9 +24,33 @@ on: - all - rpms - bootc-image + - okd-release-arm jobs: - build: + build-okd-release: + name: Build OKD release images for ARM + runs-on: ubuntu-24.04-arm + if: contains(fromJSON('["all", "okd-release-arm"]'), inputs.build) + steps: + - name: Check out MicroShift upstream repository + uses: actions/checkout@v4 + + - name: Detect OKD version tag + id: detect-okd-version + uses: ./.github/actions/okd-version + + - name: Run the OKD release images build action + uses: ./.github/actions/build-okd + with: + ushift-branch: ${{ inputs.ushift-branch }} + okd-version-tag: ${{ steps.detect-okd-version.outputs.okd-version-tag }} + target-arch: arm64 + target-registry: ${{ inputs.okd-target-registry }} + token: ${{ secrets.GITHUB_TOKEN }} + + build-microshift: + needs: build-okd-release + if: always() && contains(fromJSON('["all", "rpms", "bootc-image"]'), inputs.build) strategy: matrix: # The ARM runner is disabled because OKD images for ARM are not available. @@ -75,12 +103,9 @@ jobs: - name: Login to GitHub Container Registry if: contains(fromJSON('["all", "bootc-image"]'), inputs.build) - uses: redhat-actions/podman-login@v1 + uses: ./.github/actions/podman-login with: - registry: ghcr.io/${{ github.repository_owner }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - auth_file_path: /tmp/ghcr-auth.json + token: ${{ secrets.GITHUB_TOKEN }} - name: Publish Bootc image if: contains(fromJSON('["all", "bootc-image"]'), inputs.build) @@ -89,8 +114,8 @@ jobs: sudo podman tag microshift-okd \ ghcr.io/${{ github.repository }}:${{ inputs.ushift-branch }}-${{ inputs.okd-version-tag }} \ ghcr.io/${{ github.repository }}:latest - sudo podman push --authfile /tmp/ghcr-auth.json ghcr.io/${{ github.repository }}:${{ inputs.ushift-branch }}-${{ inputs.okd-version-tag }} - sudo podman push --authfile /tmp/ghcr-auth.json ghcr.io/${{ github.repository }}:latest + sudo podman push ghcr.io/${{ github.repository }}:${{ inputs.ushift-branch }}-${{ inputs.okd-version-tag }} + sudo podman push ghcr.io/${{ github.repository }}:latest # Prepare the release note for the bootc image usage TAG=${{ inputs.ushift-branch }}-${{ inputs.okd-version-tag }} envsubst < .github/workflows/release.md > /tmp/release.md diff --git a/Makefile b/Makefile index 34d3e197..f7f37b17 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,14 @@ ISOLATED_NETWORK ?= 0 # Internal variables SHELL := /bin/bash +ARCH := $(shell uname -m) +# Override the default OKD_RELEASE_IMAGE variable based on the architecture +ifeq ($(ARCH),aarch64) +OKD_RELEASE_IMAGE ?= ghcr.io/microshift-io/okd/okd-release-arm64 +else +OKD_RELEASE_IMAGE ?= quay.io/okd/scos-release +endif + BUILDER_IMAGE := microshift-okd-builder USHIFT_IMAGE := microshift-okd LVM_DISK := /var/lib/microshift-okd/lvmdisk.image @@ -56,6 +64,7 @@ rpm: --ulimit nofile=524288:524288 \ --build-arg USHIFT_BRANCH="${USHIFT_BRANCH}" \ --build-arg OKD_VERSION_TAG="${OKD_VERSION_TAG}" \ + --build-arg OKD_RELEASE_IMAGE="${OKD_RELEASE_IMAGE}" \ -f packaging/microshift-builder.Containerfile . @echo "Extracting the MicroShift RPMs" diff --git a/packaging/microshift-builder.Containerfile b/packaging/microshift-builder.Containerfile index 21a338b6..26345b10 100644 --- a/packaging/microshift-builder.Containerfile +++ b/packaging/microshift-builder.Containerfile @@ -2,10 +2,10 @@ FROM quay.io/centos-bootc/centos-bootc:stream9 # Variables controlling the source of MicroShift components to build ARG USHIFT_BRANCH=main +ARG OKD_RELEASE_IMAGE=quay.io/okd/scos-release ARG OKD_VERSION_TAG # Internal variables -ARG OKD_REPO=quay.io/okd/scos-release ARG USHIFT_GIT_URL=https://github.com/openshift/microshift.git ENV USER=microshift ENV HOME=/home/microshift @@ -14,9 +14,9 @@ ARG USHIFT_PREBUILD_SCRIPT=/tmp/prebuild.sh ARG USHIFT_POSTBUILD_SCRIPT=/tmp/postbuild.sh # Verify mandatory build arguments -RUN if [ -z "${OKD_VERSION_TAG}" ]; then \ - echo "ERROR: OKD_VERSION_TAG is not set"; \ - echo "See quay.io/okd/scos-release for a list of tags"; \ +RUN if [ -z "${OKD_VERSION_TAG}" ] ; then \ + echo "ERROR: OKD_VERSION_TAG is not set" ; \ + echo "See ${OKD_RELEASE_IMAGE} for a list of tags" ; \ exit 1; \ fi @@ -38,7 +38,7 @@ RUN git clone --branch "${USHIFT_BRANCH}" --single-branch "${USHIFT_GIT_URL}" "$ # Preparing the build scripts COPY --chmod=755 ./src/image/prebuild.sh ${USHIFT_PREBUILD_SCRIPT} -RUN "${USHIFT_PREBUILD_SCRIPT}" --replace "${OKD_REPO}" "${OKD_VERSION_TAG}" +RUN "${USHIFT_PREBUILD_SCRIPT}" --replace "${OKD_RELEASE_IMAGE}" "${OKD_VERSION_TAG}" # Building all MicroShift downstream RPMs and SRPMs # hadolint ignore=DL3059 @@ -50,7 +50,7 @@ COPY --chown=${USER}:${USER} ./src/kindnet/assets/ "${HOME}/microshift/assets/o COPY --chown=${USER}:${USER} ./src/kindnet/dropins/ "${HOME}/microshift/packaging/kindnet/" COPY --chown=${USER}:${USER} ./src/kindnet/crio.conf.d/ "${HOME}/microshift/packaging/crio.conf.d/" # Prepare and build Kindnet upstream RPM -RUN "${USHIFT_PREBUILD_SCRIPT}" --replace-kindnet "${OKD_REPO}" "${OKD_VERSION_TAG}" && \ +RUN "${USHIFT_PREBUILD_SCRIPT}" --replace-kindnet "${OKD_RELEASE_IMAGE}" "${OKD_VERSION_TAG}" && \ MICROSHIFT_VARIANT="community" make -C "${HOME}/microshift" rpm # Building TopoLVM upstream RPM diff --git a/src/image/prebuild.sh b/src/image/prebuild.sh index 6aa25a55..63f1ff0e 100755 --- a/src/image/prebuild.sh +++ b/src/image/prebuild.sh @@ -2,14 +2,33 @@ set -euo pipefail MICROSHIFT_ROOT="/home/microshift/microshift" +UNAME_M="$(uname -m)" declare -A UNAME_TO_GOARCH_MAP=( ["x86_64"]="amd64" ["aarch64"]="arm64" ) -verify() { +oc_release_info() { local -r okd_url=$1 local -r okd_releaseTag=$2 + local -r image=${3:-} - if ! stdout=$(oc adm release info "${okd_url}:${okd_releaseTag}" 2>&1) ; then - echo -e "error verifying okd release (URL: ${okd_url} , TAG: ${okd_releaseTag}) \nERROR: ${stdout}" + if [ -z "${image}" ] ; then + oc adm release info "${okd_url}:${okd_releaseTag}" + return + fi + + local -r okd_image="$(oc adm release info --image-for="${image}" "${okd_url}:${okd_releaseTag}")" + if [ -z "${okd_image}" ] ; then + echo "ERROR: No OKD image found for '${image}'" + exit 1 + fi + echo "${okd_image}" +} + +verify_okd_release() { + local -r okd_url=$1 + local -r okd_releaseTag=$2 + + if ! oc_release_info "${okd_url}" "${okd_releaseTag}" >/dev/null ; then + echo "ERROR: No OKD release found at '${okd_url}:${okd_releaseTag}'" exit 1 fi } @@ -17,89 +36,84 @@ verify() { replace_base_assets() { local -r okd_url=$1 local -r okd_releaseTag=$2 - local -r arch=$(uname -m) - local -r temp_json=$(mktemp "/tmp/release-${arch}.XXXXX.json") - - # replace Microshift images with upstream (from OKD release) - for op in $(jq -e -r '.images | keys []' "${MICROSHIFT_ROOT}/assets/release/release-${arch}.json") - do - local image - image=$(oc adm release info --image-for="${op}" "${okd_url}:${okd_releaseTag}" || true) - if [ -n "${image}" ] ; then - echo "${op} ${image}" - jq --arg a "${op}" --arg b "${image}" '.images[$a] = $b' "${MICROSHIFT_ROOT}/assets/release/release-${arch}.json" >"${temp_json}" - mv "${temp_json}" "${MICROSHIFT_ROOT}/assets/release/release-${arch}.json" + local -r temp_json="$(mktemp "/tmp/release-${UNAME_M}.XXXXX.json")" + + # Replace MicroShift images with OKD upstream images + for cur_image in $(jq -e -r '.images | keys []' "${MICROSHIFT_ROOT}/assets/release/release-${UNAME_M}.json") ; do + # LVMS operator is not part of the OKD release + if [ "${cur_image}" = "lvms_operator" ] ; then + echo "Skipping '${cur_image}'" + continue fi + + local new_image + new_image=$(oc_release_info "${okd_url}" "${okd_releaseTag}" "${cur_image}") + + echo "Replacing '${cur_image}' with '${new_image}'" + jq --arg a "${cur_image}" --arg b "${new_image}" '.images[$a] = $b' "${MICROSHIFT_ROOT}/assets/release/release-${UNAME_M}.json" >"${temp_json}" + mv "${temp_json}" "${MICROSHIFT_ROOT}/assets/release/release-${UNAME_M}.json" done - pod_image=$(oc adm release info --image-for=pod "${okd_url}:${okd_releaseTag}" || true) - # update the infra pods for crio - sed -i 's,pause_image .*,pause_image = '"\"${pod_image}\""',' "${MICROSHIFT_ROOT}/packaging/crio.conf.d/10-microshift_${UNAME_TO_GOARCH_MAP[${arch}]}.conf" + # Update the infra pods for crio + local -r pod_image=$(oc_release_info "${okd_url}" "${okd_releaseTag}" "pod") + sed -i 's,pause_image .*,pause_image = '"\"${pod_image}\""',' "${MICROSHIFT_ROOT}/packaging/crio.conf.d/10-microshift_${UNAME_TO_GOARCH_MAP[${UNAME_M}]}.conf" } +# This code is extracted from openshift/microshift/scripts/auto-rebase/rebase.sh +# and modified to work with OKD release replace_olm_assets() { local -r okd_url=$1 local -r okd_releaseTag=$2 - local -r arch=$(uname -m) - local -r temp_json=$(mktemp "/tmp/release-olm-${arch}.XXXXX.json") + local -r temp_json=$(mktemp "/tmp/release-olm-${UNAME_M}.XXXXX.json") # Install the yq tool "${MICROSHIFT_ROOT}"/scripts/fetch_tools.sh yq - # Replace olm images with upstream (from OKD release) - # This is extracted from openshift/microshift/scripts/auto-rebase/rebase.sh and modified to work with OKD release + # Replace OLM images with OKD upstream images local olm_image_refs_file="${MICROSHIFT_ROOT}/assets/optional/operator-lifecycle-manager/image-references" - local kustomization_arch_file="${MICROSHIFT_ROOT}/assets/optional/operator-lifecycle-manager/kustomization.${arch}.yaml" - local olm_release_json="${MICROSHIFT_ROOT}/assets/optional/operator-lifecycle-manager/release-olm-${arch}.json" + local kustomization_arch_file="${MICROSHIFT_ROOT}/assets/optional/operator-lifecycle-manager/kustomization.${UNAME_M}.yaml" + local olm_release_json="${MICROSHIFT_ROOT}/assets/optional/operator-lifecycle-manager/release-olm-${UNAME_M}.json" - # Create the OLM release-${arch}.json file with base structure + # Create the OLM release json file with base structure jq -n '{"release": {"base": "upstream"}, "images": {}}' > "${olm_release_json}" - # Create extra kustomization for each arch in separate file + # Create extra kustomization file for each architecture cat < "${kustomization_arch_file}" - images: EOF # Read from the image-references file to find the images we need to update local -r containers=$("${MICROSHIFT_ROOT}"/_output/bin/yq -r '.spec.tags[].name' "${olm_image_refs_file}") - for container in "${containers[@]}" ; do + # shellcheck disable=SC2068 + for container in ${containers[@]} ; do # Get image (registry.com/image) without the tag or digest from image-references local orig_image_name orig_image_name=$("${MICROSHIFT_ROOT}"/_output/bin/yq -r ".spec.tags[] | select(.name == \"${container}\") | .from.name" "${olm_image_refs_file}" | awk -F '[@:]' '{ print $1; }') # Get the new image from OKD release local new_image - new_image=$(oc adm release info --image-for="${container}" "${okd_url}:${okd_releaseTag}" || true) - - if [ -n "${new_image}" ] ; then - echo "${container} ${new_image}" - local new_image_name="${new_image%@*}" - local new_image_digest="${new_image#*@}" + new_image=$(oc_release_info "${okd_url}" "${okd_releaseTag}" "${container}") + echo "Replacing '${container}' with '${new_image}'" + local new_image_name="${new_image%@*}" + local new_image_digest="${new_image#*@}" - # Update kustomization file with image mapping - cat <> "${kustomization_arch_file}" + # Update kustomization file with image mapping + cat <> "${kustomization_arch_file}" - name: ${orig_image_name} newName: ${new_image_name} digest: ${new_image_digest} EOF - - # Update JSON file - jq --arg container "${container}" --arg img "${new_image}" '.images[$container] = $img' "${olm_release_json}" >"${temp_json}" - mv "${temp_json}" "${olm_release_json}" - fi + # Update JSON file + jq --arg container "${container}" --arg img "${new_image}" '.images[$container] = $img' "${olm_release_json}" >"${temp_json}" + mv "${temp_json}" "${olm_release_json}" done # Add patches section for environment variables # Get specific images for the patches - local olm_image - olm_image=$(oc adm release info --image-for="operator-lifecycle-manager" "${okd_url}:${okd_releaseTag}" || true) - local registry_image - registry_image=$(oc adm release info --image-for="operator-registry" "${okd_url}:${okd_releaseTag}" || true) - - if [ -n "${olm_image}" ] && [ -n "${registry_image}" ] ; then - cat << EOF >> "${kustomization_arch_file}" + local -r olm_image=$(oc_release_info "${okd_url}" "${okd_releaseTag}" "operator-lifecycle-manager") + local -r registry_image=$(oc_release_info "${okd_url}" "${okd_releaseTag}" "operator-registry") + cat >> "${kustomization_arch_file}" <, # where the image name and digest (hash) are combined in a single string. # However, in the kustomization.${arch}.yaml file, we need the image name (newName) and @@ -143,10 +155,10 @@ replace_kindnet_assets() { # Update the image and hash "${MICROSHIFT_ROOT}"/_output/bin/yq eval \ ".images[] |= select(.name == \"kube-proxy\") |= (.newName = \"${image_name}\" | .digest = \"${image_hash}\")" \ - -i "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/kustomization.${arch}.yaml" + -i "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/kustomization.${UNAME_M}.yaml" jq --arg img "$image_with_hash" '.images["kube-proxy"] = $img' \ - "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/release-kube-proxy-${arch}.json" >"${temp_json}" - mv "${temp_json}" "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/release-kube-proxy-${arch}.json" + "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/release-kube-proxy-${UNAME_M}.json" >"${temp_json}" + mv "${temp_json}" "${MICROSHIFT_ROOT}/assets/optional/kube-proxy/release-kube-proxy-${UNAME_M}.json" } fix_rpm_spec() { @@ -171,17 +183,17 @@ fi case "$1" in --replace) - verify "$2" "$3" + verify_okd_release "$2" "$3" replace_base_assets "$2" "$3" replace_olm_assets "$2" "$3" fix_rpm_spec ;; --replace-kindnet) - verify "$2" "$3" + verify_okd_release "$2" "$3" replace_kindnet_assets "$2" "$3" ;; --verify) - verify "$2" "$3" + verify_okd_release "$2" "$3" ;; *) usage diff --git a/src/okd/build_images.sh b/src/okd/build_images.sh new file mode 100755 index 00000000..2e432ae7 --- /dev/null +++ b/src/okd/build_images.sh @@ -0,0 +1,371 @@ +#!/bin/bash +set -euo pipefail + +export LC_ALL=C.UTF-8 +export LANG=C.UTF-8 + +TARGET_REGISTRY=${TARGET_REGISTRY:-ghcr.io/microshift-io/okd} +PULL_SECRET=${PULL_SECRET:-~/.pull-secret.json} + +WORKDIR=$(mktemp -d /tmp/okd-build-images-XXXXXX) +trap 'cd ; rm -rf "${WORKDIR}"' EXIT + +usage() { + echo "Usage: $(basename "$0") " + echo " okd-version: The version of OKD to build (see https://amd64.origin.releases.ci.openshift.org/)" + echo " ocp-branch: The branch of OCP to build (e.g. release-4.19)" + echo " target-arch: The architecture of the target images (amd64 or arm64)" + exit 1 +} + +check_prereqs() { + for tool in git oc skopeo podman ; do + if ! which "${tool}" >/dev/null ; then + echo "ERROR: Cannot find '${tool}' in the PATH" + exit 1 + fi + done +} + +check_podman_login() { + if ! podman login --get-login "${TARGET_REGISTRY}" &>/dev/null ; then + echo "ERROR: Login to the registry using 'podman login ${TARGET_REGISTRY}' and try again" + exit 1 + fi +} + +check_release_image_exists() { + # Check if the release image exists, hardcoding the architecture to amd64 as + # the source release image is only available for the amd64 architecture + if skopeo inspect \ + --override-os="linux" \ + --override-arch="amd64" \ + --format "Digest: {{.Digest}}" "docker://${OKD_RELEASE_IMAGE}" &>/dev/null ; then + echo "The '${OKD_RELEASE_IMAGE}' release image already exists. Exiting..." + exit 0 + fi +} + +git_clone_repo() { + local -r repo_url="$1" + local branch="$2" + local -r repo_dir="$3" + + if [ "${branch}" == "main" ] || [ "${branch}" == "master" ] ; then + branch="$(git ls-remote --symref "${repo_url}" HEAD 2>/dev/null \ + | grep 'ref: refs/heads/' \ + | awk '{print $2}' \ + | sed 's#refs/heads/##')" + fi + + git clone --branch "${branch}" --single-branch "${repo_url}" "${repo_dir}" + cd "${repo_dir}" || { echo "Failed to access repository directory"; return 1; } +} + +# Function to handle base-image repository +base_image() { + local -r repo_url="https://github.com/openshift/images" + local -r dockerfile_path="base/Dockerfile.rhel9" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|^FROM registry.ci.openshift.org/ocp/.*|FROM quay.io/centos/centos:stream9|' "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[base]}" -f "${dockerfile_path}" . +} + +# Function to handle router-image repository +router_image() { + local -r repo_url="https://github.com/openshift/router" + local -r dockerfile_base_path="images/router/base/Dockerfile.ocp" + local -r dockerfile_haproxy_path="images/router/haproxy/Dockerfile.ocp" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "$dockerfile_base_path" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_base_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[haproxy-router-base]}" -f "${dockerfile_base_path}" . + + # TODO: Implement a proper way to handle the haproxy28 package for the amd64 architecture + if [ "${TARGET_ARCH}" != "arm64" ] ; then + return + fi + + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*|FROM ${images[haproxy-router-base]}|" "${dockerfile_haproxy_path}" + sed -i "s|haproxy28|https://github.com/praveenkumar/minp/releases/download/v0.0.1/haproxy28-2.8.10-1.rhaos4.17.el9.aarch64.rpm|" "${dockerfile_haproxy_path}" + # shellcheck disable=SC2016 + sed -i 's|yum install -y $INSTALL_PKGS|yum --disablerepo=rt install -y $INSTALL_PKGS|' "${dockerfile_haproxy_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[haproxy-router]}" -f "${dockerfile_haproxy_path}" . +} + +# Function to handle kube-proxy repository +kube_proxy_image() { + local -r repo_url="https://github.com/openshift/sdn" + local -r dockerfile_path="images/kube-proxy/Dockerfile.rhel" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + # This is a special case because the 4.17 image is not available in the registry + # for the ARM64 platform, so we use the 4.19 image instead. + sed -i 's|^FROM registry.ci.openshift.org/ocp/builder.*|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang-1.22-openshift-4.19 AS builder|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + # shellcheck disable=SC2016 + sed -i 's|yum install -y --setopt=tsflags=nodocs $INSTALL_PKGS|yum --disablerepo=rt install -y --setopt=tsflags=nodocs $INSTALL_PKGS|' "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[kube-proxy]}" -f "${dockerfile_path}" . +} + +# Function to handle coredns-image repository +coredns_image() { + local -r repo_url="https://github.com/openshift/coredns" + local -r dockerfile_path="Dockerfile.ocp" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[coredns]}" -f "${dockerfile_path}" . +} + +# Function to handle csi-external-snapshotter-image repository +csi_external_snapshotter_image() { + local -r repo_url="https://github.com/openshift/csi-external-snapshotter" + local -r dockerfile_snapshot_controller_path="Dockerfile.snapshot-controller.openshift.rhel7" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_snapshot_controller_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_snapshot_controller_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[csi-snapshot-controller]}" -f "${dockerfile_snapshot_controller_path}" . +} + +# Function to handle kube-rbac-proxy-image repository +kube_rbac_proxy_image() { + local -r repo_url="https://github.com/openshift/kube-rbac-proxy" + local -r dockerfile_path="Dockerfile.ocp" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[kube-rbac-proxy]}" -f "${dockerfile_path}" . +} + +# Function to handle pod-image repository +pod_image() { + local -r repo_url="https://github.com/openshift/kubernetes" + local -r dockerfile_path="build/pause/Dockerfile.Rhel" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + + pushd build/pause &>/dev/null + podman build --platform "linux/${TARGET_ARCH}" -t "${images[pod]}" -f "$(basename "${dockerfile_path}")" . + popd &>/dev/null +} + +# Function to handle cli-image repository +cli_image() { + local -r repo_url="https://github.com/openshift/oc" + local -r dockerfile_path="images/cli/Dockerfile.rhel" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[cli]}" -f "${dockerfile_path}" . +} + +# Function to handle service-ca-operator-image repository +service_ca_operator_image() { + local -r repo_url="https://github.com/openshift/service-ca-operator" + local -r dockerfile_path="Dockerfile.rhel7" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[service-ca-operator]}" -f "${dockerfile_path}" . +} + +# Function to handle operator-lifecycle-manager-image repository +operator_lifecycle_manager_image() { + local -r repo_url="https://github.com/openshift/operator-framework-olm" + local -r dockerfile_base_path="operator-lifecycle-manager.Dockerfile" + local -r dockerfile_reg_path="operator-registry.Dockerfile" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_base_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_base_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[operator-lifecycle-manager]}" -f "${dockerfile_base_path}" . + + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_reg_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_reg_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[operator-registry]}" -f "${dockerfile_reg_path}" . +} + +# Function to handle ovn-kubernetes-image repository +ovn_kubernetes_microshift_image() { + local -r repo_url="https://github.com/openshift/ovn-kubernetes" + local -r dockerfile_base_path="Dockerfile.base" + local -r dockerfile_microshift_path="Dockerfile.microshift" + local -r repo="${WORKDIR}/$(basename "${repo_url}")" + + git_clone_repo "${repo_url}" "${OCP_BRANCH}" "${repo}" + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_base_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:base-rhel9|FROM ${images[base]}|" "${dockerfile_base_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[ovn-kubernetes-base]}" -f "${dockerfile_base_path}" . + + sed -i 's|FROM registry.ci.openshift.org/ocp/builder:rhel-9-golang|FROM registry.ci.openshift.org/openshift/release:rhel-9-release-golang|' "${dockerfile_microshift_path}" + sed -i "s|^FROM registry.ci.openshift.org/ocp/.*:ovn-kubernetes-base|FROM ${images[ovn-kubernetes-base]}|" "${dockerfile_microshift_path}" + + podman build --platform "linux/${TARGET_ARCH}" -t "${images[ovn-kubernetes-microshift]}" -f "${dockerfile_microshift_path}" . +} + +# Run all the image creation procedures +create_images() { + base_image + router_image + kube_proxy_image + coredns_image + csi_external_snapshotter_image + kube_rbac_proxy_image + pod_image + cli_image + service_ca_operator_image + operator_lifecycle_manager_image + # ovn_kubernetes_microshift_image +} + +# Push the images and manifests to the registry +push_image_manifests() { + local digest + local manifest_name + local alt_image + + for key in "${!images[@]}" ; do + # TODO: Implement a proper way to handle the haproxy-router for the amd64 architecture + if [ "${TARGET_ARCH}" != "arm64" ] && [ "${key}" = "haproxy-router" ] ; then + echo "Skipping haproxy-router for ${TARGET_ARCH}" + continue + fi + + # Push the image to the registry and get its digest + podman push "${images[$key]}" + digest="$(skopeo inspect --format "{{.Name}}@{{.Digest}}" docker://"${images["${key}"]}")" + images_sha["${key}"]="${digest}" + + # Create a manifest for the image without the architecture suffix + manifest_name="${images[$key]//-${TARGET_ARCH}/}" + alt_image="${images[$key]//-${TARGET_ARCH}/-${ALT_ARCH}}" + + # Create a manifest and add the target architecture image + podman manifest create --amend "${manifest_name}" + podman manifest add "${manifest_name}" "${images_sha[$key]}" + + # Add the alternate architecture image to the manifest if it exists + if skopeo inspect --raw docker://"${alt_image}" &>/dev/null ; then + digest="$(skopeo inspect --format "{{.Name}}@{{.Digest}}" docker://"${alt_image}")" + podman manifest add "${manifest_name}" "${digest}" + fi + podman manifest push "${manifest_name}" + done +} + +# Create a new release of OKD using oc +create_new_okd_release() { + # TODO: Implement a proper way to handle the haproxy-router for the amd64 architecture + local haproxy_router_image + if [ "${TARGET_ARCH}" != "arm64" ] ; then + haproxy_router_image="" + else + haproxy_router_image="haproxy-router=${images_sha[haproxy-router]}" + fi + + # shellcheck disable=SC2086 + oc adm release new --from-release "quay.io/okd/scos-release:${OKD_VERSION}" \ + --keep-manifest-list \ + "cli=${images_sha[cli]}" \ + ${haproxy_router_image} \ + "kube-proxy=${images_sha[kube-proxy]}" \ + "coredns=${images_sha[coredns]}" \ + "csi-snapshot-controller=${images_sha[csi-snapshot-controller]}" \ + "kube-rbac-proxy=${images_sha[kube-rbac-proxy]}" \ + "pod=${images_sha[pod]}" \ + "service-ca-operator=${images_sha[service-ca-operator]}" \ + "operator-lifecycle-manager=${images_sha[operator-lifecycle-manager]}" \ + "operator-registry=${images_sha[operator-registry]}" \ + --to-image "${OKD_RELEASE_IMAGE}" + + # "ovn-kubernetes-base=${images_sha[ovn-kubernetes-base]}" \ + # "ovn-kubernetes-microshift=${images_sha[ovn-kubernetes-microshift]}" \ +} + +# +# Main +# +if [[ $# -ne 3 ]]; then + usage +fi + +OKD_VERSION="$1" +OCP_BRANCH="$2" +TARGET_ARCH="$3" +OKD_RELEASE_IMAGE="${TARGET_REGISTRY}/okd-release-${TARGET_ARCH}:${OKD_VERSION}" + +# Determine the alternate architecture +case "${TARGET_ARCH}" in + "amd64") + ALT_ARCH="arm64" + ;; + "arm64") + ALT_ARCH="amd64" + ;; + *) + echo "ERROR: Invalid target architecture: ${TARGET_ARCH}" + exit 1 + ;; +esac + +# Populate associative arrays with image names and tags +declare -A images +declare -A images_sha +images=( + [base]="${TARGET_REGISTRY}/scos-${OKD_VERSION}:base-stream9-${TARGET_ARCH}" + [cli]="${TARGET_REGISTRY}/cli:${OKD_VERSION}-${TARGET_ARCH}" + [haproxy-router-base]="${TARGET_REGISTRY}/haproxy-router-base:${OKD_VERSION}-${TARGET_ARCH}" + [haproxy-router]="${TARGET_REGISTRY}/haproxy-router:${OKD_VERSION}-${TARGET_ARCH}" + [kube-proxy]="${TARGET_REGISTRY}/kube-proxy:${OKD_VERSION}-${TARGET_ARCH}" + [coredns]="${TARGET_REGISTRY}/coredns:${OKD_VERSION}-${TARGET_ARCH}" + [csi-snapshot-controller]="${TARGET_REGISTRY}/csi-snapshot-controller:${OKD_VERSION}-${TARGET_ARCH}" + [kube-rbac-proxy]="${TARGET_REGISTRY}/kube-rbac-proxy:${OKD_VERSION}-${TARGET_ARCH}" + [pod]="${TARGET_REGISTRY}/pod:${OKD_VERSION}-${TARGET_ARCH}" + [service-ca-operator]="${TARGET_REGISTRY}/service-ca-operator:${OKD_VERSION}-${TARGET_ARCH}" + [operator-lifecycle-manager]="${TARGET_REGISTRY}/operator-lifecycle-manager:${OKD_VERSION}-${TARGET_ARCH}" + [operator-registry]="${TARGET_REGISTRY}/operator-registry:${OKD_VERSION}-${TARGET_ARCH}" + # [ovn-kubernetes-base]="${TARGET_REGISTRY}/ovn-kubernetes-base:${OKD_VERSION}-${TARGET_ARCH}" + # [ovn-kubernetes-microshift]="${TARGET_REGISTRY}/ovn-kubernetes-microshift:${OKD_VERSION}-${TARGET_ARCH}" +) + +# Check the prerequisites +check_prereqs +check_podman_login +check_release_image_exists +# Create and push images +create_images +push_image_manifests +# Create a new OKD release +create_new_okd_release