diff --git a/pipelines/internal/publish-index-image-pipeline/README.md b/pipelines/internal/publish-index-image-pipeline/README.md index 8bc983b60f..9088614350 100644 --- a/pipelines/internal/publish-index-image-pipeline/README.md +++ b/pipelines/internal/publish-index-image-pipeline/README.md @@ -8,6 +8,7 @@ Tekton pipeline to publish a built FBC index image using skopeo |-----------------------|---------------------------------------------------------------------------------------|----------|-----------------------------------------------------------| | sourceIndex | sourceIndex signing image | No | - | | targetIndex | targetIndex signing image | No | - | +| targetOcpVersion | target OCP Version of the index image | Yes | "" | | retries | Number of skopeo retries | Yes | 0 | | publishingCredentials | The credentials used to access the registries | No | - | | requestUpdateTimeout | Max seconds waiting for the status update | Yes | 360 | diff --git a/pipelines/internal/publish-index-image-pipeline/publish-index-image-pipeline.yaml b/pipelines/internal/publish-index-image-pipeline/publish-index-image-pipeline.yaml index db8a4b5cbe..86c81ceaab 100644 --- a/pipelines/internal/publish-index-image-pipeline/publish-index-image-pipeline.yaml +++ b/pipelines/internal/publish-index-image-pipeline/publish-index-image-pipeline.yaml @@ -16,6 +16,10 @@ spec: - name: targetIndex type: string description: targetIndex signing image + - name: targetOcpVersion + type: string + default: "" + description: target OCP Version of the index image - name: retries type: string default: "0" @@ -50,6 +54,8 @@ spec: value: $(params.sourceIndex) - name: targetIndex value: $(params.targetIndex) + - name: targetOcpVersion + value: $(params.targetOcpVersion) - name: retries value: $(params.retries) - name: publishingCredentials diff --git a/tasks/internal/publish-index-image-task/README.md b/tasks/internal/publish-index-image-task/README.md index 4c9e3c7d90..c73ea95bb0 100644 --- a/tasks/internal/publish-index-image-task/README.md +++ b/tasks/internal/publish-index-image-task/README.md @@ -8,6 +8,7 @@ Tekton task to publish a built FBC index image using skopeo |-----------------------|-----------------------------------------------------------------------|----------|----------------------------| | sourceIndex | sourceIndex signing image | No | - | | targetIndex | targetIndex signing image | No | - | +| targetOcpVersion | OCP Version this image was built to | Yes | "" | | retries | Number of skopeo retries | Yes | 0 | | publishingCredentials | The credentials used to access the registries | Yes | fbc-publishing-credentials | | requestUpdateTimeout | Max seconds waiting for the status update | Yes | 360 | diff --git a/tasks/internal/publish-index-image-task/publish-index-image-task.yaml b/tasks/internal/publish-index-image-task/publish-index-image-task.yaml index 3a63bc6de4..14317e83a3 100644 --- a/tasks/internal/publish-index-image-task/publish-index-image-task.yaml +++ b/tasks/internal/publish-index-image-task/publish-index-image-task.yaml @@ -16,6 +16,10 @@ spec: - name: targetIndex type: string description: targetIndex signing image + - name: targetOcpVersion + description: OCP Version this image was built to + type: string + default: "" - name: retries type: string default: "0" @@ -75,15 +79,51 @@ spec: #!/usr/bin/env bash set -euo pipefail + #function getIndexVersion() { + # # if dest- and src- string is given, remove them from the auth param + # if [[ "$#" -gt 1 ]]; then + # shift + # indexVersion="$(skopeo inspect --config --creds "$@" \ + # | jq -r '.config.Labels."com.redhat.index.delivery.version"')" + # else + # # no auth given + # indexVersion="$(skopeo inspect --config "$@" \ + # | jq -r '.config.Labels."com.redhat.index.delivery.version"')" + # fi + + # echo "$indexVersion" + #} + + function getIndexVersion() { + local image_url=$1 + local creds=${2:-} + + local creds_args=() + if [ -n "${creds}" ]; then + creds_args=(--creds "${creds}") + fi + + indexVersion="$(skopeo inspect --config "${creds_args[@]}" "${image_url}" \ + | jq -r '.config.Labels."com.redhat.index.delivery.version"')" + + echo "$indexVersion" + } + SOURCE_INDEX_CREDENTIAL="$(cat /mnt/publishingCredentials/sourceIndexCredential)" TARGET_INDEX_CREDENTIAL="$(cat /mnt/publishingCredentials/targetIndexCredential)" PATH=/bin:/usr/bin:/usr/local/bin export PATH - SOURCE_AUTH_ARGS=() - if [[ ! "$(params.sourceIndex)" =~ ^registry-proxy(\-stage)?.engineering.redhat.com ]]; then - SOURCE_AUTH_ARGS=("--src-creds" "${SOURCE_INDEX_CREDENTIAL}") + #SOURCE_AUTH_ARGS=() + #if [[ ! "$(params.sourceIndex)" =~ ^registry-proxy(\-stage)?.engineering.redhat.com ]]; then + # SOURCE_AUTH_ARGS=("--src-creds" "${SOURCE_INDEX_CREDENTIAL}") + #fi + SKOPEO_COPY_AUTH_ARGS=("--dest-creds" "${TARGET_INDEX_CREDENTIAL}") + if [[ "$(params.sourceIndex)" =~ ^registry-proxy(\-stage)?.engineering.redhat.com ]]; then + SOURCE_INDEX_CREDENTIAL="" + else + SKOPEO_COPY_AUTH_ARGS+=("--src-creds" "${SOURCE_INDEX_CREDENTIAL}") fi TARGET_AUTH_ARGS=("--dest-creds" "${TARGET_INDEX_CREDENTIAL}") @@ -111,13 +151,34 @@ spec: echo "Target image does not exist. Proceeding to copy the image." fi + # check to make sure the image to be published is for the intended ocpVersion + targetOcpVersion="$(params.targetOcpVersion)" + sourceVer=$(getIndexVersion "${SOURCE_AUTH_ARGS[@]}" "docker://$(params.sourceIndex)") + if [[ "${sourceVer}" != "${targetOcpVersion}" ]]; then + echo -n "The source index does not match its targetOcpVersion ($sourceVer != $targetOcpVersion)" \ + | tee "$(results.requestMessage.path)" + exit 0 + fi + + # hotfix and pre-ga targetIndex should be skip the next check, as they don't exist in the upstream quay + # until skopeo copy runs. + if [[ "$(params.targetIndex)" =~ .*\:v[0-9]{1}\.[0-9]{2}$ ]]; then + targetVer=$(getIndexVersion "${TARGET_AUTH_ARGS[@]}" "docker://$(params.targetIndex)") + # check if both indexes are of the same OCP version and exit in case they mismatch. + if [ "${sourceVer}" != "${targetVer}" ]; then + echo -n "The indexes versions does not match ($sourceVer != $targetVer)" \ + | tee "$(results.requestMessage.path)" + exit 0 + fi + fi + # Proceed with copying the image echo "Copying image from $(params.sourceIndex) to $(params.targetIndex)" (skopeo copy \ --all \ --preserve-digests \ --retry-times "$(params.retries)" \ - --src-tls-verify=false "${SOURCE_AUTH_ARGS[@]}" \ + --src-tls-verify=false "${SKOPEO_COPY_AUTH_ARGS[@]}" \ "docker://$(params.sourceIndex)" \ "${TARGET_AUTH_ARGS[@]}" \ "docker://$(params.targetIndex)" && \ diff --git a/tasks/internal/publish-index-image-task/tests/mocks.sh b/tasks/internal/publish-index-image-task/tests/mocks.sh index f819cdf9e2..cceac81e14 100644 --- a/tasks/internal/publish-index-image-task/tests/mocks.sh +++ b/tasks/internal/publish-index-image-task/tests/mocks.sh @@ -7,8 +7,15 @@ function skopeo() { echo Mock skopeo called with: $* >&2 if [[ "$1" == "inspect" ]]; then + if [[ "$*" == *"config"* ]]; then + if [[ "$*" == *"quay.io/mismatch-ver-target-digest"* ]]; then + echo '{ "config": { "Labels": { "com.redhat.index.delivery.version": "v4.13"} } }' + else + echo '{ "config": { "Labels": { "com.redhat.index.delivery.version": "v4.12"} } }' + fi + return 0 # Handle `skopeo inspect` - if [[ "$*" == *"docker://quay.io/match-target-digest"* ]]; then + elif [[ "$*" == *"docker://quay.io/match-target-digest"* ]]; then echo "sha256:match1234567890" # Mock target digest for idempotency check return 0 elif [[ "$*" == *"docker://quay.io/target"* ]]; then @@ -22,6 +29,12 @@ function skopeo() { return 0 elif [[ "$*" == *"--tls-verify=false docker://registry-proxy.engineering.redhat.com/fail"* ]]; then return 1 + elif [[ "$*" == *"registry-proxy.engineering.redhat.com/mismatchver@sha256:1234567890"* ]]; then + echo "sha256:target1234567890" + return 0 + elif [[ "$*" == *"quay.io/mismatch-ver-target-digest"* ]]; then + echo "sha256:0987654321fedcba" + return 0 else echo "Error: Unexpected inspect call" exit 1 diff --git a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-digest-match.yaml b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-digest-match.yaml index e99a2d9dd9..7665452b73 100644 --- a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-digest-match.yaml +++ b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-digest-match.yaml @@ -15,6 +15,8 @@ spec: value: "registry-proxy.engineering.redhat.com/match@sha256:match1234567890" - name: targetIndex value: "quay.io/match-target-digest" + - name: targetOcpVersion + value: "v4.12" - name: publishingCredentials value: "publish-index-image-secret" - name: check-result diff --git a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail-mismatch-ver.yaml b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail-mismatch-ver.yaml new file mode 100644 index 0000000000..da0381b7b0 --- /dev/null +++ b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail-mismatch-ver.yaml @@ -0,0 +1,47 @@ +--- +apiVersion: tekton.dev/v1 +kind: Pipeline +metadata: + name: test-publish-index-image-fail-mismatch-ver +spec: + description: | + Run the publish-index-image task where source and target images versions mismatch + tasks: + - name: run-task + taskRef: + name: publish-index-image-task + params: + - name: sourceIndex + value: "registry-proxy.engineering.redhat.com/mismatchver@sha256:1234567890" + - name: targetIndex + value: "quay.io/mismatch-ver-target-digest:v4.20" + - name: targetOcpVersion + value: "v4.12" + - name: publishingCredentials + value: "publish-index-image-secret" + - name: check-result + runAfter: + - run-task + params: + - name: requestMessage + value: $(tasks.run-task.results.requestMessage) + taskSpec: + params: + - name: requestMessage + type: string + steps: + - name: check-result + image: quay.io/konflux-ci/release-service-utils@sha256:5546fa78d3c88d7b6a2e8cff8902f7757f00541d0bbaf113b9f293133894afa3 + script: | + #!/usr/bin/env bash + set -ex + + ACTUAL_RESULT="$(echo -n "$(params.requestMessage)" | tr -d '\n' | xargs)" + EXPECTED_RESULT="The indexes versions does not match (v4.12 != v4.13)" + + if [[ "$ACTUAL_RESULT" != "$EXPECTED_RESULT" ]]; then + echo "Error: requestMessage task result is not correct" + echo "Expected: '$EXPECTED_RESULT'" + echo "Got: '$ACTUAL_RESULT'" + exit 1 + fi diff --git a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail.yaml b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail.yaml index fedc080234..62e9e18d97 100644 --- a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail.yaml +++ b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-fail.yaml @@ -16,6 +16,8 @@ spec: value: "registry-proxy.engineering.redhat.com/fail@sha256:0987654321fedcba" - name: targetIndex value: "quay.io/target" + - name: targetOcpVersion + value: "v4.12" - name: publishingCredentials value: "publish-index-image-secret" - name: check-result diff --git a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-registry-proxy.yaml b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-registry-proxy.yaml index 47a12273c6..00c63952df 100644 --- a/tasks/internal/publish-index-image-task/tests/test-publish-index-image-registry-proxy.yaml +++ b/tasks/internal/publish-index-image-task/tests/test-publish-index-image-registry-proxy.yaml @@ -16,6 +16,8 @@ spec: value: "registry-proxy.engineering.redhat.com/foo@sha256:0987654321fedcba" - name: targetIndex value: "quay.io/target" + - name: targetOcpVersion + value: "v4.12" - name: publishingCredentials value: "publish-index-image-secret" - name: check-result diff --git a/tasks/internal/publish-index-image-task/tests/test-publish-index-image.yaml b/tasks/internal/publish-index-image-task/tests/test-publish-index-image.yaml index a2af752a57..5ea0d2676f 100644 --- a/tasks/internal/publish-index-image-task/tests/test-publish-index-image.yaml +++ b/tasks/internal/publish-index-image-task/tests/test-publish-index-image.yaml @@ -15,6 +15,8 @@ spec: value: "quay.io/source@sha256:abcdef1234567890" - name: targetIndex value: "quay.io/target" + - name: targetOcpVersion + value: "v4.12" - name: publishingCredentials value: "publish-index-image-secret" - name: check-result diff --git a/tasks/managed/publish-index-image/publish-index-image.yaml b/tasks/managed/publish-index-image/publish-index-image.yaml index 03b910a0eb..56b0d650a7 100644 --- a/tasks/managed/publish-index-image/publish-index-image.yaml +++ b/tasks/managed/publish-index-image/publish-index-image.yaml @@ -151,8 +151,11 @@ spec: targetIndex=$(jq -r --argjson i "$i" \ '.components[$i].target_index' "$(params.dataDir)/$(params.internalRequestResultsFile)") - sourceIndex=$(jq -r --argjson i "$i" \ - '.components[$i].index_image' "$(params.dataDir)/$(params.internalRequestResultsFile)") + sourceIndex="$(jq -r --argjson i "$i" \ + '.components[$i].index_image' "$(params.dataDir)/$(params.internalRequestResultsFile)")" + + targetOcpVersion=$(jq -r --argjson i "$i" \ + '.components[$i].ocp_version' "$(params.dataDir)/$(params.internalRequestResultsFile)") buildTimestamp=$(jq -r --argjson i "$i" '.components[$i].completion_time' \ "$(params.dataDir)/$(params.internalRequestResultsFile)") @@ -179,6 +182,7 @@ spec: internal-request --pipeline "${request}" \ -p sourceIndex="${sourceIndex}" \ -p targetIndex="${publishingImages[$x]}" \ + -p targetOcpVersion="${targetOcpVersion}" \ -p publishingCredentials="${credentials}" \ -p retries="$(params.retries)" \ -p taskGitUrl="$(params.taskGitUrl)" \ diff --git a/tasks/managed/publish-index-image/tests/test-publish-index-image.yaml b/tasks/managed/publish-index-image/tests/test-publish-index-image.yaml index 5683b7350a..5946ddd094 100644 --- a/tasks/managed/publish-index-image/tests/test-publish-index-image.yaml +++ b/tasks/managed/publish-index-image/tests/test-publish-index-image.yaml @@ -208,6 +208,11 @@ spec: fi fi + if [ "$(jq -r '.targetOcpVersion' <<< "${params}")" != "v4.12" ]; then + echo "targetOcpVersion does not match" + exit 1 + fi + if [ "$(jq -r '.taskGitUrl' <<< "${params}")" != "http://localhost" ]; then echo "taskGitUrl image does not match" exit 1