Skip to content

Commit 114883f

Browse files
committed
unpack retry e2e tests
Signed-off-by: Ankita Thomas <[email protected]>
1 parent a9c554a commit 114883f

File tree

5 files changed

+301
-4
lines changed

5 files changed

+301
-4
lines changed

Diff for: pkg/controller/bundle/bundle_unpacker.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"k8s.io/apimachinery/pkg/api/resource"
1919
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020
k8slabels "k8s.io/apimachinery/pkg/labels"
21+
"k8s.io/apiserver/pkg/storage/names"
2122
"k8s.io/client-go/kubernetes"
2223
listersbatchv1 "k8s.io/client-go/listers/batch/v1"
2324
listerscorev1 "k8s.io/client-go/listers/core/v1"
@@ -687,10 +688,10 @@ func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath
687688

688689
if failed {
689690
if time.Now().After(lastFailureTime.Add(unpackRetryInterval)) {
690-
fresh.SetName(fmt.Sprintf("%s-%d", fresh.GetName(), len(jobs)))
691+
fresh.SetName(names.SimpleNameGenerator.GenerateName(fresh.GetName()))
691692
job, err = c.client.BatchV1().Jobs(fresh.GetNamespace()).Create(context.TODO(), fresh, metav1.CreateOptions{})
692-
return
693693
}
694+
return
694695
}
695696
}
696697
}
@@ -880,7 +881,7 @@ func OperatorGroupBundleUnpackRetryInterval(ogLister v1listers.OperatorGroupName
880881

881882
d, err := time.ParseDuration(timeoutStr)
882883
if err != nil {
883-
return 0, fmt.Errorf("failed to parse unpack timeout annotation(%s: %s): %w", BundleUnpackRetryMinimumIntervalAnnotationKey, timeoutStr, err)
884+
return 0, fmt.Errorf("failed to parse unpack retry annotation(%s: %s): %w", BundleUnpackRetryMinimumIntervalAnnotationKey, timeoutStr, err)
884885
}
885886

886887
return d, nil

Diff for: pkg/controller/bundle/bundle_unpacker_test.go

+112
Original file line numberDiff line numberDiff line change
@@ -1820,3 +1820,115 @@ func TestOperatorGroupBundleUnpackTimeout(t *testing.T) {
18201820
})
18211821
}
18221822
}
1823+
1824+
func TestOperatorGroupBundleUnpackRetryInterval(t *testing.T) {
1825+
nsName := "fake-ns"
1826+
1827+
for _, tc := range []struct {
1828+
name string
1829+
operatorGroups []*operatorsv1.OperatorGroup
1830+
expectedTimeout time.Duration
1831+
expectedError error
1832+
}{
1833+
{
1834+
name: "No operator groups exist",
1835+
expectedTimeout: 0,
1836+
expectedError: errors.New("found 0 operatorGroups, expected 1"),
1837+
},
1838+
{
1839+
name: "Multiple operator groups exist",
1840+
operatorGroups: []*operatorsv1.OperatorGroup{
1841+
{
1842+
TypeMeta: metav1.TypeMeta{
1843+
Kind: operatorsv1.OperatorGroupKind,
1844+
APIVersion: operatorsv1.GroupVersion.String(),
1845+
},
1846+
ObjectMeta: metav1.ObjectMeta{
1847+
Name: "og1",
1848+
Namespace: nsName,
1849+
},
1850+
},
1851+
{
1852+
TypeMeta: metav1.TypeMeta{
1853+
Kind: operatorsv1.OperatorGroupKind,
1854+
APIVersion: operatorsv1.GroupVersion.String(),
1855+
},
1856+
ObjectMeta: metav1.ObjectMeta{
1857+
Name: "og2",
1858+
Namespace: nsName,
1859+
},
1860+
},
1861+
},
1862+
expectedTimeout: 0,
1863+
expectedError: errors.New("found 2 operatorGroups, expected 1"),
1864+
},
1865+
{
1866+
name: "One operator group exists with valid unpack retry annotation",
1867+
operatorGroups: []*operatorsv1.OperatorGroup{
1868+
{
1869+
TypeMeta: metav1.TypeMeta{
1870+
Kind: operatorsv1.OperatorGroupKind,
1871+
APIVersion: operatorsv1.GroupVersion.String(),
1872+
},
1873+
ObjectMeta: metav1.ObjectMeta{
1874+
Name: "og",
1875+
Namespace: nsName,
1876+
Annotations: map[string]string{BundleUnpackRetryMinimumIntervalAnnotationKey: "1m"},
1877+
},
1878+
},
1879+
},
1880+
expectedTimeout: 1 * time.Minute,
1881+
expectedError: nil,
1882+
},
1883+
{
1884+
name: "One operator group exists with no unpack retry annotation",
1885+
operatorGroups: []*operatorsv1.OperatorGroup{
1886+
{
1887+
TypeMeta: metav1.TypeMeta{
1888+
Kind: operatorsv1.OperatorGroupKind,
1889+
APIVersion: operatorsv1.GroupVersion.String(),
1890+
},
1891+
ObjectMeta: metav1.ObjectMeta{
1892+
Name: "og",
1893+
Namespace: nsName,
1894+
},
1895+
},
1896+
},
1897+
expectedTimeout: 0,
1898+
expectedError: nil,
1899+
},
1900+
{
1901+
name: "One operator group exists with invalid unpack retry annotation",
1902+
operatorGroups: []*operatorsv1.OperatorGroup{
1903+
{
1904+
TypeMeta: metav1.TypeMeta{
1905+
Kind: operatorsv1.OperatorGroupKind,
1906+
APIVersion: operatorsv1.GroupVersion.String(),
1907+
},
1908+
ObjectMeta: metav1.ObjectMeta{
1909+
Name: "og",
1910+
Namespace: nsName,
1911+
Annotations: map[string]string{BundleUnpackRetryMinimumIntervalAnnotationKey: "invalid"},
1912+
},
1913+
},
1914+
},
1915+
expectedTimeout: 0,
1916+
expectedError: fmt.Errorf("failed to parse unpack retry annotation(operatorframework.io/bundle-unpack-min-retry-interval: invalid): %w", errors.New("time: invalid duration \"invalid\"")),
1917+
},
1918+
} {
1919+
t.Run(tc.name, func(t *testing.T) {
1920+
ogIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
1921+
ogLister := v1listers.NewOperatorGroupLister(ogIndexer).OperatorGroups(nsName)
1922+
1923+
for _, og := range tc.operatorGroups {
1924+
err := ogIndexer.Add(og)
1925+
assert.NoError(t, err)
1926+
}
1927+
1928+
timeout, err := OperatorGroupBundleUnpackRetryInterval(ogLister)
1929+
1930+
assert.Equal(t, tc.expectedTimeout, timeout)
1931+
assert.Equal(t, tc.expectedError, err)
1932+
})
1933+
}
1934+
}

