Skip to content

Commit

Permalink
feat(sub): Indicate resolution conflicts on Subscription statuses
Browse files Browse the repository at this point in the history
Resolution considers everything in a given namespace as a unit, so conflicts aren't
generated on a per-Subscription basis. However, as the main entry point to resolution,
users first look at the Subscription to understand resolution failures. The set of conflicts
today is written to Events with type ResolutionFailed. This PR projects resolution errors onto
all subscriptions on the namespace for any resolution failure.

Signed-off-by: Anik Bhattacharjee <anikbhattacharya93@gmail.com>
  • Loading branch information
anik120 committed Jul 22, 2021
1 parent ac82fec commit 4a2c5c0
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
62 changes: 62 additions & 0 deletions pkg/controller/operators/catalog/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -909,9 +909,22 @@ func (o *Operator) syncResolvingNamespace(obj interface{}) error {
// not-satisfiable error
if _, ok := err.(solver.NotSatisfiable); ok {
logger.WithError(err).Debug("resolution failed")
updateErr := o.setSubsCond(subs, v1alpha1.SubscriptionResolutionFailed, "ConstraintsNotSatisfiable", err.Error(), true)
if updateErr != nil {
logger.WithError(updateErr).Debug("failed to update subs conditions")
}
return nil
}
updateErr := o.setSubsCond(subs, v1alpha1.SubscriptionResolutionFailed, "ErrorPreventedResolution", err.Error(), true)
if updateErr != nil {
logger.WithError(updateErr).Debug("failed to update subs conditions")
}
return err
} else {
updateErr := o.setSubsCond(subs, v1alpha1.SubscriptionResolutionFailed, "", "", false)
if updateErr != nil {
logger.WithError(updateErr).Debug("failed to update subs conditions")
}
}

// create installplan if anything updated
Expand Down Expand Up @@ -1189,6 +1202,55 @@ func (o *Operator) createInstallPlan(namespace string, gen int, subs []*v1alpha1
return reference.GetReference(res)
}

func (o *Operator) setSubsCond(subs []*v1alpha1.Subscription, condType v1alpha1.SubscriptionConditionType, reason, message string, setTrue bool) error {
var (
errs []error
mu sync.Mutex
wg sync.WaitGroup
getOpts = metav1.GetOptions{}
updateOpts = metav1.UpdateOptions{}
lastUpdated = o.now()
)
for _, sub := range subs {
sub.Status.LastUpdated = lastUpdated
cond := sub.Status.GetCondition(condType)
cond.Reason = reason
cond.Message = message
if setTrue {
cond.Status = corev1.ConditionTrue
} else {
cond.Status = corev1.ConditionFalse
}
sub.Status.SetCondition(cond)

wg.Add(1)
go func(s v1alpha1.Subscription) {
defer wg.Done()

update := func() error {
// Update the status of the latest revision
latest, err := o.client.OperatorsV1alpha1().Subscriptions(s.GetNamespace()).Get(context.TODO(), s.GetName(), getOpts)
if err != nil {
return err
}

latest.Status = s.Status
_, err = o.client.OperatorsV1alpha1().Subscriptions(sub.Namespace).UpdateStatus(context.TODO(), latest, updateOpts)

return err
}
if err := retry.RetryOnConflict(retry.DefaultRetry, update); err != nil {
mu.Lock()
defer mu.Unlock()
errs = append(errs, err)
}
}(*sub)
}
wg.Wait()

return utilerrors.NewAggregate(errs)
}

type UnpackedBundleReference struct {
Kind string `json:"kind"`
Name string `json:"name"`
Expand Down
54 changes: 54 additions & 0 deletions test/e2e/subscription_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2105,6 +2105,60 @@ var _ = Describe("Subscription", func() {
require.NoError(GinkgoT(), err)
})

When("A subscription is created for an operator that requires an API that is not available", func() {

var (
c operatorclient.ClientInterface
crc versioned.Interface
teardown func()
cleanup func()
packages []registry.PackageManifest
subName = genName("test-subscription")
catSrcName = genName("test-catalog")
)

BeforeEach(func() {
c = newKubeClient()
crc = newCRClient()

packages = []registry.PackageManifest{
{
PackageName: "packageA",
Channels: []registry.PackageChannel{
{Name: "alpha", CurrentCSVName: "csvA"},
},
DefaultChannelName: "alpha",
},
}
crd := newCRD(genName("foo"))
csv := newCSV("csvA", testNamespace, "", semver.MustParse("1.0.0"), nil, []apiextensions.CustomResourceDefinition{crd}, nil)

_, teardown = createInternalCatalogSource(c, ctx.Ctx().OperatorClient(), catSrcName, testNamespace, packages, nil, []operatorsv1alpha1.ClusterServiceVersion{csv})

// Ensure that the catalog source is resolved before we create a subscription.
_, err := fetchCatalogSourceOnStatus(crc, catSrcName, testNamespace, catalogSourceRegistryPodSynced)
require.NoError(GinkgoT(), err)

cleanup = createSubscriptionForCatalog(crc, testNamespace, subName, catSrcName, "packageA", "alpha", "", operatorsv1alpha1.ApprovalAutomatic)
})

AfterEach(func() {
cleanup()
teardown()
})

It("the subscription has a condition in it's status that indicates the resolution error", func() {
Eventually(func() (corev1.ConditionStatus, error) {
sub, err := crc.OperatorsV1alpha1().Subscriptions(testNamespace).Get(context.Background(), subName, metav1.GetOptions{})
if err != nil {
return corev1.ConditionUnknown, err
}
return sub.Status.GetCondition(operatorsv1alpha1.SubscriptionResolutionFailed).Status, nil
}).Should(Equal(corev1.ConditionTrue))
})

})

When("an unannotated ClusterServiceVersion exists with an associated Subscription", func() {
var (
teardown func()
Expand Down

0 comments on commit 4a2c5c0

Please sign in to comment.