Skip to content

Commit d1619eb

Browse files
committed
preserve failed unpack jobs, enforce minimum interval between failing unpack jobs
Signed-off-by: Ankita Thomas <[email protected]> Upstream-repository: operator-lifecycle-manager Upstream-commit: 07d8b910f42b90869b6fa7686cf2a8ad09956540
1 parent 1aa0c41 commit d1619eb

File tree

6 files changed

+158
-28
lines changed

6 files changed

+158
-28
lines changed

staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ const (
4141
// e.g 1m30s
4242
BundleUnpackTimeoutAnnotationKey = "operatorframework.io/bundle-unpack-timeout"
4343
BundleUnpackPodLabel = "job-name"
44+
45+
// BundleUnpackRetryMinimumIntervalAnnotationKey sets a minimum interval to wait before
46+
// attempting to recreate a failed unpack job for a bundle.
47+
BundleUnpackRetryMinimumIntervalAnnotationKey = "operatorframework.io/bundle-unpack-min-retry-interval"
48+
49+
// bundleUnpackRefLabel is used to filter for all unpack jobs for a specific bundle.
50+
bundleUnpackRefLabel = "operatorframework.io/bundle-unpack-ref"
4451
)
4552

4653
type BundleUnpackResult struct {
@@ -239,6 +246,7 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
239246
}
240247
job.SetNamespace(cmRef.Namespace)
241248
job.SetName(cmRef.Name)
249+
job.SetLabels(map[string]string{bundleUnpackRefLabel: cmRef.Name})
242250
job.SetOwnerReferences([]metav1.OwnerReference{ownerRef(cmRef)})
243251
if c.runAsUser > 0 {
244252
job.Spec.Template.Spec.SecurityContext.RunAsUser = &c.runAsUser
@@ -279,7 +287,7 @@ func (c *ConfigMapUnpacker) job(cmRef *corev1.ObjectReference, bundlePath string
279287
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . Unpacker
280288

281289
type Unpacker interface {
282-
UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration) (result *BundleUnpackResult, err error)
290+
UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout, retryInterval time.Duration) (result *BundleUnpackResult, err error)
283291
}
284292

