Skip to content

Commit 47d6aa1

Browse files
authored
Moves bundle unpack timeout into OperatorGroup (#2952)
`operatorframework.io/bundle-unpack-timeout` is an internal annotation used for E2E testing. We need to move this out of InstallPlan in preparation to changes in the unpacking process: OLM will soon be creating unpack jobs before creating InstallPlan so we need to find a new place where we can set this annotation. Signed-off-by: Mikalai Radchuk <[email protected]>
1 parent c24d256 commit 47d6aa1

File tree

6 files changed

+187
-44
lines changed

6 files changed

+187
-44
lines changed

pkg/controller/bundle/bundle_unpacker.go

+28
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
"github.com/operator-framework/api/pkg/operators/reference"
2828
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
29+
v1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1"
2930
listersoperatorsv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
3031
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
3132
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/projection"
@@ -771,3 +772,30 @@ func getCondition(job *batchv1.Job, conditionType batchv1.JobConditionType) (con
771772
}
772773
return
773774
}
775+
776+
// OperatorGroupBundleUnpackTimeout returns bundle timeout from annotation if specified.
777+
// If the timeout annotation is not set, return timeout < 0 which is subsequently ignored.
778+
// This is to overrides the --bundle-unpack-timeout flag value on per-OperatorGroup basis.
779+
func OperatorGroupBundleUnpackTimeout(ogLister v1listers.OperatorGroupNamespaceLister) (time.Duration, error) {
780+
ignoreTimeout := -1 * time.Minute
781+
782+
ogs, err := ogLister.List(k8slabels.Everything())
783+
if err != nil {
784+
return ignoreTimeout, err
785+
}
786+
if len(ogs) != 1 {
787+
return ignoreTimeout, fmt.Errorf("found %d operatorGroups, expected 1", len(ogs))
788+
}
789+
790+
timeoutStr, ok := ogs[0].GetAnnotations()[BundleUnpackTimeoutAnnotationKey]
791+
if !ok {
792+
return ignoreTimeout, nil
793+
}
794+
795+
d, err := time.ParseDuration(timeoutStr)
796+
if err != nil {
797+
return ignoreTimeout, fmt.Errorf("failed to parse unpack timeout annotation(%s: %s): %w", BundleUnpackTimeoutAnnotationKey, timeoutStr, err)
798+
}
799+
800+
return d, nil
801+
}

pkg/controller/bundle/bundle_unpacker_test.go

+116
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package bundle
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"testing"
78
"time"
89