Diff for: test/e2e/fbc_provider.go

+6
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,9 @@ func NewFileBasedFiledBasedCatalogProvider(path string) (FileBasedCatalogProvide
3131
func (f *fileBasedFileBasedCatalogProvider) GetCatalog() string {
3232
return f.fbc
3333
}
34+
35+
func NewRawFileBasedCatalogProvider(data string) (FileBasedCatalogProvider, error) {
36+
return &fileBasedFileBasedCatalogProvider{
37+
fbc: string(data),
38+
}, nil
39+
}

Diff for: test/e2e/subscription_e2e_test.go

+166
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,172 @@ var _ = Describe("Subscription", func() {
25202520
_, err = fetchSubscription(crc, generatedNamespace.GetName(), subName, subscriptionHasCurrentCSV("example-operator.v0.2.0"))
25212521
Expect(err).Should(BeNil())
25222522
})
2523+
2524+
It("should recreate failing unpacking jobs when unpack retry is specified", func() {
2525+
By("Ensuring a registry to host bundle images")
2526+
local, err := Local(c)
2527+
Expect(err).NotTo(HaveOccurred(), "cannot determine if test running locally or on CI: %s", err)
2528+
2529+
var registryURL string
2530+
var copyImage func(src, srcTag, dst, dstTag string) error
2531+
if local {
2532+
registryURL, err = createDockerRegistry(c, generatedNamespace.GetName())
2533+
Expect(err).NotTo(HaveOccurred(), "error creating container registry: %s", err)
2534+
defer deleteDockerRegistry(c, generatedNamespace.GetName())
2535+
2536+
// ensure registry pod is ready before attempting port-forwarding
2537+
_ = awaitPod(GinkgoT(), c, generatedNamespace.GetName(), registryName, podReady)
2538+
2539+
err = registryPortForward(generatedNamespace.GetName())
2540+
Expect(err).NotTo(HaveOccurred(), "port-forwarding local registry: %s", err)
2541+
copyImage = func(src, srcTag, dst, dstTag string) error {
2542+
_, err := skopeoLocalCopy(src, srcTag, dst, dstTag)
2543+
return err
2544+
}
2545+
} else {
2546+
registryURL = fmt.Sprintf("%s/%s", openshiftregistryFQDN, generatedNamespace.GetName())
2547+
registryAuth, err := openshiftRegistryAuth(c, generatedNamespace.GetName())
2548+
Expect(err).NotTo(HaveOccurred(), "error getting openshift registry authentication: %s", err)
2549+
copyImage = func(src, srcTag, dst, dstTag string) error {
2550+
skopeoArgs := skopeoCopyCmd(src, srcTag, dst, dstTag, registryAuth)
2551+
err = createSkopeoPod(c, skopeoArgs, generatedNamespace.GetName())
2552+
if err != nil {
2553+
return fmt.Errorf("error creating skopeo pod: %v", err)
2554+
}
2555+
2556+
// wait for skopeo pod to exit successfully
2557+
awaitPod(GinkgoT(), c, generatedNamespace.GetName(), skopeo, func(pod *corev1.Pod) bool {
2558+
return pod.Status.Phase == corev1.PodSucceeded
2559+
})
2560+
2561+
if err := deleteSkopeoPod(c, generatedNamespace.GetName()); err != nil {
2562+
return fmt.Errorf("error deleting skopeo pod: %s", err)
2563+
}
2564+
return nil
2565+
}
2566+
}
2567+
2568+
// testImage is the name of the image used throughout the test - the image overwritten by skopeo
2569+
// the tag is generated randomly and appended to the end of the testImage
2570+
srcImage := "quay.io/olmtest/example-operator-bundle:0.1.0"
2571+
srcTag := "0.1.0"
2572+
bundleImage := fmt.Sprint("docker://", registryURL, "/unpack-retry-bundle", ":")
2573+
bundleTag := genName("x")
2574+
2575+
unpackRetryCatalog := fmt.Sprintf(`
2576+
---
2577+
schema: olm.package
2578+
name: unpack-retry-package
2579+
defaultChannel: stable
2580+
---
2581+
schema: olm.channel
2582+
package: unpack-retry-package
2583+
name: stable
2584+
entries:
2585+
- name: unpack-retry-operator.v1.0.0
2586+
---
2587+
schema: olm.bundle
2588+
name: unpack-retry-operator.v1.0.0
2589+
package: unpack-retry-package
2590+
image: %s%s
2591+
properties:
2592+
- type: olm.package
2593+
value:
2594+
packageName: unpack-retry-package
2595+
version: 1.0.0
2596+
`, bundleImage, bundleTag)
2597+
2598+
By("creating a catalog referencing a non-existent bundle image")
2599+
unpackRetryProvider, err := NewRawFileBasedCatalogProvider(unpackRetryCatalog)
2600+
Expect(err).ToNot(HaveOccurred())
2601+
Expect(magicCatalog.UpdateCatalog(context.Background(), unpackRetryProvider)).To(Succeed())
2602+
2603+
By("creating a subscription for the missing bundle")
2604+
unpackRetrySubName := fmt.Sprintf("%s-unpack-retry-package-sub", generatedNamespace.GetName())
2605+
createSubscriptionForCatalog(crc, generatedNamespace.GetName(), unpackRetrySubName, catalogSourceName, "unpack-retry-package", stableChannel, "", operatorsv1alpha1.ApprovalAutomatic)
2606+
2607+
By("waiting for bundle unpack to fail")
2608+
Eventually(
2609+
func() error {
2610+
fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
2611+
if err != nil {
2612+
return err
2613+
}
2614+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpackFailed); cond.Status != corev1.ConditionTrue || cond.Reason != "BundleUnpackFailed" {
2615+
return fmt.Errorf("%s condition not found", v1alpha1.SubscriptionBundleUnpackFailed)
2616+
}
2617+
return nil
2618+
},
2619+
5*time.Minute,
2620+
interval,
2621+
).Should(Succeed())
2622+
2623+
By("patching operator group to enable unpack retries")
2624+
ogNN := types.NamespacedName{Name: operatorGroup.GetName(), Namespace: generatedNamespace.GetName()}
2625+
setBundleUnpackRetryMinimumIntervalAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
2626+
2627+
By("ensuring failing bundle unpack jobs are retried")
2628+
Eventually(func() error {
2629+
fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
2630+
if err != nil {
2631+
return err
2632+
}
2633+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpacking); cond.Status != corev1.ConditionTrue {
2634+
return fmt.Errorf("missing expected condition %s on subscription %s", v1alpha1.SubscriptionBundleUnpacking, unpackRetrySubName)
2635+
}
2636+
return nil
2637+
}).Should(Succeed())
2638+
2639+
By("Ensuring successful bundle unpack jobs are not retried")
2640+
Consistently(func() error {
2641+
fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), subName, metav1.GetOptions{})
2642+
if err != nil {
2643+
return err
2644+
}
2645+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpacking); cond.Status != corev1.ConditionUnknown {
2646+
return fmt.Errorf("unexpected unpack condition %s on subscription %s", v1alpha1.SubscriptionBundleUnpacking, subName)
2647+
}
2648+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpackFailed); cond.Status != corev1.ConditionUnknown {
2649+
return fmt.Errorf("unexpected unpack condition %s on subscription %s", v1alpha1.SubscriptionBundleUnpackFailed, subName)
2650+
}
2651+
return nil
2652+
}).Should(Succeed())
2653+
2654+
By("patching operator group to disable unpack retries")
2655+
setBundleUnpackRetryMinimumIntervalAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "")
2656+
2657+
By("ensuring failing bundle unpack jobs are no longer retried")
2658+
Eventually(func() error {
2659+
fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
2660+
if err != nil {
2661+
return err
2662+
}
2663+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpackFailed); cond.Status != corev1.ConditionTrue {
2664+
return fmt.Errorf("missing expected condition %s on subscription %s", v1alpha1.SubscriptionBundleUnpacking, unpackRetrySubName)
2665+
}
2666+
return nil
2667+
}).Should(Succeed())
2668+
2669+
Consistently(func() error {
2670+
fetched, err := crc.OperatorsV1alpha1().Subscriptions(generatedNamespace.GetName()).Get(context.Background(), unpackRetrySubName, metav1.GetOptions{})
2671+
if err != nil {
2672+
return err
2673+
}
2674+
if cond := fetched.Status.GetCondition(v1alpha1.SubscriptionBundleUnpacking); cond.Status != corev1.ConditionUnknown {
2675+
return fmt.Errorf("mnexpected condition %s on subscription %s", v1alpha1.SubscriptionBundleUnpacking, unpackRetrySubName)
2676+
}
2677+
return nil
2678+
}).Should(Succeed())
2679+
2680+
By("pushing missing bundle image")
2681+
Expect(copyImage(srcImage, srcTag, bundleImage, bundleTag)).To(Succeed())
2682+
2683+
By("patching operator group to enable unpack retries")
2684+
setBundleUnpackRetryMinimumIntervalAnnotation(context.Background(), ctx.Ctx().Client(), ogNN, "1s")
2685+
2686+
By("waiting for checking for installPlan to indicate unpack succeeds")
2687+
Expect(fetchSubscription(crc, magicCatalog.GetNamespace(), unpackRetrySubName, subscriptionHasInstallPlanChecker)).To(Succeed())
2688+
})
25232689
})
25242690
})
25252691
})