285293
type ConfigMapUnpacker struct {
@@ -440,7 +448,7 @@ const (
440448
NotUnpackedMessage = "bundle contents have not yet been persisted to installplan status"
441449
)
442450

443-
func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout time.Duration) (result *BundleUnpackResult, err error) {
451+
func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup, timeout, retryInterval time.Duration) (result *BundleUnpackResult, err error) {
444452
result = newBundleUnpackResult(lookup)
445453

446454
// if bundle lookup failed condition already present, then there is nothing more to do
@@ -502,7 +510,7 @@ func (c *ConfigMapUnpacker) UnpackBundle(lookup *operatorsv1alpha1.BundleLookup,
502510
secrets = append(secrets, corev1.LocalObjectReference{Name: secretName})
503511
}
504512
var job *batchv1.Job
505-
job, err = c.ensureJob(cmRef, result.Path, secrets, timeout)
513+
job, err = c.ensureJob(cmRef, result.Path, secrets, timeout, retryInterval)
506514
if err != nil || job == nil {
507515
// ensureJob can return nil if the job present does not match the expected job (spec and ownerefs)
508516
// The current job is deleted in that case so UnpackBundle needs to be retried
@@ -641,7 +649,7 @@ func (c *ConfigMapUnpacker) ensureConfigmap(csRef *corev1.ObjectReference, name
641649
return
642650
}
643651

644-
func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, timeout time.Duration) (job *batchv1.Job, err error) {
652+
func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath string, secrets []corev1.LocalObjectReference, timeout time.Duration, unpackRetryInterval time.Duration) (job *batchv1.Job, err error) {
645653
fresh := c.job(cmRef, bundlePath, secrets, timeout)
646654
job, err = c.jobLister.Jobs(fresh.GetNamespace()).Get(fresh.GetName())
647655
if err != nil {
@@ -651,13 +659,40 @@ func (c *ConfigMapUnpacker) ensureJob(cmRef *corev1.ObjectReference, bundlePath
651659

652660
return
653661
}
654-
// Cleanup old unpacking job and retry
655-
if _, isFailed := getCondition(job, batchv1.JobFailed); isFailed {
656-
err = c.client.BatchV1().Jobs(job.GetNamespace()).Delete(context.TODO(), job.GetName(), metav1.DeleteOptions{})
657-
if err == nil {
658-
job, err = c.client.BatchV1().Jobs(fresh.GetNamespace()).Create(context.TODO(), fresh, metav1.CreateOptions{})
662+
663+
// only check for retries if an unpackRetryInterval is specified
664+
if unpackRetryInterval > 0 {
665+
if failedCond, isFailed := getCondition(job, batchv1.JobFailed); isFailed {
666+
lastFailureTime := failedCond.LastTransitionTime.Time
667+
// Look for other unpack jobs for the same bundle
668+
var jobs []*batchv1.Job
669+
jobs, err = c.jobLister.Jobs(fresh.GetNamespace()).List(k8slabels.ValidatedSetSelector{bundleUnpackRefLabel: cmRef.Name})
670+
if err != nil {
671+
return
672+
}
673+
674+
var failed bool
675+
var cond *batchv1.JobCondition
676+
for _, j := range jobs {
677+
cond, failed = getCondition(j, batchv1.JobFailed)
678+
if !failed {
679+
// found an in-progress unpack attempt
680+
job = j
681+
break
682+
}
683+
if cond != nil && lastFailureTime.Before(cond.LastTransitionTime.Time) {
684+
lastFailureTime = cond.LastTransitionTime.Time
685+
}
686+
}
687+
688+
if failed {
689+
if time.Now().After(lastFailureTime.Add(unpackRetryInterval)) {
690+
fresh.SetName(fmt.Sprintf("%s-%d", fresh.GetName(), len(jobs)))
691+
job, err = c.client.BatchV1().Jobs(fresh.GetNamespace()).Create(context.TODO(), fresh, metav1.CreateOptions{})
692+
return
693+
}
694+
}
659695
}
660-
return
661696
}
662697

663698
if equality.Semantic.DeepDerivative(fresh.GetOwnerReferences(), job.GetOwnerReferences()) && equality.Semantic.DeepDerivative(fresh.Spec, job.Spec) {
@@ -825,3 +860,28 @@ func OperatorGroupBundleUnpackTimeout(ogLister v1listers.OperatorGroupNamespaceL
825860

826861
return d, nil
827862
}
863+
864+
// OperatorGroupBundleUnpackRetryInterval returns bundle unpack retry interval from annotation if specified.
865+
// If the retry annotation is not set, return retry = 0 which is subsequently ignored. This interval, if > 0,
866+
// determines the minimum interval between recreating a failed unpack job.
867+
func OperatorGroupBundleUnpackRetryInterval(ogLister v1listers.OperatorGroupNamespaceLister) (time.Duration, error) {
868+
ogs, err := ogLister.List(k8slabels.Everything())
869+
if err != nil {
870+
return 0, err
871+
}
872+
if len(ogs) != 1 {
873+
return 0, fmt.Errorf("found %d operatorGroups, expected 1", len(ogs))
874+
}
875+
876+
timeoutStr, ok := ogs[0].GetAnnotations()[BundleUnpackRetryMinimumIntervalAnnotationKey]
877+
if !ok {
878+
return 0, nil
879+
}
880+
881+
d, err := time.ParseDuration(timeoutStr)
882+
if err != nil {
883+
return 0, fmt.Errorf("failed to parse unpack timeout annotation(%s: %s): %w", BundleUnpackRetryMinimumIntervalAnnotationKey, timeoutStr, err)
884+
}
885+
886+
return d, nil
887+
}

staging/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1654,7 +1654,7 @@ func TestConfigMapUnpacker(t *testing.T) {
16541654
)
16551655
require.NoError(t, err)
16561656

1657-
res, err := unpacker.UnpackBundle(tt.args.lookup, tt.args.annotationTimeout)
1657+
res, err := unpacker.UnpackBundle(tt.args.lookup, tt.args.annotationTimeout, 0)
16581658
require.Equal(t, tt.expected.err, err)
16591659

16601660
if tt.expected.res == nil {

staging/operator-lifecycle-manager/pkg/controller/bundle/bundlefakes/fake_unpacker.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -955,6 +955,11 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
955955
return err
956956
}
957957

958+
minUnpackRetryInterval, err := bundle.OperatorGroupBundleUnpackRetryInterval(ogLister)
959+
if err != nil {
960+
return err
961+
}
962+
958963
// TODO: parallel
959964
maxGeneration := 0
960965
subscriptionUpdated := false
@@ -1050,7 +1055,7 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
10501055
logger.Debug("unpacking bundles")
10511056

10521057
var unpacked bool
1053-
unpacked, steps, bundleLookups, err = o.unpackBundles(namespace, steps, bundleLookups, unpackTimeout)
1058+
unpacked, steps, bundleLookups, err = o.unpackBundles(namespace, steps, bundleLookups, unpackTimeout, minUnpackRetryInterval)
10541059
if err != nil {
10551060
// If the error was fatal capture and fail
10561061
if olmerrors.IsFatal(err) {
@@ -1505,7 +1510,7 @@ type UnpackedBundleReference struct {
15051510
Properties string `json:"properties"`
15061511
}
15071512

1508-
func (o *Operator) unpackBundles(namespace string, installPlanSteps []*v1alpha1.Step, bundleLookups []v1alpha1.BundleLookup, unpackTimeout time.Duration) (bool, []*v1alpha1.Step, []v1alpha1.BundleLookup, error) {
1513+
func (o *Operator) unpackBundles(namespace string, installPlanSteps []*v1alpha1.Step, bundleLookups []v1alpha1.BundleLookup, unpackTimeout, minUnpackRetryInterval time.Duration) (bool, []*v1alpha1.Step, []v1alpha1.BundleLookup, error) {
15091514
unpacked := true
15101515

15111516
outBundleLookups := make([]v1alpha1.BundleLookup, len(bundleLookups))
@@ -1520,7 +1525,7 @@ func (o *Operator) unpackBundles(namespace string, installPlanSteps []*v1alpha1.
15201525
var errs []error
15211526
for i := 0; i < len(outBundleLookups); i++ {
15221527
lookup := outBundleLookups[i]
1523-
res, err := o.bundleUnpacker.UnpackBundle(&lookup, unpackTimeout)
1528+
res, err := o.bundleUnpacker.UnpackBundle(&lookup, unpackTimeout, minUnpackRetryInterval)
15241529
if err != nil {
15251530
errs = append(errs, err)
15261531
continue

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/bundle/bundle_unpacker.go

Lines changed: 70 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go

Lines changed: 8 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)