@@ -10,7 +10,9 @@ import (
1010 g "github.com/onsi/ginkgo/v2"
1111 o "github.com/onsi/gomega"
1212
13+ ote "github.com/openshift-eng/openshift-tests-extension/pkg/ginkgo"
1314 configv1 "github.com/openshift/api/config/v1"
15+ operatorv1alpha1 "github.com/openshift/api/operator/v1alpha1"
1416 "github.com/openshift/origin/test/extended/imagepolicy"
1517 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1618 utilrand "k8s.io/apimachinery/pkg/util/rand"
@@ -309,4 +311,307 @@ var _ = g.Describe("[sig-node][Suite:openshift/disruptive-longrunning][Disruptiv
309311 o .Expect (registriesConf ).To (o .ContainSubstring ("location = \" registry.redhat.io/rhel8\" \n blocked = true" ),
310312 "registry.redhat.io/rhel8 should be blocked (NeverContactSource)" )
311313 })
314+
315+ //author: asahay@redhat.com
316+ g .It ("[OTP] ICSP and IDMS/ITMS can coexist in cluster [OCP-70203]" , ote .Informing (), func () {
317+ configClient := oc .AdminConfigClient ().ConfigV1 ()
318+ operatorClient := oc .AdminOperatorClient ().OperatorV1alpha1 ()
319+ suffix := utilrand .String (5 )
320+ icspName1 := fmt .Sprintf ("ubi8repo-%s" , suffix )
321+ idmsName := fmt .Sprintf ("digest-mirror-%s" , suffix )
322+ itmsName := fmt .Sprintf ("tag-mirror-%s" , suffix )
323+ icspName2 := fmt .Sprintf ("ubi9repo-%s" , suffix )
324+
325+ g .By ("Pre-flight: Verify both MCPs are in a stable Updated state before proceeding" )
326+ for _ , pool := range []string {"worker" , "master" } {
327+ updatedStatus , mcpErr := oc .AsAdmin ().WithoutNamespace ().Run ("get" ).
328+ Args ("mcp" , pool , "-o=jsonpath={.status.conditions[?(@.type=='Updated')].status}" ).Output ()
329+ o .Expect (mcpErr ).NotTo (o .HaveOccurred ())
330+ specName , specErr := oc .AsAdmin ().WithoutNamespace ().Run ("get" ).
331+ Args ("mcp" , pool , "-o=jsonpath={.spec.configuration.name}" ).Output ()
332+ o .Expect (specErr ).NotTo (o .HaveOccurred ())
333+ statusName , statusErr := oc .AsAdmin ().WithoutNamespace ().Run ("get" ).
334+ Args ("mcp" , pool , "-o=jsonpath={.status.configuration.name}" ).Output ()
335+ o .Expect (statusErr ).NotTo (o .HaveOccurred ())
336+ o .Expect (updatedStatus ).To (o .Equal ("True" ),
337+ "MCP %s must be in Updated=True state before running this test (spec=%s, status=%s)" , pool , specName , statusName )
338+ o .Expect (specName ).To (o .Equal (statusName ),
339+ "MCP %s spec and status configuration names must match before running this test" , pool )
340+ }
341+
342+ g .By ("Step 1: Create ICSP with digest mirrors for ubi8/ubi-minimal and openshift5" )
343+ icsp1 := & operatorv1alpha1.ImageContentSourcePolicy {
344+ ObjectMeta : metav1.ObjectMeta {
345+ Name : icspName1 ,
346+ Labels : map [string ]string {"e2e-test" : "ocp-70203" },
347+ },
348+ Spec : operatorv1alpha1.ImageContentSourcePolicySpec {
349+ RepositoryDigestMirrors : []operatorv1alpha1.RepositoryDigestMirrors {
350+ {
351+ Source : "registry.access.redhat.com/ubi8/ubi-minimal" ,
352+ Mirrors : []string {
353+ "example.io/example/ubi-minimal" ,
354+ "example.com/example/ubi-minimal" ,
355+ },
356+ },
357+ {
358+ Source : "registry.redhat.io/openshift5" ,
359+ Mirrors : []string {
360+ "mirror.example.com/redhat" ,
361+ },
362+ },
363+ },
364+ },
365+ }
366+
367+ initialWorkerSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
368+ initialMasterSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
369+
370+ _ , err := operatorClient .ImageContentSourcePolicies ().Create (ctx , icsp1 , metav1.CreateOptions {})
371+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to create ICSP %s" , icspName1 )
372+ e2e .Logf ("ICSP %s created successfully" , icspName1 )
373+
374+ g .DeferCleanup (func () {
375+ g .By ("Cleanup: Delete any remaining test resources and wait for MCP to settle" )
376+ cleanupWorkerSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
377+ cleanupMasterSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
378+ toDelete := false
379+
380+ for _ , name := range []string {icspName1 , icspName2 } {
381+ if _ , getErr := operatorClient .ImageContentSourcePolicies ().Get (ctx , name , metav1.GetOptions {}); getErr == nil {
382+ if delErr := operatorClient .ImageContentSourcePolicies ().Delete (ctx , name , metav1.DeleteOptions {}); delErr == nil {
383+ e2e .Logf ("Cleanup: deleted ICSP %s" , name )
384+ toDelete = true
385+ } else {
386+ e2e .Logf ("Cleanup: warning - failed to delete ICSP %s: %v" , name , delErr )
387+ }
388+ }
389+ }
390+ if _ , getErr := configClient .ImageTagMirrorSets ().Get (ctx , itmsName , metav1.GetOptions {}); getErr == nil {
391+ if delErr := configClient .ImageTagMirrorSets ().Delete (ctx , itmsName , metav1.DeleteOptions {}); delErr == nil {
392+ e2e .Logf ("Cleanup: deleted ITMS %s" , itmsName )
393+ toDelete = true
394+ } else {
395+ e2e .Logf ("Cleanup: warning - failed to delete ITMS %s: %v" , itmsName , delErr )
396+ }
397+ }
398+ if _ , getErr := configClient .ImageDigestMirrorSets ().Get (ctx , idmsName , metav1.GetOptions {}); getErr == nil {
399+ if delErr := configClient .ImageDigestMirrorSets ().Delete (ctx , idmsName , metav1.DeleteOptions {}); delErr == nil {
400+ e2e .Logf ("Cleanup: deleted IDMS %s" , idmsName )
401+ toDelete = true
402+ } else {
403+ e2e .Logf ("Cleanup: warning - failed to delete IDMS %s: %v" , idmsName , delErr )
404+ }
405+ }
406+
407+ if toDelete {
408+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "worker" , cleanupWorkerSpec )
409+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "master" , cleanupMasterSpec )
410+ }
411+ })
412+
413+ g .By ("Step 2: Wait for MCP rollout after ICSP creation and verify registries.conf" )
414+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "worker" , initialWorkerSpec )
415+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "master" , initialMasterSpec )
416+ e2e .Logf ("MCP rollout complete after ICSP creation" )
417+
418+ workerNode := nodeutils .GetFirstReadyWorkerNode (oc )
419+ o .Expect (workerNode ).NotTo (o .BeEmpty (), "no ready worker node found" )
420+
421+ registriesConf , err := nodeutils .ExecOnNodeWithChroot (oc , workerNode , "cat" , "/etc/containers/registries.conf" )
422+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to read registries.conf from node %s" , workerNode )
423+ e2e .Logf ("registries.conf after ICSP creation: read %d bytes, asserting expected entries" , len (registriesConf ))
424+
425+ o .Expect (registriesConf ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi8/ubi-minimal"` ),
426+ "registries.conf should contain ICSP source for ubi8/ubi-minimal" )
427+ o .Expect (registriesConf ).To (o .ContainSubstring (`location = "example.io/example/ubi-minimal"` ),
428+ "registries.conf should contain ICSP mirror example.io/example/ubi-minimal" )
429+ o .Expect (registriesConf ).To (o .ContainSubstring (`location = "example.com/example/ubi-minimal"` ),
430+ "registries.conf should contain ICSP mirror example.com/example/ubi-minimal" )
431+ o .Expect (registriesConf ).To (o .ContainSubstring (`location = "registry.redhat.io/openshift5"` ),
432+ "registries.conf should contain ICSP source for openshift5" )
433+ o .Expect (registriesConf ).To (o .ContainSubstring (`location = "mirror.example.com/redhat"` ),
434+ "registries.conf should contain ICSP mirror mirror.example.com/redhat" )
435+ o .Expect (registriesConf ).To (o .ContainSubstring (`pull-from-mirror = "digest-only"` ),
436+ "registries.conf should have pull-from-mirror = digest-only for ICSP entries" )
437+
438+ g .By ("Step 3: Create IDMS with same registry/mirror config as ICSP (AllowContactingSource) - should succeed without triggering new MC" )
439+ specBeforeIDMS := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
440+ masterSpecBeforeIDMS := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
441+ idms := & configv1.ImageDigestMirrorSet {
442+ ObjectMeta : metav1.ObjectMeta {
443+ Name : idmsName ,
444+ Labels : map [string ]string {"e2e-test" : "ocp-70203" },
445+ },
446+ Spec : configv1.ImageDigestMirrorSetSpec {
447+ ImageDigestMirrors : []configv1.ImageDigestMirrors {
448+ {
449+ Source : "registry.access.redhat.com/ubi8/ubi-minimal" ,
450+ Mirrors : []configv1.ImageMirror {
451+ "example.io/example/ubi-minimal" ,
452+ "example.com/example/ubi-minimal" ,
453+ },
454+ MirrorSourcePolicy : configv1 .AllowContactingSource ,
455+ },
456+ {
457+ Source : "registry.redhat.io/openshift5" ,
458+ Mirrors : []configv1.ImageMirror {
459+ "mirror.example.com/redhat" ,
460+ },
461+ MirrorSourcePolicy : configv1 .AllowContactingSource ,
462+ },
463+ },
464+ },
465+ }
466+ _ , err = configClient .ImageDigestMirrorSets ().Create (ctx , idms , metav1.CreateOptions {})
467+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to create IDMS %s" , idmsName )
468+ e2e .Logf ("IDMS %s created successfully" , idmsName )
469+
470+ g .By ("Step 3 (verify): Confirm no new MC was generated after IDMS creation with same config as ICSP" )
471+ noRolloutDeadline := time .Now ().Add (2 * time .Minute )
472+ for time .Now ().Before (noRolloutDeadline ) {
473+ o .Expect (imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )).To (o .Equal (specBeforeIDMS ),
474+ "worker MCP spec changed unexpectedly after IDMS creation with same config as ICSP" )
475+ o .Expect (imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )).To (o .Equal (masterSpecBeforeIDMS ),
476+ "master MCP spec changed unexpectedly after IDMS creation with same config as ICSP" )
477+ time .Sleep (15 * time .Second )
478+ }
479+ e2e .Logf ("Confirmed: no new MC generated after IDMS creation (worker and master polled for 2 minutes)" )
480+
481+ g .By ("Step 4.1: Delete ICSP - should succeed without triggering a new MC (IDMS covers the same config)" )
482+ specBeforeICSPDelete := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
483+ masterSpecBeforeICSPDelete := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
484+ err = operatorClient .ImageContentSourcePolicies ().Delete (ctx , icspName1 , metav1.DeleteOptions {})
485+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to delete ICSP %s" , icspName1 )
486+ e2e .Logf ("ICSP %s deleted successfully" , icspName1 )
487+
488+ g .By ("Step 4.2: Confirm no new MC was generated after ICSP deletion (IDMS still covers the same config)" )
489+ noRolloutDeadline = time .Now ().Add (2 * time .Minute )
490+ for time .Now ().Before (noRolloutDeadline ) {
491+ o .Expect (imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )).To (o .Equal (specBeforeICSPDelete ),
492+ "worker MCP spec changed unexpectedly after ICSP deletion" )
493+ o .Expect (imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )).To (o .Equal (masterSpecBeforeICSPDelete ),
494+ "master MCP spec changed unexpectedly after ICSP deletion" )
495+ time .Sleep (15 * time .Second )
496+ }
497+ e2e .Logf ("Confirmed: no new MC generated after ICSP deletion (worker and master polled for 2 minutes)" )
498+
499+ g .By ("Step 5: Verify registries.conf is unchanged after ICSP deletion (IDMS maintains same mirror config)" )
500+ registriesConfAfterICSPDelete , err := nodeutils .ExecOnNodeWithChroot (oc , workerNode , "cat" , "/etc/containers/registries.conf" )
501+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to read registries.conf from node %s" , workerNode )
502+ o .Expect (registriesConfAfterICSPDelete ).To (o .Equal (registriesConf ),
503+ "registries.conf should be unchanged after ICSP deletion when IDMS covers the same mirror config" )
504+ e2e .Logf ("Confirmed: registries.conf unchanged after ICSP deletion" )
505+
506+ g .By ("Step 6: Create ITMS with different registry/mirror config from IDMS - should trigger MCP rollout" )
507+ itmsWorkerSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
508+ itmsMasterSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
509+ itms := & configv1.ImageTagMirrorSet {
510+ ObjectMeta : metav1.ObjectMeta {
511+ Name : itmsName ,
512+ Labels : map [string ]string {"e2e-test" : "ocp-70203" },
513+ },
514+ Spec : configv1.ImageTagMirrorSetSpec {
515+ ImageTagMirrors : []configv1.ImageTagMirrors {
516+ {
517+ Source : "registry.access.redhat.com/ubi9/ubi-minimal" ,
518+ Mirrors : []configv1.ImageMirror {
519+ "example.io/example/ubi-minimal-1" ,
520+ "example.com/example/ubi-minimal-1" ,
521+ },
522+ MirrorSourcePolicy : configv1 .AllowContactingSource ,
523+ },
524+ },
525+ },
526+ }
527+ _ , err = configClient .ImageTagMirrorSets ().Create (ctx , itms , metav1.CreateOptions {})
528+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to create ITMS %s" , itmsName )
529+ e2e .Logf ("ITMS %s created successfully" , itmsName )
530+
531+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "worker" , itmsWorkerSpec )
532+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "master" , itmsMasterSpec )
533+ e2e .Logf ("MCP rollout complete after ITMS creation" )
534+
535+ g .By ("Step 7: Verify registries.conf updated with new ITMS tag-only mirror entries" )
536+ registriesConfAfterITMS , err := nodeutils .ExecOnNodeWithChroot (oc , workerNode , "cat" , "/etc/containers/registries.conf" )
537+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to read registries.conf from node %s" , workerNode )
538+ e2e .Logf ("registries.conf after ITMS creation: read %d bytes, asserting expected entries" , len (registriesConfAfterITMS ))
539+
540+ o .Expect (registriesConfAfterITMS ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi9/ubi-minimal"` ),
541+ "registries.conf should contain the ITMS source" )
542+ o .Expect (registriesConfAfterITMS ).To (o .ContainSubstring (`location = "example.io/example/ubi-minimal-1"` ),
543+ "registries.conf should contain ITMS mirror example.io/example/ubi-minimal-1" )
544+ o .Expect (registriesConfAfterITMS ).To (o .ContainSubstring (`location = "example.com/example/ubi-minimal-1"` ),
545+ "registries.conf should contain ITMS mirror example.com/example/ubi-minimal-1" )
546+ o .Expect (registriesConfAfterITMS ).To (o .ContainSubstring (`pull-from-mirror = "tag-only"` ),
547+ "registries.conf should have pull-from-mirror = tag-only for ITMS entries" )
548+ o .Expect (registriesConfAfterITMS ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi8/ubi-minimal"` ),
549+ "registries.conf should still contain IDMS entries for ubi8/ubi-minimal" )
550+
551+ g .By ("Step 8: Create second ICSP with different registry/mirror config from both ITMS and IDMS - should trigger MCP rollout" )
552+ icsp2WorkerSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
553+ icsp2MasterSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
554+ icsp2 := & operatorv1alpha1.ImageContentSourcePolicy {
555+ ObjectMeta : metav1.ObjectMeta {
556+ Name : icspName2 ,
557+ Labels : map [string ]string {"e2e-test" : "ocp-70203" },
558+ },
559+ Spec : operatorv1alpha1.ImageContentSourcePolicySpec {
560+ RepositoryDigestMirrors : []operatorv1alpha1.RepositoryDigestMirrors {
561+ {
562+ Source : "registry.example.com/example/myimage" ,
563+ Mirrors : []string {
564+ "mirror.example.net/image" ,
565+ },
566+ },
567+ },
568+ },
569+ }
570+ _ , err = operatorClient .ImageContentSourcePolicies ().Create (ctx , icsp2 , metav1.CreateOptions {})
571+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to create ICSP %s" , icspName2 )
572+ e2e .Logf ("ICSP %s created successfully" , icspName2 )
573+
574+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "worker" , icsp2WorkerSpec )
575+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "master" , icsp2MasterSpec )
576+ e2e .Logf ("MCP rollout complete after second ICSP creation" )
577+
578+ g .By ("Step 9: Verify registries.conf updated with new ICSP2 digest-only entries alongside existing IDMS and ITMS entries" )
579+ registriesConfAfterICSP2 , err := nodeutils .ExecOnNodeWithChroot (oc , workerNode , "cat" , "/etc/containers/registries.conf" )
580+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to read registries.conf from node %s" , workerNode )
581+ e2e .Logf ("registries.conf after second ICSP creation: read %d bytes, asserting expected entries" , len (registriesConfAfterICSP2 ))
582+
583+ o .Expect (registriesConfAfterICSP2 ).To (o .ContainSubstring (`location = "registry.example.com/example/myimage"` ),
584+ "registries.conf should contain the ICSP2 source" )
585+ o .Expect (registriesConfAfterICSP2 ).To (o .ContainSubstring (`location = "mirror.example.net/image"` ),
586+ "registries.conf should contain the ICSP2 mirror" )
587+ o .Expect (registriesConfAfterICSP2 ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi8/ubi-minimal"` ),
588+ "registries.conf should still contain IDMS entries for ubi8/ubi-minimal" )
589+ o .Expect (registriesConfAfterICSP2 ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi9/ubi-minimal"` ),
590+ "registries.conf should still contain ITMS entries for ubi9/ubi-minimal" )
591+
592+ g .By ("Step 10: Delete IDMS and wait for MCP rollout" )
593+ idmsDeleteWorkerSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "worker" )
594+ idmsDeleteMasterSpec := imagepolicy .GetMCPCurrentSpecConfigName (oc , "master" )
595+ err = configClient .ImageDigestMirrorSets ().Delete (ctx , idmsName , metav1.DeleteOptions {})
596+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to delete IDMS %s" , idmsName )
597+ e2e .Logf ("IDMS %s deleted successfully" , idmsName )
598+
599+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "worker" , idmsDeleteWorkerSpec )
600+ imagepolicy .WaitForMCPConfigSpecChangeAndUpdated (oc , "master" , idmsDeleteMasterSpec )
601+ e2e .Logf ("MCP rollout complete after IDMS deletion" )
602+
603+ g .By ("Step 11: Verify registries.conf updated after IDMS deletion - IDMS entries removed, ITMS and ICSP2 entries remain" )
604+ registriesConfAfterIDMSDelete , err := nodeutils .ExecOnNodeWithChroot (oc , workerNode , "cat" , "/etc/containers/registries.conf" )
605+ o .Expect (err ).NotTo (o .HaveOccurred (), "failed to read registries.conf from node %s" , workerNode )
606+ e2e .Logf ("registries.conf after IDMS deletion: read %d bytes, asserting expected entries" , len (registriesConfAfterIDMSDelete ))
607+
608+ o .Expect (registriesConfAfterIDMSDelete ).NotTo (o .ContainSubstring (`location = "registry.access.redhat.com/ubi8/ubi-minimal"` ),
609+ "registries.conf should not contain IDMS source registry.access.redhat.com/ubi8/ubi-minimal after IDMS deletion" )
610+ o .Expect (registriesConfAfterIDMSDelete ).NotTo (o .ContainSubstring (`location = "registry.redhat.io/openshift5"` ),
611+ "registries.conf should not contain IDMS source registry.redhat.io/openshift5 after IDMS deletion" )
612+ o .Expect (registriesConfAfterIDMSDelete ).To (o .ContainSubstring (`location = "registry.access.redhat.com/ubi9/ubi-minimal"` ),
613+ "registries.conf should still contain ITMS entries for ubi9/ubi-minimal" )
614+ o .Expect (registriesConfAfterIDMSDelete ).To (o .ContainSubstring (`location = "registry.example.com/example/myimage"` ),
615+ "registries.conf should still contain ICSP2 entries for registry.example.com/example/myimage" )
616+ })
312617})
0 commit comments