@@ -2524,6 +2524,185 @@ var _ = Describe("Subscription", func() {
2524
2524
})
2525
2525
})
2526
2526
})
2527
+ When ("bundle unpack retries are enabled" , func () {
2528
+ FIt ("should retry failing unpack jobs" , func () {
2529
+ By ("Ensuring a registry to host bundle images" )
2530
+ local , err := Local (c )
2531
+ Expect (err ).NotTo (HaveOccurred (), "cannot determine if test running locally or on CI: %s" , err )
2532
+
2533
+ var registryURL string
2534
+ var copyImage func (dst , dstTag , src , srcTag string ) error
2535
+ if local {
2536
+ registryURL , err = createDockerRegistry (c , generatedNamespace .GetName ())
2537
+ Expect (err ).NotTo (HaveOccurred (), "error creating container registry: %s" , err )
2538
+ defer deleteDockerRegistry (c , generatedNamespace .GetName ())
2539
+
2540
+ // ensure registry pod is ready before attempting port-forwarding
2541
+ _ = awaitPod (GinkgoT (), c , generatedNamespace .GetName (), registryName , podReady )
2542
+
2543
+ err = registryPortForward (generatedNamespace .GetName ())
2544
+ Expect (err ).NotTo (HaveOccurred (), "port-forwarding local registry: %s" , err )
2545
+ copyImage = func (dst , dstTag , src , srcTag string ) error {
2546
+ if ! strings .HasPrefix (src , "docker://" ) {
2547
+ src = fmt .Sprintf ("docker://%s" , src )
2548
+ }
2549
+ if ! strings .HasPrefix (dst , "docker://" ) {
2550
+ dst = fmt .Sprintf ("docker://%s" , dst )
2551
+ }
2552
+ _ , err := skopeoLocalCopy (dst , dstTag , src , srcTag )
2553
+ return err
2554
+ }
2555
+ } else {
2556
+ registryURL = fmt .Sprintf ("%s/%s" , openshiftregistryFQDN , generatedNamespace .GetName ())
2557
+ registryAuth , err := openshiftRegistryAuth (c , generatedNamespace .GetName ())
2558
+ Expect (err ).NotTo (HaveOccurred (), "error getting openshift registry authentication: %s" , err )
2559
+ copyImage = func (dst , dstTag , src , srcTag string ) error {
2560
+ if ! strings .HasPrefix (src , "docker://" ) {
2561
+ src = fmt .Sprintf ("docker://%s" , src )
2562
+ }
2563
+ if ! strings .HasPrefix (dst , "docker://" ) {
2564
+ dst = fmt .Sprintf ("docker://%s" , dst )
2565
+ }
2566
+ skopeoArgs := skopeoCopyCmd (dst , dstTag , src , srcTag , registryAuth )
2567
+ err = createSkopeoPod (c , skopeoArgs , generatedNamespace .GetName ())
2568
+ if err != nil {
2569
+ return fmt .Errorf ("error creating skopeo pod: %v" , err )
2570
+ }
2571
+
2572
+ // wait for skopeo pod to exit successfully
2573
+ awaitPod (GinkgoT (), c , generatedNamespace .GetName (), skopeo , func (pod * corev1.Pod ) bool {
2574
+ return pod .Status .Phase == corev1 .PodSucceeded
2575
+ })
2576
+
2577
+ if err := deleteSkopeoPod (c , generatedNamespace .GetName ()); err != nil {
2578
+ return fmt .Errorf ("error deleting skopeo pod: %s" , err )
2579
+ }
2580
+ return nil
2581
+ }
2582
+ }
2583
+
2584
+ // testImage is the name of the image used throughout the test - the image overwritten by skopeo
2585
+ // the tag is generated randomly and appended to the end of the testImage
2586
+ srcImage := "quay.io/olmtest/example-operator-bundle:"
2587
+ srcTag := "0.1.0"
2588
+ bundleImage := fmt .Sprint (registryURL , "/unpack-retry-bundle" , ":" )
2589
+ bundleTag := genName ("x" )
2590
+ //// hash hashes data with sha256 and returns the hex string.
2591
+ //func hash(data string) string {
2592
+ // // A SHA256 hash is 64 characters, which is within the 253 character limit for kube resource names
2593
+ // h := fmt.Sprintf("%x", sha256.Sum256([]byte(data)))
2594
+ //
2595
+ // // Make the hash 63 characters instead to comply with the 63 character limit for labels
2596
+ // return fmt.Sprintf(h[:len(h)-1])
2597
+ //}
2598
+ unpackRetryCatalog := fmt .Sprintf (`
2599
+ schema: olm.package
2600
+ name: unpack-retry-package
2601
+ defaultChannel: stable
2602
+ ---
2603
+ schema: olm.channel
2604
+ package: unpack-retry-package
2605
+ name: stable
2606
+ entries:
2607
+ - name: unpack-retry-operator.v2.0.0
2608
+ skipRange: ">=0.1.0 <2.0.0"
2609
+ ---
2610
+ schema: olm.bundle
2611
+ name: unpack-retry-operator.v1.0.0
2612
+ package: unpack-retry-package
2613
+ image: %s%s
2614
+ properties:
2615
+ - type: olm.package
2616
+ value:
2617
+ packageName: unpack-retry-package
2618
+ version: 1.0.0
2619
+ ` , bundleImage , bundleTag )
2620
+
2621
+ By ("creating a catalog referencing a non-existent bundle image" )
2622
+ unpackRetryProvider , err := NewRawFileBasedCatalogProvider (unpackRetryCatalog )
2623
+ Expect (err ).ToNot (HaveOccurred ())
2624
+ catalogSourceName := fmt .Sprintf ("%s-catsrc" , generatedNamespace .GetName ())
2625
+ magicCatalog := NewMagicCatalog (ctx .Ctx ().Client (), generatedNamespace .GetName (), catalogSourceName , unpackRetryProvider )
2626
+ Expect (magicCatalog .DeployCatalog (context .Background ())).To (BeNil ())
2627
+
2628
+ By ("patching the OperatorGroup to reduce the bundle unpacking timeout" )
2629
+ ogNN := types.NamespacedName {Name : operatorGroup .GetName (), Namespace : generatedNamespace .GetName ()}
2630
+ addBundleUnpackTimeoutOGAnnotation (context .Background (), ctx .Ctx ().Client (), ogNN , "1s" )
2631
+
2632
+ By ("creating a subscription for the missing bundle" )
2633
+ unpackRetrySubName := fmt .Sprintf ("%s-unpack-retry-package-sub" , generatedNamespace .GetName ())
2634
+ createSubscriptionForCatalog (crc , generatedNamespace .GetName (), unpackRetrySubName , catalogSourceName , "unpack-retry-package" , stableChannel , "" , operatorsv1alpha1 .ApprovalAutomatic )
2635
+
2636
+ By ("waiting for bundle unpack to fail" )
2637
+ Eventually (
2638
+ func () error {
2639
+ fetched , err := crc .OperatorsV1alpha1 ().Subscriptions (generatedNamespace .GetName ()).Get (context .Background (), unpackRetrySubName , metav1.GetOptions {})
2640
+ if err != nil {
2641
+ return err
2642
+ }
2643
+ if cond := fetched .Status .GetCondition (v1alpha1 .SubscriptionBundleUnpackFailed ); cond .Status != corev1 .ConditionTrue || cond .Reason != "BundleUnpackFailed" {
2644
+ return fmt .Errorf ("%s condition not found" , v1alpha1 .SubscriptionBundleUnpackFailed )
2645
+ }
2646
+ return nil
2647
+ },
2648
+ 5 * time .Minute ,
2649
+ interval ,
2650
+ ).Should (Succeed ())
2651
+
2652
+ By ("pushing missing bundle image" )
2653
+ Expect (copyImage (bundleImage , bundleTag , srcImage , srcTag )).To (Succeed ())
2654
+
2655
+ By ("patching the OperatorGroup to increase the bundle unpacking timeout" )
2656
+ addBundleUnpackTimeoutOGAnnotation (context .Background (), ctx .Ctx ().Client (), ogNN , "" ) // revert to default unpack timeout
2657
+
2658
+ By ("patching operator group to enable unpack retries" )
2659
+ setBundleUnpackRetryMinimumIntervalAnnotation (context .Background (), ctx .Ctx ().Client (), ogNN , "1s" )
2660
+
2661
+ By ("waiting for checking for installPlan to indicate unpack succeeds" )
2662
+ Expect (fetchSubscription (crc , magicCatalog .GetNamespace (), unpackRetrySubName , subscriptionHasInstallPlanChecker )).To (Succeed ())
2663
+ time .Sleep (20 * time .Minute )
2664
+ })
2665
+
2666
+ It ("should not retry successful unpack jobs" , func () {
2667
+ By ("deploying the testing catalog" )
2668
+ provider , err := NewFileBasedFiledBasedCatalogProvider (filepath .Join (testdataDir , failForwardTestDataBaseDir , "example-operator.v0.1.0.yaml" ))
2669
+ Expect (err ).To (BeNil ())
2670
+ catalogSourceName := fmt .Sprintf ("%s-catsrc" , generatedNamespace .GetName ())
2671
+ magicCatalog := NewMagicCatalog (ctx .Ctx ().Client (), generatedNamespace .GetName (), catalogSourceName , provider )
2672
+ Expect (magicCatalog .DeployCatalog (context .Background ())).To (BeNil ())
2673
+
2674
+ By ("creating the testing subscription" )
2675
+ subName := fmt .Sprintf ("%s-packagea-sub" , generatedNamespace .GetName ())
2676
+ createSubscriptionForCatalog (crc , generatedNamespace .GetName (), subName , catalogSourceName , "packageA" , stableChannel , "" , operatorsv1alpha1 .ApprovalAutomatic )
2677
+
2678
+ By ("waiting until the subscription has an IP reference" )
2679
+ subscription , err := fetchSubscription (crc , generatedNamespace .GetName (), subName , subscriptionHasInstallPlanChecker )
2680
+ Expect (err ).Should (BeNil ())
2681
+
2682
+ By ("waiting for the v0.1.0 CSV to report a succeeded phase" )
2683
+ _ , err = fetchCSV (crc , subscription .Status .CurrentCSV , generatedNamespace .GetName (), buildCSVConditionChecker (operatorsv1alpha1 .CSVPhaseSucceeded ))
2684
+ Expect (err ).ShouldNot (HaveOccurred ())
2685
+
2686
+ By ("patching operator group to enable unpack retries" )
2687
+ ogNN := types.NamespacedName {Name : operatorGroup .GetName (), Namespace : generatedNamespace .GetName ()}
2688
+ setBundleUnpackRetryMinimumIntervalAnnotation (context .Background (), ctx .Ctx ().Client (), ogNN , "1s" )
2689
+
2690
+ By ("Ensuring successful bundle unpack jobs are not retried" )
2691
+ Consistently (func () error {
2692
+ fetched , err := crc .OperatorsV1alpha1 ().Subscriptions (generatedNamespace .GetName ()).Get (context .Background (), subName , metav1.GetOptions {})
2693
+ if err != nil {
2694
+ return err
2695
+ }
2696
+ if cond := fetched .Status .GetCondition (v1alpha1 .SubscriptionBundleUnpacking ); cond .Status == corev1 .ConditionTrue {
2697
+ return fmt .Errorf ("unexpected condition status for %s on subscription %s" , v1alpha1 .SubscriptionBundleUnpacking , subName )
2698
+ }
2699
+ if cond := fetched .Status .GetCondition (v1alpha1 .SubscriptionBundleUnpackFailed ); cond .Status == corev1 .ConditionTrue {
2700
+ return fmt .Errorf ("unexpected condition status for %s on subscription %s" , v1alpha1 .SubscriptionBundleUnpackFailed , subName )
2701
+ }
2702
+ return nil
2703
+ }).Should (Succeed ())
2704
+ })
2705
+ })
2527
2706
})
2528
2707
2529
2708
const (
0 commit comments