10+
"github.com/stretchr/testify/assert"
911
"github.com/stretchr/testify/require"
1012
batchv1 "k8s.io/api/batch/v1"
1113
corev1 "k8s.io/api/core/v1"
@@ -15,11 +17,14 @@ import (
1517
"k8s.io/apimachinery/pkg/runtime"
1618
"k8s.io/client-go/informers"
1719
k8sfake "k8s.io/client-go/kubernetes/fake"
20+
"k8s.io/client-go/tools/cache"
1821
"k8s.io/utils/pointer"
1922

23+
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
2024
operatorsv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
2125
crfake "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake"
2226
crinformers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions"
27+
v1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1"
2328
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
2429
"github.com/operator-framework/operator-registry/pkg/api"
2530
"github.com/operator-framework/operator-registry/pkg/configmap"
@@ -1572,3 +1577,114 @@ func TestConfigMapUnpacker(t *testing.T) {
15721577
})
15731578
}
15741579
}
1580+
1581+
func TestOperatorGroupBundleUnpackTimeout(t *testing.T) {
1582+
nsName := "fake-ns"
1583+
1584+
for _, tc := range []struct {
1585+
name string
1586+
operatorGroups []*operatorsv1.OperatorGroup
1587+
expectedTimeout time.Duration
1588+
expectedError error
1589+
}{
1590+
{
1591+
name: "No operator groups exist",
1592+
expectedTimeout: -1 * time.Minute,
1593+
expectedError: errors.New("found 0 operatorGroups, expected 1"),
1594+
},
1595+
{
1596+
name: "Multiple operator groups exist",
1597+
operatorGroups: []*operatorsv1.OperatorGroup{
1598+
{
1599+
TypeMeta: metav1.TypeMeta{
1600+
Kind: operatorsv1.OperatorGroupKind,
1601+
APIVersion: operatorsv1.GroupVersion.String(),
1602+
},
1603+
ObjectMeta: metav1.ObjectMeta{
1604+
Name: "og1",
1605+
Namespace: nsName,
1606+
},
1607+
},
1608+
{
1609+
TypeMeta: metav1.TypeMeta{
1610+
Kind: operatorsv1.OperatorGroupKind,
1611+
APIVersion: operatorsv1.GroupVersion.String(),
1612+
},
1613+
ObjectMeta: metav1.ObjectMeta{
1614+
Name: "og2",
1615+
Namespace: nsName,
1616+
},
1617+
},
1618+
},
1619+
expectedTimeout: -1 * time.Minute,
1620+
expectedError: errors.New("found 2 operatorGroups, expected 1"),
1621+
},
1622+
{
1623+
name: "One operator group exists with valid timeout annotation",
1624+
operatorGroups: []*operatorsv1.OperatorGroup{
1625+
{
1626+
TypeMeta: metav1.TypeMeta{
1627+
Kind: operatorsv1.OperatorGroupKind,
1628+
APIVersion: operatorsv1.GroupVersion.String(),
1629+
},
1630+
ObjectMeta: metav1.ObjectMeta{
1631+
Name: "og",
1632+
Namespace: nsName,
1633+
Annotations: map[string]string{BundleUnpackTimeoutAnnotationKey: "1m"},
1634+
},
1635+
},
1636+
},
1637+
expectedTimeout: 1 * time.Minute,
1638+
expectedError: nil,
1639+
},
1640+
{
1641+
name: "One operator group exists with no timeout annotation",
1642+
operatorGroups: []*operatorsv1.OperatorGroup{
1643+
{
1644+
TypeMeta: metav1.TypeMeta{
1645+
Kind: operatorsv1.OperatorGroupKind,
1646+
APIVersion: operatorsv1.GroupVersion.String(),
1647+
},
1648+
ObjectMeta: metav1.ObjectMeta{
1649+
Name: "og",
1650+
Namespace: nsName,
1651+
},
1652+
},
1653+
},
1654+
expectedTimeout: -1 * time.Minute,
1655+
},
1656+
{
1657+
name: "One operator group exists with invalid timeout annotation",
1658+
operatorGroups: []*operatorsv1.OperatorGroup{
1659+
{
1660+
TypeMeta: metav1.TypeMeta{
1661+
Kind: operatorsv1.OperatorGroupKind,
1662+
APIVersion: operatorsv1.GroupVersion.String(),
1663+
},
1664+
ObjectMeta: metav1.ObjectMeta{
1665+
Name: "og",
1666+
Namespace: nsName,
1667+
Annotations: map[string]string{BundleUnpackTimeoutAnnotationKey: "invalid"},
1668+
},
1669+
},
1670+
},
1671+
expectedTimeout: -1 * time.Minute,
1672+
expectedError: fmt.Errorf("failed to parse unpack timeout annotation(operatorframework.io/bundle-unpack-timeout: invalid): %w", errors.New("time: invalid duration \"invalid\"")),
1673+
},
1674+
} {
1675+
t.Run(tc.name, func(t *testing.T) {
1676+
ogIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
1677+
ogLister := v1listers.NewOperatorGroupLister(ogIndexer).OperatorGroups(nsName)
1678+
1679+
for _, og := range tc.operatorGroups {
1680+
err := ogIndexer.Add(og)
1681+
assert.NoError(t, err)
1682+
}
1683+
1684+
timeout, err := OperatorGroupBundleUnpackTimeout(ogLister)
1685+
1686+
assert.Equal(t, tc.expectedTimeout, timeout)
1687+
assert.Equal(t, tc.expectedError, err)
1688+
})
1689+
}
1690+
}

pkg/controller/operators/catalog/operator.go

+8-16
Original file line numberDiff line numberDiff line change
@@ -1408,23 +1408,10 @@ type UnpackedBundleReference struct {
14081408
Properties string `json:"properties"`
14091409
}
14101410

