k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/images/image-util.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2017 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 set -o errexit 18 set -o nounset 19 set -o pipefail 20 21 TASK=${1} 22 WHAT=${2} 23 24 # docker buildx command is still experimental as of Docker 19.03.0 25 export DOCKER_CLI_EXPERIMENTAL="enabled" 26 27 # Connecting to a Remote Docker requires certificates for authentication, which can be found 28 # at this path. By default, they can be found in the ${HOME} folder. We're expecting to find 29 # here ".docker-${os_version}" folders which contains the necessary certificates. 30 DOCKER_CERT_BASE_PATH="${DOCKER_CERT_BASE_PATH:-${HOME}}" 31 32 KUBE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd -P)" 33 source "${KUBE_ROOT}/hack/lib/logging.sh" 34 source "${KUBE_ROOT}/hack/lib/util.sh" 35 36 # Mapping of go ARCH to actual architectures shipped part of multiarch/qemu-user-static project 37 declare -A QEMUARCHS=( ["amd64"]="x86_64" ["arm"]="arm" ["arm64"]="aarch64" ["ppc64le"]="ppc64le" ["s390x"]="s390x" ) 38 39 # NOTE(claudiub): In the test image build jobs, this script is not being run in a git repository, 40 # which would cause git log to fail. Instead, we can use the GIT_COMMIT_ID set in cloudbuild.yaml. 41 GIT_COMMIT_ID=$(git log -1 --format=%h || echo "${GIT_COMMIT_ID}") 42 windows_os_versions=(1809 ltsc2022) 43 declare -A WINDOWS_OS_VERSIONS_MAP 44 45 initWindowsOsVersions() { 46 for os_version in "${windows_os_versions[@]}"; do 47 img_base="mcr.microsoft.com/windows/nanoserver:${os_version}" 48 # we use awk to also trim the quotes around the OS version string. 49 full_version=$(docker manifest inspect "${img_base}" | grep "os.version" | head -n 1 | awk -F\" '{print $4}') || true 50 WINDOWS_OS_VERSIONS_MAP["${os_version}"]="${full_version}" 51 done 52 } 53 54 initWindowsOsVersions 55 56 # Returns list of all supported architectures from BASEIMAGE file 57 listOsArchs() { 58 local image=${1} 59 cut -d "=" -f 1 "${image}"/BASEIMAGE 60 } 61 62 splitOsArch() { 63 local image=${1} 64 local os_arch=${2} 65 66 if [[ $os_arch =~ .*/.*/.* ]]; then 67 # for Windows, we have to support both LTS and SAC channels, so we're building multiple Windows images. 68 # the format for this case is: OS/ARCH/OS_VERSION. 69 os_name=$(echo "$os_arch" | cut -d "/" -f 1) 70 arch=$(echo "$os_arch" | cut -d "/" -f 2) 71 os_version=$(echo "$os_arch" | cut -d "/" -f 3) 72 suffix="$os_name-$arch-$os_version" 73 elif [[ $os_arch =~ .*/.* ]]; then 74 os_name=$(echo "$os_arch" | cut -d "/" -f 1) 75 arch=$(echo "$os_arch" | cut -d "/" -f 2) 76 os_version="" 77 suffix="$os_name-$arch" 78 else 79 echo "The BASEIMAGE file for the ${image} image is not properly formatted. Expected entries to start with 'os/arch', found '${os_arch}' instead." 80 exit 1 81 fi 82 } 83 84 # Returns baseimage need to used in Dockerfile for any given architecture 85 getBaseImage() { 86 os_arch=$1 87 grep "${os_arch}=" BASEIMAGE | cut -d= -f2 88 } 89 90 # This function will build test image for all the architectures 91 # mentioned in BASEIMAGE file. In the absence of BASEIMAGE file, 92 # it will build for all the supported arch list - amd64, arm, 93 # arm64, ppc64le, s390x 94 build() { 95 local image=${1} 96 local img_folder=${1} 97 local output_type=${2} 98 docker_version_check 99 100 if [[ -f "${img_folder}/BASEIMAGE" ]]; then 101 os_archs=$(listOsArchs "$image") 102 else 103 # prepend linux/ to the QEMUARCHS items. 104 os_archs=$(printf 'linux/%s\n' "${!QEMUARCHS[@]}") 105 fi 106 107 # image tag 108 TAG=$(<"${img_folder}/VERSION") 109 110 alias_name="$(cat "${img_folder}/ALIAS" 2>/dev/null || true)" 111 if [[ -n "${alias_name}" ]]; then 112 echo "Found an alias for '${image}'. Building / tagging image as '${alias_name}.'" 113 image="${alias_name}" 114 fi 115 116 kube::util::ensure-gnu-sed 117 kube::util::ensure-docker-buildx 118 119 for os_arch in ${os_archs}; do 120 splitOsArch "${image}" "${os_arch}" 121 if [[ "${os_name}" == "windows" && "${output_type}" == "docker" ]]; then 122 echo "Cannot build the image '${image}' for ${os_arch}. Built Windows container images need to be pushed to a registry." 123 continue 124 fi 125 126 echo "Building image for ${image} OS/ARCH: ${os_arch}..." 127 128 # Create a temporary directory for every architecture and copy the image content 129 # and build the image from temporary directory 130 mkdir -p "${KUBE_ROOT}"/_tmp 131 temp_dir=$(mktemp -d "${KUBE_ROOT}"/_tmp/test-images-build.XXXXXX) 132 kube::util::trap_add "rm -rf ${temp_dir}" EXIT 133 134 cp -r "${img_folder}"/* "${temp_dir}" 135 if [[ -f ${img_folder}/Makefile ]]; then 136 # make bin will take care of all the prerequisites needed 137 # for building the docker image 138 make -C "${img_folder}" bin OS="${os_name}" ARCH="${arch}" TARGET="${temp_dir}" 139 fi 140 pushd "${temp_dir}" 141 142 # NOTE(claudiub): Some Windows images might require their own Dockerfile 143 # while simpler ones will not. If we're building for Windows, check if 144 # "Dockerfile_windows" exists or not. 145 dockerfile_name="Dockerfile" 146 if [[ "$os_name" = "windows" && -f "Dockerfile_windows" ]]; then 147 dockerfile_name="Dockerfile_windows" 148 fi 149 150 base_image="" 151 if [[ -f BASEIMAGE ]]; then 152 base_image=$(getBaseImage "${os_arch}" | "${SED}" "s|REGISTRY|${REGISTRY}|g") 153 "${SED}" -i "s|BASEARCH|${arch}|g" $dockerfile_name 154 fi 155 156 # Only the cross-build on x86 is guaranteed by far, other arches like aarch64 doesn't support cross-build 157 # thus, there is no need to tackle a disability feature on those platforms, and also help to prevent from 158 # ending up a wrong image tag on non-amd64 platforms. 159 build_arch=$(uname -m) 160 if [[ ${build_arch} = 'x86_64' ]]; then 161 # copy the qemu-*-static binary to docker image to build the multi architecture image on x86 platform 162 if grep -q 'CROSS_BUILD_' Dockerfile; then 163 if [[ "${arch}" = 'amd64' ]]; then 164 "${SED}" -i '/CROSS_BUILD_/d' Dockerfile 165 else 166 "${SED}" -i "s|QEMUARCH|${QEMUARCHS[$arch]}|g" Dockerfile 167 # Register qemu-*-static for all supported processors except the current one 168 echo 'Registering qemu-*-static binaries in the kernel' 169 local sudo="" 170 if [[ $(id -u) -ne 0 ]]; then 171 sudo="sudo" 172 fi 173 ${sudo} docker run --rm --privileged tonistiigi/binfmt:latest --install all 174 curl -sSL https://github.com/multiarch/qemu-user-static/releases/download/"${QEMUVERSION}"/x86_64_qemu-"${QEMUARCHS[$arch]}"-static.tar.gz | tar -xz -C "${temp_dir}" 175 # Ensure we don't get surprised by umask settings 176 chmod 0755 "${temp_dir}/qemu-${QEMUARCHS[$arch]}-static" 177 "${SED}" -i 's/CROSS_BUILD_//g' Dockerfile 178 fi 179 fi 180 elif [[ "${QEMUARCHS[$arch]}" != "${build_arch}" ]]; then 181 echo "skip cross-build $arch on non-supported platform ${build_arch}." 182 popd 183 continue 184 else 185 "${SED}" -i '/CROSS_BUILD_/d' Dockerfile 186 fi 187 188 # `--provenance=false --sbom=false` is set to avoid creating a manifest list: https://github.com/kubernetes/kubernetes/issues/123266 189 docker buildx build --progress=plain --no-cache --pull --output=type="${output_type}" --platform "${os_name}/${arch}" --provenance=false --sbom=false \ 190 --build-arg BASEIMAGE="${base_image}" --build-arg REGISTRY="${REGISTRY}" --build-arg OS_VERSION="${os_version}" \ 191 -t "${REGISTRY}/${image}:${TAG}-${suffix}" -f "${dockerfile_name}" \ 192 --label "image_version=${TAG}" --label "commit_id=${GIT_COMMIT_ID}" \ 193 --label "git_url=https://github.com/kubernetes/kubernetes/tree/${GIT_COMMIT_ID}/test/images/${img_folder}" . 194 195 popd 196 done 197 } 198 199 docker_version_check() { 200 # docker manifest annotate --os-version has been introduced in 20.10.0, 201 # so we need to make sure we have it. 202 docker_version=$(docker version --format '{{.Client.Version}}' | cut -d"-" -f1) 203 if [[ ${docker_version} != 20.10.0 && ${docker_version} < 20.10.0 ]]; then 204 echo "Minimum docker version 20.10.0 is required for annotating the OS Version in the manifest list images: ${docker_version}]" 205 exit 1 206 fi 207 } 208 209 # This function will push the docker images 210 push() { 211 local image=${1} 212 docker_version_check 213 214 TAG=$(<"${image}"/VERSION) 215 if [[ -f ${image}/BASEIMAGE ]]; then 216 os_archs=$(listOsArchs "$image") 217 else 218 # prepend linux/ to the QEMUARCHS items. 219 os_archs=$(printf 'linux/%s\n' "${!QEMUARCHS[@]}") 220 fi 221 222 pushd "${image}" 223 alias_name="$(cat ALIAS 2>/dev/null || true)" 224 if [[ -n "${alias_name}" ]]; then 225 echo "Found an alias for '${image}'. Pushing image as '${alias_name}.'" 226 image="${alias_name}" 227 fi 228 229 kube::util::ensure-gnu-sed 230 231 # reset manifest list; needed in case multiple images are being built / pushed. 232 manifest=() 233 # Make os_archs list into image manifest. Eg: 'linux/amd64 linux/ppc64le' to '${REGISTRY}/${image}:${TAG}-linux-amd64 ${REGISTRY}/${image}:${TAG}-linux-ppc64le' 234 while IFS='' read -r line; do manifest+=("$line"); done < <(echo "$os_archs" | "${SED}" "s~\/~-~g" | "${SED}" -e "s~[^ ]*~$REGISTRY\/$image:$TAG\-&~g") 235 docker manifest create --amend "${REGISTRY}/${image}:${TAG}" "${manifest[@]}" 236 237 for os_arch in ${os_archs}; do 238 splitOsArch "${image}" "${os_arch}" 239 240 # For Windows images, we also need to include the "os.version" in the manifest list, so the Windows node 241 # can pull the proper image it needs. 242 if [[ "$os_name" = "windows" ]]; then 243 full_version="${WINDOWS_OS_VERSIONS_MAP[$os_version]}" 244 docker manifest annotate --os "${os_name}" --arch "${arch}" --os-version "${full_version}" "${REGISTRY}/${image}:${TAG}" "${REGISTRY}/${image}:${TAG}-${suffix}" 245 else 246 docker manifest annotate --os "${os_name}" --arch "${arch}" "${REGISTRY}/${image}:${TAG}" "${REGISTRY}/${image}:${TAG}-${suffix}" 247 fi 248 done 249 popd 250 docker manifest push --purge "${REGISTRY}/${image}:${TAG}" 251 } 252 253 # This function is for building AND pushing images. Useful if ${WHAT} is "all-conformance". 254 # This will allow images to be pushed immediately after they've been built. 255 build_and_push() { 256 local image=${1} 257 build "${image}" "registry" 258 push "${image}" 259 } 260 261 # This function is for building the go code 262 bin() { 263 local arch_prefix="" 264 if [[ "${ARCH:-}" == "arm" ]]; then 265 arch_prefix="GOARM=${GOARM:-7}" 266 fi 267 for SRC in "$@"; 268 do 269 docker run --rm -v "${TARGET}:${TARGET}:Z" -v "${KUBE_ROOT}":/go/src/k8s.io/kubernetes:Z \ 270 golang:"${GOLANG_VERSION}" \ 271 /bin/bash -c "\ 272 cd /go/src/k8s.io/kubernetes/test/images/${SRC_DIR} && \ 273 CGO_ENABLED=0 ${arch_prefix} GOOS=${OS} GOARCH=${ARCH} go build -a -installsuffix cgo --ldflags \"-w ${LD_FLAGS:-}\" -o ${TARGET}/${SRC} ./$(dirname "${SRC}")" 274 done 275 } 276 277 shift 278 279 if [[ "${WHAT}" == "all-conformance" ]]; then 280 # NOTE(claudiub): Building *ALL* the images under the kubernetes/test/images folder takes an extremely 281 # long time (especially some images), and some images are rarely used and rarely updated, so there's 282 # no point in rebuilding all of them every time. This will only build the Conformance-related images. 283 # Discussed during Conformance Office Hours Meeting (2019.12.17): 284 # https://docs.google.com/document/d/1W31nXh9RYAb_VaYkwuPLd1hFxuRX3iU0DmaQ4lkCsX8/edit#heading=h.l87lu17xm9bh 285 shift 286 conformance_images=("busybox" "agnhost" "jessie-dnsutils" "kitten" "nautilus" "nonewprivs" "resource-consumer" "sample-apiserver") 287 for image in "${conformance_images[@]}"; do 288 "${TASK}" "${image}" "$@" 289 done 290 else 291 "${TASK}" "$@" 292 fi