Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 12 additions & 57 deletions pkg/api/promotion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down
28 changes: 4 additions & 24 deletions pkg/api/promotion_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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",
},
},
{
Expand Down Expand Up @@ -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",
},
},
{
Expand All @@ -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",
},
},
{
Expand Down
2 changes: 1 addition & 1 deletion pkg/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions pkg/defaults/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
}},
Expand All @@ -1731,33 +1731,33 @@ 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",
}},
},
},
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",
}},
},
},
promote: true,
expectedSteps: []string{"[output-images]", "[images]"},
expectedPost: []string{"[promotion]", "[promotion-quay]"},
expectedPost: []string{"[promotion-quay]"},
}, {
name: "duplicate input images",
config: api.ReleaseBuildConfiguration{
Expand Down
4 changes: 2 additions & 2 deletions pkg/steps/input_image_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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},
}
Expand Down
3 changes: 0 additions & 3 deletions pkg/steps/release/create_release.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
14 changes: 4 additions & 10 deletions pkg/steps/release/create_release_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/steps/release/promote.go
Original file line number Diff line number Diff line change
Expand Up @@ -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, "/")
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
25 changes: 16 additions & 9 deletions pkg/steps/release/promote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
},
},
{
Expand Down Expand Up @@ -1258,16 +1258,16 @@ 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"
for _, sub := range []string{
"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))",
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/steps/release/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading