Skip to content

Commit 2ebeb27

Browse files
author
Mikalai Radchuk
committed
Changes how InstallPlans are being created
Prevent OLM from creating `InstallPlan`s when bundle unpack fails Signed-off-by: Mikalai Radchuk <[email protected]>
1 parent 3f598d5 commit 2ebeb27

File tree

1 file changed

+80
-106
lines changed

1 file changed

+80
-106
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 80 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,54 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
10281028
return err
10291029
}
10301030

1031+
// Attempt to unpack bundles before installing
1032+
// Note: This should probably use the attenuated client to prevent users from resolving resources they otherwise don't have access to.
1033+
if len(bundleLookups) > 0 {
1034+
logger.Debug("unpacking bundles")
1035+
1036+
var unpacked bool
1037+
unpacked, steps, bundleLookups, err = o.unpackBundles(namespace, bundleLookups)
1038+
if err != nil {
1039+
return fmt.Errorf("bundle unpacking failed with an error: %v", err)
1040+
}
1041+
1042+
// Check BundleLookup status conditions to see if the BundleLookupFailed condtion is true
1043+
// which means bundle lookup has failed and subscriptions need to be updated
1044+
// with a condition indicating the failure.
1045+
isFailed, cond := hasBundleLookupFailureCondition(bundleLookups)
1046+
if isFailed {
1047+
err := fmt.Errorf("bundle unpacking failed. Reason: %v, and Message: %v", cond.Reason, cond.Message)
1048+
logger.Infof("%v", err)
1049+
1050+
_, updateErr := o.updateSubscriptionStatuses(
1051+
o.setSubsCond(subs, v1alpha1.SubscriptionCondition{
1052+
// TODO: Decide if we need a separate type and/or reason for unpack failures
1053+
Type: v1alpha1.SubscriptionResolutionFailed,
1054+
Reason: "BundleUnpackFailurePreventedResolution",
1055+
Message: err.Error(),
1056+
Status: corev1.ConditionTrue,
1057+
}))
1058+
if updateErr != nil {
1059+
logger.WithError(updateErr).Debug("failed to update subs conditions")
1060+
return updateErr
1061+
}
1062+
// Since this is likely requires intervention we do not want to
1063+
// requeue too often. We return no error here and rely on a
1064+
// periodic resync which will help to automatically resolve
1065+
// some issues such as unreachable bundle images caused by
1066+
// bad catalog updates.
1067+
return nil
1068+
}
1069+
1070+
// This means that the unpack job is still running (most likely) or
1071+
// there was some issue which we did not handle above.
1072+
if !unpacked {
1073+
logger.Debug("unpacking is not complete yet, requeueing")
1074+
o.nsResolveQueue.AddAfter(namespace, 5*time.Second)
1075+
return nil
1076+
}
1077+
}
1078+
10311079
// create installplan if anything updated
10321080
if len(updatedSubs) > 0 {
10331081
logger.Debug("resolution caused subscription changes, creating installplan")
@@ -1408,32 +1456,38 @@ type UnpackedBundleReference struct {
14081456
Properties string `json:"properties"`
14091457
}
14101458

1411-
func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.InstallPlan, error) {
1412-
out := plan.DeepCopy()
1459+
func (o *Operator) unpackBundles(namespace string, bundleLookups []v1alpha1.BundleLookup) (bool, []*v1alpha1.Step, []v1alpha1.BundleLookup, error) {
14131460
unpacked := true
14141461

1462+
outBundleLookups := make([]v1alpha1.BundleLookup, len(bundleLookups))
1463+
for i := range bundleLookups {
1464+
bundleLookups[i].DeepCopyInto(&outBundleLookups[i])
1465+
}
1466+
14151467
// The bundle timeout annotation if specified overrides the --bundle-unpack-timeout flag value
14161468
// If the timeout cannot be parsed it's set to < 0 and subsequently ignored
14171469
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-
1470+
// TODO: Update. This is internal stuff for testing. Maybe move to a sub?
1471+
// timeoutStr, ok := plan.GetAnnotations()[bundle.BundleUnpackTimeoutAnnotationKey]
1472+
// if ok {
1473+
// d, err := time.ParseDuration(timeoutStr)
1474+
// if err != nil {
1475+
// o.logger.Errorf("failed to parse unpack timeout annotation(%s: %s): %v", bundle.BundleUnpackTimeoutAnnotationKey, timeoutStr, err)
1476+
// } else {
1477+
// unpackTimeout = d
1478+
// }
1479+
// }
1480+
1481+
installPlanSteps := []*v1alpha1.Step{}
14281482
var errs []error
1429-
for i := 0; i < len(out.Status.BundleLookups); i++ {
1430-
lookup := out.Status.BundleLookups[i]
1483+
for i := 0; i < len(outBundleLookups); i++ {
1484+
lookup := outBundleLookups[i]
14311485
res, err := o.bundleUnpacker.UnpackBundle(&lookup, unpackTimeout)
14321486
if err != nil {
14331487
errs = append(errs, err)
14341488
continue
14351489
}
1436-
out.Status.BundleLookups[i] = *res.BundleLookup
1490+
outBundleLookups[i] = *res.BundleLookup
14371491

14381492
// if the failed condition is present it means the bundle unpacking has failed
14391493
failedCondition := res.GetCondition(bundle.BundleLookupFailed)
@@ -1455,11 +1509,11 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
14551509
continue
14561510
}
14571511

1458-
// Ensure that bundle can be applied by the current version of OLM by converting to steps
1459-
steps, err := resolver.NewStepsFromBundle(res.Bundle(), out.GetNamespace(), res.Replaces, res.CatalogSourceRef.Name, res.CatalogSourceRef.Namespace)
1512+
// Ensure that bundle can be applied by the current version of OLM by converting to bundleSteps
1513+
bundleSteps, err := resolver.NewStepsFromBundle(res.Bundle(), namespace, res.Replaces, res.CatalogSourceRef.Name, res.CatalogSourceRef.Namespace)
14601514
if err != nil {
14611515
if fatal := olmerrors.IsFatal(err); fatal {
1462-
return false, nil, err
1516+
return false, nil, nil, err
14631517
}
14641518

14651519
errs = append(errs, fmt.Errorf("failed to turn bundle into steps: %v", err))
@@ -1468,7 +1522,7 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
14681522
}
14691523

14701524
// step manifests are replaced with references to the configmap containing them
1471-
for i, s := range steps {
1525+
for i, s := range bundleSteps {
14721526
ref := UnpackedBundleReference{
14731527
Kind: "ConfigMap",
14741528
Namespace: res.CatalogSourceRef.Namespace,
@@ -1485,19 +1539,19 @@ func (o *Operator) unpackBundles(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.In
14851539
continue
14861540
}
14871541
s.Resource.Manifest = string(r)
1488-
steps[i] = s
1542+
bundleSteps[i] = s
14891543
}
14901544
res.RemoveCondition(resolver.BundleLookupConditionPacked)
1491-
out.Status.BundleLookups[i] = *res.BundleLookup
1492-
out.Status.Plan = append(out.Status.Plan, steps...)
1545+
outBundleLookups[i] = *res.BundleLookup
1546+
installPlanSteps = append(installPlanSteps, bundleSteps...)
14931547
}
14941548

14951549
if err := utilerrors.NewAggregate(errs); err != nil {
14961550
o.logger.Debugf("failed to unpack bundles: %v", err)
1497-
return false, nil, err
1551+
return false, nil, nil, err
14981552
}
14991553

1500-
return unpacked, out, nil
1554+
return unpacked, installPlanSteps, outBundleLookups, nil
15011555
}
15021556

15031557
// gcInstallPlans garbage collects installplans that are too old
@@ -1645,65 +1699,6 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
16451699
}
16461700
}
16471701

1648-
// Attempt to unpack bundles before installing
1649-
// Note: This should probably use the attenuated client to prevent users from resolving resources they otherwise don't have access to.
1650-
if len(plan.Status.BundleLookups) > 0 {
1651-
unpacked, out, err := o.unpackBundles(plan)
1652-
if err != nil {
1653-
// If the error was fatal capture and fail
1654-
if fatal := olmerrors.IsFatal(err); fatal {
1655-
if err := o.transitionInstallPlanToFailed(plan, logger, v1alpha1.InstallPlanReasonInstallCheckFailed, err.Error()); err != nil {
1656-
// retry for failure to update status
1657-
syncError = err
1658-
return
1659-
}
1660-
}
1661-
// Retry sync if non-fatal error
1662-
syncError = fmt.Errorf("bundle unpacking failed: %v", err)
1663-
return
1664-
}
1665-
1666-
if !reflect.DeepEqual(plan.Status, out.Status) {
1667-
logger.Warnf("status not equal, updating...")
1668-
if _, err := o.client.OperatorsV1alpha1().InstallPlans(out.GetNamespace()).UpdateStatus(context.TODO(), out, metav1.UpdateOptions{}); err != nil {
1669-
syncError = fmt.Errorf("failed to update installplan bundle lookups: %v", err)
1670-
}
1671-
1672-
return
1673-
}
1674-
1675-
// Check BundleLookup status conditions to see if the BundleLookupPending condtion is false
1676-
// which means bundle lookup has failed and the InstallPlan should be failed as well
1677-
isFailed, cond := hasBundleLookupFailureCondition(plan)
1678-
if isFailed {
1679-
err := fmt.Errorf("bundle unpacking failed. Reason: %v, and Message: %v", cond.Reason, cond.Message)
1680-
// Mark the InstallPlan as failed for a fatal bundle unpack error
1681-
logger.Infof("%v", err)
1682-
1683-
if err := o.transitionInstallPlanToFailed(plan, logger, v1alpha1.InstallPlanReasonInstallCheckFailed, err.Error()); err != nil {
1684-
// retry for failure to update status
1685-
syncError = err
1686-
return
1687-
}
1688-
1689-
// Requeue subscription to propagate SubscriptionInstallPlanFailed condtion to subscription
1690-
o.requeueSubscriptionForInstallPlan(plan, logger)
1691-
return
1692-
}
1693-
1694-
// TODO: Remove in favor of job and configmap informer requeuing
1695-
if !unpacked {
1696-
err := o.ipQueueSet.RequeueAfter(plan.GetNamespace(), plan.GetName(), 5*time.Second)
1697-
if err != nil {
1698-
syncError = err
1699-
return
1700-
}
1701-
logger.Debug("install plan not yet populated from bundle image, requeueing")
1702-
1703-
return
1704-
}
1705-
}
1706-
17071702
outInstallPlan, syncError := transitionInstallPlanState(logger.Logger, o, *plan, o.now(), o.installPlanTimeout)
17081703

17091704
if syncError != nil {
@@ -1731,8 +1726,8 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
17311726
return
17321727
}
17331728

1734-
func hasBundleLookupFailureCondition(plan *v1alpha1.InstallPlan) (bool, *v1alpha1.BundleLookupCondition) {
1735-
for _, bundleLookup := range plan.Status.BundleLookups {
1729+
func hasBundleLookupFailureCondition(bundleLookups []v1alpha1.BundleLookup) (bool, *v1alpha1.BundleLookupCondition) {
1730+
for _, bundleLookup := range bundleLookups {
17361731
for _, cond := range bundleLookup.Conditions {
17371732
if cond.Type == bundle.BundleLookupFailed && cond.Status == corev1.ConditionTrue {
17381733
return true, &cond
@@ -1742,27 +1737,6 @@ func hasBundleLookupFailureCondition(plan *v1alpha1.InstallPlan) (bool, *v1alpha
17421737
return false, nil
17431738
}
17441739

1745-
func (o *Operator) transitionInstallPlanToFailed(plan *v1alpha1.InstallPlan, logger logrus.FieldLogger, reason v1alpha1.InstallPlanConditionReason, message string) error {
1746-
now := o.now()
1747-
out := plan.DeepCopy()
1748-
out.Status.SetCondition(v1alpha1.ConditionFailed(v1alpha1.InstallPlanInstalled,
1749-
reason, message, &now))
1750-
out.Status.Phase = v1alpha1.InstallPlanPhaseFailed
1751-
1752-
logger.Info("transitioning InstallPlan to failed")
1753-
_, err := o.client.OperatorsV1alpha1().InstallPlans(plan.GetNamespace()).UpdateStatus(context.TODO(), out, metav1.UpdateOptions{})
1754-
if err == nil {
1755-
return nil
1756-
}
1757-
1758-
updateErr := errors.New("error updating InstallPlan status: " + err.Error())
1759-
logger = logger.WithField("updateError", updateErr)
1760-
logger.Errorf("error transitioning InstallPlan to failed")
1761-
1762-
// retry sync with error to update InstallPlan status
1763-
return fmt.Errorf("installplan failed: %s and error updating InstallPlan status as failed: %s", message, updateErr)
1764-
}
1765-
17661740
func (o *Operator) requeueSubscriptionForInstallPlan(plan *v1alpha1.InstallPlan, logger *logrus.Entry) {
17671741
// Notify subscription loop of installplan changes
17681742
owners := ownerutil.GetOwnersByKind(plan, v1alpha1.SubscriptionKind)

0 commit comments

Comments
 (0)