From 145f1b05ff2b550d7b97af88e3c8c4dc14c4d89e Mon Sep 17 00:00:00 2001 From: Deep Mistry Date: Wed, 3 Jun 2026 17:37:25 -0400 Subject: [PATCH] promotion: unify ocp quay promotion; drop consolidation and -quay suffix --- pkg/api/promotion.go | 69 ++++--------------- pkg/api/promotion_test.go | 28 ++------ pkg/defaults/defaults.go | 2 +- pkg/defaults/defaults_test.go | 16 ++--- pkg/steps/input_image_tag_test.go | 4 +- pkg/steps/release/create_release.go | 3 - pkg/steps/release/create_release_test.go | 14 ++-- pkg/steps/release/promote.go | 8 +-- pkg/steps/release/promote_test.go | 25 ++++--- pkg/steps/release/snapshot.go | 2 +- pkg/steps/release/snapshot_test.go | 8 +-- ...uayPromotionShell_promotion_quay_4.12.yaml | 8 +-- ...ionShell_promotion_quay_multiple_tags.yaml | 12 ++-- ..._promotion_quay_non_release_namespace.yaml | 4 +- pkg/steps/utils/image.go | 24 ++++--- pkg/steps/utils/image_test.go | 22 ++++-- 16 files changed, 99 insertions(+), 150 deletions(-) diff --git a/pkg/api/promotion.go b/pkg/api/promotion.go index 96a271893d0..1aaf1dce7fb 100644 --- a/pkg/api/promotion.go +++ b/pkg/api/promotion.go @@ -107,60 +107,11 @@ func quayImageWithTime(timestamp string, tag ImageStreamTagReference) string { return fmt.Sprintf("%s:%s_prune_%s_%s_%s", QuayOpenShiftCIRepo, timestamp, tag.Namespace, tag.Name, tag.Tag) } -func ConsolidatedQuayPromotion(c *ReleaseBuildConfiguration) bool { - if c == nil { - return false - } - if c.ReleaseTagConfiguration != nil && ConsolidatedQuayPromotionVersion(c.ReleaseTagConfiguration.Name) { - return true - } - for _, target := range PromotionTargets(c.PromotionConfiguration) { - if ConsolidatedQuayPromotionVersion(target.Name) { - return true - } - } - return false -} - -func ConsolidatedQuayPromotionVersion(name string) bool { - var major, minor int - if _, err := fmt.Sscanf(name, "%d.%d", &major, &minor); err != nil { - return false - } - return major == 4 && minor >= 11 && minor <= 22 -} - -// UsesOfficialImageTagResolution reports whether an official OCP input should resolve via -// spec/status on the source imagestream before falling back to computed quay-proxy. -// Versioned streams at 4.23+ use computed quay only; non-versioned streams (e.g. builder) use spec-first. -func UsesOfficialImageTagResolution(tag ImageStreamTagReference) bool { - if !RefersToOfficialImage(tag.Namespace, WithoutOKD) { - return false - } - if ConsolidatedQuayPromotionVersion(tag.Name) { - return true - } - var major, minor int - if _, err := fmt.Sscanf(tag.Name, "%d.%d", &major, &minor); err == nil { - return false - } - return true -} - -func quayProxyStreamSuffix(tag ImageStreamTagReference) string { - if ConsolidatedQuayPromotionVersion(tag.Name) { - return "" - } - return "-quay" -} - -// getQuayProxyTarget creates the quay-proxy target imagestream tag reference. -// Format: namespace/imagestream-name-quay:tag +// getQuayProxyTarget creates the quay-proxy app.ci imagestream tag reference. +// Format: namespace/imagestream-name:tag (e.g. ocp/4.22:ovn-kubernetes). func getQuayProxyTarget(target string, tag ImageStreamTagReference) string { - suffix := quayProxyStreamSuffix(tag) if tag.Name != "" { - proxyTarget := fmt.Sprintf("%s/%s%s:%s", tag.Namespace, tag.Name, suffix, tag.Tag) - return proxyTarget + return fmt.Sprintf("%s/%s:%s", tag.Namespace, tag.Name, tag.Tag) } // For tag-based promotion, parse the target string to extract component name @@ -174,15 +125,19 @@ func getQuayProxyTarget(target string, tag ImageStreamTagReference) string { tagStart := len(tagPart) - len(tagSuffix) targetComponent := tagPart[first+1 : tagStart] if targetComponent != "" { - proxyTarget := fmt.Sprintf("%s/%s%s:%s", targetNamespace, targetComponent, suffix, tag.Tag) - return proxyTarget + return fmt.Sprintf("%s/%s:%s", targetNamespace, targetComponent, tag.Tag) + } + } + if first > 0 && tag.Tag != "" { + targetNamespace := tagPart[:first] + remainder := tagPart[first+1:] + if componentPrefix := tag.Tag + "_"; strings.HasPrefix(remainder, componentPrefix) { + return fmt.Sprintf("%s/%s:%s", targetNamespace, tag.Tag, remainder[len(componentPrefix):]) } } } - // Fallback: use namespace and tag - proxyTarget := fmt.Sprintf("%s/%s%s:%s", tag.Namespace, tag.Tag, suffix, tag.Tag) - return proxyTarget + return fmt.Sprintf("%s/%s:%s", tag.Namespace, tag.Tag, tag.Tag) } func qciPullSpec(pipelineSource string) (string, bool) { diff --git a/pkg/api/promotion_test.go b/pkg/api/promotion_test.go index 985403ddec9..8c229e6fee1 100644 --- a/pkg/api/promotion_test.go +++ b/pkg/api/promotion_test.go @@ -63,26 +63,6 @@ func TestPromotesOfficialImages(t *testing.T) { } } -func TestUsesOfficialImageTagResolution(t *testing.T) { - tests := []struct { - tag ImageStreamTagReference - want bool - }{ - {tag: ImageStreamTagReference{Namespace: "ocp", Name: "4.22", Tag: "cli"}, want: true}, - {tag: ImageStreamTagReference{Namespace: "ocp", Name: "4.23", Tag: "cli"}, want: false}, - {tag: ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"}, want: false}, - {tag: ImageStreamTagReference{Namespace: "ocp", Name: "builder", Tag: "rhel-9-golang-1.22-openshift-4.17"}, want: true}, - {tag: ImageStreamTagReference{Namespace: "ci", Name: "tools", Tag: "latest"}, want: false}, - } - for _, tt := range tests { - t.Run(tt.tag.ISTagName(), func(t *testing.T) { - if diff := cmp.Diff(tt.want, UsesOfficialImageTagResolution(tt.tag)); diff != "" { - t.Fatalf("UsesOfficialImageTagResolution() mismatch (-want +got):\n%s", diff) - } - }) - } -} - func TestTargetName(t *testing.T) { var testCases = []struct { name string @@ -207,7 +187,7 @@ func TestQuayCombinedMirrorFunc(t *testing.T) { expected map[string]string }{ { - name: "4.12 consolidated quay proxy target", + name: "4.12 quay proxy target", source: "registry.build02.ci.openshift.org/ci-op-abc/pipeline@sha256:abc123", target: "quay.io/openshift/ci:ocp_4.12_ovn-kubernetes", tag: ImageStreamTagReference{ @@ -251,7 +231,7 @@ func TestQuayCombinedMirrorFunc(t *testing.T) { expected: map[string]string{ "quay.io/openshift/ci:ocp__ovn-kubernetes": "registry.build02.ci.openshift.org/ci-op-abc/pipeline@sha256:def456", "quay.io/openshift/ci:20241024103000_prune_ocp__ovn-kubernetes": "quay.io/openshift/ci:ocp__ovn-kubernetes", - "ocp/ovn-kubernetes-quay:ovn-kubernetes": "quay-proxy.ci.openshift.org/openshift/ci@sha256:def456", + "ocp/ovn-kubernetes:latest": "quay-proxy.ci.openshift.org/openshift/ci@sha256:def456", }, }, { @@ -297,7 +277,7 @@ func TestQuayCombinedMirrorFunc(t *testing.T) { expected: map[string]string{ "quay.io/openshift/ci:ocp_release_payload_images": "registry.build02.ci.openshift.org/ci-op-abc/pipeline@sha256:abc123", "quay.io/openshift/ci:20241024102030_prune_ocp_release_payload_images": "quay.io/openshift/ci:ocp_release_payload_images", - "ocp/release-quay:payload_images": "quay-proxy.ci.openshift.org/openshift/ci@sha256:abc123", + "ocp/release:payload_images": "quay-proxy.ci.openshift.org/openshift/ci@sha256:abc123", }, }, { @@ -313,7 +293,7 @@ func TestQuayCombinedMirrorFunc(t *testing.T) { expected: map[string]string{ "quay.io/openshift/ci:ocp__ci_a_latest": "registry.build02.ci.openshift.org/ci-op-abc/pipeline@sha256:def456", "quay.io/openshift/ci:20241024103000_prune_ocp__ci_a_latest": "quay.io/openshift/ci:ocp__ci_a_latest", - "ocp/ovn-kubernetes-quay:ci_a_latest": "quay-proxy.ci.openshift.org/openshift/ci@sha256:def456", + "ocp/ovn-kubernetes:ci_a_latest": "quay-proxy.ci.openshift.org/openshift/ci@sha256:def456", }, }, { diff --git a/pkg/defaults/defaults.go b/pkg/defaults/defaults.go index 5913e9cd92b..cc296fefb63 100644 --- a/pkg/defaults/defaults.go +++ b/pkg/defaults/defaults.go @@ -303,7 +303,7 @@ func fromConfig(ctx context.Context, cfg *Config) ([]api.Step, []api.Step, error return nil, nil, fmt.Errorf("cannot promote images, no promotion configuration defined") } - if !api.ConsolidatedQuayPromotion(cfg.CIConfig) { + if !api.PromotesOfficialImages(cfg.CIConfig, api.WithoutOKD) { promotionSteps = append(promotionSteps, releasesteps.PromotionStep(api.PromotionStepName, cfg.CIConfig, requiredNames, cfg.SkippedImages, cfg.JobSpec, cfg.podClient, cfg.PushSecret, registryDomain(cfg.CIConfig.PromotionConfiguration), api.DefaultMirrorFunc, api.DefaultTargetNameFunc, cfg.NodeArchitectures)) } // Used primarily (only?) by the ci-chat-bot diff --git a/pkg/defaults/defaults_test.go b/pkg/defaults/defaults_test.go index af8c51adce7..0913b5303ab 100644 --- a/pkg/defaults/defaults_test.go +++ b/pkg/defaults/defaults_test.go @@ -1717,11 +1717,11 @@ func TestFromConfig(t *testing.T) { expectedSteps: []string{"[output-images]", "[images]"}, expectedPost: []string{"[promotion]", "[promotion-quay]"}, }, { - name: "promote 4.12 consolidated quay", + name: "promote 4.12 quay only", config: api.ReleaseBuildConfiguration{ PromotionConfiguration: &api.PromotionConfiguration{ Targets: []api.PromotionTarget{{ - Namespace: ns, + Namespace: "ocp", Name: "4.12", Tag: "tag", }}, @@ -1731,11 +1731,11 @@ func TestFromConfig(t *testing.T) { expectedSteps: []string{"[output-images]", "[images]"}, expectedPost: []string{"[promotion-quay]"}, }, { - name: "promote 4.23 legacy quay", + name: "promote 4.23 quay only", config: api.ReleaseBuildConfiguration{ PromotionConfiguration: &api.PromotionConfiguration{ Targets: []api.PromotionTarget{{ - Namespace: ns, + Namespace: "ocp", Name: "4.23", Tag: "tag", }}, @@ -1743,13 +1743,13 @@ func TestFromConfig(t *testing.T) { }, promote: true, expectedSteps: []string{"[output-images]", "[images]"}, - expectedPost: []string{"[promotion]", "[promotion-quay]"}, + expectedPost: []string{"[promotion-quay]"}, }, { - name: "promote 5.0 legacy quay", + name: "promote 5.0 quay only", config: api.ReleaseBuildConfiguration{ PromotionConfiguration: &api.PromotionConfiguration{ Targets: []api.PromotionTarget{{ - Namespace: ns, + Namespace: "ocp", Name: "5.0", Tag: "tag", }}, @@ -1757,7 +1757,7 @@ func TestFromConfig(t *testing.T) { }, promote: true, expectedSteps: []string{"[output-images]", "[images]"}, - expectedPost: []string{"[promotion]", "[promotion-quay]"}, + expectedPost: []string{"[promotion-quay]"}, }, { name: "duplicate input images", config: api.ReleaseBuildConfiguration{ diff --git a/pkg/steps/input_image_tag_test.go b/pkg/steps/input_image_tag_test.go index 7e1e30f28da..4765e48a167 100644 --- a/pkg/steps/input_image_tag_test.go +++ b/pkg/steps/input_image_tag_test.go @@ -270,7 +270,7 @@ func TestInputImageTagStepOCPBuilderReference(t *testing.T) { } } -func TestInputImageTagStepLegacyStream(t *testing.T) { +func TestInputImageTagStepConsolidatedStream(t *testing.T) { baseImage := api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"} config := api.InputImageTagStepConfiguration{ InputImage: api.InputImage{To: "cli", BaseImage: baseImage}, @@ -320,7 +320,7 @@ func TestInputImageTagStepLegacyStream(t *testing.T) { } func TestInputImageTagStepStableFirst(t *testing.T) { - baseImage := api.ImageStreamTagReference{Namespace: "ocp", Name: "4.22", Tag: "cli"} + baseImage := api.ImageStreamTagReference{Namespace: "ocp", Name: api.StableImageStream, Tag: "cli"} config := api.InputImageTagStepConfiguration{ InputImage: api.InputImage{To: "ocp_4_22_cli", BaseImage: baseImage}, } diff --git a/pkg/steps/release/create_release.go b/pkg/steps/release/create_release.go index a10e8de4425..9e346619776 100644 --- a/pkg/steps/release/create_release.go +++ b/pkg/steps/release/create_release.go @@ -337,9 +337,6 @@ func joinOcAdmReleaseNewCommand(config *api.ReleaseTagConfiguration, namespace, } func buildOcAdmReleaseNewCommand(config *api.ReleaseTagConfiguration, namespace, streamName, cvo, destination, version string) string { - if !api.ConsolidatedQuayPromotionVersion(config.Name) { - return joinOcAdmReleaseNewCommand(config, namespace, cvo, destination, version, "--from-image-stream", streamName) - } filePathVar := "${_CI_RELEASE_IS_FILE}" fromStream := joinOcAdmReleaseNewCommand(config, namespace, cvo, destination, version, "--from-image-stream", streamName) fromFile := joinOcAdmReleaseNewCommand(config, namespace, cvo, destination, version, "--from-image-stream-file", filePathVar) diff --git a/pkg/steps/release/create_release_test.go b/pkg/steps/release/create_release_test.go index fc7a9fd435c..1436e4bce7a 100644 --- a/pkg/steps/release/create_release_test.go +++ b/pkg/steps/release/create_release_test.go @@ -97,20 +97,14 @@ func TestBuildOcAdmReleaseNewCommand(t *testing.T) { }) t.Run("assemble_script", func(t *testing.T) { - srcPol := imagev1.SourceTagReferencePolicy - config := &api.ReleaseTagConfiguration{Name: "4.12", ReferencePolicy: &srcPol} - got := buildOcAdmReleaseNewCommand(config, "test-ns", "stable", "cvo-pullspec", "dest:tag", "0.0.1-ver") want := `_CI_RELEASE_IS_FILE="/tmp/ci-operator-release-is-stable.yaml" if oc get imagestream "stable" -n "test-ns" -o yaml > "${_CI_RELEASE_IS_FILE}" 2>/dev/null; then - oc adm release new --max-per-registry=32 -n test-ns --from-image-stream-file ${_CI_RELEASE_IS_FILE} --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --keep-manifest-list || oc adm release new --max-per-registry=32 -n test-ns --from-image-stream stable --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --keep-manifest-list + oc adm release new --max-per-registry=32 -n test-ns --from-image-stream-file ${_CI_RELEASE_IS_FILE} --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --reference-mode=source --keep-manifest-list || oc adm release new --max-per-registry=32 -n test-ns --from-image-stream stable --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --reference-mode=source --keep-manifest-list else - oc adm release new --max-per-registry=32 -n test-ns --from-image-stream stable --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --keep-manifest-list + oc adm release new --max-per-registry=32 -n test-ns --from-image-stream stable --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --reference-mode=source --keep-manifest-list fi` - if diff := cmp.Diff(want, got); diff != "" { - t.Fatalf("buildOcAdmReleaseNewCommand() mismatch (-want +got):\n%s", diff) - } - got = buildOcAdmReleaseNewCommand(&api.ReleaseTagConfiguration{Name: "4.23", ReferencePolicy: &srcPol}, "test-ns", "stable", "cvo-pullspec", "dest:tag", "0.0.1-ver") - want = "oc adm release new --max-per-registry=32 -n test-ns --from-image-stream stable --to-image-base cvo-pullspec --to-image dest:tag --name 0.0.1-ver --reference-mode=source --keep-manifest-list" + config := &api.ReleaseTagConfiguration{Name: "4.23", ReferencePolicy: &sourceTagReference} + got := buildOcAdmReleaseNewCommand(config, "test-ns", "stable", "cvo-pullspec", "dest:tag", "0.0.1-ver") if diff := cmp.Diff(want, got); diff != "" { t.Fatalf("buildOcAdmReleaseNewCommand() mismatch (-want +got):\n%s", diff) } diff --git a/pkg/steps/release/promote.go b/pkg/steps/release/promote.go index a40d2f671aa..8aa75b5da4c 100644 --- a/pkg/steps/release/promote.go +++ b/pkg/steps/release/promote.go @@ -302,7 +302,7 @@ EOF`, loglevel, strings.Join(tagSpecs, "\n")) } // quayProxyTagFromISKey derives the quay-proxy floating tag from an IS tag key. -// Handles "namespace/stream-quay:tag" (4.23+) and consolidated "ocp/4.13:tag" (4.11–4.22). +// Handles "ocp/4.13:cli" and legacy "namespace/stream-quay:tag" (ci templates). // Example: "ocp/4.13:cli" → "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.13_cli". func quayProxyTagFromISKey(isTagKey string) (string, bool) { slashIdx := strings.Index(isTagKey, "/") @@ -324,7 +324,7 @@ func quayProxyTagFromISKey(isTagKey string) (string, bool) { var streamName string if strings.HasSuffix(streamPart, quayStreamSuffix) { streamName = strings.TrimSuffix(streamPart, quayStreamSuffix) - } else if api.ConsolidatedQuayPromotionVersion(streamPart) { + } else if api.RefersToOfficialImage(namespace, api.WithOKD) { streamName = streamPart } else { return "", false @@ -485,11 +485,11 @@ func getResolveAndTagRetryShell(registryConfig, quayProxyTag, isTag string, logl if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=%d --reference-policy='source' --import-mode='PreserveOriginal' --reference %s@${_digest} %s; then break fi - echo "promotion-quay: digest-tag failed for %s attempt ${r}/%d (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for %s attempt ${r}/%d (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq %d ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for %s (attempt $((r+1))/%d after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for %s (attempt $((r+1))/%d after randomized backoff)" >&2 backoff=$(($RANDOM %% %d))s sleep "${backoff}" done diff --git a/pkg/steps/release/promote_test.go b/pkg/steps/release/promote_test.go index 70bc2545571..f7be5b1455e 100644 --- a/pkg/steps/release/promote_test.go +++ b/pkg/steps/release/promote_test.go @@ -1093,10 +1093,10 @@ func TestGetImageMirror(t *testing.T) { imageMirror[target] = proxyTarget }, targetNameFunc: func(registry string, config api.PromotionTarget) string { - return fmt.Sprintf("%s/%s/%s-quay:${component}", registry, config.Namespace, config.Name) + return fmt.Sprintf("%s/%s/%s:${component}", registry, config.Namespace, config.Name) }, expected: map[string]string{ - "registry.ci.openshift.org/ocp/4.22-quay:vertical-pod-autoscaler": "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.22_vertical-pod-autoscaler", + "registry.ci.openshift.org/ocp/4.22:vertical-pod-autoscaler": "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.22_vertical-pod-autoscaler", }, }, { @@ -1258,7 +1258,7 @@ func TestGetQuayPromotionShell(t *testing.T) { func TestGetResolveAndTagRetryShell(t *testing.T) { regcfg := "/etc/push-secret/.dockerconfigjson" proxyTag := "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.21_ovn-kubernetes" - isTag := "ocp/4.21-quay:ovn-kubernetes" + isTag := "ocp/4.21:ovn-kubernetes" got := getResolveAndTagRetryShell(regcfg, proxyTag, isTag, 2, "linux/amd64") quayIOTag := "quay.io/openshift/ci:ocp_4.21_ovn-kubernetes" @@ -1266,8 +1266,8 @@ func TestGetResolveAndTagRetryShell(t *testing.T) { "for r in {1..5}", "oc image info --registry-config=" + regcfg + " --filter-by-os=linux/amd64 " + quayIOTag, "oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} " + isTag, - "promotion-quay: digest-tag failed for " + isTag, - "promotion-quay: retrying digest-tag for " + isTag, + "promotion: digest-tag failed for " + isTag, + "promotion: retrying digest-tag for " + isTag, `[ "${r}" -eq 5 ]`, "exit 1", "$(($RANDOM % 120))", @@ -1335,21 +1335,28 @@ func TestQuayProxyTagFromISKey(t *testing.T) { wantOK: false, }, { - name: "consolidated ocp stream", + name: "ocp stream", isTagKey: "ocp/4.13:secondary-scheduler-operator", wantTag: "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.13_secondary-scheduler-operator", wantOK: true, }, { - name: "consolidated 4.21 stream", + name: "ocp 4.21 stream", isTagKey: "ocp/4.21:ovn-kubernetes", wantTag: "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.21_ovn-kubernetes", wantOK: true, }, { - name: "non-consolidated stream without -quay", + name: "ocp 4.23 stream", isTagKey: "ocp/4.23:ovn-kubernetes", - wantOK: false, + wantTag: "quay-proxy.ci.openshift.org/openshift/ci:ocp_4.23_ovn-kubernetes", + wantOK: true, + }, + { + name: "ocp 5.0 stream", + isTagKey: "ocp/5.0:ansible", + wantTag: "quay-proxy.ci.openshift.org/openshift/ci:ocp_5.0_ansible", + wantOK: true, }, } for _, tt := range tests { diff --git a/pkg/steps/release/snapshot.go b/pkg/steps/release/snapshot.go index 2a057214368..c7d66c0b52a 100644 --- a/pkg/steps/release/snapshot.go +++ b/pkg/steps/release/snapshot.go @@ -141,7 +141,7 @@ func snapshotImportSource(sourceNamespace, sourceName, tag string, source *image } return nil, false } - if api.UsesOfficialImageTagResolution(base) { + if api.RefersToOfficialImage(sourceNamespace, api.WithoutOKD) { return utils.OfficialImageTagFrom(source, base), true } return from, true diff --git a/pkg/steps/release/snapshot_test.go b/pkg/steps/release/snapshot_test.go index bd102ec8a59..451ab7996cf 100644 --- a/pkg/steps/release/snapshot_test.go +++ b/pkg/steps/release/snapshot_test.go @@ -26,7 +26,7 @@ func TestSnapshotImportSource(t *testing.T) { wantFrom *coreapi.ObjectReference }{ { - name: "consolidated spec first", + name: "ocp spec first", stream: "4.18", tag: base.Tag, source: &imagev1.ImageStream{ @@ -48,14 +48,14 @@ func TestSnapshotImportSource(t *testing.T) { wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(base)}, }, { - name: "consolidated missing source imagestream", + name: "ocp missing source imagestream", stream: "4.22", tag: base.Tag, wantOK: true, wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(api.ImageStreamTagReference{Namespace: "ocp", Name: "4.22", Tag: base.Tag})}, }, { - name: "non-consolidated uses computed quay", + name: "ocp spec docker 4.23", stream: "4.23", tag: "cli", source: &imagev1.ImageStream{ @@ -66,7 +66,7 @@ func TestSnapshotImportSource(t *testing.T) { }}}, }, wantOK: true, - wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(api.ImageStreamTagReference{Namespace: "ocp", Name: "4.23", Tag: "cli"})}, + wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: specPull}, }, { name: "default quay float", diff --git a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_4.12.yaml b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_4.12.yaml index f374770ea9d..393af654426 100644 --- a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_4.12.yaml +++ b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_4.12.yaml @@ -39,11 +39,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.12:ovn-kubernetes; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.12:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.12:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.12:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.12:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done @@ -53,11 +53,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.12:ovn-kubernetes-base; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.12:ovn-kubernetes-base attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.12:ovn-kubernetes-base attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.12:ovn-kubernetes-base (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.12:ovn-kubernetes-base (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done diff --git a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_multiple_tags.yaml b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_multiple_tags.yaml index c259ae1d0a1..f0dea3e05f1 100644 --- a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_multiple_tags.yaml +++ b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_multiple_tags.yaml @@ -50,11 +50,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.21:ovn-kubernetes; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.21:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.21:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.21:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.21:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done @@ -64,11 +64,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.21:ovn-kubernetes-base; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.21:ovn-kubernetes-base attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.21:ovn-kubernetes-base attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.21:ovn-kubernetes-base (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.21:ovn-kubernetes-base (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done @@ -78,11 +78,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.21:ovn-kubernetes-microshift; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.21:ovn-kubernetes-microshift attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.21:ovn-kubernetes-microshift attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.21:ovn-kubernetes-microshift (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.21:ovn-kubernetes-microshift (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done diff --git a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_non_release_namespace.yaml b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_non_release_namespace.yaml index 527ea375eab..12053b966d8 100644 --- a/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_non_release_namespace.yaml +++ b/pkg/steps/release/testdata/zz_fixture_TestGetQuayPromotionShell_promotion_quay_non_release_namespace.yaml @@ -37,11 +37,11 @@ data: if [ -n "${_digest}" ] && oc tag --source=docker --loglevel=2 --reference-policy='source' --import-mode='PreserveOriginal' --reference quay-proxy.ci.openshift.org/openshift/ci@${_digest} ocp/4.21:ovn-kubernetes; then break fi - echo "promotion-quay: digest-tag failed for ocp/4.21:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 + echo "promotion: digest-tag failed for ocp/4.21:ovn-kubernetes attempt ${r}/5 (QCI digest may have moved after mirror)" >&2 if [ "${r}" -eq 5 ]; then exit 1 fi - echo "promotion-quay: retrying digest-tag for ocp/4.21:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 + echo "promotion: retrying digest-tag for ocp/4.21:ovn-kubernetes (attempt $((r+1))/5 after randomized backoff)" >&2 backoff=$(($RANDOM % 120))s sleep "${backoff}" done diff --git a/pkg/steps/utils/image.go b/pkg/steps/utils/image.go index 8f45d1755eb..82290d4b20b 100644 --- a/pkg/steps/utils/image.go +++ b/pkg/steps/utils/image.go @@ -129,20 +129,22 @@ func PullSpecForImageStreamTag(registryURL string, source *imagev1.ImageStream, // ResolveOfficialInputFrom resolves official ocp inputs: stable in job ns, then spec/status/quay on source IS. // When ok is false, callers use QuayImageReference with Source policy (e.g. 4.23, 5.0). func ResolveOfficialInputFrom(ctx context.Context, client ctrlruntimeclient.Client, jobNamespace string, base api.ImageStreamTagReference) (*coreapi.ObjectReference, bool, error) { - if !api.UsesOfficialImageTagResolution(base) { + if !api.RefersToOfficialImage(base.Namespace, api.WithoutOKD) { return nil, false, nil } - stable := &imagev1.ImageStream{} - if err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Namespace: jobNamespace, Name: api.StableImageStream}, stable); err == nil { - if _, exists, _ := util.ResolvePullSpec(stable, base.Tag, true); exists { - return &coreapi.ObjectReference{ - Kind: "ImageStreamTag", - Namespace: jobNamespace, - Name: fmt.Sprintf("%s:%s", api.StableImageStream, base.Tag), - }, true, nil + if base.Name == api.StableImageStream || api.IsReleaseStream(base.Name) { + stable := &imagev1.ImageStream{} + if err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Namespace: jobNamespace, Name: api.StableImageStream}, stable); err == nil { + if _, exists, _ := util.ResolvePullSpec(stable, base.Tag, true); exists { + return &coreapi.ObjectReference{ + Kind: "ImageStreamTag", + Namespace: jobNamespace, + Name: fmt.Sprintf("%s:%s", api.StableImageStream, base.Tag), + }, true, nil + } + } else if !kerrors.IsNotFound(err) { + return nil, false, fmt.Errorf("get stable imagestream in %s: %w", jobNamespace, err) } - } else if !kerrors.IsNotFound(err) { - return nil, false, fmt.Errorf("get stable imagestream in %s: %w", jobNamespace, err) } source := &imagev1.ImageStream{} if err := client.Get(ctx, ctrlruntimeclient.ObjectKey{Namespace: base.Namespace, Name: base.Name}, source); err != nil && !kerrors.IsNotFound(err) { diff --git a/pkg/steps/utils/image_test.go b/pkg/steps/utils/image_test.go index ffe1776835e..c076c773e96 100644 --- a/pkg/steps/utils/image_test.go +++ b/pkg/steps/utils/image_test.go @@ -39,8 +39,8 @@ func TestResolveOfficialInputFrom(t *testing.T) { wantOK bool wantFrom *coreapi.ObjectReference }{ - {name: "non-consolidated", base: api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"}, wantOK: false}, - {name: "4.23 uses computed quay", base: api.ImageStreamTagReference{Namespace: "ocp", Name: "4.23", Tag: "cli"}, wantOK: false}, + {name: "official ocp 5.0", base: api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"}, wantOK: true, wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"})}}, + {name: "non-official namespace", base: api.ImageStreamTagReference{Namespace: "ci", Name: "5.0", Tag: "cli"}, wantOK: false}, { name: "ocp builder spec quay-proxy digest", base: api.ImageStreamTagReference{Namespace: "ocp", Name: "builder", Tag: "rhel-9-golang-1.22-openshift-4.17"}, @@ -113,8 +113,8 @@ func TestResolveOfficialInputFrom(t *testing.T) { wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(api.ImageStreamTagReference{Namespace: "ocp", Name: "4.16", Tag: "base-rhel9"})}, }, { - name: "stable first", - base: api.ImageStreamTagReference{Namespace: "ocp", Name: "4.22", Tag: "cli"}, + name: "stable stream in job namespace", + base: api.ImageStreamTagReference{Namespace: "ocp", Name: api.StableImageStream, Tag: "cli"}, objects: []runtime.Object{&imagev1.ImageStream{ ObjectMeta: metav1.ObjectMeta{Namespace: "job-ns", Name: api.StableImageStream}, Spec: imagev1.ImageStreamSpec{Tags: []imagev1.TagReference{{Name: "cli"}}}, @@ -126,6 +126,20 @@ func TestResolveOfficialInputFrom(t *testing.T) { wantOK: true, wantFrom: &coreapi.ObjectReference{Kind: "ImageStreamTag", Name: "stable:cli", Namespace: "job-ns"}, }, + { + name: "versioned ocp stream not redirected to job stable", + base: api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"}, + objects: []runtime.Object{&imagev1.ImageStream{ + ObjectMeta: metav1.ObjectMeta{Namespace: "job-ns", Name: api.StableImageStream}, + Spec: imagev1.ImageStreamSpec{Tags: []imagev1.TagReference{{Name: "cli"}}}, + Status: imagev1.ImageStreamStatus{ + PublicDockerImageRepository: "registry/job-ns/stable", + Tags: []imagev1.NamedTagEventList{{Tag: "cli", Items: []imagev1.TagEvent{{Image: "sha256:1111"}}}}, + }, + }}, + wantOK: true, + wantFrom: &coreapi.ObjectReference{Kind: "DockerImage", Name: api.QuayImageReference(api.ImageStreamTagReference{Namespace: "ocp", Name: "5.0", Tag: "cli"})}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {