diff --git a/.pipelines/containerSourceData/Dockerfile-Initial b/.pipelines/containerSourceData/Dockerfile-Initial index cc6adfa9780..3dc9b7e3b9a 100644 --- a/.pipelines/containerSourceData/Dockerfile-Initial +++ b/.pipelines/containerSourceData/Dockerfile-Initial @@ -7,7 +7,7 @@ RUN --mount=type=bind,source=./Stage/,target=/dockerStage/ \\\ tdnf install -y createrepo; \\\ cp -r ${RPMS_PATH} ${LOCAL_REPO_PATH}; \\\ cat /dockerStage/marinerLocalRepo.repo >> /etc/yum.repos.d/local.repo; \\\ - createrepo --database ${LOCAL_REPO_PATH} --workers 10; tdnf makecache \&\& tdnf makecache; \\\ + createrepo --database ${LOCAL_REPO_PATH} --workers 10; tdnf makecache; \\\ tdnf autoremove -y createrepo; \\\ for rpm in "${RPMS_TO_INSTALL[@]}"; do \\\ echo "RPM: $rpm"; \\\ diff --git a/.pipelines/containerSourceData/azurecli/Dockerfile-AzureCLI b/.pipelines/containerSourceData/azurecli/Dockerfile-AzureCLI deleted file mode 100644 index 48754ed2a5c..00000000000 --- a/.pipelines/containerSourceData/azurecli/Dockerfile-AzureCLI +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -ARG BASE_IMAGE - -FROM $BASE_IMAGE - -@INCLUDE_MAIN_RUN_INSTRUCTION@ - -# basic smoke test -RUN az version - -# set default command for the container -CMD ["bash"] diff --git a/.pipelines/containerSourceData/azurecli/azurecli.pkg b/.pipelines/containerSourceData/azurecli/azurecli.pkg deleted file mode 100644 index 1a90f07a0ca..00000000000 --- a/.pipelines/containerSourceData/azurecli/azurecli.pkg +++ /dev/null @@ -1 +0,0 @@ -azure-cli diff --git a/.pipelines/containerSourceData/base/Dockerfile-Base-Nonroot-Template b/.pipelines/containerSourceData/base/Dockerfile-Base-Nonroot-Template index 1a1987af456..7b46c295cad 100644 --- a/.pipelines/containerSourceData/base/Dockerfile-Base-Nonroot-Template +++ b/.pipelines/containerSourceData/base/Dockerfile-Base-Nonroot-Template @@ -5,14 +5,14 @@ ARG BASE_IMAGE FROM $BASE_IMAGE AS BASE -ARG MARINER_VERSION=2.0 +ARG AZL_VERSION=2.0 ARG USERNAME=nonroot ARG USER_UID=65532 ARG USER_GID=$USER_UID ARG SET_USER=$USERNAME RUN mkdir -p /staging/etc \ - && tdnf install -y --releasever=$MARINER_VERSION shadow-utils \ + && tdnf install -y --releasever=$AZL_VERSION shadow-utils \ && groupadd --gid $USER_GID $USERNAME \ && useradd --gid $USER_GID -g $USERNAME $USERNAME -u $USER_UID \ && tdnf clean all \ diff --git a/.pipelines/containerSourceData/busybox/Dockerfile-Busybox-Template b/.pipelines/containerSourceData/base/Dockerfile-Busybox-Template similarity index 56% rename from .pipelines/containerSourceData/busybox/Dockerfile-Busybox-Template rename to .pipelines/containerSourceData/base/Dockerfile-Busybox-Template index 91a14429418..3103372b2e9 100644 --- a/.pipelines/containerSourceData/busybox/Dockerfile-Busybox-Template +++ b/.pipelines/containerSourceData/base/Dockerfile-Busybox-Template @@ -5,12 +5,32 @@ ARG BASE_IMAGE FROM $BASE_IMAGE AS BASE -ARG MARINER_VERSION=2.0 +ARG AZL_VERSION=2.0 + +ARG RPMS +ARG LOCAL_REPO_FILE="local.repo" +ARG LOCAL_REPO_PATH="/localrepo" + +COPY ${RPMS} /WORKDIR/RPMS +COPY ${LOCAL_REPO_FILE} /WORKDIR/REPO/local.repo + +# Create local repo if RPMS are provided +# This will allow the user to install packages from the local repo +# instead of fetching from PMC +RUN if [ "${RPMS}" ]; then \ + mkdir -p $LOCAL_REPO_PATH; \ + tdnf install -y --releasever=$AZL_VERSION createrepo; \ + cp -r /WORKDIR/RPMS ${LOCAL_REPO_PATH}; \ + cp /WORKDIR/REPO/local.repo /etc/yum.repos.d/local.repo; \ + createrepo --database ${LOCAL_REPO_PATH} --workers 10; \ + tdnf makecache; \ + tdnf autoremove -y createrepo; \ +fi # Install busybox, glibc, and their dependencies into a staging location. # Staging directory is copied into the final scratch image. RUN mkdir /staging \ - && tdnf install -y --releasever=$MARINER_VERSION --installroot /staging \ + && tdnf install -y --releasever=$AZL_VERSION --installroot /staging \ busybox glibc \ && tdnf clean all \ && pushd /staging \ @@ -37,5 +57,5 @@ FROM scratch # Copy dependencies into the scratch image. COPY --from=BASE /staging/ . - +COPY --from=BASE EULA-Container.txt / CMD [ "sh" ] diff --git a/.pipelines/containerSourceData/distroless/Dockerfile-Distroless-Nonroot-Template b/.pipelines/containerSourceData/base/Dockerfile-Distroless-Nonroot-Template similarity index 87% rename from .pipelines/containerSourceData/distroless/Dockerfile-Distroless-Nonroot-Template rename to .pipelines/containerSourceData/base/Dockerfile-Distroless-Nonroot-Template index 194ef5fd060..a4ddfdb1490 100644 --- a/.pipelines/containerSourceData/distroless/Dockerfile-Distroless-Nonroot-Template +++ b/.pipelines/containerSourceData/base/Dockerfile-Distroless-Nonroot-Template @@ -6,14 +6,14 @@ ARG FINAL_IMAGE FROM $BASE_IMAGE AS BASE -ARG MARINER_VERSION=2.0 +ARG AZL_VERSION=2.0 ARG USERNAME=nonroot ARG USER_UID=65532 ARG USER_GID=$USER_UID ARG SET_USER=$USERNAME RUN mkdir -p /staging/etc \ - && tdnf install -y --releasever=$MARINER_VERSION shadow-utils \ + && tdnf install -y --releasever=$AZL_VERSION shadow-utils \ && groupadd --gid $USER_GID $USERNAME \ && useradd --gid $USER_GID -g $USERNAME $USERNAME -u $USER_UID \ && tdnf clean all \ diff --git a/.pipelines/containerSourceData/distroless/Dockerfile-Distroless-Template b/.pipelines/containerSourceData/base/Dockerfile-Distroless-Template similarity index 100% rename from .pipelines/containerSourceData/distroless/Dockerfile-Distroless-Template rename to .pipelines/containerSourceData/base/Dockerfile-Distroless-Template diff --git a/.pipelines/containerSourceData/nodejs/distroless/holdback-nodejs18.pkg b/.pipelines/containerSourceData/nodejs/distroless/holdback-nodejs18.pkg new file mode 100644 index 00000000000..7c44c2d724f --- /dev/null +++ b/.pipelines/containerSourceData/nodejs/distroless/holdback-nodejs18.pkg @@ -0,0 +1,8 @@ +bash +bzi +coreutils +gmp +grep +libselinux +pcre +pcre-libs diff --git a/.pipelines/containerSourceData/nodejs/distroless/nodejs18.pkg b/.pipelines/containerSourceData/nodejs/distroless/nodejs18.pkg new file mode 100644 index 00000000000..4ef3b28e241 --- /dev/null +++ b/.pipelines/containerSourceData/nodejs/distroless/nodejs18.pkg @@ -0,0 +1,2 @@ +distroless-packages-base +nodejs18 diff --git a/.pipelines/containerSourceData/prometheus/distroless/holdback-prometheus.pkg b/.pipelines/containerSourceData/prometheus/distroless/holdback-prometheus.pkg new file mode 100644 index 00000000000..7c44c2d724f --- /dev/null +++ b/.pipelines/containerSourceData/prometheus/distroless/holdback-prometheus.pkg @@ -0,0 +1,8 @@ +bash +bzi +coreutils +gmp +grep +libselinux +pcre +pcre-libs diff --git a/.pipelines/containerSourceData/prometheus/distroless/prometheus.pkg b/.pipelines/containerSourceData/prometheus/distroless/prometheus.pkg new file mode 100644 index 00000000000..f293a3ce61b --- /dev/null +++ b/.pipelines/containerSourceData/prometheus/distroless/prometheus.pkg @@ -0,0 +1,2 @@ +distroless-packages-base +prometheus diff --git a/.pipelines/containerSourceData/prometheusadapter/distroless/holdback-prometheusadapter.pkg b/.pipelines/containerSourceData/prometheusadapter/distroless/holdback-prometheusadapter.pkg new file mode 100644 index 00000000000..7c44c2d724f --- /dev/null +++ b/.pipelines/containerSourceData/prometheusadapter/distroless/holdback-prometheusadapter.pkg @@ -0,0 +1,8 @@ +bash +bzi +coreutils +gmp +grep +libselinux +pcre +pcre-libs diff --git a/.pipelines/containerSourceData/prometheusadapter/distroless/prometheusadapter.pkg b/.pipelines/containerSourceData/prometheusadapter/distroless/prometheusadapter.pkg new file mode 100644 index 00000000000..e3becaca0cc --- /dev/null +++ b/.pipelines/containerSourceData/prometheusadapter/distroless/prometheusadapter.pkg @@ -0,0 +1,2 @@ +distroless-packages-base +prometheus-adapter diff --git a/.pipelines/containerSourceData/python/distroless/holdback-python.pkg b/.pipelines/containerSourceData/python/distroless/holdback-python.pkg new file mode 100644 index 00000000000..7c44c2d724f --- /dev/null +++ b/.pipelines/containerSourceData/python/distroless/holdback-python.pkg @@ -0,0 +1,8 @@ +bash +bzi +coreutils +gmp +grep +libselinux +pcre +pcre-libs diff --git a/.pipelines/containerSourceData/python/distroless/python.pkg b/.pipelines/containerSourceData/python/distroless/python.pkg new file mode 100644 index 00000000000..c1c777bedcc --- /dev/null +++ b/.pipelines/containerSourceData/python/distroless/python.pkg @@ -0,0 +1,2 @@ +distroless-packages-base +python3 diff --git a/.pipelines/containerSourceData/scripts/BuildBaseContainers.sh b/.pipelines/containerSourceData/scripts/BuildBaseContainers.sh index 13cc2a3f234..d4423f166ca 100755 --- a/.pipelines/containerSourceData/scripts/BuildBaseContainers.sh +++ b/.pipelines/containerSourceData/scripts/BuildBaseContainers.sh @@ -4,31 +4,60 @@ set -e -if [[ $(uname -p) == "x86_64" ]]; then - ARCHITECTURE="amd64" -else - ARCHITECTURE="arm64" -fi - -PUBLISHING_LEVEL="development" -BRANCH_NAME="main" - -# parse script parameters: -# -m -> folder containing artifacts of CBL-Mariner -# -n -> name of the container registry -# -o -> folder where to put artifacts to be published -# -b -> branch name -# -p -> publishing level -# -x -> container source dir from cbl-mariner -while getopts ":m:n:o:b:p:x:" OPTIONS; do +# This script is used to build base containers and publish them to ACR. +# The script takes the following inputs: +# - ACR: Azure Container Registry name. +# - CONTAINER_TARBALLS_DIR: Directory containing the tarballs for the base and distroless containers. +# - RPMS_TARBALL: Tarball containing the RPMs to be used in the containers. +# - CONTAINER_SRC_DIR: Directory containing the source files for the containers. +# - OUTPUT_DIR: Directory to save the published container names. +# - PUBLISHING_LEVEL: The publishing level for the containers. It can be "preview" or "development". +# - REPO_PREFIX: Prefix for the repository in ACR. +# - PUBLISH_TO_ACR: Flag to publish the containers to ACR. It can be "true" or "false". + +# Assuming you are in your current working directory. Below should be the directory structure: +# │ rpms.tar.gz +# │ out +# | ├── +# │ containerSourceData +# │ ├── base +# │ │ ├── Dockerfile-Base-Template +# │ │ ├── Dockerfile-Base-Nonroot-Template +# │ | ├── Dockerfile-Busybox-Template +# │ │ ├── Dockerfile-Distroless-Template +# │ │ ├── Dockerfile-Distroless-Nonroot-Template +# │ container_tarballs +# │ ├── container_base +# │ │ ├── core-2.0.20230607.tar.gz +# │ ├── distroless_base +# │ │ ├── distroless-base-2.0.20230607.tar.gz +# │ ├── distroless_debug +# │ │ ├── distroless-debug-2.0.20230607.tar.gz +# │ ├── distroless_minimal +# │ │ ├── distroless-minimal-2.0.20230607.tar.gz +# │ marinaraLocalRepo.repo + +# Example usage: +# ./BuildBaseContainers.sh \ +# -a azuerlinuxpreview \ +# -c ./container_tarballs \ +# -k ./rpms.tar.gz \ +# -l ./containerSourceData \ +# -o ./out \ +# -p development \ +# -r "" \ +# -q "false" + +while getopts ":a:c:k:l:o:p:r:q:" OPTIONS; do case ${OPTIONS} in - m ) MARINER_ARTIFACTS_FOLDER=$OPTARG;; - n ) CONTAINER_REGISTRY_NAME=$OPTARG - CONTAINER_REGISTRY_NAME_FULL="$CONTAINER_REGISTRY_NAME.azurecr.io";; - o ) OUTPUT_FOLDER=$OPTARG;; - b ) BRANCH_NAME=$OPTARG;; + a ) ACR=$OPTARG;; + c ) CONTAINER_TARBALLS_DIR=$OPTARG;; + k ) RPMS_TARBALL=$OPTARG;; + l ) CONTAINER_SRC_DIR=$OPTARG;; + o ) OUTPUT_DIR=$OPTARG;; p ) PUBLISHING_LEVEL=$OPTARG;; - x ) CONTAINER_SRC_DIR=$OPTARG;; + r ) REPO_PREFIX=$OPTARG;; + q ) PUBLISH_TO_ACR=$OPTARG;; \? ) echo "Error - Invalid Option: -$OPTARG" 1>&2 @@ -41,388 +70,267 @@ while getopts ":m:n:o:b:p:x:" OPTIONS; do esac done -echo "- MARINER_ARTIFACTS_FOLDER -> $MARINER_ARTIFACTS_FOLDER" -echo "- CONTAINER_REGISTRY_NAME -> $CONTAINER_REGISTRY_NAME" -echo "- CONTAINER_REGISTRY_NAME_FULL -> $CONTAINER_REGISTRY_NAME_FULL" -echo "- ARCHITECTURE -> $ARCHITECTURE" -echo "- BRANCH_NAME -> $BRANCH_NAME" -echo "- PUBLISHING_LEVEL -> $PUBLISHING_LEVEL" -echo "- OUTPUT_FOLDER -> $OUTPUT_FOLDER" - -ROOT_FOLDER="$(git rev-parse --show-toplevel)" - -BASE_IMAGE_TARBALL=$(find "$MARINER_ARTIFACTS_FOLDER" -name "core-[0-9.]*.tar.gz") -if [[ ! -f $BASE_IMAGE_TARBALL ]]; then - echo "Error - No base image tarball in $MARINER_ARTIFACTS_FOLDER" - exit 1 -fi - -DISTROLESS_IMAGE_TARBALL=$(find "$MARINER_ARTIFACTS_FOLDER" -name "distroless-base-[0-9.]*.tar.gz") -DISTROLESS_DEBUG_IMAGE_TARBALL=$(find "$MARINER_ARTIFACTS_FOLDER" -name "distroless-debug-[0-9.]*.tar.gz") -DISTROLESS_MINIMAL_IMAGE_TARBALL=$(find "$MARINER_ARTIFACTS_FOLDER" -name "distroless-minimal-[0-9.]*.tar.gz") -if [[ (! -f $DISTROLESS_IMAGE_TARBALL) || \ - (! -f $DISTROLESS_DEBUG_IMAGE_TARBALL) || \ - (! -f $DISTROLESS_MINIMAL_IMAGE_TARBALL) ]]; then - echo "Error - Missing some distroless image tarball(s) in $MARINER_ARTIFACTS_FOLDER" - exit 1 -fi - - -echo "+++ create temp folder" -TEMPDIR=$(mktemp -d) - +echo "+++ Create temp folder" +WORK_DIR=$(mktemp -d) function cleanup { - echo "+++ remove $TEMPDIR" - rm -rf "$TEMPDIR" + echo "+++ Remove temp folder: $WORK_DIR" + sudo rm -rf "$WORK_DIR" } trap cleanup EXIT -readonly BASE="base" -readonly DISTROLESS="distroless" -readonly BUSYBOX="busybox" - -# Use this global variable to store the most recently built base image. -LAST_BASE_IMAGE="" - -# Use this global variable to store the most recently built distroless image. -LAST_DISTROLESS_IMAGE="" - -# Use this global variable to store full container tag from base container image. -# This variable is set in the create_base_image function. -FULL_CONTAINER_TAG="" - -# these variables are used to create text files listing golden image names. -readonly file_name_prefix='PublishedContainers' -readonly file_ext='.txt' - -function get_container_info { - local container_file - local file_name - local prefix - local registryPrefix # (e.g.: public/cbl-mariner for container that go to MCR) - local temp_name - local repo_name - local __name - local __tag - - # $1: container tarball file name - # $2: name [out param] - # $3: tag [out param] - # $4: acr repo - # $5: prefix [optional, must be the last param] - container_file=$1 - __name=$2 - __tag=$3 - repo_name=$4 - if [[ -n $5 ]]; then - prefix=$5 - fi - - # remove path and extension - file_name=$(basename "$container_file") - file_name=${file_name%.tar.gz} - # Mariner 2.0 preview hack (remove "-Preview-" and following char(s) from name) - file_name=$(echo $file_name | sed "s/-Preview-.*//") - - # get container name and tag - oldifs=$IFS - IFS='#' - read -ra name_parts <<< "$(echo "$file_name" | sed -r 's/-([^-]*)$/#\1/')" - IFS=$oldifs - temp_name=${name_parts[0]} - temp_name=${temp_name//-/\/} - - # build full container name (all base containers are under 'base' in config file) - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME registryPrefix - if [[ -n $registryPrefix ]]; then - repo_name=$repo_name/$registryPrefix - fi - if [[ -n $prefix ]]; then - eval "$__name"="$repo_name/$prefix/$temp_name" - else - eval "$__name"="$repo_name/$temp_name" - fi - eval "$__tag"="${name_parts[1]}-$ARCHITECTURE" +function print_inputs { + echo "ACR -> $ACR" + echo "CONTAINER_TARBALLS_DIR -> $CONTAINER_TARBALLS_DIR" + echo "RPMS_TARBALL -> $RPMS_TARBALL" + echo "CONTAINER_SRC_DIR -> $CONTAINER_SRC_DIR" + echo "REPO_PREFIX -> $REPO_PREFIX" + echo "PUBLISHING_LEVEL -> $PUBLISHING_LEVEL" + echo "PUBLISH_TO_ACR -> $PUBLISH_TO_ACR" + echo "OUTPUT_DIR -> $OUTPUT_DIR" } -function create_base_image { - local container_name_prefix=$1 - local container_type=$2 - local container_tarball=$3 - local dockerfile=$4 - local container_name - local container_tag - - get_container_info "$container_tarball" container_name container_tag "$CONTAINER_REGISTRY_NAME_FULL" "$container_name_prefix" - - local full_container_name - full_container_name="$container_name:$container_tag" - - # FULL_CONTAINER_TAG is used to tag the marinara builder image. - FULL_CONTAINER_TAG="$container_tag" - - if [[ $container_type == "$BASE" ]]; then - LAST_BASE_IMAGE=$full_container_name - elif [[ $container_type == "$DISTROLESS" ]]; then - LAST_DISTROLESS_IMAGE=$full_container_name +function validate_inputs { + if [[ -z "$ACR" ]]; then + echo "Error - ACR name cannot be empty." + exit 1 fi - echo - echo "container_name_prefix: -> $container_name_prefix" - echo "container_type: -> $container_type" - echo "container_tarball: -> $container_tarball" - echo "LAST_BASE_IMAGE: -> $LAST_BASE_IMAGE" - echo "LAST_DISTROLESS_IMAGE: -> $LAST_DISTROLESS_IMAGE" - echo "full_container_name: -> $full_container_name" - echo "dockerfile -> $dockerfile" - echo - - echo "----------------------------------------------------------------------" - echo "+++ create container $full_container_name" - echo " from $(basename "$container_tarball")" - echo - - cat "$container_tarball" | docker import - "$full_container_name" - - echo "$full_container_name" >> "$TEMPDIR/$file_name_prefix-$container_type$file_ext" - echo "----------------------------------------------------------------------" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - mkdir -p "$containerBuildDir" + if [[ -z "$CONTAINER_TARBALLS_DIR" ]]; then + echo "Error - Container tarballs directory cannot be empty." + exit 1 + fi - cp "$ROOT_FOLDER/pipelines/publish-containers/common/data/EULA-Container.txt" "$containerBuildDir"/ - cp "$CONTAINER_SRC_DIR/$container_type/$dockerfile" "$containerBuildDir/Dockerfile" + BASE_TARBALL=$(find "$CONTAINER_TARBALLS_DIR" -name "core-[0-9.]*.tar.gz") + DISTROLESS_BASE_TARBALL=$(find "$CONTAINER_TARBALLS_DIR" -name "distroless-base-[0-9.]*.tar.gz") + DISTROLESS_DEBUG_TARBALL=$(find "$CONTAINER_TARBALLS_DIR" -name "distroless-debug-[0-9.]*.tar.gz") + DISTROLESS_MINIMAL_TARBALL=$(find "$CONTAINER_TARBALLS_DIR" -name "distroless-minimal-[0-9.]*.tar.gz") + if [[ (! -f $BASE_TARBALL) || \ + (! -f $DISTROLESS_BASE_TARBALL) || \ + (! -f $DISTROLESS_DEBUG_TARBALL) || \ + (! -f $DISTROLESS_MINIMAL_TARBALL) ]]; then + echo "Error - Missing some tarball(s) in $CONTAINER_TARBALLS_DIR" + exit 1 + fi - pushd "$containerBuildDir" > /dev/null + if [[ ! -f $RPMS_TARBALL ]]; then + echo "Error - No RPMs tarball found." + # exit 1 + fi - # Build image - docker build . \ - --build-arg EULA="EULA-Container.txt" \ - --build-arg BASE_IMAGE="$full_container_name" \ - -t "$full_container_name" \ - --no-cache \ - --progress=plain + if [ ! -d "$CONTAINER_SRC_DIR" ]; then + echo "Error - Container source directory does not exist." + exit 1 + fi - popd > /dev/null + if [[ -z "$PUBLISHING_LEVEL" ]]; then + echo "Error - Publishing level cannot be empty." + exit 1 + fi - # Clean up temp folder - sudo rm -rf "$containerBuildDir" + if [ ! -d "$OUTPUT_DIR" ]; then + echo "Create output directory: $OUTPUT_DIR" + mkdir -p "$OUTPUT_DIR" + fi } -function create_base_nonroot_image { - local mariner_version - local mariner_build_arch - - local base_container_full_name="$LAST_BASE_IMAGE" - local base_container_name=${base_container_full_name%:*} - local base_container_tag=${base_container_full_name#*:} - mariner_version=$(awk -F '.' '{print $1"."$2}' <<< "$base_container_tag") # 2.0.20220426-amd64 -> 2.0 - - mariner_build_arch=$(awk -F '.' '{print $3}' <<< "$base_container_tag") # 2.0.20220426-amd64 -> 20220426-amd64 - local full_new_tag=$mariner_version-nonroot.$mariner_build_arch # 2.0-nonroot.20220426-amd64 - local full_container_name="$base_container_name:$full_new_tag" - local dockerfile="Dockerfile-Base-Nonroot-Template" +function initialization { + echo "+++ Initialization" - echo - echo "base_container_full_name: -> $base_container_full_name" - echo "base_container_name: -> $base_container_name" - echo "base_container_tag: -> $base_container_tag" - echo "mariner_version: -> $mariner_version" - echo "full_container_name: -> $full_container_name" - echo "dockerfile -> $dockerfile" - echo + echo "+++ Extracting RPMs into $WORK_DIR" + tar -xf "$RPMS_TARBALL" -C "$WORK_DIR" + RPMS_DIR="RPMS" - echo "----------------------------------------------------------------------" - echo "+++ create container $full_container_name" - echo " from $base_container_full_name" - echo + echo "+++ Copy local repo files to $WORK_DIR" + LOCAL_REPO_FILE="local.repo" + cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$WORK_DIR/$LOCAL_REPO_FILE" - echo "$full_container_name" >> "$TEMPDIR/$file_name_prefix-$BASE$file_ext" - echo "----------------------------------------------------------------------" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - mkdir -p "$containerBuildDir" - - cp "$CONTAINER_SRC_DIR/base/$dockerfile" "$containerBuildDir/Dockerfile" - - pushd "$containerBuildDir" > /dev/null + if [ "$PUBLISHING_LEVEL" = "preview" ]; then + ACR_NAME_FULL=${ACR}.azurecr.io/${REPO_PREFIX} + elif [ "$PUBLISHING_LEVEL" = "development" ]; then + ACR_NAME_FULL=${ACR}.azurecr.io + fi - # Build image - docker build . \ - --build-arg BASE_IMAGE="$base_container_full_name" \ - --build-arg MARINER_VERSION="$mariner_version" \ - -t "$full_container_name" \ - --no-cache \ - --progress=plain + echo "ACR Name Full -> $ACR_NAME_FULL" - popd > /dev/null + if [[ $(uname -p) == "x86_64" ]]; then + ARCHITECTURE="amd64" + else + ARCHITECTURE="arm64" + fi - # Clean up temp folder - sudo rm -rf "$containerBuildDir" + echo "ARCHITECTURE -> $ARCHITECTURE" + + EULA_FILE_NAME="EULA-Container.txt" + + # Image types + BASE="base" + DISTROLESS="distroless" + BUSYBOX="busybox" + MARINARA="marinara" + + base_tarball_file_name=$(basename "$BASE_TARBALL") # core-2.0.20230607.tar.gz + base_tag_tar_gz=${base_tarball_file_name##*-} # 2.0.20230607.tar.gz + BASE_IMAGE_TAG=${base_tag_tar_gz%.tar.gz} # 2.0.20230607 + echo "BASE_IMAGE_TAG -> $BASE_IMAGE_TAG" + AZL_VERSION=${BASE_IMAGE_TAG%.*} # 2.0 + echo "AZL_VERSION -> $AZL_VERSION" + BUILD_ID=${BASE_IMAGE_TAG##*.} # 20230607 + echo "BUILD_ID -> $BUILD_ID" + + IMAGE_TAG=$BASE_IMAGE_TAG-$ARCHITECTURE + NONROOT_IMAGE_TAG=$AZL_VERSION-nonroot.$BUILD_ID-$ARCHITECTURE + + # Set various image names. + BASE_IMAGE_NAME="$ACR_NAME_FULL/base/core:$IMAGE_TAG" + BASE_NONROOT_IMAGE_NAME="$ACR_NAME_FULL/base/core:$NONROOT_IMAGE_TAG" + + DISTROLESS_BASE_IMAGE_NAME="$ACR_NAME_FULL/distroless/base:$IMAGE_TAG" + DISTROLESS_BASE_NONROOT_IMAGE_NAME="$ACR_NAME_FULL/distroless/base:$NONROOT_IMAGE_TAG" + + DISTROLESS_MINIMAL_IMAGE_NAME="$ACR_NAME_FULL/distroless/minimal:$IMAGE_TAG" + DISTROLESS_MINIMAL_NONROOT_IMAGE_NAME="$ACR_NAME_FULL/distroless/minimal:$NONROOT_IMAGE_TAG" + + DISTROLESS_DEBUG_NONROOT_IMAGE_NAME="$ACR_NAME_FULL/distroless/debug:$NONROOT_IMAGE_TAG" + DISTROLESS_DEBUG_IMAGE_NAME="$ACR_NAME_FULL/distroless/debug:$IMAGE_TAG" + + BUSYBOX_IMAGE_NAME="$ACR_NAME_FULL/busybox:$IMAGE_TAG" + MARINARA_IMAGE_NAME="$ACR_NAME_FULL/marinara:$IMAGE_TAG" + + echo "BASE_IMAGE_NAME -> $BASE_IMAGE_NAME" + echo "BASE_NONROOT_IMAGE_NAME -> $BASE_NONROOT_IMAGE_NAME" + echo "DISTROLESS_BASE_IMAGE_NAME -> $DISTROLESS_BASE_IMAGE_NAME" + echo "DISTROLESS_BASE_NONROOT_IMAGE_NAME -> $DISTROLESS_BASE_NONROOT_IMAGE_NAME" + echo "DISTROLESS_MINIMAL_IMAGE_NAME -> $DISTROLESS_MINIMAL_IMAGE_NAME" + echo "DISTROLESS_MINIMAL_NONROOT_IMAGE_NAME -> $DISTROLESS_MINIMAL_NONROOT_IMAGE_NAME" + echo "DISTROLESS_DEBUG_IMAGE_NAME -> $DISTROLESS_DEBUG_IMAGE_NAME" + echo "DISTROLESS_DEBUG_NONROOT_IMAGE_NAME -> $DISTROLESS_DEBUG_NONROOT_IMAGE_NAME" + echo "BUSYBOX_IMAGE_NAME -> $BUSYBOX_IMAGE_NAME" + echo "MARINARA_IMAGE_NAME -> $MARINARA_IMAGE_NAME" } -function create_busybox_image { - local mariner_version - local registryPrefix # (e.g.: public/cbl-mariner for container that go to MCR) +function docker_build { + local image_type=$1 + local image_full_name=$2 + local image_tarball=$3 + local dockerfile=$4 - mariner_version=$(awk -F '.' '{print $1"."$2}' <<< "$FULL_CONTAINER_TAG") # 2.0.20220426-amd64 -> 2.0 + echo "+++ Importing container image: $image_full_name" + local temp_image=${image_full_name}_temp + docker import - "$temp_image" < "$image_tarball" - # Get registry prefix for busybox container. Use the same registry destination as the base container. - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME registryPrefix + local build_dir="$WORK_DIR/container_build_dir" + mkdir -p "$build_dir" - if [[ -n $registryPrefix ]]; then - full_busybox_container_name="$CONTAINER_REGISTRY_NAME_FULL/$registryPrefix/$BUSYBOX:$FULL_CONTAINER_TAG" - else - full_busybox_container_name="$CONTAINER_REGISTRY_NAME_FULL/$BUSYBOX:$FULL_CONTAINER_TAG" + ROOT_FOLDER="$(git rev-parse --show-toplevel)" + EULA_FILE_PATH="$ROOT_FOLDER/.pipelines/container_artifacts/data" + if [ -d "$EULA_FILE_PATH" ]; then + cp "$EULA_FILE_PATH/$EULA_FILE_NAME" "$build_dir"/ fi - echo "----------------------------------------------------------------------" - echo "+++ create container $full_busybox_container_name" - echo - echo "$full_busybox_container_name" >> "$TEMPDIR/$file_name_prefix-$BUSYBOX$file_ext" - echo "----------------------------------------------------------------------" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - mkdir -p "$containerBuildDir" + cp "$CONTAINER_SRC_DIR/base/$dockerfile" "$build_dir/dockerfile" - cp "$CONTAINER_SRC_DIR/busybox/Dockerfile-Busybox-Template" "$containerBuildDir/Dockerfile" - - pushd "$containerBuildDir" > /dev/null + pushd "$build_dir" > /dev/null + echo "+++ Build image: $image_full_name" docker build . \ - --build-arg BASE_IMAGE="$LAST_BASE_IMAGE" \ - --build-arg MARINER_VERSION="$mariner_version" \ - -t "$full_busybox_container_name" \ - --no-cache \ - --progress=plain + --build-arg EULA="$EULA_FILE_NAME" \ + --build-arg BASE_IMAGE="$temp_image" \ + -t "$image_full_name" \ + --no-cache + docker rmi "$temp_image" popd > /dev/null + sudo rm -rf "$build_dir" - # Clean up temp folder - sudo rm -rf "$containerBuildDir" + publish_to_acr "$image_full_name" + save_container_image "$image_type" "$image_full_name" } -function create_marinara_image { - local mariner_version - local registryPrefix # (e.g.: public/cbl-mariner for container that go to MCR) - - mariner_version=$(awk -F '.' '{print $1"."$2}' <<< "$FULL_CONTAINER_TAG") # 2.0.20220426-amd64 -> 2.0 - marinara="marinara" - - # get registry prefix for marinara container (note that marinara is under 'base' in config file) - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME registryPrefix - if [[ -n $registryPrefix ]]; then - full_marinara_container_name="$CONTAINER_REGISTRY_NAME_FULL/$registryPrefix/$marinara:$FULL_CONTAINER_TAG" - else - full_marinara_container_name="$CONTAINER_REGISTRY_NAME_FULL/$marinara:$FULL_CONTAINER_TAG" - fi - - marinaraSrcDir="$TEMPDIR/$marinara-src" - git clone "https://github.com/microsoft/$marinara.git" "$marinaraSrcDir" - pushd "$marinaraSrcDir" - - echo "----------------------------------------------------------------------" - echo "+++ create container $full_marinara_container_name" - echo - echo "$full_marinara_container_name" >> "$TEMPDIR/$file_name_prefix-$marinara$file_ext" - echo "----------------------------------------------------------------------" +function docker_build_custom { + local image_type=$1 + local image_full_name=$2 + local final_image_to_use=$3 + local dockerfile=$4 - # Update dockerfile-marinara to use the current base container - sed -E "s|^FROM mcr\..*installer$|FROM $LAST_BASE_IMAGE as installer|g" -i "dockerfile-$marinara" + # $WORK_DIR has $RPMS_DIR directory and $LOCAL_REPO_FILE file. + pushd "$WORK_DIR" > /dev/null + echo "+++ Build image: $image_full_name" docker build . \ - -t "$full_marinara_container_name" \ - -f dockerfile-$marinara \ - --no-cache \ - --progress=plain + --build-arg BASE_IMAGE="$BASE_IMAGE_NAME" \ + --build-arg FINAL_IMAGE="$final_image_to_use" \ + --build-arg AZL_VERSION="$AZL_VERSION" \ + --build-arg RPMS="$RPMS_DIR" \ + --build-arg LOCAL_REPO_FILE="$LOCAL_REPO_FILE" \ + -t "$image_full_name" \ + -f "$CONTAINER_SRC_DIR/base/$dockerfile" \ + --no-cache popd > /dev/null - echo "+++ remove $marinaraSrcDir" - sudo rm -rf "$marinaraSrcDir" -} - -function create_distroless_nonroot_image { - local mariner_version - local mariner_build_arch - - local base_container_full_name="$LAST_BASE_IMAGE" - local distroless_container_full_name="$LAST_DISTROLESS_IMAGE" - local distroless_container_name=${distroless_container_full_name%:*} - local distroless_container_tag=${distroless_container_full_name#*:} - mariner_version=$(awk -F '.' '{print $1"."$2}' <<< "$distroless_container_tag") # 2.0.20220426-amd64 -> 2.0 - - mariner_build_arch=$(awk -F '.' '{print $3}' <<< "$distroless_container_tag") # 2.0.20220426-amd64 -> 20220426-amd64 - local full_new_tag=$mariner_version-nonroot.$mariner_build_arch # 2.0-nonroot.20220426-amd64 - local full_container_name="$distroless_container_name:$full_new_tag" - local dockerfile="Dockerfile-Distroless-Nonroot-Template" - - echo - echo "base_container_full_name: -> $base_container_full_name" - echo "distroless_container_full_name: -> $distroless_container_full_name" - echo "distroless_container_name: -> $distroless_container_name" - echo "distroless_container_tag: -> $distroless_container_tag" - echo "mariner_version: -> $mariner_version" - echo "full_container_name: -> $full_container_name" - echo "dockerfile -> $dockerfile" - echo - - echo "----------------------------------------------------------------------" - echo "+++ create container $full_container_name" - echo " from $distroless_container_full_name" - echo - - echo "$full_container_name" >> "$TEMPDIR/$file_name_prefix-$DISTROLESS$file_ext" - echo "----------------------------------------------------------------------" - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - mkdir -p "$containerBuildDir" - - cp "$CONTAINER_SRC_DIR/distroless/$dockerfile" "$containerBuildDir/Dockerfile" - - pushd "$containerBuildDir" > /dev/null + publish_to_acr "$image_full_name" + save_container_image "$image_type" "$image_full_name" +} - # Build image +function docker_build_marinara { + echo "+++ Build Marinara image: $MARINARA_IMAGE_NAME" + + local build_dir="$WORK_DIR/marinara_build_dir" + mkdir -p "$build_dir" + git clone "https://github.com/microsoft/$MARINARA.git" "$build_dir" + pushd "$build_dir" + + sed -E "s|^FROM mcr\..*installer$|FROM $BASE_IMAGE_NAME as installer|g" -i "dockerfile-$MARINARA" docker build . \ - --build-arg BASE_IMAGE="$base_container_full_name" \ - --build-arg FINAL_IMAGE="$distroless_container_full_name" \ - --build-arg MARINER_VERSION="$mariner_version" \ - -t "$full_container_name" \ - --no-cache \ - --progress=plain + -t "$MARINARA_IMAGE_NAME" \ + -f dockerfile-$MARINARA \ + --no-cache popd > /dev/null + sudo rm -rf "$build_dir" + + publish_to_acr "$MARINARA_IMAGE_NAME" + save_container_image "$MARINARA" "$MARINARA_IMAGE_NAME" +} - # Clean up temp folder - sudo rm -rf "$containerBuildDir" +function publish_to_acr { + local image=$1 + if [[ ! "$PUBLISH_TO_ACR" =~ [Tt]rue ]]; then + echo "+++ Skip publishing to ACR" + return + fi + echo "+++ Publish container $image" + echo "login into ACR: $ACR" + az acr login --name "$ACR" + docker image push "$image" } -function start_building_containers { - echo - echo "=====================================================================" - echo "Create Base and Distroless Mariner Containers" - echo "=====================================================================" - echo +function save_container_image { + local image_type=$1 + local image_name=$2 + echo "+++ Save image name to file PublishedContainers-$image_type.txt" + echo "$image_name" >> "$OUTPUT_DIR/PublishedContainers-$image_type.txt" +} - create_base_image $BASE $BASE "$BASE_IMAGE_TARBALL" "Dockerfile-Base-Template" - create_base_nonroot_image +function build_images { + echo "+++ Build images" - create_busybox_image - create_marinara_image + docker_build $BASE "$BASE_IMAGE_NAME" "$BASE_TARBALL" "Dockerfile-Base-Template" + docker_build $DISTROLESS "$DISTROLESS_BASE_IMAGE_NAME" "$DISTROLESS_BASE_TARBALL" "Dockerfile-Distroless-Template" + docker_build $DISTROLESS "$DISTROLESS_MINIMAL_IMAGE_NAME" "$DISTROLESS_MINIMAL_TARBALL" "Dockerfile-Distroless-Template" + docker_build $DISTROLESS "$DISTROLESS_DEBUG_IMAGE_NAME" "$DISTROLESS_DEBUG_TARBALL" "Dockerfile-Distroless-Template" - create_base_image "" $DISTROLESS "$DISTROLESS_IMAGE_TARBALL" "Dockerfile-Distroless-Template" - create_distroless_nonroot_image + docker_build_custom $BASE "$BASE_NONROOT_IMAGE_NAME" "" "Dockerfile-Base-Nonroot-Template" + docker_build_custom $DISTROLESS "$DISTROLESS_BASE_NONROOT_IMAGE_NAME" "$DISTROLESS_BASE_IMAGE_NAME" "Dockerfile-Distroless-Nonroot-Template" + docker_build_custom $DISTROLESS "$DISTROLESS_MINIMAL_NONROOT_IMAGE_NAME" "$DISTROLESS_MINIMAL_IMAGE_NAME" "Dockerfile-Distroless-Nonroot-Template" + docker_build_custom $DISTROLESS "$DISTROLESS_DEBUG_NONROOT_IMAGE_NAME" "$DISTROLESS_DEBUG_IMAGE_NAME" "Dockerfile-Distroless-Nonroot-Template" - create_base_image "" $DISTROLESS "$DISTROLESS_DEBUG_IMAGE_TARBALL" "Dockerfile-Distroless-Template" - create_distroless_nonroot_image + docker_build_custom $BUSYBOX "$BUSYBOX_IMAGE_NAME" "" "Dockerfile-Busybox-Template" - create_base_image "" $DISTROLESS "$DISTROLESS_MINIMAL_IMAGE_TARBALL" "Dockerfile-Distroless-Template" - create_distroless_nonroot_image + docker_build_marinara } -# source the CommonFunctions script to get the following function: -# - save_container_list -# - getRegistryPrefix -source $CONTAINER_SRC_DIR/scripts/CommonFunctions.sh - -start_building_containers -save_container_list +print_inputs +validate_inputs +initialization +build_images diff --git a/.pipelines/containerSourceData/scripts/BuildCdiContainers.sh b/.pipelines/containerSourceData/scripts/BuildCdiContainers.sh deleted file mode 100755 index 66cc834497a..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildCdiContainers.sh +++ /dev/null @@ -1,207 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -function create_cdi_container_image_base { - local componentName - local baseContainerName - local baseContainerTag - local initialDockerfile - local containerBuildDir - local binaryPath - local containerUser - local packagesToInstall - - # $1: sub-component name - # $2: container type - # $3: base container name - # $4: base container tag - # $5: packages to install - # $6: initial Dockerfile - # $7: binary path - # $8: container user - componentName=$1 - containerType=$2 - baseContainerName=$3 - baseContainerTag=$4 - packagesToInstall=$5 - initialDockerfile=$6 - binaryPath=$7 - containerUser=$8 - - echo "------ Display Arguments ------" - echo "Component Name: -> $componentName" - echo "Container Type: -> $containerType" - echo "Base Container Name: -> $baseContainerName" - echo "Base Container Tag: -> $baseContainerTag" - echo "Packages to Install: -> $packagesToInstall" - echo "Initial Dockerfile: -> $initialDockerfile" - echo "Binary Path: -> $binaryPath" - echo "Container User: -> $containerUser" - - # compose the container name. E.g. for branch-main this will look like - # cblmarinermain.azurecr.io/kubevirt/cdi-apiserver:1.51.0-1-cm2.0.20220811-amd64 - # cblmarinermain.azurecr.io -> repo - # kubevirt -> CDI_FOLDER_PREFIX - # cdi-apiserver -> $containerType (sub component) - # 1.51.0-1-cm2.0.20220811-amd64 -> version for cdi v1.51.0 rpms with base version details - - local originalContainerName="$CONTAINER_REGISTRY_NAME_FULL/base/$CDI_FOLDER_PREFIX/$containerType" - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $originalContainerName" - - containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp $initialDockerfile $containerBuildDir/Dockerfile - - # Workaround till proper binaries are built as part of the cdi rpm & renames are removed - # https://github.com/microsoft/CBL-Mariner/pull/5708/files# - cp "$CONTAINER_SRC_DIR/$CDI_BASE_COMPONENT/configuration-files"/* "$containerBuildDir" - pushd $containerBuildDir > /dev/null - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - --build-arg BINARY_NAME="$(basename $binaryPath)" \ - --build-arg USER="$containerUser" \ - -t "$originalContainerName" --no-cache --progress=plain . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - local containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - local installedPackage=$(docker exec "$containerId" rpm -qa | grep ^"$componentName") # nodejs-16.16.0-1.cm2.x86_64 - echo "Full Installed Package: -> $installedPackage" - local componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - docker rm -f "$containerId" - - # Rename the image to include package version - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - local containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - publish_container "$containerName" - - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - - local containerTypeNoDash=${containerType//-/} # Removes dash from containerType. Ex: azure-cli -> azurecli - echo "$containerName" >> $TEMPDIR/$file_name_prefix-$containerTypeNoDash$file_ext - - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Clean up docker storage folder - sudo rm -rf "$newDockerStorageLocation" - - - # clean up temp folder - popd > /dev/null - sudo rm -rf $containerBuildDir - - echo "----------------------------------------------------------------------" -} - -# Create containers for cdi-apiserver, cdi-cloner, cdi-controller, cdi-importer, -# cdi-operator, cdi-uploadproxy, cdi-uploadserver for CDI_BASE_COMPONENT -function create_cdi_subcomp_containers { - declare -A cdi_container_components - declare -A cdi_binary_path - declare -A cdi_container_user - - local sub_components - local CDI_PACKAGE_BASE="containerized-data-importer" - - sub_components=('api' 'cloner' 'controller' 'importer' 'operator' 'uploadproxy' 'uploadserver') - - # populate the cdi container names - for comp in ${sub_components[@]} - do - cdi_container_components[$comp]=$comp - - # replace 'api with 'apiserver' - [ "$comp" = "api" ] && cdi_container_components[$comp]='apiserver' - - cdi_binary_path[$comp]="/usr/bin/cdi-${cdi_container_components[$comp]}" - - # Setting the active user in the container based on upstream images - # By default set the user to be a non-root user (who is in the root group) - cdi_container_user[$comp]=1001 - done - - mkdir -p $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - - for comp in ${sub_components[@]} - do - # To build for specific versions - include it here with the name - dependency_component=$CDI_PACKAGE_BASE-$comp - echo "+++ CDI component name for $comp set at ${cdi_container_components[$comp]}" - cdi_comp=$CDI_BASE_COMPONENT-${cdi_container_components[$comp]} - - local pkgsFileName="$comp.pkg" - local packagesToInstall=() - getPkgsFromFile $CDI_BASE_COMPONENT $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - - echo "+++ CDI binary path for $comp ==> ${cdi_binary_path[$comp]}" - echo "+++ create container based on $base_container_name:$base_container_tag for $dependency_component" - create_cdi_container_image_base \ - "$dependency_component" \ - "$cdi_comp" \ - "$base_container_name"\ - "$base_container_tag" \ - "$packages" \ - "$CONTAINER_SRC_DIR/$CDI_BASE_COMPONENT/Dockerfile-$cdi_comp" \ - ${cdi_binary_path[$comp]} \ - ${cdi_container_user[$comp]} - - # Save text files generated in TEMPDIR - echo "+++ publish container list into pipeline artifacts" - cp $TEMPDIR/$file_name_prefix-*$file_ext $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - - done -} diff --git a/.pipelines/containerSourceData/scripts/BuildCertManagerContainers.sh b/.pipelines/containerSourceData/scripts/BuildCertManagerContainers.sh deleted file mode 100755 index df650ab4d79..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildCertManagerContainers.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -function create_cert_manager_container_image_base { - local componentName - local baseContainerName - local baseContainerTag - local originalContainerName - local initialDockerfile - local binaryPath - local packagesToInstall - - # $1: component name - # $2: container name - # $3: container tag - # $4: packages to install - # $5: initial Dockerfile - # $6: the path of the binary file to use as the container entrypoint - componentName=$1 - baseContainerName=$2 - baseContainerTag=$3 - packagesToInstall=$4 - initialDockerfile=$5 - binaryPath=$6 - - originalContainerName="$CONTAINER_REGISTRY_NAME_FULL/base/$componentName" - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $originalContainerName" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp $initialDockerfile $containerBuildDir/Dockerfile - - pushd $containerBuildDir > /dev/null - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - sed -i -E "s|@BINARY_PATH@|\"$binaryPath\"|" "$containerBuildDir/Dockerfile" - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - -t "$originalContainerName" --no-cache --progress=plain . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - - local containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - local installedPackage=$(docker exec "$containerId" rpm -qa | grep ^"$componentName") # nodejs-16.16.0-1.cm2.x86_64 - echo "Full Installed Package: -> $installedPackage" - local componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - docker rm -f "$containerId" - - # Rename the image to include package version - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - # replace base container registry prefix by golden container registry prefix (if any) - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - local containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - - publish_container "$containerName" - - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - - echo "$containerName" >> $TEMPDIR/$file_name_prefix-$componentName$file_ext - - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Clean up docker storage folder - sudo rm -rf "$newDockerStorageLocation" - - # clean up temp folder - popd > /dev/null - sudo rm -rf $containerBuildDir - - echo "----------------------------------------------------------------------" -} - -function create_cert_manager_subcomp_containers { - local sub_components - local dependency_component - local binary_path - - sub_components=('acmesolver' 'cainjector' 'controller' 'cmctl' 'webhook') - - mkdir -p $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - - for subcomp in ${sub_components[@]} - do - dependency_component=$CERT_MANAGER-$subcomp - binary_path=/usr/bin/$subcomp - local pkgsFileName="$subcomp.pkg" - local packagesToInstall=() - getPkgsFromFile $CERT_MANAGER_NO_DASH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - - echo "+++ create container based on $base_container_name:$base_container_tag for $dependency_component" - create_cert_manager_container_image_base \ - "$dependency_component" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "$CONTAINER_SRC_DIR/$CERT_MANAGER_NO_DASH/Dockerfile-cert-manager" \ - "$binary_path" - - # Save text files generated in TEMPDIR - echo "+++ publish container list into pipeline artifacts" - cp $TEMPDIR/$file_name_prefix-*$file_ext $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - - done -} diff --git a/.pipelines/containerSourceData/scripts/BuildGoldenContainer.sh b/.pipelines/containerSourceData/scripts/BuildGoldenContainer.sh new file mode 100755 index 00000000000..0e9a85836f3 --- /dev/null +++ b/.pipelines/containerSourceData/scripts/BuildGoldenContainer.sh @@ -0,0 +1,379 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +set -e + +# This script is used to build a golden container image for a given component. +# The script takes the following inputs: +# - a) Base container image name (e.g. mcr.microsoft.com/cbl-mariner/base/core:2.0) +# - b) ACR name (e.g. azurelinepreview, acrafoimages, etc.) +# - c) Container repository name (e.g. base/nodejs, base/postgres, base/kubevirt/cdi-apiserver, etc.) +# - d) Image name (e.g. nodejs, postgres, cdi, etc.) +# - e) Component name (e.g. nodejs18, postgresql, containerized-data-importer-api, etc.) +# - f) Package file name (e.g. nodejs18.pkg, postgres.pkg, api.pkg, etc.) +# - g) Dockerfile name (e.g. Dockerfile-nodejs, Dockerfile-Postgres, Dockerfile-cdi-apiserver, etc.) +# - h) Docker build arguments (e.g. '--build-arg BINARY_NAME="cdi-apiserver" --build-arg USER=1001') +# - i) Dockerfile text replacement (e.g. '@BINARY_PATH@ \"/usr/bin/acmesolver\"') +# - j) Output directory for container artifacts. +# - k) RPMS tarball file path (e.g. ./rpms.tar.gz) +# - l) Container source directory (e.g. ~/workspace/CBL-Mariner/.pipelines/containerSourceData) +# - m) Is HCI image (e.g. true, false. HCI images have different naming convention) +# - n) Use rpm -qa command (e.g. true, false. Some images use rpm -qa command to get installed package) +# - o) Repo prefix (e.g. public/cbl-mariner, unlisted/cbl-mariner, etc.) +# - p) Publishing level (e.g. preview, development) +# - q) Publish to ACR (e.g. true, false. If true, the script will push the container to ACR) +# - r) Create SBOM (e.g. true, false. If true, the script will create SBOM for the container) +# - s) SBOM tool path. +# - t) Script to create SBOM for the container image. +# - u) Create Distroless container (e.g. true, false. If true, the script will also create a distroless container) + +# Assuming you are in your current working directory. Below should be the directory structure: +# │ rpms.tar.gz +# │ OUTPUT +# │ ├── + +# Assuming CBL-Mariner repo is cloned in your home directory. Below should be the directory structure: +# ~/CBL-Mariner/.pipelines/containerSourceData +# ├── nodejs +# │ ├── distroless +# │ │ ├── holdback-nodejs18.pkg +# │ │ ├── nodejs18.pkg +# │ ├── Dockerfile-Nodejs +# │ ├── nodejs18.pkg +# ├── configuration +# │ ├── acrRepoV2.json +# ├── scripts +# │ ├── BuildGoldenContainer.sh +# ├── Dockerfile-Initial +# ├── marinerLocalRepo.repo + +# Example usage: +# /bin/bash ~/CBL-Mariner/.pipelines/containerSourceData/scripts/BuildGoldenContainer.sh \ +# -a "mcr.microsoft.com/cbl-mariner/base/core:2.0" -b azurelinuxlocal \ +# -c "base/nodejs" -d "nodejs" -e "nodejs18" -f nodejs18.pkg -g Dockerfile-Nodejs \ +# -j OUTPUT -k ./rpms.tar.gz -l ~/CBL-Mariner/.pipelines/containerSourceData \ +# -m "false" -n "false" -p development -q "false" -u "true" + +while getopts ":a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:" OPTIONS; do + case ${OPTIONS} in + a ) BASE_IMAGE_NAME_FULL=$OPTARG;; + b ) ACR=$OPTARG;; + c ) REPOSITORY=$OPTARG;; + d ) IMAGE=$OPTARG;; + e ) COMPONENT=$OPTARG;; + f ) PACKAGE_FILE=$OPTARG;; + g ) DOCKERFILE=$OPTARG;; + h ) DOCKER_BUILD_ARGS=$OPTARG;; + i ) DOCKERFILE_TEXT_REPLACEMENT=$OPTARG;; + j ) OUTPUT_DIR=$OPTARG;; + k ) RPMS_TARBALL=$OPTARG;; + l ) CONTAINER_SRC_DIR=$OPTARG;; + m ) IS_HCI_IMAGE=$OPTARG;; + n ) USE_RPM_QA_CMD=$OPTARG;; + o ) REPO_PREFIX=$OPTARG;; + p ) PUBLISHING_LEVEL=$OPTARG;; + q ) PUBLISH_TO_ACR=$OPTARG;; + r ) CREATE_SBOM=$OPTARG;; + s ) SBOM_TOOL_PATH=$OPTARG;; + t ) SBOM_SCRIPT=$OPTARG;; + u ) DISTROLESS=$OPTARG;; + + \? ) + echo "Error - Invalid Option: -$OPTARG" 1>&2 + exit 1 + ;; + : ) + echo "Error - Invalid Option: -$OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac +done + +echo "+++ Create temp folder" +WORK_DIR=$(mktemp -d) +function cleanup { + echo "+++ Remove temp folder: $WORK_DIR" + sudo rm -rf "$WORK_DIR" +} +trap cleanup EXIT + +function print_inputs { + echo "BASE_IMAGE_NAME_FULL -> $BASE_IMAGE_NAME_FULL" + echo "ACR -> $ACR" + echo "REPOSITORY -> $REPOSITORY" + echo "IMAGE -> $IMAGE" + echo "COMPONENT -> $COMPONENT" + echo "PACKAGE_FILE -> $PACKAGE_FILE" + echo "DOCKERFILE -> $DOCKERFILE" + echo "DOCKER_BUILD_ARGS -> $DOCKER_BUILD_ARGS" + echo "DOCKERFILE_TEXT_REPLACEMENT -> $DOCKERFILE_TEXT_REPLACEMENT" + echo "OUTPUT_DIR -> $OUTPUT_DIR" + echo "RPMS_TARBALL -> $RPMS_TARBALL" + echo "CONTAINER_SRC_DIR -> $CONTAINER_SRC_DIR" + echo "IS_HCI_IMAGE -> $IS_HCI_IMAGE" + echo "USE_RPM_QA_CMD -> $USE_RPM_QA_CMD" + echo "REPO_PREFIX -> $REPO_PREFIX" + echo "PUBLISHING_LEVEL -> $PUBLISHING_LEVEL" + echo "PUBLISH_TO_ACR -> $PUBLISH_TO_ACR" + echo "CREATE_SBOM -> $CREATE_SBOM" + echo "SBOM_TOOL_PATH -> $SBOM_TOOL_PATH" + echo "SBOM_SCRIPT -> $SBOM_SCRIPT" + echo "DISTROLESS -> $DISTROLESS" +} + +function validate_inputs { + if [[ -z "$BASE_IMAGE_NAME_FULL" ]]; then + echo "Error - Base container image name cannot be empty." + exit 1 + fi + + if [[ -z "$ACR" ]]; then + echo "Error - ACR name cannot be empty." + exit 1 + fi + + if [[ -z "$REPOSITORY" ]]; then + echo "Error - Container repository name cannot be empty." + exit 1 + fi + + if [[ -z "$IMAGE" ]]; then + echo "Error - Image name cannot be empty." + exit 1 + fi + + if [[ -z "$PACKAGE_FILE" ]]; then + echo "Error - Package file name cannot be empty." + exit 1 + fi + + if [[ -z "$DOCKERFILE" ]]; then + echo "Error - Dockerfile name cannot be empty." + exit 1 + fi + + if [ ! -d "$OUTPUT_DIR" ]; then + echo "Create output directory: $OUTPUT_DIR" + mkdir -p "$OUTPUT_DIR" + fi + + if [[ ! -f $RPMS_TARBALL ]]; then + echo "Error - No RPMs tarball found." + exit 1 + fi + + if [ ! -d "$CONTAINER_SRC_DIR" ]; then + echo "Error - Container source directory does not exist." + exit 1 + fi + + if [[ -z "$PUBLISHING_LEVEL" ]]; then + echo "Error - Publishing level cannot be empty." + exit 1 + fi + + if [[ "$CREATE_SBOM" =~ [Tt]rue ]]; then + if [[ -z "$SBOM_TOOL_PATH" ]] ; then + echo "Error - SBOM tool path cannot be empty." + exit 1 + fi + if [[ ! -f "$SBOM_SCRIPT" ]]; then + echo "Error - SBOM script does not exist." + exit 1 + fi + fi +} + +function initialization { + echo "+++ Initialization" + if [ "$PUBLISHING_LEVEL" = "preview" ]; then + GOLDEN_IMAGE_NAME=${ACR}.azurecr.io/${REPO_PREFIX}/${REPOSITORY} + elif [ "$PUBLISHING_LEVEL" = "development" ]; then + GOLDEN_IMAGE_NAME=${ACR}.azurecr.io/${REPOSITORY} + fi + + BASE_IMAGE_NAME=${BASE_IMAGE_NAME_FULL%:*} # mcr.microsoft.com/cbl-mariner/base/core + BASE_IMAGE_TAG=${BASE_IMAGE_NAME_FULL#*:} # 2.0 + AZURE_LINUX_VERSION=${BASE_IMAGE_TAG%.*} # 2.0 + + # For Azure Linux 2.0, we have shipped the container images with + # the below value of DISTRO_IDENTIFIER in the image tag. + # TODO: We may need to update this value for Azure Linux 3.0. + DISTRO_IDENTIFIER="cm" + + echo "Golden Image Name -> $GOLDEN_IMAGE_NAME" + echo "Base ACR Container Name -> $BASE_IMAGE_NAME" + echo "Base ACR Container Tag -> $BASE_IMAGE_TAG" + echo "Azure Linux Version -> $AZURE_LINUX_VERSION" + echo "Distro Identifier -> $DISTRO_IDENTIFIER" +} + +function prepare_dockerfile { + echo "+++ Prepare dockerfile" + # Copy original dockerfile from CBL-Mariner repo. + cp "$CONTAINER_SRC_DIR/$IMAGE/$DOCKERFILE" "$WORK_DIR/dockerfile" + + # Update the copied dockerfile for later use in container build. + mainRunInstruction=$(cat "$CONTAINER_SRC_DIR/Dockerfile-Initial") + sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i "$WORK_DIR/dockerfile" + + if [ -n "$DOCKERFILE_TEXT_REPLACEMENT" ]; then + TEXT_REPLACEMENT_ARRAY=($DOCKERFILE_TEXT_REPLACEMENT) + sed -E "s|${TEXT_REPLACEMENT_ARRAY[0]}|${TEXT_REPLACEMENT_ARRAY[1]}|g" -i "$WORK_DIR/dockerfile" + fi + + echo " Output content of final dockerfile" + echo "------------------------------------" + cat "$WORK_DIR/dockerfile" + echo "" +} + +function get_packages_to_install { + echo "+++ Get packages to install" + packagesFilePath="$CONTAINER_SRC_DIR/$IMAGE/$PACKAGE_FILE" + PACKAGES_TO_INSTALL=$(paste -s -d' ' < "$packagesFilePath") + echo "Packages to install -> $PACKAGES_TO_INSTALL" +} + +function prepare_docker_directory { + echo "+++ Prepare docker directory" + # Get additional required files for the container build from CBL-Mariner repo. + configurationDirectoryPath="$CONTAINER_SRC_DIR/$IMAGE/configuration-files" + if [ -d "$configurationDirectoryPath" ]; then + cp -v "$configurationDirectoryPath"/* "$WORK_DIR" + fi + + HOST_MOUNTED_DIR="$WORK_DIR/Stage" + mkdir -pv "$HOST_MOUNTED_DIR" + + # Copy files into docker context directory + tar -xf "$RPMS_TARBALL" -C "$HOST_MOUNTED_DIR"/ + cp -v "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$HOST_MOUNTED_DIR"/ +} + +function docker_build { + echo "+++ Build container" + pushd "$WORK_DIR" > /dev/null + echo " docker build command" + echo "----------------------" + echo "docker buildx build $DOCKER_BUILD_ARGS" \ + "--build-arg BASE_IMAGE=$BASE_IMAGE_NAME_FULL" \ + "--build-arg RPMS_TO_INSTALL=$PACKAGES_TO_INSTALL" \ + "-t $GOLDEN_IMAGE_NAME --no-cache --progress=plain" \ + "-f $WORK_DIR/Dockerfile ." + + echo "" + docker buildx build $DOCKER_BUILD_ARGS \ + --build-arg BASE_IMAGE="$BASE_IMAGE_NAME_FULL" \ + --build-arg RPMS_TO_INSTALL="$PACKAGES_TO_INSTALL" \ + -t "$GOLDEN_IMAGE_NAME" --no-cache --progress=plain \ + -f "$WORK_DIR/Dockerfile" . + popd > /dev/null +} + +function set_image_tag { + echo "+++ Get version of the installed package in the container." + local containerId + local installedPackage + + containerId=$(docker run --entrypoint /bin/bash -dt "$GOLDEN_IMAGE_NAME") + + echo "Container ID -> $containerId" + + if [[ $USE_RPM_QA_CMD =~ [Tt]rue ]] ; then + echo "Using rpm -qa command to get installed package." + installedPackage=$(docker exec "$containerId" rpm -qa | grep ^"$COMPONENT") + else + echo "Using tdnf repoquery command to get installed package." + # exec as root as the default user for some containers is non-root + installedPackage=$(docker exec -u 0 "$containerId" tdnf repoquery --installed "$COMPONENT" | grep ^"$COMPONENT") + fi + + echo "Full Installed Package: -> $installedPackage" + COMPONENT_VERSION=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 + echo "Component Version -> $COMPONENT_VERSION" + docker rm -f "$containerId" + + # Rename the image to include package version + # For HCI Images, do not include "-$DISTRO_IDENTIFIER" in the image tag; Instead use a "." + if [ "$IS_HCI_IMAGE" = true ]; then + # Example: acrafoimages.azurecr.io/base/kubevirt/virt-operator:0.59.0-2.2.0.20230607-amd64 + GOLDEN_IMAGE_NAME_FINAL="$GOLDEN_IMAGE_NAME:$COMPONENT_VERSION.$BASE_IMAGE_TAG" + else + # Example: azurelinuxpreview.azurecr.io/base/nodejs:16.19.1-2-$DISTRO_IDENTIFIER2.0.20230607-amd64 + GOLDEN_IMAGE_NAME_FINAL="$GOLDEN_IMAGE_NAME:$COMPONENT_VERSION-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + fi +} + +function finalize { + echo "+++ Finalize" + docker image tag "$GOLDEN_IMAGE_NAME" "$GOLDEN_IMAGE_NAME_FINAL" + docker rmi -f "$GOLDEN_IMAGE_NAME" + echo "+++ Save container image name to file PublishedContainers-$IMAGE.txt" + echo "$GOLDEN_IMAGE_NAME_FINAL" >> "$OUTPUT_DIR/PublishedContainers-$IMAGE.txt" +} + +function publish_to_acr { + CONTAINER_IMAGE=$1 + if [[ ! "$PUBLISH_TO_ACR" =~ [Tt]rue ]]; then + echo "+++ Skip publishing to ACR" + return + fi + echo "+++ Publish container $CONTAINER_IMAGE" + echo "login into ACR: $ACR" + az acr login --name "$ACR" + docker image push "$CONTAINER_IMAGE" +} + +function generate_image_sbom { + if [[ ! "$CREATE_SBOM" =~ [Tt]rue ]]; then + echo "+++ Skip creating SBOM" + return + fi + + echo "+++ Generate SBOM for the container image" + echo "Sanitized image name has '/' replaced with '-' and ':' replaced with '_'." + GOLDEN_IMAGE_NAME_SANITIZED=$(echo "$GOLDEN_IMAGE_NAME_FINAL" | tr '/' '-' | tr ':' '_') + echo "GOLDEN_IMAGE_NAME_SANITIZED -> $GOLDEN_IMAGE_NAME_SANITIZED" + + DOCKER_BUILD_DIR=$(mktemp -d) + # SBOM script will create the SBOM at the following path. + IMAGE_SBOM_MANIFEST_PATH="$DOCKER_BUILD_DIR/_manifest/spdx_2.2/manifest.spdx.json" + /bin/bash "$SBOM_SCRIPT" \ + "$DOCKER_BUILD_DIR" \ + "$GOLDEN_IMAGE_NAME_FINAL" \ + "$SBOM_TOOL_PATH" \ + "$BASE_IMAGE_NAME-$COMPONENT" \ + "$COMPONENT_VERSION-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + + SBOM_IMAGES_DIR="$OUTPUT_DIR/SBOM_IMAGES" + mkdir -p "$SBOM_IMAGES_DIR" + cp -v "$IMAGE_SBOM_MANIFEST_PATH" "$SBOM_IMAGES_DIR/$GOLDEN_IMAGE_NAME_SANITIZED.spdx.json" + echo "Generated SBOM:'$SBOM_IMAGES_DIR/$GOLDEN_IMAGE_NAME_SANITIZED.spdx.json'" + sudo rm -rf "$DOCKER_BUILD_DIR" +} + +function distroless_container { + if [[ ! "$DISTROLESS" =~ [Tt]rue ]]; then + echo "+++ Skip creating distroless container" + return + fi + + # shellcheck source=/dev/null + source "$CONTAINER_SRC_DIR/scripts/BuildGoldenDistrolessContainer.sh" + create_distroless_container +} + +print_inputs +validate_inputs +initialization +prepare_dockerfile +get_packages_to_install +prepare_docker_directory +docker_build +set_image_tag +finalize +publish_to_acr "$GOLDEN_IMAGE_NAME_FINAL" +generate_image_sbom +distroless_container diff --git a/.pipelines/containerSourceData/scripts/BuildGoldenContainers.sh b/.pipelines/containerSourceData/scripts/BuildGoldenContainers.sh deleted file mode 100755 index 050b322277b..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildGoldenContainers.sh +++ /dev/null @@ -1,1045 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -set -e - -# parse script parameters: -# -i -> published base container file -# -m -> folder containing artifacts of CBL-Mariner -# -n -> name of the container registry -# -g -> golden container image -# -o -> folder where to put artifacts to be published -# -s -> manifest tool directory path -# -b -> branch name -# -p -> publishing level -# -while getopts ":i:m:n:g:o:s:b:p:x:" OPTIONS; do - case ${OPTIONS} in - i ) BASE_IMAGE_FOLDER=$OPTARG;; - m ) MARINER_ARTIFACTS_FOLDER=$OPTARG;; - n ) CONTAINER_REGISTRY_NAME=$OPTARG - CONTAINER_REGISTRY_NAME_FULL="$CONTAINER_REGISTRY_NAME.azurecr.io";; - g ) GOLDEN_CONTAINER_IMAGE=$OPTARG;; - o ) OUTPUT_FOLDER=$OPTARG;; - s ) MANIFEST_TOOL_DIR=$OPTARG;; - b ) BRANCH_NAME=$OPTARG;; - p ) PUBLISHING_LEVEL=$OPTARG;; - x ) CONTAINER_SRC_DIR=$OPTARG;; - - \? ) - echo "Error - Invalid Option: -$OPTARG" 1>&2 - exit 1 - ;; - : ) - echo "Error - Invalid Option: -$OPTARG requires an argument" 1>&2 - exit 1 - ;; - esac -done - -MANIFEST_TOOL_DIR="$(cd "$MANIFEST_TOOL_DIR"; pwd)" -OUTPUT_FOLDER="$(cd "$OUTPUT_FOLDER"; pwd)" - -echo "- BASE IMAGE_FOLDER -> $BASE_IMAGE_FOLDER" -echo "- MARINER_ARTIFACTS_FOLDER -> $MARINER_ARTIFACTS_FOLDER" -echo "- CONTAINER_REGISTRY_NAME -> $CONTAINER_REGISTRY_NAME" -echo "- CONTAINER_REGISTRY_NAME_FULL -> $CONTAINER_REGISTRY_NAME_FULL" -echo "- GOLDEN_CONTAINER_IMAGE -> $GOLDEN_CONTAINER_IMAGE" -echo "- BRANCH_NAME -> $BRANCH_NAME" -echo "- PUBLISHING_LEVEL -> $PUBLISHING_LEVEL" -echo "- MANIFEST_TOOL_DIR -> $MANIFEST_TOOL_DIR" -echo "- OUTPUT_FOLDER -> $OUTPUT_FOLDER" - -readonly SCRIPT_FOLDER="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )" -readonly ROOT_FOLDER="$(git rev-parse --show-toplevel)" - -# define golden images dependency components -readonly AZURECLI="azure-cli" -readonly AZURECLI_NO_DASH="azurecli" -readonly CDI_BASE_COMPONENT="cdi" -readonly CERT_MANAGER='cert-manager' -readonly CERT_MANAGER_NO_DASH='certmanager' -readonly INFLUX_DB="influxdb" -readonly KUBEVIRT_BASE_COMPONENT="kubevirt" -readonly MEMCACHED="memcached" -readonly MULTUS="multus" -readonly NGINX="nginx" -readonly NODEJS="nodejs" -readonly OPENMPI="openmpi" -readonly PHP="php" -readonly POSTGRES="postgres" -readonly PROMETHEUS="prometheus" -readonly PROMETHEUS_ADAPTER="prometheus-adapter" -readonly PROMETHEUS_ADAPTER_NO_DASH="prometheusadapter" -readonly PYTHON="python" -readonly PYTORCH="pytorch" -readonly RABBITMQSERVER="rabbitmq-server" -readonly RABBITMQSERVER_NO_DASH="rabbitmqserver" -readonly REDIS="redis" -readonly RUBY="ruby" -readonly RUST="rust" -readonly SRIOV_NETWORK_DEVICE_PLUGIN='sriov-network-device-plugin' -readonly SRIOV_NETWORK_DEVICE_PLUGIN_NO_DASH='sriovnetworkdeviceplugin' -readonly TELEGRAF="telegraf" -readonly TENSORFLOW="tensorflow" - -# The RPMS of CDI have containerized-data-importer as its prefix whereas the -# containers must have cdi as its prefix. Hence, the BASE component -# is set to cdi. The folder prefix is same as kubevirt. -readonly CDI_FOLDER_PREFIX=$KUBEVIRT_BASE_COMPONENT -readonly KUBEVIRT_FOLDER_PREFIX=$KUBEVIRT_BASE_COMPONENT - -echo "+++ create temp folder" -TEMPDIR=$ROOT_FOLDER/TEMPDIR_CONTAINER -mkdir -pv "$OUTPUT_FOLDER/SBOM_IMAGES" - -function cleanup { - echo "+++ remove $TEMPDIR" - sudo rm -rf "$TEMPDIR" -} -trap cleanup EXIT - -declare -A COMPONENT_VERSIONS -declare -A BUILDER_IMAGES - -# these variables are used to create text files listing golden image names. -readonly file_name_prefix='PublishedContainers' -readonly file_ext='.txt' - -# Validates the input such as base images exist and the Mariner RPMs tarball exists. -function input_validation { - BASE_IMAGE_FILE=$(find "$BASE_IMAGE_FOLDER" -name "PublishedContainers-base.txt") - if [[ ! -f $BASE_IMAGE_FILE ]]; then - echo "Error - No base image file in $BASE_IMAGE_FOLDER" - exit 1 - fi - - MARINER_RPMS_TARBALL=$(find "$MARINER_ARTIFACTS_FOLDER" -name "rpms.tar.gz" -maxdepth 1) - if [[ ! -f $MARINER_RPMS_TARBALL ]]; then - echo "Error - No Mariner RPMs tarball in $MARINER_ARTIFACTS_FOLDER" - exit 1 - fi -} - -# Reads base container names from the passed in text files -function read_base_container_name { - baseImageName="none" - - while read image; do - if [[ $baseImageName == "none" ]]; then - baseImageName=$image - fi - done < "$BASE_IMAGE_FILE" - - echo "- Full base ACR image name: $baseImageName" - base_container_acr=${baseImageName%%.*} - base_container_name_with_core=${baseImageName%:*} - base_container_name=${base_container_name_with_core%/*} - base_container_tag=${baseImageName#*:} - - echo "Base ACR Name -> $base_container_acr" - echo "Base ACR Container Name -> $base_container_name" - echo "Base ACR Container Tag -> $base_container_tag" -} - -# Builds, Tests, and Publishes Golden Container Image. -# The first argument is the main package name i.e., component name (e.g., nodejs, azure-cli, postgresql, etc) -# The second argument is the image name i.e., container type (e.g., nodejs, azure-cli, postgres, etc) -# The third argument is the base container name -# The fourth argument is the base container tag -# The fifth argument is the set of packages to be installed in the image. -# The sixth argument is the path to the corresponding dockerfile. -# The seventh argument is the runTest flag (0/1) -# The eighth argument is the passed in full containerName -function CreateGoldenContainer { - local componentName=$1 - local containerType=$2 - local baseContainerName=$3 - local baseContainerTag=$4 - local packagesToInstall=$5 - local goldenImageDockerfile=$6 - local runTest=$7 - local originalContainerName=$8 - local containerTypeNoDash - - echo "------ Display Arguments ------" - echo "Component Name: -> $componentName" - echo "Container Type: -> $containerType" - echo "Base Container Name: -> $baseContainerName" - echo "Base Container Tag: -> $baseContainerTag" - echo "Packages to Install: -> $packagesToInstall" - echo "Dockerfile: -> $goldenImageDockerfile" - echo "Test Container: -> $runTest" - echo "Container Name: -> $originalContainerName" - - echo "+++ create container based on $baseContainerName/core:$baseContainerTag for $componentName" - containerTypeNoDash=${containerType//-/} # Removes dash from containerType. Ex: azure-cli -> azurecli - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $originalContainerName" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp "$CONTAINER_SRC_DIR/$containerTypeNoDash/$goldenImageDockerfile" "$containerBuildDir/Dockerfile" - - # Ensure that the path exists before copying files. - if [ -d "$CONTAINER_SRC_DIR/$containerTypeNoDash/configuration-files" ]; then - cp "$CONTAINER_SRC_DIR/$containerTypeNoDash/configuration-files"/* "$containerBuildDir" - fi - - pushd "$containerBuildDir" - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - - cat Dockerfile - - if [ "$DISABLE_DOCKER_REDIRECTION" != "true" ]; then - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - fi - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - -t "$originalContainerName" --no-cache --progress=plain \ - -f $containerBuildDir/Dockerfile . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - - local containerId - local installedPackage - local componentVersion - - containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - # exec as root as the default user for some containers is non-root - # componentName e.g. nodejs-16.16.0-1.cm2.x86_64 - installedPackage=$(docker exec -u 0 "$containerId" tdnf repoquery --installed "$componentName" | grep ^"$componentName") - echo "Full Installed Package: -> $installedPackage" - componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - COMPONENT_VERSIONS[$containerType]=$componentVersion - docker rm -f "$containerId" - - # Rename the image to include package version - # For HCI Images, do not include "-cm" in the image tag; Instead use a "." - if $IS_HCI_IMAGE; then - # Example: acrafoimages.azurecr.io/base/kubevirt/virt-operator:0.59.0-2.2.0.20230607-amd64 - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - else - # Example: cblmarinermain.azurecr.io/base/nodejs:16.19.1-2-cm2.0.20230607-amd64 - local containerName="$originalContainerName:$componentVersion-cm$baseContainerTag" - fi - - # replace base container registry prefix by golden container registry prefix (if any) - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - BUILDER_IMAGES[$componentName]=$containerName - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - # Test image - if [ $runTest -ne 0 ]; then - test_golden_container "$containerTypeNoDash" "$containerName" - fi - - # Publish image - publish_container "$containerName" - - local containerNameSanitized - containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - - if [[ "$DISABLE_SBOM_GENERATION" != "true" ]]; then - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - fi - popd - - if [ "$DISABLE_DOCKER_REDIRECTION" != "true" ]; then - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - fi - - sudo rm -rf "$newDockerStorageLocation" - - # Clean up temp folder - sudo rm -rf "$containerBuildDir" - - # Save container name - echo "$containerName" >> "$TEMPDIR/$file_name_prefix-$containerTypeNoDash$file_ext" - echo "----------------------------------------------------------------------" - - save_container_list -} - -function DockerBuild { - local containerName=$1 - local marinerVersion=$2 - local imageType=$3 - local packagesToInstall=$4 - local packagesToHoldback=$5 - local installNonrootUser=$6 - local user=root - local userUid=0 - - if $installNonrootUser; then - user="nonroot" - userUid=65532 - fi - - # Create container - docker build . \ - -t "$containerName" \ - -f dockerfiles/dockerfile-new-image \ - --build-arg MARINER_VERSION="$marinerVersion" \ - --build-arg IMAGE_TYPE="$imageType" \ - --build-arg PACKAGES_TO_INSTALL="$packagesToInstall" \ - --build-arg PACKAGES_TO_HOLDBACK="$packagesToHoldback" \ - --build-arg USER="$user" \ - --build-arg USER_UID=$userUid \ - --no-cache \ - --progress=plain -} - -# Builds, Tests, and Publishes Distroless Golden Container Image. -# The first argument is the main package name i.e., component name (e.g., nodejs, azure-cli, postgresql, etc). -# The second argument is the image name i.e., container type (e.g., nodejs, azure-cli, postgres, etc). -# The third argument is the base container tag. -# The fourth argument is the set of packages to be installed in the image. -# The fifth argument is the set of packages to holdback from getting installed. -# The sixth argument is component version. -# The seventh argument is the passed in full containerName. -# The eighth argument is builder image to use in distroless test. -# The ninth argument is the flag to indicate whether to run the test or not. -function CreateDistrolessGoldenContainers { - local componentName=$1 - local containerType=$2 - local baseContainerTag=$3 - local packagesToInstall=$4 - local packagesToHoldback=$5 - local componentVersion=$6 - local containerName=$7 - local builderImage=$8 - local runTest=$9 - local containerTypeNoDash - - echo "------ Display Arguments ------" - echo "Component Name: -> $componentName" - echo "Container Type: -> $containerType" - echo "Base Container Tag: -> $baseContainerTag" - echo "Packages to Install: -> $packagesToInstall" - echo "Packages to Holdback: -> $packagesToHoldback" - echo "Component Version: -> $componentVersion" - echo "Container Name: -> $containerName" - echo "Run Test: -> $runTest" - - echo "+++ create distroless container for $componentName" - containerTypeNoDash=${containerType//-/} # Removes dash from containerType. Ex: azure-cli -> azurecli - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $containerName" - - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - echo " -> Modified Container Name: $containerName" - fi - - standardContainerName="$containerName:$componentVersion-cm$base_container_tag" - debugContainerName="$containerName:$componentVersion-debug-cm$base_container_tag" - nonrootContainerName="$containerName:$componentVersion-nonroot-cm$base_container_tag" - debugNonrootContainerName="$containerName:$componentVersion-debug-nonroot-cm$base_container_tag" - - marinara="marinara" - marinaraSrcDir="$TEMPDIR/$marinara-src" - git clone "https://github.com/microsoft/$marinara.git" "$marinaraSrcDir" - pushd "$marinaraSrcDir" - - # replace base container registry prefix by golden container registry prefix (if any) - if [[ -n $baseRegistryPrefix ]]; then - # add base container registry prefix to MARINARA - MARINARA_IMAGE=$CONTAINER_REGISTRY_NAME_FULL/$baseRegistryPrefix/$marinara:$baseContainerTag - else - MARINARA_IMAGE=$CONTAINER_REGISTRY_NAME_FULL/$marinara:$baseContainerTag - fi - echo "MARINARA_IMAGE -> $MARINARA_IMAGE" - - # Get Mariner version from base container tag - OLDIFS=$IFS - IFS='.' - read -ra tag_parts <<< "$baseContainerTag" - IFS=$OLDIFS - - mariner_version="${tag_parts[0]}.0" - - # Update dockerfile-marinara to use the current base container - sed -E "s|^FROM .*builder$|FROM $MARINARA_IMAGE as builder|g" -i "dockerfiles/dockerfile-new-image" - - # Create standard container - DockerBuild "$standardContainerName" "$mariner_version" "custom" "$packagesToInstall" "$packagesToHoldback" false - - # Create debug container - DockerBuild "$debugContainerName" "$mariner_version" "custom-debug" "$packagesToInstall" "$packagesToHoldback" false - - # Create nonroot container - DockerBuild "$nonrootContainerName" "$mariner_version" "custom-nonroot" "$packagesToInstall" "$packagesToHoldback" true - - # Create debug nonroot container - DockerBuild "$debugNonrootContainerName" "$mariner_version" "custom-debug-nonroot" "$packagesToInstall" "$packagesToHoldback" true - - popd > /dev/null - - echo "+++ remove $marinaraSrcDir" - sudo rm -rf "$marinaraSrcDir" - - # Test image - if [ $runTest -ne 0 ]; then - test_distroless_container "$containerTypeNoDash-distroless" "$builderImage" "$standardContainerName" - test_distroless_container "$containerTypeNoDash-distroless" "$builderImage" "$debugContainerName" - test_distroless_container "$containerTypeNoDash-distroless" "$builderImage" "$nonrootContainerName" - test_distroless_container "$containerTypeNoDash-distroless" "$builderImage" "$debugNonrootContainerName" - fi - - # Publish containers - publish_container "$standardContainerName" - publish_container "$debugContainerName" - publish_container "$nonrootContainerName" - publish_container "$debugNonrootContainerName" - - # Save containers names - { - echo "$standardContainerName"; - echo "$debugContainerName"; - echo "$nonrootContainerName"; - echo "$debugNonrootContainerName"; - } >> "$TEMPDIR/$file_name_prefix-$containerTypeNoDash$file_ext" - echo "----------------------------------------------------------------------" - - save_container_list -} - -function getPkgsFromFile() { - local folderName=$1 - local fileName=$2 - local -n array=$3 - while read -r pkg; do - array+=("$pkg") - done < "$CONTAINER_SRC_DIR/$folderName/$fileName" -} - -# Creates azurecli container -function create_azurecli_container { - local pkgsFileName="$AZURECLI_NO_DASH.pkg" - local packagesToInstall=() - getPkgsFromFile $AZURECLI_NO_DASH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$AZURECLI" \ - "$AZURECLI" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-AzureCLI" \ - 1 \ - "$base_container_name/$AZURECLI" -} - -# Creates memcached container -function create_memcached_container { - local pkgsFileName="$MEMCACHED.pkg" - local packagesToInstall=() - getPkgsFromFile $MEMCACHED $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$MEMCACHED" \ - "$MEMCACHED" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Memcached" \ - 1 \ - "$base_container_name/$MEMCACHED" -} - -# Creates nginx container -function create_nginx_container { - local pkgsFileName="$NGINX.pkg" - local packagesToInstall=() - getPkgsFromFile $NGINX $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$NGINX" \ - "$NGINX" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Nginx" \ - 1 \ - "$base_container_name/$NGINX" -} - -# Creates nodejs container -function create_nodejs_container { - local nodejsPkgsFileName="$NODEJS.pkg" - local packagesToInstall=() - getPkgsFromFile $NODEJS $nodejsPkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$NODEJS" \ - "$NODEJS" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Nodejs" \ - 1 \ - "$base_container_name/$NODEJS" - - local packagesToInstallInDistrolessNodejs=('distroless-packages-base' 'nodejs') - local packagesInDistrolessNodejs="${packagesToInstallInDistrolessNodejs[*]}" - - local packagesToHoldbackInDistrolessNodejs=('bash' 'bzi' 'coreutils' 'gmp' 'grep' 'libselinux' 'pcre' 'pcre-libs') - local holdbackInDistroless="${packagesToHoldbackInDistrolessNodejs[*]}" - - componentVersion=${COMPONENT_VERSIONS[$NODEJS]} - builderImage=${BUILDER_IMAGES[$NODEJS]} - CreateDistrolessGoldenContainers \ - "$NODEJS" \ - "$NODEJS" \ - "$base_container_tag" \ - "$packagesInDistrolessNodejs" \ - "$holdbackInDistroless" \ - "$componentVersion" \ - "$CONTAINER_REGISTRY_NAME_FULL/distroless/$NODEJS" \ - "$builderImage" \ - 1 - - local nodejs18PkgsFileName="${NODEJS}18.pkg" - local packagesToInstall18=() - getPkgsFromFile $NODEJS $nodejs18PkgsFileName packagesToInstall18 - local packages18="${packagesToInstall18[*]}" - CreateGoldenContainer \ - "${NODEJS}18" \ - "$NODEJS" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages18" \ - "Dockerfile-Nodejs" \ - 1 \ - "$base_container_name/$NODEJS" - - - local packagesToInstallInDistrolessNodejs18=('distroless-packages-base' 'nodejs18') - local packagesInDistrolessNodejs18="${packagesToInstallInDistrolessNodejs18[*]}" - componentVersion=${COMPONENT_VERSIONS[$NODEJS]} - builderImage=${BUILDER_IMAGES[${NODEJS}18]} - CreateDistrolessGoldenContainers \ - "${NODEJS}18" \ - "$NODEJS" \ - "$base_container_tag" \ - "$packagesInDistrolessNodejs18" \ - "$holdbackInDistroless" \ - "$componentVersion" \ - "$CONTAINER_REGISTRY_NAME_FULL/distroless/$NODEJS" \ - "$builderImage" \ - 1 -} - -# Creates php container -function create_php_container { - local pkgsFileName="$PHP.pkg" - local packagesToInstall=() - getPkgsFromFile $PHP $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$PHP" \ - "$PHP" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-PHP" \ - 1 \ - "$base_container_name/$PHP" -} - -# Creates python container -function create_python_container { - local pkgsFileName="$PYTHON.pkg" - local packagesToInstall=() - getPkgsFromFile $PYTHON $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$PYTHON" \ - "$PYTHON" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Python" \ - 1 \ - "$base_container_name/$PYTHON" - - local packagesToInstallInDistroless=('distroless-packages-base' 'python3') - local packagesInDistroless="${packagesToInstallInDistroless[*]}" - - local packagesToHoldbackInDistroless=('bash' 'grep' 'coreutils' 'gmp' 'libselinux' 'pcre' 'pcre-libs') - local holdbackInDistroless="${packagesToHoldbackInDistroless[*]}" - - componentVersion=${COMPONENT_VERSIONS[$PYTHON]} - builderImage=${BUILDER_IMAGES[$PYTHON]} - CreateDistrolessGoldenContainers \ - "$PYTHON" \ - "$PYTHON" \ - "$base_container_tag" \ - "$packagesInDistroless" \ - "$holdbackInDistroless" \ - "$componentVersion" \ - "$CONTAINER_REGISTRY_NAME_FULL/distroless/$PYTHON" \ - "$builderImage" \ - 0 -} - -# Creates pytorch container -function create_pytorch_container { - local pkgsFileName="$PYTORCH.pkg" - local packagesToInstall=() - getPkgsFromFile $PYTORCH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "${PYTHON}3-$PYTORCH" \ - "$PYTORCH" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Pytorch" \ - 1 \ - "$base_container_name/$PYTORCH" -} - -# Creates rabbitmq-server container -function create_rabbitmqserver_container { - local pkgsFileName="$RABBITMQSERVER_NO_DASH.pkg" - local packagesToInstall=() - getPkgsFromFile $RABBITMQSERVER_NO_DASH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$RABBITMQSERVER" \ - "$RABBITMQSERVER" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-rabbitmq-server" \ - 0 \ - "$base_container_name/$RABBITMQSERVER" -} - -# Creates ruby container -function create_ruby_container { - # Packages already installed in base mariner -> readline, zlib, bzip2. - # Replacement ruby runtime dependency: - # musl -> glibc, kernel-headers, binutils; no musl rpm in PMC. - local pkgsFileName="$RUBY.pkg" - local packagesToInstall=() - getPkgsFromFile $RUBY $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$RUBY" \ - "$RUBY" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Ruby" \ - 1 \ - "$base_container_name/$RUBY" -} - -# Creates rust container -function create_rust_container { - local pkgsFileName="$RUST.pkg" - local packagesToInstall=() - getPkgsFromFile $RUST $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$RUST" \ - "$RUST" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Rust" \ - 0 \ - "$base_container_name/$RUST" -} - -# Creates postgres container -function create_postgres_container { - local pkgsFileName="$POSTGRES.pkg" - local packagesToInstall=() - getPkgsFromFile $POSTGRES $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "${POSTGRES}ql" \ - "$POSTGRES" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Postgres" \ - 1 \ - "$base_container_name/$POSTGRES" -} - -# Creates InfluxDB container -function create_influxdb_container { - local pkgsFileName="$INFLUX_DB.pkg" - local packagesToInstall=() - getPkgsFromFile $INFLUX_DB $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$INFLUX_DB" \ - "$INFLUX_DB" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Influxdb" \ - 1 \ - "$base_container_name/$INFLUX_DB" -} - -# Creates prometheus container -function create_prometheus_container { - local pkgsFileName="$PROMETHEUS.pkg" - local packagesToInstall=() - getPkgsFromFile $PROMETHEUS $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$PROMETHEUS" \ - "$PROMETHEUS" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Prometheus" \ - 1 \ - "$base_container_name/$PROMETHEUS" - - local packagesToInstallInDistroless=('distroless-packages-base' 'prometheus') - local packagesInDistroless="${packagesToInstallInDistroless[*]}" - - # Potentially extraneous, can be investigated more. - local packagesToHoldbackInDistroless=('bash' 'grep' 'coreutils' 'gmp' 'libselinux' 'pcre' 'pcre-libs') - local holdbackInDistroless="${packagesToHoldbackInDistroless[*]}" - - componentVersion=${COMPONENT_VERSIONS["$PROMETHEUS"]} - builderImage=${BUILDER_IMAGES[$PROMETHEUS]} - CreateDistrolessGoldenContainers \ - "$PROMETHEUS" \ - "$PROMETHEUS" \ - "$base_container_tag" \ - "$packagesInDistroless" \ - "$holdbackInDistroless" \ - "$componentVersion" \ - "$CONTAINER_REGISTRY_NAME_FULL/distroless/$PROMETHEUS" \ - "$builderImage" \ - 0 -} - -function create_redis_container { - local pkgsFileName="$REDIS.pkg" - local packagesToInstall=() - getPkgsFromFile $REDIS $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$REDIS" \ - "$REDIS" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Redis" \ - 1 \ - "$base_container_name/$REDIS" -} - -# Creates prometheus-adapter container -function create_prometheus_adapter_container { - local pkgsFileName="$PROMETHEUS_ADAPTER_NO_DASH.pkg" - local packagesToInstall=() - getPkgsFromFile $PROMETHEUS_ADAPTER_NO_DASH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$PROMETHEUS_ADAPTER" \ - "$PROMETHEUS_ADAPTER" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Prometheus-Adapter" \ - 0 \ - "$base_container_name/$PROMETHEUS_ADAPTER" - - local packagesToInstallInDistroless=('distroless-packages-base' 'prometheus-adapter') - local packagesInDistroless="${packagesToInstallInDistroless[*]}" - - # Potentially extraneous, can be investigated more. - local packagesToHoldbackInDistroless=('bash' 'grep' 'coreutils' 'gmp' 'libselinux' 'pcre' 'pcre-libs') - local holdbackInDistroless="${packagesToHoldbackInDistroless[*]}" - - componentVersion=${COMPONENT_VERSIONS["$PROMETHEUS_ADAPTER"]} - builderImage=${BUILDER_IMAGES[$PROMETHEUS_ADAPTER]} - CreateDistrolessGoldenContainers \ - "$PROMETHEUS_ADAPTER" \ - "$PROMETHEUS_ADAPTER" \ - "$base_container_tag" \ - "$packagesInDistroless" \ - "$holdbackInDistroless" \ - "$componentVersion" \ - "$CONTAINER_REGISTRY_NAME_FULL/distroless/$PROMETHEUS_ADAPTER" \ - "$builderImage" \ - 0 -} - -# Creates telegraf container -function create_telegraf_container { - local pkgsFileName="$TELEGRAF.pkg" - local packagesToInstall=() - getPkgsFromFile $TELEGRAF $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$TELEGRAF" \ - "$TELEGRAF" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Telegraf" \ - 1 \ - "$base_container_name/$TELEGRAF" -} - -# Creates tensorflow container -function create_tensorflow_container { - local pkgsFileName="$TENSORFLOW.pkg" - local packagesToInstall=() - getPkgsFromFile $TENSORFLOW $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "python3-${TENSORFLOW}" \ - "$TENSORFLOW" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Tensorflow" \ - 1 \ - "$base_container_name/$TENSORFLOW" -} - -# Creates openmpi container -function create_openmpi_container { - local pkgsFileName="$OPENMPI.pkg" - local packagesToInstall=() - getPkgsFromFile $OPENMPI $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - CreateGoldenContainer \ - "$OPENMPI" \ - "$OPENMPI" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-Openmpi" \ - 1 \ - "$base_container_name/$OPENMPI" -} - -# ---- Mariner HCI IMAGES ---- - -# Creates Cdi containers -function create_cdi_containers { - source $CONTAINER_SRC_DIR/scripts/BuildCdiContainers.sh - create_cdi_subcomp_containers -} - -# Creates Cert-Manager containers -function create_cert_manager_containers { - source $CONTAINER_SRC_DIR/scripts/BuildCertManagerContainers.sh - create_cert_manager_subcomp_containers -} - -# Create containers for each of the kubevirt sub components - -# virt-operator, virt-api, virt-handler, virt-launcher, virt-controller -function create_kubevirt_containers { - source $CONTAINER_SRC_DIR/scripts/BuildKubevirtContainers.sh - create_kubevirt_subcomp_containers -} - -# Create Multus container -function create_multus_container_helper { - source $CONTAINER_SRC_DIR/scripts/BuildMultusContainer.sh - create_multus_container -} - -# Create Sriov network device plugin container -function create_sriov_dp_containers { - source $CONTAINER_SRC_DIR/scripts/BuildSriovDpContainer.sh - create_sriov_dp_container -} - -function start_building_containers { - case $GOLDEN_CONTAINER_IMAGE in - - "$AZURECLI_NO_DASH") - create_azurecli_container - ;; - - "$MEMCACHED") - create_memcached_container - ;; - - "$NGINX") - create_nginx_container - ;; - - "$NODEJS") - create_nodejs_container - ;; - - "$PHP") - create_php_container - ;; - - "$PYTHON") - create_python_container - ;; - - "$RABBITMQSERVER_NO_DASH") - create_rabbitmqserver_container - ;; - - "$REDIS") - create_redis_container - ;; - - "$RUBY") - create_ruby_container - ;; - - "$RUST") - create_rust_container - ;; - - "$POSTGRES") - create_postgres_container - ;; - - "$INFLUX_DB") - create_influxdb_container - ;; - - "$PROMETHEUS") - create_prometheus_container - ;; - - "$PROMETHEUS_ADAPTER_NO_DASH") - create_prometheus_adapter_container - ;; - - "$PYTORCH") - create_pytorch_container - ;; - - "$TELEGRAF") - create_telegraf_container - ;; - - "$TENSORFLOW") - create_tensorflow_container - ;; - - "$OPENMPI") - create_openmpi_container - ;; - - "$CDI_BASE_COMPONENT") - create_cdi_containers - ;; - - "$CERT_MANAGER_NO_DASH") - create_cert_manager_containers - ;; - - "$KUBEVIRT_BASE_COMPONENT") - create_kubevirt_containers - ;; - - "$MULTUS") - create_multus_container_helper - ;; - - "$SRIOV_NETWORK_DEVICE_PLUGIN_NO_DASH") - create_sriov_dp_containers - ;; - esac -} - -# source the CommonFunctions script to get the following functions: -# - azure_login -# - generate_container_sbom -# - SetDockerDefaultStorageLocation -# - ResetDockerDefaultStorageLocation -# - save_container_list -# - test_golden_container -# - publish_container -# - getRegistryPrefix -source $CONTAINER_SRC_DIR/scripts/CommonFunctions.sh - -input_validation -read_base_container_name -azure_login "$base_container_acr" - -# Create a variable to store the value of whether GOLDEN_CONTAINER_IMAGE is an HCI image -export IS_HCI_IMAGE=false -checkIfHciImage IS_HCI_IMAGE -echo "Is this an HCI Image: $IS_HCI_IMAGE" - -start_building_containers diff --git a/.pipelines/containerSourceData/scripts/BuildGoldenDistrolessContainer.sh b/.pipelines/containerSourceData/scripts/BuildGoldenDistrolessContainer.sh new file mode 100644 index 00000000000..0beb234b4ea --- /dev/null +++ b/.pipelines/containerSourceData/scripts/BuildGoldenDistrolessContainer.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +set -e + +function DockerBuild { + local containerName=$1 + local azureLinuxVersion=$2 + local imageType=$3 + local packagesToInstall=$4 + local packagesToHoldback=$5 + local installNonrootUser=$6 + local rpmsDir=$7 + local user=root + local userUid=0 + + if $installNonrootUser; then + user="nonroot" + userUid=65532 + fi + + # Create container + echo "+++ Create container $containerName" + docker build . \ + -t "$containerName" \ + -f "$marinaraSrcDir/dockerfiles/dockerfile-new-image" \ + --build-arg MARINER_VERSION="$azureLinuxVersion" \ + --build-arg IMAGE_TYPE="$imageType" \ + --build-arg PACKAGES_TO_INSTALL="$packagesToInstall" \ + --build-arg PACKAGES_TO_HOLDBACK="$packagesToHoldback" \ + --build-arg USER="$user" \ + --build-arg USER_UID=$userUid \ + --build-arg RPMS="$rpmsDir" \ + --build-arg LOCAL_REPO_FILE="$marinaraSrcDir/local.repo" \ + --no-cache \ + --progress=plain +} + +function create_distroless_container { + echo "+++ Create distroless container" + + distrolessPkgsFile="$CONTAINER_SRC_DIR/$IMAGE/distroless/$PACKAGE_FILE" + DISTROLESS_PACKAGES_TO_INSTALL=$(paste -s -d' ' < "$distrolessPkgsFile") + distrolessPkgsHoldbackFile="$CONTAINER_SRC_DIR/$IMAGE/distroless/holdback-$PACKAGE_FILE" + DISTROLESS_PACKAGES_TO_HOLD_BACK=$(paste -s -d' ' < "$distrolessPkgsHoldbackFile") + echo "Distroless Packages to install -> $DISTROLESS_PACKAGES_TO_INSTALL" + echo "Distroless Packages to hold back -> $DISTROLESS_PACKAGES_TO_HOLD_BACK" + + DISTROLESS_GOLDEN_IMAGE_NAME=${GOLDEN_IMAGE_NAME//base/distroless} + standardContainerName="$DISTROLESS_GOLDEN_IMAGE_NAME:$COMPONENT_VERSION-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + debugContainerName="$DISTROLESS_GOLDEN_IMAGE_NAME:$COMPONENT_VERSION-debug-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + nonrootContainerName="$DISTROLESS_GOLDEN_IMAGE_NAME:$COMPONENT_VERSION-nonroot-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + debugNonrootContainerName="$DISTROLESS_GOLDEN_IMAGE_NAME:$COMPONENT_VERSION-debug-nonroot-$DISTRO_IDENTIFIER$BASE_IMAGE_TAG" + + marinara="marinara" + marinaraSrcDir="$marinara-src" + + echo "+++ Clone marinara repo" + git clone "https://github.com/microsoft/$marinara.git" "$WORK_DIR/$marinaraSrcDir" + + # It is important to operate from the $WORK_DIR to ensure that docker can access the files. + pushd "$WORK_DIR" > /dev/null + + MARINARA_IMAGE=${BASE_IMAGE_NAME_FULL/base\/core/$marinara} + echo "MARINARA_IMAGE -> $MARINARA_IMAGE" + + sed -E "s|^FROM .*builder$|FROM $MARINARA_IMAGE as builder|g" -i "$marinaraSrcDir/dockerfiles/dockerfile-new-image" + + # WORK_DIR has a directory named 'Stage' which created inside prepare_docker_directory function + # This directory has a directory named RPMS which contains the RPMs to be installed in the container. + # The path to rpms is /Stage/RPMS + rpmsPath="/Stage/RPMS" + + # Create standard container + DockerBuild \ + "$standardContainerName" \ + "$AZURE_LINUX_VERSION" \ + "custom" \ + "$DISTROLESS_PACKAGES_TO_INSTALL" \ + "$DISTROLESS_PACKAGES_TO_HOLD_BACK" \ + false \ + "$rpmsPath" + + # Create debug container + DockerBuild \ + "$debugContainerName" \ + "$AZURE_LINUX_VERSION" \ + "custom-debug" \ + "$DISTROLESS_PACKAGES_TO_INSTALL" \ + "$DISTROLESS_PACKAGES_TO_HOLD_BACK" \ + false \ + "$rpmsPath" + + # Create nonroot container + DockerBuild \ + "$nonrootContainerName" \ + "$AZURE_LINUX_VERSION" \ + "custom-nonroot" \ + "$DISTROLESS_PACKAGES_TO_INSTALL" \ + "$DISTROLESS_PACKAGES_TO_HOLD_BACK" \ + true \ + "$rpmsPath" + + # Create debug nonroot container + DockerBuild \ + "$debugNonrootContainerName" \ + "$AZURE_LINUX_VERSION" \ + "custom-debug-nonroot" \ + "$DISTROLESS_PACKAGES_TO_INSTALL" \ + "$DISTROLESS_PACKAGES_TO_HOLD_BACK" \ + true \ + "$rpmsPath" + + popd > /dev/null + + echo "+++ Save distroless container images to file PublishedContainers-$IMAGE.txt" + { + echo "$standardContainerName"; + echo "$debugContainerName"; + echo "$nonrootContainerName"; + echo "$debugNonrootContainerName"; + } >> "$OUTPUT_DIR/PublishedContainers-$IMAGE.txt" + + publish_to_acr "$standardContainerName" + publish_to_acr "$debugContainerName" + publish_to_acr "$nonrootContainerName" + publish_to_acr "$debugNonrootContainerName" +} diff --git a/.pipelines/containerSourceData/scripts/BuildKubevirtContainers.sh b/.pipelines/containerSourceData/scripts/BuildKubevirtContainers.sh deleted file mode 100755 index 47b57091281..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildKubevirtContainers.sh +++ /dev/null @@ -1,180 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -function create_kubevirt_container_image_base { - local componentName=$1 - local containerType=$2 - local baseContainerName=$3 - local baseContainerTag=$4 - local packagesToInstall=$5 - local goldenImageDockerfile=$6 - local originalContainerName=$7 - local containerTypeNoDash - - echo "------ Display Arguments ------" - echo "Component Name: -> $componentName" - echo "Container Type: -> $containerType" - echo "Base Container Name: -> $baseContainerName" - echo "Base Container Tag: -> $baseContainerTag" - echo "Packages to Install: -> $packagesToInstall" - echo "Dockerfile: -> $goldenImageDockerfile" - echo "Container Name: -> $originalContainerName" - - echo "+++ create container based on $baseContainerName/core:$baseContainerTag for $componentName" - containerTypeNoDash=${containerType//-/} - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $originalContainerName" - - local containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp "$CONTAINER_SRC_DIR/$KUBEVIRT_BASE_COMPONENT/$goldenImageDockerfile" "$containerBuildDir/Dockerfile" - - # Ensure that the path exists before copying files. - if [ -d "$CONTAINER_SRC_DIR/$KUBEVIRT_BASE_COMPONENT/configuration-files" ]; then - cp "$CONTAINER_SRC_DIR/$KUBEVIRT_BASE_COMPONENT/configuration-files"/* "$containerBuildDir" - fi - - pushd "$containerBuildDir" - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - - cat Dockerfile - - if [ "$DISABLE_DOCKER_REDIRECTION" != "true" ]; then - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - fi - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - -t "$originalContainerName" --no-cache --progress=plain \ - -f $containerBuildDir/Dockerfile . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - - local containerId - local installedPackage - local componentVersion - - containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - # exec as root as the default user for some containers is non-root - # componentName e.g. nodejs-16.16.0-1.cm2.x86_64 - installedPackage=$(docker exec -u 0 "$containerId" tdnf repoquery --installed "$componentName" | grep ^"$componentName") - echo "Full Installed Package: -> $installedPackage" - componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - docker rm -f "$containerId" - - # Rename the image to include package version - # For HCI Images, do not include "-cm" in the image tag; Instead use a "." - if $IS_HCI_IMAGE; then - # Example: acrafoimages.azurecr.io/base/kubevirt/virt-operator:0.59.0-2.2.0.20230607-amd64 - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - else - # Example: cblmarinermain.azurecr.io/base/nodejs:16.19.1-2-cm2.0.20230607-amd64 - local containerName="$originalContainerName:$componentVersion-cm$baseContainerTag" - fi - - # replace base container registry prefix by golden container registry prefix (if any) - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - # Publish image - publish_container "$containerName" - - local containerNameSanitized - containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - - if [[ "$DISABLE_SBOM_GENERATION" != "true" ]]; then - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - fi - popd - - if [ "$DISABLE_DOCKER_REDIRECTION" != "true" ]; then - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - fi - - sudo rm -rf "$newDockerStorageLocation" - - # Clean up temp folder - sudo rm -rf "$containerBuildDir" - - # Save container name - echo "$containerName" >> "$TEMPDIR/$file_name_prefix-$containerTypeNoDash$file_ext" - echo "----------------------------------------------------------------------" - - save_container_list -} - -function create_kubevirt_subcomp_containers { - # NOTE: qemu and edk2 are architecture specific packages. - # Include this if when edk2 is availble for ARM as well - # if [[ $CONTAINER_ARCHITECTURE == "*AMD64*" ]]; then - # else add virtlauncher_rpmsToInstall+=('qemu-system-aarch64') - - mkdir -p $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - local sub_components - sub_components=('virt-operator' 'virt-api' 'virt-controller' 'virt-handler' 'virt-launcher') - - for comp in ${sub_components[@]} - do - # To build for specific versions - include it here with the name - dependency_component=$KUBEVIRT_BASE_COMPONENT-$comp - local pkgsFileName="$comp.pkg" - local packagesToInstall=() - getPkgsFromFile $KUBEVIRT_BASE_COMPONENT $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - echo "packages to install " $packages - create_kubevirt_container_image_base \ - "$dependency_component" \ - "$comp" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "Dockerfile-$dependency_component" \ - "$CONTAINER_REGISTRY_NAME_FULL/base/$KUBEVIRT_FOLDER_PREFIX/$comp" - done -} diff --git a/.pipelines/containerSourceData/scripts/BuildMultusContainer.sh b/.pipelines/containerSourceData/scripts/BuildMultusContainer.sh deleted file mode 100644 index 8ac9670b71b..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildMultusContainer.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -function create_multus_container_image_base { - local componentName - local baseContainerName - local baseContainerTag - local containerBuildDir - local initialDockerfile - local packagesToInstall - - # $1: component name - # $2: container name - # $3: container tag - # $4: packages to install - # $5: initial Dockerfile - componentName=$1 - baseContainerName=$2 - baseContainerTag=$3 - packagesToInstall=$4 - initialDockerfile=$5 - - local originalContainerName="$CONTAINER_REGISTRY_NAME_FULL/base/$componentName" - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $containerName" - - containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp $initialDockerfile $containerBuildDir/Dockerfile - - pushd $containerBuildDir > /dev/null - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - -t "$originalContainerName" --no-cache --progress=plain . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - - local containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - local installedPackage=$(docker exec "$containerId" rpm -qa | grep ^"$componentName") # nodejs-16.16.0-1.cm2.x86_64 - echo "Full Installed Package: -> $installedPackage" - local componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - docker rm -f "$containerId" - - # Rename the image to include package version - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - # replace base container registry prefix by golden container registry prefix (if any) - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - local containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - - publish_container "$containerName" - - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - - echo "$containerName" >> $TEMPDIR/$file_name_prefix-$componentName$file_ext - - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Clean up docker storage folder - sudo rm -rf "$newDockerStorageLocation" - - # clean up temp folder - popd > /dev/null - sudo rm -rf $containerBuildDir - - echo "----------------------------------------------------------------------" -} - -function create_multus_container { - mkdir -p $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - local dependency_component=$MULTUS - local pkgsFileName="$MULTUS.pkg" - local packagesToInstall=() - getPkgsFromFile $MULTUS $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - - echo "+++ create container based on $base_container_name:$base_container_tag for $dependency_component" - create_multus_container_image_base \ - "$dependency_component" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "$CONTAINER_SRC_DIR/$MULTUS/Dockerfile-Multus" - - # Save text files generated in TEMPDIR - echo "+++ publish container list into pipeline artifacts" - cp $TEMPDIR/$file_name_prefix-*$file_ext $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - -} \ No newline at end of file diff --git a/.pipelines/containerSourceData/scripts/BuildSriovDpContainer.sh b/.pipelines/containerSourceData/scripts/BuildSriovDpContainer.sh deleted file mode 100644 index 83ef625e87f..00000000000 --- a/.pipelines/containerSourceData/scripts/BuildSriovDpContainer.sh +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -function create_sriov_dp_container_image_base { - local componentName - local baseContainerName - local baseContainerTag - local containerBuildDir - local initialDockerfile - local packagesToInstall - - # $1: component name - # $2: container name - # $3: container tag - # $4: packages to install - # $5: initial Dockerfile - componentName=$1 - baseContainerName=$2 - baseContainerTag=$3 - packagesToInstall=$4 - initialDockerfile=$5 - - local originalContainerName="$CONTAINER_REGISTRY_NAME_FULL/base/$componentName" - - echo - echo "----------------------------------------------------------------------" - echo "+++ create container $containerName" - - containerBuildDir="$TEMPDIR/ContainerBuildDir" - hostMountedDir="$TEMPDIR/ContainerBuildDir/Stage" - newDockerStorageLocation="$TEMPDIR/storage" - - mkdir -p "$containerBuildDir" - mkdir -p "$hostMountedDir" - mkdir -p "$newDockerStorageLocation" - - # Copy files into docker context directory - tar -xf "$MARINER_RPMS_TARBALL" -C "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/marinerLocalRepo.repo" "$hostMountedDir"/ - cp "$CONTAINER_SRC_DIR/Dockerfile-Initial" "$containerBuildDir/Dockerfile-Initial" - cp $initialDockerfile $containerBuildDir/Dockerfile - - pushd $containerBuildDir > /dev/null - - # set Dockerfile - echo "+++ Updating Dockerfile" - mainRunInstruction=$(cat Dockerfile-Initial) - sed -E "s|@INCLUDE_MAIN_RUN_INSTRUCTION@|$mainRunInstruction|g" -i Dockerfile - - SetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Build image - docker buildx build \ - --build-arg BASE_IMAGE="$baseContainerName/core:$baseContainerTag" \ - --build-arg RPMS_TO_INSTALL="$packagesToInstall" \ - -t "$originalContainerName" --no-cache --progress=plain . - - # Get the installed package's version - echo "+++ Get version of the installed package in the container" - - local containerId=$(docker run --entrypoint /bin/bash -dt "$originalContainerName") - local installedPackage=$(docker exec "$containerId" rpm -qa | grep ^"$componentName") # nodejs-16.16.0-1.cm2.x86_64 - echo "Full Installed Package: -> $installedPackage" - local componentVersion=$(echo "$installedPackage" | awk '{n=split($0,a,"-")};{split(a[n],b,".")}; {print a[n-1]"-"b[1]}') # 16.16.0-1 - echo "Component Version -> $componentVersion" - docker rm -f "$containerId" - - # Rename the image to include package version - local containerName="$originalContainerName:$componentVersion.$baseContainerTag" - # replace base container registry prefix by golden container registry prefix (if any) - local baseRegistryPrefix="" - local goldenRegistryPrefix="" - getRegistryPrefix 'base' $PUBLISHING_LEVEL $BRANCH_NAME baseRegistryPrefix - getRegistryPrefix $GOLDEN_CONTAINER_IMAGE $PUBLISHING_LEVEL $BRANCH_NAME goldenRegistryPrefix - if [[ -n $goldenRegistryPrefix ]]; then - if [[ -n $baseRegistryPrefix && \ - $containerName == *"$baseRegistryPrefix"* ]]; then - # replace base container registry prefix by golden container registry prefix - echo "replace $baseRegistryPrefix with $goldenRegistryPrefix in $containerName" - containerName=${containerName/"$baseRegistryPrefix"/"$goldenRegistryPrefix"} - else - # add golden container registry prefix - echo "add $goldenRegistryPrefix prefix to $containerName" - containerName=${containerName/"$CONTAINER_REGISTRY_NAME_FULL"/"$CONTAINER_REGISTRY_NAME_FULL/$goldenRegistryPrefix"} - fi - fi - - docker image tag "$originalContainerName" "$containerName" - docker rmi -f "$originalContainerName" - echo "Container Name: -> $containerName" - - local containerNameSanitized=$(echo "$containerName" | tr '/' '-' | tr ':' '_') - - publish_container "$containerName" - - # Call generate_container_sbom function to generate SBOM - generate_container_sbom \ - "$componentName" \ - "$baseContainerName" \ - "$baseContainerTag" \ - "$containerName" \ - "$componentVersion" \ - "$containerNameSanitized" - - echo "$containerName" >> $TEMPDIR/$file_name_prefix-$componentName$file_ext - - ResetDockerDefaultStorageLocation "$newDockerStorageLocation" - - # Clean up docker storage folder - sudo rm -rf "$newDockerStorageLocation" - - # clean up temp folder - popd > /dev/null - sudo rm -rf $containerBuildDir - - echo "----------------------------------------------------------------------" -} - -function create_sriov_dp_container { - mkdir -p $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER - local dependency_component=$SRIOV_NETWORK_DEVICE_PLUGIN - local pkgsFileName="$SRIOV_NETWORK_DEVICE_PLUGIN_NO_DASH.pkg" - local packagesToInstall=() - getPkgsFromFile $SRIOV_NETWORK_DEVICE_PLUGIN_NO_DASH $pkgsFileName packagesToInstall - local packages="${packagesToInstall[*]}" - - echo "+++ create container based on $base_container_name:$base_container_tag for $dependency_component" - create_sriov_dp_container_image_base \ - "$dependency_component" \ - "$base_container_name" \ - "$base_container_tag" \ - "$packages" \ - "$CONTAINER_SRC_DIR/$SRIOV_NETWORK_DEVICE_PLUGIN_NO_DASH/Dockerfile-sriov-network-device-plugin" - - # Save text files generated in TEMPDIR - echo "+++ publish container list into pipeline artifacts" - cp $TEMPDIR/$file_name_prefix-*$file_ext $OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER -} \ No newline at end of file diff --git a/.pipelines/containerSourceData/scripts/CommonFunctions.sh b/.pipelines/containerSourceData/scripts/CommonFunctions.sh deleted file mode 100755 index 0b06d2990bb..00000000000 --- a/.pipelines/containerSourceData/scripts/CommonFunctions.sh +++ /dev/null @@ -1,243 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# Logs into Azure -function azure_login { - # Note this script assumes that az login has already been done - echo " -> login into ACR $1" - az acr login --name "$1" -} - -# Builds SBOM -function generate_container_sbom { - local component_name=$1 - local base_container_name=$2 - local base_container_tag=$3 - local container_name=$4 - local component_version_revision=$5 - local container_name_sanitized=$6 - - echo - echo "=====================================================================" - echo "Generate SBOM for containers $container_image_name" - echo "=====================================================================" - echo - - DOCKER_BUILD_DIR="$(pwd)" - - # generate-container-sbom.sh will create the SBOM at the following path - IMAGE_SBOM_MANIFEST_PATH="$DOCKER_BUILD_DIR/_manifest/spdx_2.2/manifest.spdx.json" - "$ROOT_FOLDER"/.pipelines/generate-container-sbom.sh \ - "$DOCKER_BUILD_DIR" \ - "$container_name" \ - "$MANIFEST_TOOL_DIR" \ - "$base_container_name-$component_name" \ - "$component_version_revision-cm$base_container_tag" - - cp -v "$IMAGE_SBOM_MANIFEST_PATH" "$OUTPUT_FOLDER/SBOM_IMAGES/$container_name_sanitized.spdx.json" - echo "Generated SBOM:'$OUTPUT_FOLDER/SBOM_IMAGES/$container_name_sanitized.spdx.json'" -} - -readonly DOCKER_DAEMON_JSON_FILE="/etc/docker/daemon.json" -readonly DOCKER_DAEMON_JSON_BACKUP_FILE="/etc/docker/daemon.json.cfbackup" - -# Sets the docker storage location to a user provided path which larger disk space -function SetDockerDefaultStorageLocation { - local newLocation=$1 - echo "Change docker default storage location" - echo "Default docker storage location" - sudo systemctl start docker - docker info | grep "Docker Root Dir" - - echo "Stop docker" - sudo systemctl stop docker.service - sudo systemctl stop docker.socket - - ls -lR /etc/docker - - # Do not clobber existing backup to not accidentally overwrite the valid backup - if [ ! -f $DOCKER_DAEMON_JSON_BACKUP_FILE ] && [ -f $DOCKER_DAEMON_JSON_FILE ]; then - echo "Backup daemon.json" - sudo cp $DOCKER_DAEMON_JSON_FILE $DOCKER_DAEMON_JSON_BACKUP_FILE - fi - - echo "Copy data-root property to daemon.json" - echo "{ \"data-root\": \"${newLocation}\" }" > daemon.json - - echo "Display daemon.json" - sudo cat daemon.json - - echo "Copy daemon.json to docker" - sudo cp daemon.json $DOCKER_DAEMON_JSON_FILE - - mkdir -p "${newLocation}" - - echo "Restart docker" - sudo systemctl daemon-reload - sudo systemctl start docker - - echo "New docker storage location" - docker info | grep "Docker Root Dir" - - echo "--------------------------------------------" -} - -# Resets the docker storage location from backup -function ResetDockerDefaultStorageLocation { - local currentLocation=$1 - echo "Reset docker default storage location" - echo "Stop docker" - sudo systemctl stop docker.service - sudo systemctl stop docker.socket - - echo "Recovering daemon.json from backup" - if [ -f $DOCKER_DAEMON_JSON_BACKUP_FILE ]; then - sudo mv $DOCKER_DAEMON_JSON_BACKUP_FILE $DOCKER_DAEMON_JSON_FILE - else - sudo rm $DOCKER_DAEMON_JSON_FILE - fi - - echo "Restart docker" - sudo systemctl daemon-reload - sudo systemctl start docker - - echo "New docker storage location" - docker info | grep "Docker Root Dir" -} - -# Saves the container list in folder named CONTAINER_LISTS_FOLDER -function save_container_list { - # Save text files generated in TEMPDIR - echo - echo "=====================================================================" - echo "Publish container list into pipeline artifacts" - echo "=====================================================================" - echo - - mkdir -pv "$OUTPUT_FOLDER/CONTAINER_LISTS_FOLDER" - cp "$TEMPDIR"/$file_name_prefix-*$file_ext "$OUTPUT_FOLDER"/CONTAINER_LISTS_FOLDER -} - -# Tests golden container -function test_golden_container { - local container_type=$1 - local container_image_name=$2 - - echo - echo "=====================================================================" - echo "Test container $container_image_name" - echo "=====================================================================" - echo - - "$ROOT_FOLDER/pipelines/test-golden-image-pipeline/test-source-artifacts/$container_type/TestRunner.sh" \ - -n "$container_image_name" \ - -o "$PWD" -} - -function test_distroless_container { - local test_dir_name=$1 - local builder_image=$2 - local container_image_name=$3 - - echo - echo "=====================================================================" - echo "Test container $container_image_name" - echo "=====================================================================" - echo - - "$ROOT_FOLDER/pipelines/test-golden-image-pipeline/test-source-artifacts/$test_dir_name/TestRunner.sh" \ - -b "$builder_image" \ - -n "$container_image_name" \ - -o "$PWD" -} - -# Publishes the given golden container to azure container registry -function publish_container { - local container_name=$1 - echo - echo "=====================================================================" - echo "Publish container $container_name" - echo "=====================================================================" - echo - - previous_login="none" - OLDIFS=$IFS - IFS='.' - read -ra name_parts <<< "$container_name" - IFS=$OLDIFS - container_registry="${name_parts[0]}" - - if [[ "$previous_login" != "$container_registry" ]]; then - echo " -> login into ACR $container_registry" - az acr login --name "$container_registry" - previous_login=$container_registry - fi - - docker image push "$container_name" - echo -} - -# Checks if $GOLDEN_CONTAINER_IMAGE is an HCI image by looking at the config file. -# Assigns a boolean to the out variables. -# The caller must define ROOT_FOLDER and GOLDEN_CONTAINER_IMAGE. -function checkIfHciImage { - local __containerImageName=$1 # [out parameter] - local isHciImage=false - ACR_MAPPING_CONFIG_FILE="$ROOT_FOLDER/pipelines/publish-containers/common/configuration/acrRepoMapping.json" - marinerHciGoldenImagesArray=$(jq ".MarinerHciGoldenImages[]" "$ACR_MAPPING_CONFIG_FILE" | tr -d \") - for marinerHciGoldenImage in $marinerHciGoldenImagesArray; do - if [[ $marinerHciGoldenImage == "$GOLDEN_CONTAINER_IMAGE" ]]; then - isHciImage=true - break - fi - done - eval $__containerImageName=$isHciImage -} - -# get registry prefix (if any) -# Assigns a string to the out variables. -# The caller must define ROOT_FOLDER -function getRegistryPrefix { - local container_name=$1 - local publishingLevel=$2 - local gitBranch=$3 - local __registryPrefix=$4 # [out parameter] - local prefix="" - - local git_branch_json="" - local acr_repo_mapping_json="" - local image_json="" - - ACR_MAPPING_CONFIG_FILE="$ROOT_FOLDER/pipelines/publish-containers/common/configuration/acrRepoMapping.json" - eval $__registryPrefix=$prefix - - git_branch_json=$(jq ".gitBranches[]|select(.gitBranch == \"$gitBranch\")" "$ACR_MAPPING_CONFIG_FILE") - if [[ -z $git_branch_json ]]; then - echo "No branch tag '$gitBranch' in json ($ACR_MAPPING_CONFIG_FILE)" - return - fi - - acr_repo_mapping_json=$(echo $git_branch_json | jq ".acrRepoMapping[]|select(.publishingLevel == \"$publishingLevel\")") - if [[ -z $acr_repo_mapping_json ]]; then - echo "No publishing level '$publishingLevel' for branch '$gitBranch' in json ($ACR_MAPPING_CONFIG_FILE)" - return - fi - - image_json=$(echo $acr_repo_mapping_json | jq ".images[]|select(.name == \"$container_name\")") - if [[ -z $image_json ]]; then - echo "No container named '$container_name' for publishing level '$publishingLevel' for branch '$gitBranch' in json ($ACR_MAPPING_CONFIG_FILE)" - return - fi - - prefix=$(echo $image_json | jq .repoPrefix | tr -d \") - # reset registry prefix to "" if it is not defined in json (jq return 'null') - if [[ $prefix == "null" ]]; then - prefix="" - echo "No registry prefix for '$container_name' branch '$gitBranch' publishing level '$publishingLevel'" - else - echo "Registry prefix '$prefix' for '$container_name' branch '$gitBranch' publishing level '$publishingLevel'" - fi - - eval $__registryPrefix=$prefix -} \ No newline at end of file diff --git a/.pipelines/containerSourceData/scripts/PublishContainers.sh b/.pipelines/containerSourceData/scripts/PublishContainers.sh new file mode 100755 index 00000000000..ce6c560515a --- /dev/null +++ b/.pipelines/containerSourceData/scripts/PublishContainers.sh @@ -0,0 +1,412 @@ +#!/bin/bash +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +set -e + +# This script is used to publish the multi-arch tags for the container images. +# Note that this script assumes that 'az login' has already been done. + +# CONTAINER_SRC_DIR is expected to contain the acrRepoParser.py script and the configuration file acrRepoV2.json. +# The acrRepoParser.py script is used to parse the ACR repository details from the acrRepoV2.json configuration file. +# The directory (assuming it is called container_artifacts) is expected to have the following structure: +# container_artifacts +# ├── configuration +# │ └── acrRepoV2.json +# └── scripts +# └── acrRepoParser.py + +# parse script parameters: +# -c -> Container pipelines' configuration directory (e.g. $(Build.SourcesDirectory)/.pipelines/container_artifacts) +# -d -> Directory containing the containers list +# -e -> Containers file name prefix +# -f -> Containers file name suffix +# -g -> GitHub branch +# -p -> Publishing level +# -o -> Output folder +while getopts ":c:d:e:f:g:p:o:" OPTIONS; do + case ${OPTIONS} in + c ) CONTAINER_SRC_DIR=$OPTARG;; + d ) PUBLISHED_CONTAINERS_DIR=$OPTARG;; + e ) PUBLISHED_CONTAINER_FILE_PREFIX=$OPTARG;; + f ) PUBLISHED_CONTAINER_FILE_SUFFIX=$OPTARG;; + g ) GITHUB_BRANCH=$OPTARG;; + p ) PUBLISHING_LEVEL=$OPTARG;; + o ) OUTPUT_FOLDER=$OPTARG;; + + \? ) + echo "Error - Invalid Option: -$OPTARG" 1>&2 + exit 1 + ;; + : ) + echo "Error - Invalid Option: -$OPTARG requires an argument" 1>&2 + exit 1 + ;; + esac +done + +FILE_SEARCH_PATTERN="$PUBLISHED_CONTAINER_FILE_PREFIX*$PUBLISHED_CONTAINER_FILE_SUFFIX" +echo "CONTAINER_SRC_DIR -> $CONTAINER_SRC_DIR" +echo "FILE_SEARCH_PATTERN -> $FILE_SEARCH_PATTERN" +echo "GITHUB_BRANCH -> $GITHUB_BRANCH" +echo "PUBLISHING_LEVEL -> $PUBLISHING_LEVEL" +echo "OUTPUT_FOLDER -> $OUTPUT_FOLDER" + +PUBLISHED_CONTAINER_FILES=$(find "$PUBLISHED_CONTAINERS_DIR" -name "$FILE_SEARCH_PATTERN") +if [[ -z $PUBLISHED_CONTAINER_FILES ]]; then + echo "Error - No published container lists in $PUBLISHED_CONTAINERS_DIR" + exit 1 +fi + +function cleanup { + echo "+++ logout from Azure Container Registry" + docker logout + docker system prune -f +} +trap cleanup EXIT + +CONTAINER_TAGS_DIR="$OUTPUT_FOLDER/CONTAINER_TAGS_FOLDER" +mkdir -p "$CONTAINER_TAGS_DIR" +FILE_NAME_PREFIX='PublishedTags' +FILE_EXT='.txt' + +# For Azure Linux 2.0, we have shipped the container images with +# the below value in the os-version field in the image manifest. +# TODO: We may need to update this value for Azure Linux 3.0. +OS_VERSION_PREFIX="cbl-mariner-" +DISTRO_IDENTIFIER="cm" + +function create_multi_arch_tags { + # $1: original container (without '-amd64' or '-arm64' extension in tag) + # $2: multi-arch name + # $3: multi-arch tag + # $4: azure linux version + # $5: architecture to build + local original_container=$1 + local multiarch_name=$2 + local multiarch_tag=$3 + local azure_linux_version=$4 + local architecture_build=$5 + + echo "-------------------------------------------------------" + echo "original_container -> $original_container" + echo "multiarch_name -> $multiarch_name" + echo "multiarch_tag -> $multiarch_tag" + echo "azure_linux_version -> $azure_linux_version" + echo "-------------------------------------------------------" + + full_multiarch_tag="$multiarch_name:$multiarch_tag" + + # First check if the already published tag is on the next Azure Linux version. + # If it is on the next version, then do not overwrite it. + set +e + manifest_json=$(docker manifest inspect "$full_multiarch_tag") + set -e + + if [[ -n $manifest_json ]]; then + echo "docker manifest found for container $full_multiarch_tag" + + # Parse the manifest json and look for the azure linux version in the key "os.version". + # Loop through the .manifests array and look for the os.version key. + # If the os.version key is found, then look for the version in its value starting with $OS_VERSION_PREFIX. + published_tag_os_version_key="null" + manifests=$(echo "$manifest_json" | jq .manifests) + manifest_array=$(echo "$manifests" | jq -c '.[]' | jq -r '.platform | with_entries(select(.key | contains("os.version")))' | jq -c '.[]') + for key in ${manifest_array[*]}; do + if [[ $key == *"$OS_VERSION_PREFIX"* ]]; then + # Remove the quotes from the value. + key=$(echo "$key" | tr -d \") + published_tag_os_version_key=$key + break + fi + done + + echo "published_tag_os_version_key -> $published_tag_os_version_key" + + if [[ $published_tag_os_version_key == "null" ]]; then + echo "OS Version key not found in the manifest file." + else + # OS version found. Look for the version in its value starting with $OS_VERSION_PREFIX. + published_tag_os_version=$(echo "$published_tag_os_version_key" | tr -d \" | tr -d $OS_VERSION_PREFIX) + + echo "published_tag_os_version -> $published_tag_os_version" + + # Check if the published tag has a greater Azure Linux version than the current tag's Azure Linux version. + # 1.0 > 2.0 => 0 (false) + # 2.0 > 1.0 => 1 (true) + # 2.0 > 2.0 => 0 (false) + is_published_tag_os_version_strictly_greater=$(echo "$published_tag_os_version>$azure_linux_version" | bc) + + # If the published tag is on the next Azure Linux version, then do not proceed. + if [ "$is_published_tag_os_version_strictly_greater" -eq 1 ]; then + echo "Published tag is already on the next Azure Linux version i.e., $published_tag_os_version." + echo "Do not overwrite it with $azure_linux_version." + return + fi + + echo "Published tag is on Azure Linux version $published_tag_os_version." + echo "Proceed with overwriting it with Azure Linux version $azure_linux_version." + echo "+++ update $full_multiarch_tag tag" + fi + else + echo "Manifest does not exist. Proceed with creating new tag." + echo "+++ create $full_multiarch_tag tag" + fi + + # create, annotate, and push manifest + docker manifest create "$full_multiarch_tag" --amend "$original_container-amd64" + docker manifest annotate "$full_multiarch_tag" "$original_container-amd64" \ + --os-version "$OS_VERSION_PREFIX$azure_linux_version" + + if [[ $architecture_build == *"ARM64"* ]]; then + docker manifest create "$full_multiarch_tag" --amend "$original_container-arm64" + docker manifest annotate "$full_multiarch_tag" "$original_container-arm64" \ + --os-version "$OS_VERSION_PREFIX$azure_linux_version" \ + --variant "v8" + fi + + echo "+++ push $full_multiarch_tag tag" + docker manifest push "$full_multiarch_tag" + echo "+++ $full_multiarch_tag tag pushed successfully" + + # Save the multi-arch tag to a file. + image_basename=${multiarch_name#*/} + dash_removed_name=${image_basename//-/} + final_name=${dash_removed_name////_} + + output_file="$CONTAINER_TAGS_DIR/$FILE_NAME_PREFIX-$final_name$FILE_EXT" + echo "Save the multi-arch tag to a file: $output_file" + + echo "$original_container-amd64" >> "$output_file" + if [[ $architecture_build == *"ARM64"* ]]; then + echo "$original_container-arm64" >> "$output_file" + fi + echo "$full_multiarch_tag" >> "$output_file" +} + +for PUBLISHED_CONTAINER_FILE in $PUBLISHED_CONTAINER_FILES +do + file_basename=$(basename "$PUBLISHED_CONTAINER_FILE") + container_type=$(echo "$file_basename" | sed -e "s/$PUBLISHED_CONTAINER_FILE_PREFIX-//" -e "s/$PUBLISHED_CONTAINER_FILE_SUFFIX//") + + # Rename core images to base to get the ACR Repo details. + if [[ "$container_type" =~ ^(distroless|busybox|marinara)$ ]]; then + container_type="base" + fi + echo "Container Type -> $container_type" + + TEMP_FILE=$(mktemp) + + python3 "$CONTAINER_SRC_DIR"/scripts/acrRepoParser.py \ + --config-file-path "$CONTAINER_SRC_DIR"/configuration/acrRepoV2.json \ + --image-name "$container_type" \ + --git-branch "$GITHUB_BRANCH" \ + --publishing-level "$PUBLISHING_LEVEL" \ + --output-file-path "$TEMP_FILE" + + IS_CORE_IMAGE=$(jq -r '.data_is_core_image' "$TEMP_FILE") + IS_GOLDEN_IMAGE=$(jq -r '.data_is_golden_image' "$TEMP_FILE") + IS_HCI_GOLDEN_IMAGE=$(jq -r '.data_is_hci_golden_image' "$TEMP_FILE") + ARCHITECTURE_TO_BUILD=$(jq -r '.data_architecture_to_build' "$TEMP_FILE") + TARGET_ACR=$(jq -r '.data_target_acr' "$TEMP_FILE") + + if [[ -z $TARGET_ACR ]]; then + echo "##vso[task.logissue type=warning]Target ACR not found for image $container_type" + continue + fi + + # Remove the temp file. + [ -f "$TEMP_FILE" ] && rm "$TEMP_FILE" + + echo "Container Type -> $container_type" + echo "IS_CORE_IMAGE -> $IS_CORE_IMAGE" + echo "IS_GOLDEN_IMAGE -> $IS_GOLDEN_IMAGE" + echo "IS_HCI_GOLDEN_IMAGE -> $IS_HCI_GOLDEN_IMAGE" + echo "ARCHITECTURE_TO_BUILD -> $ARCHITECTURE_TO_BUILD" + echo "TARGET_ACR -> $TARGET_ACR" + + while IFS= read -r image_name + do + echo + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Image name: $image_name" + echo + container_registry="${image_name%%.*}" + echo "+++ login into Azure ACR $container_registry" + az acr login --name "$container_registry" + + amd64_image=${image_name%-*}-amd64 + docker pull "$amd64_image" + + # Some container images are only built for AMD64 architecture. + if [[ $ARCHITECTURE_TO_BUILD == *"ARM64"* ]]; then + arm64_image=${image_name%-*}-arm64 + docker pull "$arm64_image" + fi + + if [[ $container_registry != "$TARGET_ACR" ]]; then + echo "+++ login into Azure ACR $TARGET_ACR" + az acr login --name "$TARGET_ACR" + + echo "Retagging the images to $TARGET_ACR" + # E.g., If container_registry is azurelinuxdevpreview and TARGET_ACR is azurelinuxpreview, then + # azurelinuxdevpreview.azurecr.io/base/core:2.0 -> azurelinuxpreview.azurecr.io/base/core:2.0 + + amd64_retagged_image_name=${amd64_image/"$container_registry"/"$TARGET_ACR"} + echo "Retagged amd64 image: $amd64_retagged_image_name" + docker image tag "$amd64_image" "$amd64_retagged_image_name" + docker rmi "$amd64_image" + docker image push "$amd64_retagged_image_name" + + if [[ $ARCHITECTURE_TO_BUILD == *"ARM64"* ]]; then + arm64_retagged_image_name=${arm64_image/"$container_registry"/"$TARGET_ACR"} + echo "Retagged arm64 image: $arm64_retagged_image_name" + docker image tag "$arm64_image" "$arm64_retagged_image_name" + docker rmi "$arm64_image" + docker image push "$arm64_retagged_image_name" + fi + + image_name=$amd64_retagged_image_name + fi + + # image_name has the following format [registry name].azurecr.io/[name]:tag-[amd64 or arm64] + # e.g.: azurelinuxpreview.azurecr.io/base/core:1.0.20210628-amd64 + # container name is [registry name].azurecr.io/[name] + # container tag is tag (without -[amd64 or arm64]) + image_name_with_noarch=${image_name%-*} + container_name=${image_name_with_noarch%:*} + container_tag=${image_name_with_noarch#*:} + + echo "Image Name: ------------------>" "$image_name" + echo "Image Name w/o Arch: ------------------>" "$image_name_with_noarch" + echo "Container Name: ------------------>" "$container_name" + echo "Container Tag: ------------------>" "$container_tag" + + if "$IS_CORE_IMAGE"; then + # For core images, we need to create multi-arch tags for + # the major version and the full version. + echo "Create multi-arch tags for core image: $container_type" + OLDIFS=$IFS + IFS='.' + read -ra tag_parts <<< "$container_tag" + IFS=$OLDIFS + + major_version="${tag_parts[0]}.${tag_parts[1]}" + azure_linux_version="${tag_parts[0]}.0" + + # create multi-arch tag full version (e.g.: 2.0.20210127) + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$container_tag" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create major version tag (e.g.: 2.0) + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$major_version" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + elif "$IS_GOLDEN_IMAGE"; then + # For golden images, we need to create multi-arch tags for + # the major version, the major and minor version, and the full version. + echo "Create multi-arch tags for golden image: $container_type" + package_version=${container_tag%-*} # 16.14.0 + package_version_major=${package_version%%.*} # 16 + package_version_major_minor=${package_version%.*} # 16.14 + + if [[ $package_version == *"-debug-nonroot" ]]; then + package_version_major=$package_version_major"-debug-nonroot" + package_version_major_minor=$package_version_major_minor"-debug-nonroot" + elif [[ $package_version == *"-nonroot" ]]; then + package_version_major=$package_version_major"-nonroot" + package_version_major_minor=$package_version_major_minor"-nonroot" + elif [[ $package_version == *"-debug" ]]; then + package_version_major=$package_version_major"-debug" + package_version_major_minor=$package_version_major_minor"-debug" + fi + + echo "Package Version: ------------------>" "$package_version" + echo "Package Version Major: ------------------>" "$package_version_major" + echo "Package Version Minor: ------------------>" "$package_version_major_minor" + + if $IS_HCI_GOLDEN_IMAGE; then + azure_linux_version=$(awk -F '-' '{print $2}' <<< "$container_tag") # 0.59.0-2.2.0.20230607 -> 2.2.0.20230607 + azure_linux_version=$(awk -F '.' '{print $2"."$3}' <<< "$azure_linux_version") # [2].[2].[0].[20230607] -> 2.0 + # ^ ^ + else + azure_linux_version=$(awk -F $DISTRO_IDENTIFIER '{print $2}' <<< "$container_tag") # 16.19.1-2-cm2.0.20230607 -> 2.0.20230607 + azure_linux_version=$(awk -F '.' '{print $1"."$2}' <<< "$azure_linux_version") # [2].[0].[20230607] -> 2.0 + # ^ ^ + fi + + # create multi-arch tag full version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16.14.0-1-cm2.0.20220412 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$container_tag" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create multi-arch tag with major version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version_major" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create multi-arch tag with major version and azure linux version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16-cm2.0 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version_major-$DISTRO_IDENTIFIER$azure_linux_version" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create multi-arch tag with major and minor version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16.14 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version_major_minor" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create multi-arch tag with major and minor version and azure linux version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16.14-cm2.0 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version_major_minor-$DISTRO_IDENTIFIER$azure_linux_version" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + if $IS_HCI_GOLDEN_IMAGE; then + # create multi-arch tag with major, minor, and patch version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16.14 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + + # create multi-arch tag with major, minor, and patch version and azure linux version + # e.g. azurelinuxpreview.azurecr.io/base/nodejs:16.14-cm2.0 + create_multi_arch_tags \ + "$image_name_with_noarch" \ + "$container_name" \ + "$package_version-$DISTRO_IDENTIFIER$azure_linux_version" \ + "$azure_linux_version" \ + "$ARCHITECTURE_TO_BUILD" + fi + fi + done < "$PUBLISHED_CONTAINER_FILE" +done