Diff for: test/e2e/util.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,25 @@ func objectRefToNamespacedName(ip *corev1.ObjectReference) types.NamespacedName
9393
// adding the "operatorframework.io/bundle-unpack-timeout" annotation to an OperatorGroup
9494
// resource.
9595
func addBundleUnpackTimeoutOGAnnotation(ctx context.Context, c k8scontrollerclient.Client, ogNN types.NamespacedName, timeout string) {
96+
setOGAnnotation(ctx, c, ogNN, bundle.BundleUnpackTimeoutAnnotationKey, timeout)
97+
}
98+
99+
func setBundleUnpackRetryMinimumIntervalAnnotation(ctx context.Context, c k8scontrollerclient.Client, ogNN types.NamespacedName, interval string) {
100+
setOGAnnotation(ctx, c, ogNN, bundle.BundleUnpackRetryMinimumIntervalAnnotationKey, interval)
101+
}
102+
103+
func setOGAnnotation(ctx context.Context, c k8scontrollerclient.Client, ogNN types.NamespacedName, key, value string) {
96104
Eventually(func() error {
97105
og := &operatorsv1.OperatorGroup{}
98106
if err := c.Get(ctx, ogNN, og); err != nil {
99107
return err
100108
}
101109
annotations := og.GetAnnotations()
102-
annotations[bundle.BundleUnpackTimeoutAnnotationKey] = timeout
110+
if len(value) == 0 {
111+
delete(annotations, key)
112+
} else {
113+
annotations[key] = value
114+
}
103115
og.SetAnnotations(annotations)
104116

105117
return c.Update(ctx, og)

0 commit comments

Comments
 (0)