1411-
func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.InstallPlan, error) {
1411+
func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan, unpackTimeout time.Duration) (bool, *v1alpha1.InstallPlan, error) {
14121412
out := plan.DeepCopy()
14131413
unpacked := true
14141414

1415-
// The bundle timeout annotation if specified overrides the --bundle-unpack-timeout flag value
1416-
// If the timeout cannot be parsed it's set to < 0 and subsequently ignored
1417-
unpackTimeout := -1 * time.Minute
1418-
timeoutStr, ok := plan.GetAnnotations()[bundle.BundleUnpackTimeoutAnnotationKey]
1419-
if ok {
1420-
d, err := time.ParseDuration(timeoutStr)
1421-
if err != nil {
1422-
o.logger.Errorf("failed to parse unpack timeout annotation(%s: %s): %v", bundle.BundleUnpackTimeoutAnnotationKey, timeoutStr, err)
1423-
} else {
1424-
unpackTimeout = d
1425-
}
1426-
}
1427-
14281415
var errs []error
14291416
for i := 0; i < len(out.Status.BundleLookups); i++ {
14301417
lookup := out.Status.BundleLookups[i]
@@ -1599,7 +1586,6 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
15991586

16001587
querier := o.serviceAccountQuerier.NamespaceQuerier(plan.GetNamespace())
16011588
ref, err := querier()
1602-
16031589
out := plan.DeepCopy()
16041590
if err != nil {
16051591
// Set status condition/message and retry sync if any error
@@ -1645,10 +1631,16 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
16451631
}
16461632
}
16471633

1634+
ogLister := o.lister.OperatorsV1().OperatorGroupLister().OperatorGroups(plan.GetNamespace())
1635+
unpackTimeout, err := bundle.OperatorGroupBundleUnpackTimeout(ogLister)
1636+
if err != nil {
1637+
return err
1638+
}
1639+
16481640
// Attempt to unpack bundles before installing
16491641
// Note: This should probably use the attenuated client to prevent users from resolving resources they otherwise don't have access to.
16501642
if len(plan.Status.BundleLookups) > 0 {
1651-
unpacked, out, err := o.unpackBundles(plan)
1643+
unpacked, out, err := o.unpackBundles(plan, unpackTimeout)
16521644
if err != nil {
16531645
// If the error was fatal capture and fail
16541646
if fatal := olmerrors.IsFatal(err); fatal {

test/e2e/fail_forward_e2e_test.go

+20-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
. "github.com/onsi/gomega"
1010
corev1 "k8s.io/api/core/v1"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/types"
1213
"sigs.k8s.io/controller-runtime/pkg/client"
1314

1415
operatorsv1 "github.com/operator-framework/api/pkg/operators/v1"
@@ -27,6 +28,7 @@ var _ = Describe("Fail Forward Upgrades", func() {
2728
ns corev1.Namespace
2829
crclient versioned.Interface
2930
c client.Client
31+
ogName string
3032
)
3133

3234
BeforeEach(func() {
@@ -45,6 +47,7 @@ var _ = Describe("Fail Forward Upgrades", func() {
4547
},
4648
}
4749
ns = SetupGeneratedTestNamespaceWithOperatorGroup(namespaceName, og)
50+
ogName = og.GetName()
4851
})
4952

5053
AfterEach(func() {
@@ -61,9 +64,9 @@ var _ = Describe("Fail Forward Upgrades", func() {
6164
)
6265

6366
BeforeEach(func() {
67+
By("deploying the testing catalog")
6468
provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, failForwardTestDataBaseDir, "example-operator.v0.1.0.yaml"))
6569
Expect(err).To(BeNil())
66-
6770
catalogSourceName = genName("mc-ip-failed-")
6871
magicCatalog = NewMagicCatalog(c, ns.GetName(), catalogSourceName, provider)
6972
Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())
@@ -93,6 +96,9 @@ var _ = Describe("Fail Forward Upgrades", func() {
9396
_, err = fetchCSV(crclient, subscription.Status.CurrentCSV, ns.GetName(), buildCSVConditionChecker(operatorsv1alpha1.CSVPhaseSucceeded))
9497
Expect(err).ShouldNot(HaveOccurred())
9598

99+
By("patching the OperatorGroup to reduce the bundle unpacking timeout")
100+
addBundleUnpackTimeoutOGAnnotation(context.Background(), c, types.NamespacedName{Name: ogName, Namespace: ns.GetName()}, "1s")
101+
96102
By("updating the catalog with a broken v0.2.0 bundle image")
97103
brokenProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, failForwardTestDataBaseDir, "example-operator.v0.2.0.yaml"))
98104
Expect(err).To(BeNil())
@@ -104,9 +110,6 @@ var _ = Describe("Fail Forward Upgrades", func() {
104110
subscription, err = fetchSubscription(crclient, subscription.GetNamespace(), subscription.GetName(), subscriptionHasInstallPlanDifferentChecker(originalInstallPlanRef.Name))
105111
Expect(err).Should(BeNil())
106112

107-
By("patching the installplan to reduce the bundle unpacking timeout")
108-
addBundleUnpackTimeoutIPAnnotation(context.Background(), c, objectRefToNamespacedName(subscription.Status.InstallPlanRef), "1s")
109-
110113
By("waiting for the bad InstallPlan to report a failed installation state")
111114
ref := subscription.Status.InstallPlanRef
112115
_, err = fetchInstallPlan(GinkgoT(), crclient, ref.Name, ref.Namespace, buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed))
@@ -129,18 +132,17 @@ var _ = Describe("Fail Forward Upgrades", func() {
129132
subscription, err = fetchSubscription(crclient, subscription.GetNamespace(), subscription.GetName(), subscriptionHasCurrentCSV("example-operator.v0.2.1"))
130133
Expect(err).Should(BeNil())
131134

132-
By("patching the installplan to reduce the bundle unpacking timeout")
133-
addBundleUnpackTimeoutIPAnnotation(context.Background(), c, objectRefToNamespacedName(subscription.Status.InstallPlanRef), "1s")
134-
135135
By("waiting for the bad v0.2.1 InstallPlan to report a failed installation state")
136136
ref := subscription.Status.InstallPlanRef
137137
_, err = fetchInstallPlan(GinkgoT(), crclient, ref.Name, ref.Namespace, buildInstallPlanPhaseCheckFunc(operatorsv1alpha1.InstallPlanPhaseFailed))
138138
Expect(err).To(BeNil())
139139

140+
By("patching the OperatorGroup to increase the bundle unpacking timeout")
141+
addBundleUnpackTimeoutOGAnnotation(context.Background(), c, types.NamespacedName{Name: ogName, Namespace: ns.GetName()}, "5m")
142+
140143
By("patching the catalog with a fixed version")
141144
fixedProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, "fail-forward/multiple-bad-versions", "example-operator.v0.3.0.yaml"))
142145
Expect(err).To(BeNil())
143-
144146
err = magicCatalog.UpdateCatalog(context.Background(), fixedProvider)
145147
Expect(err).To(BeNil())
146148

@@ -150,10 +152,12 @@ var _ = Describe("Fail Forward Upgrades", func() {
150152
})
151153

152154
It("eventually reports a successful state when using skip ranges", func() {
155+
By("patching the OperatorGroup to increase the bundle unpacking timeout")
156+
addBundleUnpackTimeoutOGAnnotation(context.Background(), c, types.NamespacedName{Name: ogName, Namespace: ns.GetName()}, "5m")
157+
153158
By("patching the catalog with a fixed version")
154159
fixedProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, "fail-forward/skip-range", "example-operator.v0.3.0.yaml"))
155160
Expect(err).To(BeNil())
156-
157161
err = magicCatalog.UpdateCatalog(context.Background(), fixedProvider)
158162
Expect(err).To(BeNil())
159163

@@ -162,10 +166,12 @@ var _ = Describe("Fail Forward Upgrades", func() {
162166
Expect(err).Should(BeNil())
163167
})
164168
It("eventually reports a successful state when using skips", func() {
169+
By("patching the OperatorGroup to increase the bundle unpacking timeout")
170+
addBundleUnpackTimeoutOGAnnotation(context.Background(), c, types.NamespacedName{Name: ogName, Namespace: ns.GetName()}, "5m")
171+
165172
By("patching the catalog with a fixed version")
166173
fixedProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, "fail-forward/skips", "example-operator.v0.3.0.yaml"))
167174
Expect(err).To(BeNil())
168-
169175
err = magicCatalog.UpdateCatalog(context.Background(), fixedProvider)
170176
Expect(err).To(BeNil())
171177

@@ -174,10 +180,12 @@ var _ = Describe("Fail Forward Upgrades", func() {
174180
Expect(err).Should(BeNil())
175181
})
176182
It("eventually reports a failed state when using replaces", func() {
183+
By("patching the OperatorGroup to increase the bundle unpacking timeout")
184+
addBundleUnpackTimeoutOGAnnotation(context.Background(), c, types.NamespacedName{Name: ogName, Namespace: ns.GetName()}, "5m")
185+
177186
By("patching the catalog with a fixed version")
178187
fixedProvider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, "fail-forward/replaces", "example-operator.v0.3.0.yaml"))
179188
Expect(err).To(BeNil())
180-
181189
err = magicCatalog.UpdateCatalog(context.Background(), fixedProvider)
182190
Expect(err).To(BeNil())
183191

@@ -200,9 +208,9 @@ var _ = Describe("Fail Forward Upgrades", func() {
200208
)
201209

202210
BeforeEach(func() {
211+
By("deploying the testing catalog")
203212
provider, err := NewFileBasedFiledBasedCatalogProvider(filepath.Join(testdataDir, failForwardTestDataBaseDir, "example-operator.v0.1.0.yaml"))
204213
Expect(err).To(BeNil())
205-
206214
catalogSourceName = genName("mc-csv-failed-")
207215
magicCatalog = NewMagicCatalog(c, ns.GetName(), catalogSourceName, provider)
208216
Expect(magicCatalog.DeployCatalog(context.Background())).To(BeNil())

0 commit comments

Comments
 (0)