From 4bf45e782a6d49a2fbd5137d68c351f1c99dd0ed Mon Sep 17 00:00:00 2001 From: arewm Date: Mon, 20 Jan 2025 15:57:44 -0500 Subject: [PATCH] feat: expanding the functionality of utils.sh Adding more functionality to the utils scripts in order to then reduce the amount of duplication in task scripts within build-definitions. fixes #353 Signed-off-by: arewm --- test/utils.sh | 140 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 130 insertions(+), 10 deletions(-) diff --git a/test/utils.sh b/test/utils.sh index 2127758..3012b51 100644 --- a/test/utils.sh +++ b/test/utils.sh @@ -177,6 +177,99 @@ handle_error() fi } +# The function can be used to parse an image url. It will return a json +# object with keys: +# +# `repository` - The repository of the image including an the registry and its optional port +# `tag` - The tag of the image reference +# `digest` - The digest of the image reference +# +# If an image tag or digest is not found then the values will be empty. +parse_image_url() { + local image_url=$1 + + if [ -z "$image_url" ]; then + echo "Missing image pull spec" >&2 + exit 2 + fi + + + digest="" + tag="" + repo="$(echo -n "$image_url" | cut -d@ -f1)" + + # Digest will be the last portion after an "@" + at_number=$(echo -n "$image_url" | tr -cd "@" | wc -c | tr -d '[:space:]') + colon_number=$(echo -n "$repo" | tr -cd ":" | wc -c | tr -d '[:space:]') + if [[ $at_number == 1 ]]; then + digest="$(echo -n "${image_url}" | cut -d@ -f2)" + elif [[ $at_number != 0 ]]; then + # The only other supported format is registry/repository + echo "$image_url does not match the format registry(:port)/repository(:tag)(@digest)" + exit 3 + fi + + # Isolate to find the tag and name + # Trim off digest + repo="$(echo -n "$image_url" | cut -d@ -f1)" + if [[ $colon_number == 2 ]]; then + # format is now registry:port/repository:tag + # trim off everything after the last colon + tag=${repo##*:} + repo=${repo%:*} + elif [[ $colon_number == 1 ]]; then + # we have either a port or a tag so inspect the content after + # the colon to determine if it is a valid tag. + # https://github.com/opencontainers/distribution-spec/blob/main/spec.md + # [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127} is the regex for a valid tag + # If not a valid tag, leave the colon alone. + if [[ "$(echo -n "$repo" | cut -d: -f2 | tr -d '[:space:]')" =~ ^([a-zA-Z0-9_][a-zA-Z0-9._-]{0,127})$ ]]; then + # We match a tag so trim it off + tag=$(echo -n "$repo" | cut -d: -f2) + repo=$(echo -n "$repo" | cut -d: -f1) + fi + elif [[ $colon_number != 0 ]]; then + # The only other supported format is registry/repository + echo "$image_url does not match the format registry(:port)/repository(:tag)(@digest)" + exit 3 + fi + + echo -n "{\"repository\": \"$repo\", \"tag\": \"$tag\", \"digest\": \"$digest\"}" +} + +# Helper function to just get the pullspec in repository:tag format +get_image_repository_tag() { + local image_url=$1 + + if [ -z "$image_url" ]; then + echo "Missing image pull spec" >&2 + exit 2 + fi + parse_image_url "$image_url" | jq -jr '.repository + if .tag != "" then ":" + .tag else "" end' +} + +# Helper function to just get the pullspec in repository:tag@digest format +get_image_repository_tag_digest() { + local image_url=$1 + + if [ -z "$image_url" ]; then + echo "Missing image pull spec" >&2 + exit 2 + fi + parse_image_url "$image_url" | jq -jr '.repository + if .tag != "" then ":" + .tag else "" end + if .digest != "" then "@" + .digest else "" end' +} + +# Helper function to just get the pullspec in repository@digest format +get_image_repository_digest() { + local image_url=$1 + + if [ -z "$image_url" ]; then + echo "Missing image pull spec" >&2 + exit 2 + fi + parse_image_url "$image_url" | jq -jr '.repository + if .digest != "" then "@" + .digest else "" end' +} + # The function will be used by the tekton tasks of build-definitions # It returns a map of {arch:digest} for the given image_url @@ -208,6 +301,8 @@ get_image_manifests() { exit 2 fi + # Ensure that we don't have a tag and digest for skopeo + image_url=$(get_image_repository_digest "$image_url") if ! raw_inspect_output=$(skopeo inspect --no-tags --raw docker://"${image_url}"); then echo "The raw image inspect command failed" >&2 exit 1 @@ -238,15 +333,20 @@ get_ocp_version_from_fbc_fragment() { exit 2 fi + # Ensure that we have an image manifest + fbc_manifest_digest=$(get_image_manifests -i "$FBC_FRAGMENT" | jq -er ".amd64") + fbc_manifest=$(parse_image_url "$FBC_FRAGMENT" | jq -ej ".repository + \"@$fbc_manifest_digest\"") #Get target ocp version from the fragment local ocp_version - if ! ocp_version=$(skopeo inspect --no-tags --raw docker://"$FBC_FRAGMENT"); then + if ! ocp_version=$(skopeo inspect --no-tags docker://"$FBC_FRAGMENT"); then echo "Could not inspect image $FBC_FRAGMENT" exit 1 fi - ocp_version=$(echo "$ocp_version" | jq -r '.annotations."org.opencontainers.image.base.name"' | sed -e "s/@.*$//" -e "s/^.*://") - echo "$ocp_version" + # strips hash first due to greedy match + base_image=$(get_image_annotations "$fbc_manifest" | grep 'org.opencontainers.image.base.name=' | cut -d= -f2 || true) + ocp_version=$(parse_image_url "$base_image" | jq -ej '.tag') + echo -n "$ocp_version" } # Given output of `opm render` command and package name, this function returns @@ -340,9 +440,7 @@ get_unreleased_bundle() { # If the index image is provided and has a tag, remove it. # The target ocp version is determined from the fragment - if [[ "$INDEX_IMAGE" == *:* ]]; then - INDEX_IMAGE="${INDEX_IMAGE%%:*}" - fi + IMAGE_INDEX=$(parse_image_url "$IMAGE_INDEX" | jq -jr '.repository') #Get target ocp version from the fragment local ocp_version @@ -416,9 +514,7 @@ get_unreleased_fbc_related_images() { # If the index image is provided and has a tag, remove it. # The target ocp version is determined from the fragment - if [[ "$INDEX_IMAGE" == *:* ]]; then - INDEX_IMAGE="${INDEX_IMAGE%%:*}" - fi + IMAGE_INDEX=$(parse_image_url "$IMAGE_INDEX" | jq -jr '.repository') #Get target ocp version from the fragment local ocp_version @@ -478,7 +574,31 @@ get_image_labels() { exit 1 fi - echo "${image_labels}" | jq -r '.config.Labels // {} | to_entries[] | "\(.key)=\(.value)"' + echo "${image_labels}" | jq -jr '.config.Labels // {} | to_entries[] | "\(.key)=\(.value)"' + +} + +# This function will be used by tekton tasks in build-definitions +# It returns a list of annotations on the image +# If no annotations exist, it returns an empty string +get_image_annotations() { + local image=$1 + + if [ -z "$image" ]; then + echo "Missing image pull spec" >&2 + exit 2 + fi + + # Ensure that we don't have a tag and digest for skopeo + image=$(get_image_repository_digest "$image") + + local image_annotations + if ! image_annotations=$(skopeo inspect --raw docker://"${image}"); then + echo "Failed to inspect the image" >&2 + exit 1 + fi + + echo "${image_annotations}" | jq -jr 'if .annotations != null then .annotations | to_entries[] | "\(.key)=\(.value)" else "" end' }