From a1d43fcea40a02f8620ecc617942054afceb88ad Mon Sep 17 00:00:00 2001 From: Daniel Black Date: Fri, 29 Nov 2024 18:09:08 +1100 Subject: [PATCH] docker library: build when all base packages are ready 1. Save space Rather than building parts of the manifest on the assumption that all architectures will catch up, wait for all of them. When the are all there, build them all in one go, and clean them all up at the end. The *-reference.txt file contains the name of the builders that are ready. Its used as a counter so the only thing that matters is the number of unique lines. Removed use of podman. There where probably some incompatible things here. 2. be retriggerable If any part of the build fails, clean the images up. Any manual retrigger on the tarbuildnum for a ubi/non-ubi will retrigger the building of the manifest, and test. This saves us rebuilding the packages. So this includes docker-library-build.sh. Failure of an architecture cleans them all. 3. tests now x86_64 only The docker-library-test.sh is adjusted because of the manifest name. The less obvious change is because the image is a manifest, running the test suite on this will only run on the native amd64 architecture. There where timeouts on non-amd64 sometimes still which where pretty much always false positives. 4. simplified docker-library-manifest With the manifest already done in the build step, this is simply pushing that manifest and all the tags around it. A re-arrangements means we push the tags (latest/earliest...) first (with image). Then the image with its main tag (like :10.5). Then we build the debug images. As there is now an easy implementation for this so its a one run process. Cleanup is aslo part of this script. The reference.txt files are removed. buildah rmi --prune --force will remove all untagged images, including any that had running containers on them during a build process. The rest of the cleanup is the same (without an if condition around it). The is probably scope for cleaning this up some more. --- scripts/docker-library-build.sh | 130 ++++++++++---- scripts/docker-library-manifest.sh | 267 ++++++++++++----------------- scripts/docker-library-test.sh | 7 +- 3 files changed, 205 insertions(+), 199 deletions(-) diff --git a/scripts/docker-library-build.sh b/scripts/docker-library-build.sh index 24fa2289..16d220a3 100755 --- a/scripts/docker-library-build.sh +++ b/scripts/docker-library-build.sh @@ -33,6 +33,7 @@ commit=${4:-0} branch=${5:-${master_branch}} artifacts_url=${ARTIFACTS_URL:-https://ci.mariadb.org} + # keep in sync with docker-cleanup script if [[ $branch =~ ^preview ]]; then container_tag=${branch#preview-} @@ -45,6 +46,7 @@ fi # Container tags must be lower case. container_tag=${container_tag,,*} ubi= +arches=( linux/amd64 linux/arm64/v8 linux/ppc64le linux/s390x ) case "${buildername#*ubuntu-}" in 2404-deb-autobake) @@ -58,6 +60,8 @@ case "${buildername#*ubuntu-}" in ;; *-rhel-9-rpm-autobake) ubi=-ubi + # first arch only + arches=( linux/amd64 ) master_branch=${master_branch}-ubi ;; *) @@ -66,8 +70,30 @@ case "${buildername#*ubuntu-}" in ;; esac -builderarch=${buildername%%-*} +image=mariadb-${tarbuildnum}${ubi} + +# keep a count of architectures that have reached this step +# and only build once they are all here. + +# UBI for the moment only triggers on one arch +reffile="${container_tag}-${tarbuildnum}${ubi}-reference.txt" + +# ensure unique entries for each arch +echo "$buildername" >> "$reffile" +sort -u "$reffile" -o "$reffile" + +entries=$(wc -l < "$reffile") +if [ "$entries" -lt ${#arches[@]} ]; then + echo "Only $entries architectures so far" + exit +fi + +# Don't remove file here. Leave a manual retrigger of +# any build of the same tarbuildnum / ubi there to redo +# start the rebuild, without the server rebuild. +# rm "$reffile" +# Annotations - https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys declare -a annotations=( "--annotation" "org.opencontainers.image.authors=MariaDB Foundation" "--annotation" "org.opencontainers.image.documentation=https://hub.docker.com/_/mariadb" @@ -87,49 +113,81 @@ annotate() { done } -image=mariadb-${tarbuildnum}-${builderarch}$ubi -if [ -n "$ubi" ] -then +# +# BUILD Image +# +builder_noarch=${buildername#*-} -build() { - local repo="mariadb-docker/$master_branch"/MariaDB.repo - curl "$artifacts_url"/galera/mariadb-4.x-latest-gal-"${buildername%-rpm-autobake}".repo \ - -o "$repo" - curl "$artifacts_url/$tarbuildnum/${buildername}"/MariaDB.repo \ - >> "$repo" - buildah bud --tag "${image}" \ - --layers \ - --arch "$@" \ - --build-arg MARIADB_VERSION="$mariadb_version" \ - "${annotations[@]}" \ - "mariadb-docker/$master_branch" -} +galera_distro=${buildername%-*-autobake} +galera_distro_noarch=${galera_distro#*-} -else -# Annotations - https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys build() { - local galera_repo - galera_repo="deb [trusted=yes] $(curl "$artifacts_url"/galera/mariadb-4.x-latest-gal-"${buildername%-deb-autobake}".sources | sed '/URIs: /!d ; s///;q') ./" - buildah bud --tag "${image}" \ + local arch=$1 + declare -a args + local bbarch=${arch#*/} + if [ "$bbarch" = arm64/v8 ]; then + bbarch=aarch64; + fi + if [ -n "$ubi" ] + then + local repo="mariadb-docker/$master_branch"/MariaDB.repo + curl "${artifacts_url}/galera/mariadb-4.x-latest-gal-${bbarch}-${galera_distro_noarch}".repo \ + -o "$repo" + curl "${artifacts_url}/$tarbuildnum/${bbarch}-${builder_noarch}"/MariaDB.repo \ + >> "$repo" + args=( --build-arg MARIADB_VERSION="$mariadb_version" ) + else + local galera_repo + galera_repo="deb [trusted=yes] $(curl "${artifacts_url}/galera/mariadb-4.x-latest-gal-${bbarch}-${galera_distro_noarch}.sources" | sed '/URIs: /!d ; s///;q') ./" + args=( + --build-arg REPOSITORY="[trusted=yes] ${artifacts_url}/${tarbuildnum}/${bbarch}-${builder_noarch}/debs ./\n$galera_repo" \ + --build-arg MARIADB_VERSION="1:$mariadb_version+maria~$pkgver" ) + fi + buildah bud --tag "${image}-${arch}" \ --layers \ - --arch "$@" \ - --build-arg REPOSITORY="[trusted=yes] $artifacts_url/$tarbuildnum/${buildername}/debs ./\n$galera_repo" \ - --build-arg MARIADB_VERSION="1:$mariadb_version+maria~$pkgver" \ + --platform "$arch" \ + "${args[@]}" \ "${annotations[@]}" \ - "mariadb-docker/$master_branch" + "mariadb-docker/$master_branch" } -fi +## Because our repos aren't multiarch, or paramitizable by $TARGET_ARCH, we do it separately +## +## intentionally array to simple +## shellcheck disable=SC2124 +#archlist="${arches[@]}" # -# BUILD Image +## comma separated +#archlist=${archlist// /,} +#buildah bud --manifest "${image}" \ +# --jobs 4 \ +# --layers \ +# --platform "${archlist}" \ +# "${args[@]}" \ +# "${annotations[@]}" \ +# "mariadb-docker/$master_branch" + +buildah manifest rm "$image" || echo "already not there" + +cleanup() +{ + buildah manifest rm "$image" || echo "already gone" + for arch in "${arches[@]}"; do + # -f will remove all tags of same, like wordpress + buildah rmi -f "${image}-${arch}" || echo "already gone" + done + buildah rmi --prune --force +} -if [ "${builderarch}" = aarch64 ]; then - build arm64 --variant v8 -else - build "${builderarch}" -fi +trap cleanup ERR -if [ "${builderarch}" = amd64 ]; then - podman tag "${image}" "${image}-wordpress" -fi +buildah manifest create "$image" + +for arch in "${arches[@]}"; do + build "$arch" + buildah manifest add "$image" "$image-$arch" + if [ "$arch" = amd64 ]; then + buildah tag "${image}-${arch}" "${image}-wordpress" + fi +done diff --git a/scripts/docker-library-manifest.sh b/scripts/docker-library-manifest.sh index 005f3471..5ce501db 100755 --- a/scripts/docker-library-manifest.sh +++ b/scripts/docker-library-manifest.sh @@ -7,11 +7,12 @@ mariadb_version=${2} mariadb_version=${mariadb_version#*-} buildername=${3:-amd64-ubuntu-2004-deb-autobake} master_branch=${mariadb_version%\.*} -commit=${4:-0} +#commit=${4:-0} branch=${5:-${master_branch}} prod_environment=${6:-True} rm -f last_tag + # keep in sync with docker-cleanup script if [[ $branch = *pkgtest* ]]; then container_tag=${branch#bb-} @@ -30,194 +31,136 @@ fi # Container tags must be lower case. container_tag=${container_tag,,*}${ubi} -builderarch=${buildername%%-*} - - -declare -a annotations=( - "--annotation" "org.opencontainers.image.authors=MariaDB Foundation" - "--annotation" "org.opencontainers.image.documentation=https://hub.docker.com/_/mariadb" - "--annotation" "org.opencontainers.image.source=https://github.com/MariaDB/mariadb-docker/tree/$( - cd "mariadb-docker/$master_branch" - git rev-parse HEAD - )/$master_branch" - "--annotation" "org.opencontainers.image.licenses=GPL-2.0" - "--annotation" "org.opencontainers.image.title=MariaDB Server $container_tag CI build" - "--annotation" "org.opencontainers.image.description=This is not a Release.\nBuild of the MariaDB Server from CI as of commit $commit" - "--annotation" "org.opencontainers.image.version=$mariadb_version+$commit" - "--annotation" "org.opencontainers.image.revision=$commit") - -annotate() { - for item in "${annotations[@]}"; do - echo " --annotation" \""$item"\" - done -} +image=mariadb-${tarbuildnum}${ubi} -image=mariadb-${tarbuildnum}-${builderarch}${ubi} +# keep a count of architectures that have reached this step +# and only build once they are all here. +if ! buildah manifest exists "$image"; then + echo "No manifest we can't push" + exit +fi + +# +# Dev manifest is already made # -# METADATA: -# Add manifest file of version and fix mariadb version in the configuration -# because otherwise 'buildah manifest add "$devmanifest" "$image"' would be sufficient +devmanifest=$image -container=$(buildah from "$image") -manifestfile=$(mktemp) -for item in "${annotations[@]}"; do - [ "$item" != "--annotation" ] && echo -e "$item\n" -done >"$manifestfile" -buildah copy --add-history "$container" "$manifestfile" /manifest.txt -rm -f "$manifestfile" # -# MAKE it part of the mariadb-devel manifest +# PUSHIT - if the manifest if complete, i.e. all supported arches are there, we push # -buildmanifest() { - manifest=$1 - shift - container=$1 +manifest_image_cleanup() { + local manifest=$1 + buildah manifest rm "$manifest" || echo "already removed" + + local arches=( linux/amd64 linux/arm64/v8 linux/ppc64le linux/s390x ) + for arch in "${arches[@]}"; do + # -f will remove all tags of same, like wordpress + buildah rmi -f "${manifest}-${arch}" || echo "already gone" + done + buildah rmi --prune --force + shift - # create a manifest, and if it already exists, remove the one for the - # current architecture as we're replacing this. - # This could happen due to triggered rebuilds on buildbot. - - buildah manifest create "$manifest" || buildah manifest inspect "$manifest" | - jq ".manifests[] | select( .platform.architecture == \"$builderarch\") | .digest" | - xargs --no-run-if-empty -n 1 buildah manifest remove "$manifest" - - t=$(mktemp) - buildah commit "$@" --iidfile "$t" --manifest "$manifest" "$container" - image=$(<"$t") - ##buildah push --rm "$image" "docker://quay.io/mariadb-foundation/${base}:${container_tag}-${builderarch}" && - ## buildah rmi "$image" - # $image is the wrong sha for annotation. Config vs Blog? - # Even below doesn't annotate manifest. Unknown reason, doesn't error - buildah manifest inspect "$manifest" | - jq ".manifests[] | select( .platform.architecture == \"$builderarch\") | .digest" | - xargs --no-run-if-empty -n 1 buildah manifest annotate \ - "${annotations[@]}" \ - "$manifest" + local t=$1 rm -f "$t" } -devmanifest=mariadb-devel-${container_tag}-$commit +t=$(mktemp) -trap 'buildah rm "$container"' EXIT -buildmanifest "$devmanifest" "$container" +# Anything fails, like the API, cleanup +trap 'manifest_image_cleanup "$devmanifest" "$t"' EXIT + +declare -A specialtags +if ! wget -nv https://downloads.mariadb.org/rest-api/mariadb/ -O "$t"; then + echo >&2 "Wget failed" +fi +if [ "$branch" = 'main' ]; then + specialtags['verylatest']=\"${container_tag}\" +else + specialtags['verylatest']=$(jq '.major_releases[0].release_id' <"$t") +fi +specialtags['latest']=$(jq '.major_releases | map(select(.release_status == "Stable"))[0].release_id' <"$t") +specialtags['latest-lts']=$(jq '.major_releases | map(select(.release_status == "Stable" and .release_support_type == "Long Term Support"))[0].release_id' <"$t") +specialtags['earliest']=$(jq '.major_releases | map(select( (( (.release_eol_date // "2031-01-01") + "T00:00:00Z") | fromdate) > now))[-1].release_id' <"$t") +specialtags['earliest-lts']=$(jq '.major_releases | map(select(.release_status == "Stable" and .release_support_type == "Long Term Support" and (( (.release_eol_date // "2031-01-01") + "T00:00:00Z") | fromdate) > now ))[-1].release_id' <"$t") +for tag in "${!specialtags[@]}"; do + if [ \""$container_tag"\" == "${specialtags[$tag]}" ]; then + if [ "$prod_environment" = "True" ]; then + buildah manifest push --all "$devmanifest" "docker://quay.io/mariadb-foundation/mariadb-devel:${tag}${ubi}" + else + echo "not pushing quay.io/mariadb-foundation/mariadb-devel:${tag}${ubi} as in DEV environment" + fi + fi +done +rm "$t" + +buildah manifest inspect "$devmanifest" | tee "${t}" +trap 'manifest_image_cleanup "$devmanifest" "$t"' EXIT + +if [ "$prod_environment" = "True" ]; then + buildah manifest push --all "$devmanifest" "docker://quay.io/mariadb-foundation/mariadb-devel:${container_tag}" + echo "${container_tag}" > last_tag +else + rm -f last_tag +fi # # MAKE Debug manifest -debugimage=mariadb-debug-${tarbuildnum}-${builderarch}${ubi} -buildah bud --tag "$debugimage" --build-arg BASE="$image" -f "mariadb-docker/Containerfile.debug$ubi" -container=$(buildah from "$debugimage") +debugmanifest=${image}-debug -debugmanifest=mariadb-debug-${container_tag}-$commit +buildah bud --all-platforms --jobs 4 --manifest "$debugmanifest" --build-arg BASE="$image" -f "mariadb-docker/Containerfile.debug$ubi" -buildmanifest "$debugmanifest" "$container" --rm -trap - EXIT +# now $debugmanifest is build, we can cleanup $devmanifest +manifest_image_cleanup "$devmanifest" "$t" -expected=4 +buildah manifest inspect "$debugmanifest" -if [ -n "$ubi" ]; then - expected=1 +if [ "$prod_environment" = "True" ]; then + buildah manifest push --all --rm "$debugmanifest" "docker://quay.io/mariadb-foundation/mariadb-debug:${container_tag}" +else + buildah manifest rm "$debugmanifest" fi -# -# -# PUSHIT - if the manifest if complete, i.e. all supported arches are there, we push -# +# all untagged images removed, and any containers that might be running on them +buildah rmi --prune --force -manifest_image_cleanup() { - local manifest=$1 - shift - local t=$1 - if [ ! -f "$t" ]; then - return - fi - buildah manifest rm "$manifest" || echo "already removed" - for arch in amd64 ppc64le s390x aarch64; do - buildah rm "mariadb-${tarbuildnum}-${arch}${ubi}" || echo 'trouble removing this image' - buildah rm "mariadb-debug-${tarbuildnum}-${arch}${ubi}" || echo 'trouble removing this image' - done - rm -f "$t" -} +# Delete old reference files +find . -name \*reference.txt -type f -mtime +7 -delete -if (($(buildah manifest inspect "$devmanifest" | jq '.manifests | length') >= expected)); then - t=$(mktemp) +buildah images +# lost and forgotten (or just didn't make enough manifest items - build failure on an arch) +lastweek=$(date +%s --date='1 week ago') +# note - jq args are treated as strings and need to be cast tonumber to make the value comparable. - declare -A specialtags - if ! wget -nv https://downloads.mariadb.org/rest-api/mariadb/ -O "$t"; then - echo >&2 "Wget failed" - fi - if [ "$branch" = 'main' ]; then - specialtags['verylatest']=\"${container_tag}\" - else - specialtags['verylatest']=$(jq '.major_releases[0].release_id' <"$t") - fi - specialtags['latest']=$(jq '.major_releases | map(select(.release_status == "Stable"))[0].release_id' <"$t") - specialtags['latest-lts']=$(jq '.major_releases | map(select(.release_status == "Stable" and .release_support_type == "Long Term Support"))[0].release_id' <"$t") - specialtags['earliest']=$(jq '.major_releases | map(select( (( (.release_eol_date // "2031-01-01") + "T00:00:00Z") | fromdate) > now))[-1].release_id' <"$t") - specialtags['earliest-lts']=$(jq '.major_releases | map(select(.release_status == "Stable" and .release_support_type == "Long Term Support" and (( (.release_eol_date // "2031-01-01") + "T00:00:00Z") | fromdate) > now ))[-1].release_id' <"$t") - for tag in "${!specialtags[@]}"; do - if [ \""$container_tag"\" == "${specialtags[$tag]}" ]; then - if [ "$prod_environment" = "True" ]; then - buildah manifest push --all "$devmanifest" "docker://quay.io/mariadb-foundation/mariadb-devel:${tag}${ubi}" - else - echo "not pushing quay.io/mariadb-foundation/mariadb-devel:${tag}${ubi} as in DEV environment" - fi - fi - done - rm "$t" - - buildah manifest inspect "$devmanifest" | tee "${t}" - trap 'manifest_image_cleanup "$devmanifest" "$t"' EXIT - if [ "$prod_environment" = "True" ]; then - buildah manifest push --all --rm "$devmanifest" "docker://quay.io/mariadb-foundation/mariadb-devel:${container_tag}" - echo "${container_tag}" > last_tag - else - rm -f last_tag - fi - manifest_image_cleanup "$devmanifest" "$t" +# clear buildah images +buildah images --json | + jq --arg lastweek "$lastweek" '.[] | select(.created <= ( $lastweek | tonumber ) and any( .names[]? ; startswith("localhost/mariadb")) ) | .id' | + xargs --no-run-if-empty buildah rmi --force || echo "had trouble removing buildah images" + +# old ubuntu and base images that got updated so are Dangling +podman images --format=json | + jq --arg lastweek "$lastweek" '.[] | select(.Created <= ( $lastweek | tonumber ) and .Dangling? ) | .Id' | + xargs --no-run-if-empty podman rmi --force || echo "continuing cleanup anyway" + +# clean buildah containers (nothing should be running) +buildah containers --format "{{.ContainerID}}" | xargs --no-run-if-empty buildah rm || echo "had trouble cleaning containers" + +# clean images +buildah images --json | + jq --arg lastweek "$lastweek" '.[] | select(.readonly ==false and .created <= ( $lastweek | tonumber ) and .names == null) | .id' | + xargs --no-run-if-empty buildah rmi || echo "had trouble cleaning images" + +# clean manifests +buildah images --json | + jq --arg lastweek "$lastweek" '.[] | select(.readonly ==false and .created <= ( $lastweek | tonumber ) and ( try .names[0]? catch "" | startswith("localhost/mariadb-") )) | .id' | + xargs --no-run-if-empty buildah manifest rm || echo "trouble cleaning manifests" + +# what's left? +buildah images - t=$(mktemp) - buildah manifest inspect "$debugmanifest" | tee "${t}" - trap 'manifest_image_cleanup "$debugmanifest" "$t"' EXIT - if [ "$prod_environment" = "True" ]; then - buildah manifest push --all --rm "$debugmanifest" "docker://quay.io/mariadb-foundation/mariadb-debug:${container_tag}" - fi - manifest_image_cleanup "$debugmanifest" "$t" - - buildah images - # lost and forgotten (or just didn't make enough manifest items - build failure on an arch) - lastweek=$(date +%s --date='1 week ago') - # note - jq args are treated as strings and need to be cast tonumber to make the value comparable. - - # clear buildah images - buildah images --json | - jq --arg lastweek "$lastweek" '.[] | select(.created <= ( $lastweek | tonumber ) and any( .names[]? ; startswith("localhost/mariadb")) ) | .id' | - xargs --no-run-if-empty buildah rmi --force || echo "had trouble removing buildah images" - - # old ubuntu and base images that got updated so are Dangling - podman images --format=json | - jq --arg lastweek "$lastweek" '.[] | select(.Created <= ( $lastweek | tonumber ) and .Dangling? ) | .Id' | - xargs --no-run-if-empty podman rmi --force || echo "continuing cleanup anyway" - - # clean buildah containers (nothing should be running) - buildah containers --format "{{.ContainerID}}" | xargs --no-run-if-empty buildah rm || echo "had trouble cleaning containers" - - # clean images - buildah images --json | - jq --arg lastweek "$lastweek" '.[] | select(.readonly ==false and .created <= ( $lastweek | tonumber ) and .names == null) | .id' | - xargs --no-run-if-empty buildah rmi || echo "had trouble cleaning images" - - # clean manifests - buildah images --json | - jq --arg lastweek "$lastweek" '.[] | select(.readonly ==false and .created <= ( $lastweek | tonumber ) and ( try .names[0]? catch "" | startswith("localhost/mariadb-") )) | .id' | - xargs --no-run-if-empty buildah manifest rm || echo "trouble cleaning manifests" - - # what's left? - buildah images -fi trap - EXIT diff --git a/scripts/docker-library-test.sh b/scripts/docker-library-test.sh index 98c10d98..a93207c8 100755 --- a/scripts/docker-library-test.sh +++ b/scripts/docker-library-test.sh @@ -7,11 +7,16 @@ buildername=${2} builderarch=${buildername%%-*} -image=mariadb-${tarbuildnum}-${builderarch} +image=mariadb-${tarbuildnum} + if [[ "$buildername" = *-rhel-9-rpm-autobake ]]; then image=${image}-ubi fi +if ! buildah manifest exists "$image"; then + echo "No manifest we can't test" + exit +fi # # TEST Image #