@@ -4,8 +4,10 @@ import (
44 "context"
55 "fmt"
66 "strconv"
7+ "strings"
78 "time"
89
10+ "github.com/openshift/release-controller/pkg/apis/release/v1alpha1"
911 releasecontroller "github.com/openshift/release-controller/pkg/release-controller"
1012
1113 batchv1 "k8s.io/api/batch/v1"
@@ -417,3 +419,95 @@ func (c *Controller) ensureReleaseMirrorJob(release *releasecontroller.Release,
417419func releaseMirrorJobName (tagName string ) string {
418420 return fmt .Sprintf ("%s-alternate-mirror" , tagName )
419421}
422+
423+ // ensureRCPayloadTagJob creates a job to mirror the release to rc_payload__{version} in quay.io for CI payloads
424+ // This is required for the pruner to preserve component images referenced by CI payloads
425+ func (c * Controller ) ensureRCPayloadTagJob (release * releasecontroller.Release , name string , mirror * imagev1.ImageStream ) (* batchv1.Job , error ) {
426+ if len (release .Config .AlternateImageRepository ) == 0 || len (release .Config .AlternateImageRepositorySecretName ) == 0 {
427+ return nil , fmt .Errorf ("alternate repository or secret not configured" )
428+ }
429+
430+ // Only create rc_payload__ tags for CI payloads (tags containing .ci-)
431+ // Nightly payloads reference ART images that are permanently stored
432+ if ! strings .Contains (name , ".ci-" ) {
433+ return nil , nil
434+ }
435+
436+ jobName := fmt .Sprintf ("%s-rc-payload-tag" , name )
437+ return c .ensureJob (jobName , nil , func () (* batchv1.Job , error ) {
438+ fromImage := fmt .Sprintf ("%s:%s" , release .Target .Status .PublicDockerImageRepository , name )
439+ rcPayloadTag := fmt .Sprintf ("rc_payload__%s" , name )
440+ toImage := fmt .Sprintf ("%s:%s" , release .Config .AlternateImageRepository , rcPayloadTag )
441+
442+ cliImage := fmt .Sprintf ("%s:cli" , mirror .Status .DockerImageRepository )
443+ if len (release .Config .OverrideCLIImage ) > 0 {
444+ cliImage = release .Config .OverrideCLIImage
445+ }
446+
447+ job , prefix := newReleaseJobBase (jobName , cliImage , release .Config .AlternateImageRepositorySecretName )
448+
449+ manifestListMode := "false"
450+ if c .manifestListMode && ! release .Config .DisableManifestListMode {
451+ manifestListMode = "true"
452+ }
453+
454+ job .Spec .Template .Spec .Containers [0 ].Command = []string {
455+ "/bin/bash" , "-c" ,
456+ prefix + `
457+ oc image mirror --keep-manifest-list=$1 $2 $3
458+ ` ,
459+ "" ,
460+ manifestListMode , fromImage , toImage ,
461+ }
462+
463+ job .Annotations [releasecontroller .ReleaseAnnotationSource ] = mirror .Annotations [releasecontroller .ReleaseAnnotationSource ]
464+ job .Annotations [releasecontroller .ReleaseAnnotationTarget ] = mirror .Annotations [releasecontroller .ReleaseAnnotationTarget ]
465+ job .Annotations [releasecontroller .ReleaseAnnotationGeneration ] = strconv .FormatInt (release .Target .Generation , 10 )
466+ job .Annotations [releasecontroller .ReleaseAnnotationReleaseTag ] = mirror .Annotations [releasecontroller .ReleaseAnnotationReleaseTag ]
467+
468+ klog .V (2 ).Infof ("Creating rc_payload__ tag job %s/%s to mirror %s to %s" , c .jobNamespace , job .Name , fromImage , toImage )
469+ return job , nil
470+ })
471+ }
472+
473+ // ensureRemoveTagJob creates a job to copy the release tag to remove__rc_payload__{version} in quay.io
474+ func (c * Controller ) ensureRemoveTagJob (payload * v1alpha1.ReleasePayload , release * releasecontroller.Release ) (* batchv1.Job , error ) {
475+ if len (release .Config .AlternateImageRepository ) == 0 || len (release .Config .AlternateImageRepositorySecretName ) == 0 {
476+ return nil , fmt .Errorf ("alternate repository or secret not configured" )
477+ }
478+
479+ jobName := fmt .Sprintf ("%s-remove-tag" , payload .Name )
480+ return c .ensureJob (jobName , nil , func () (* batchv1.Job , error ) {
481+ // Get cli image from mirror or config
482+ cliImage := "registry.ci.openshift.org/ocp/4.21:cli"
483+ if mirror , err := c .releaseLister .ImageStreams (release .Target .Namespace ).Get (release .Target .Name ); err == nil {
484+ cliImage = fmt .Sprintf ("%s:cli" , mirror .Status .DockerImageRepository )
485+ }
486+ if len (release .Config .OverrideCLIImage ) > 0 {
487+ cliImage = release .Config .OverrideCLIImage
488+ }
489+
490+ job , prefix := newReleaseJobBase (jobName , cliImage , release .Config .AlternateImageRepositorySecretName )
491+
492+ // Mirror from the actual release tag (which exists in quay.io) to the removal request tag
493+ // The pruner only cares about the tag name, not the content
494+ removeTag := fmt .Sprintf ("remove__rc_payload__%s" , payload .Name )
495+ fromImage := fmt .Sprintf ("%s:%s" , release .Config .AlternateImageRepository , payload .Name )
496+ toImage := fmt .Sprintf ("%s:%s" , release .Config .AlternateImageRepository , removeTag )
497+
498+ job .Spec .Template .Spec .Containers [0 ].Command = []string {
499+ "/bin/bash" , "-c" ,
500+ prefix + `
501+ oc image mirror --keep-manifest-list=true $1 $2
502+ ` ,
503+ "" ,
504+ fromImage , toImage ,
505+ }
506+
507+ job .Annotations [releasecontroller .ReleaseAnnotationReleaseTag ] = payload .Name
508+ job .Annotations [releasecontroller .ReleaseAnnotationTarget ] = fmt .Sprintf ("%s/%s" , payload .Spec .PayloadCoordinates .Namespace , payload .Spec .PayloadCoordinates .ImagestreamName )
509+
510+ klog .V (2 ).Infof ("Creating remove tag job %s/%s to copy %s to %s" , c .jobNamespace , job .Name , fromImage , toImage )
511+ return job , nil
512+ })
513+ }
0 commit comments