Skip to content

Commit ee37f17

Browse files
committed
support multiple subscriptions resolving the same bundles
this better aligns the on-cluster status with the output of the resolver we may want to prevent this case from happening at a higher layer (i.e. a webhook that prevents two subscriptions to the same package), but for now this behavior ensures that the actions the resolver takes are clearly visible in the subscription status. this also adds test cases for multiple subscriptions to the same package Signed-off-by: Evan <[email protected]>
1 parent 8e98472 commit ee37f17

File tree

2 files changed

+74
-16
lines changed

2 files changed

+74
-16
lines changed

pkg/controller/registry/resolver/step_resolver.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,8 @@ func (r *OperatorStepResolver) ResolveSteps(namespace string, _ SourceQuerier) (
9999
updatedSubs := []*v1alpha1.Subscription{}
100100
bundleLookups := []v1alpha1.BundleLookup{}
101101
for name, op := range operators {
102-
// Find an existing subscription that resolves to this operator.
103-
var (
104-
existingSubscription *v1alpha1.Subscription
105-
alreadyExists bool
106-
)
102+
// Find any existing subscriptions that resolve to this operator.
103+
existingSubscriptions := make(map[*v1alpha1.Subscription]bool)
107104
sourceInfo := *op.SourceInfo()
108105
for _, sub := range subs {
109106
if sub.Spec.Package != sourceInfo.Package {
@@ -119,17 +116,24 @@ func (r *OperatorStepResolver) ResolveSteps(namespace string, _ SourceQuerier) (
119116
if !subCatalogKey.Empty() && !subCatalogKey.Equal(sourceInfo.Catalog) {
120117
continue
121118
}
122-
alreadyExists, err = r.hasExistingCurrentCSV(sub)
119+
alreadyExists, err := r.hasExistingCurrentCSV(sub)
123120
if err != nil {
124-
return nil, nil, nil, fmt.Errorf("unable to determine whether subscription has a preexisting CSV")
121+
return nil, nil, nil, fmt.Errorf("unable to determine whether subscription %s has a preexisting CSV", sub.GetName())
125122
}
126-
existingSubscription = sub
127-
break
123+
existingSubscriptions[sub] = alreadyExists
128124
}
129125

130-
// subscription exists and is up to date
131-
if existingSubscription != nil && existingSubscription.Status.CurrentCSV == op.Identifier() && alreadyExists {
132-
continue
126+
if len(existingSubscriptions) > 0 {
127+
upToDate := true
128+
for sub, exists := range existingSubscriptions {
129+
if !exists || sub.Status.CurrentCSV != op.Identifier() {
130+
upToDate = false
131+
}
132+
}
133+
// all matching subscriptions are up to date
134+
if upToDate {
135+
continue
136+
}
133137
}
134138

135139
// add steps for any new bundle
@@ -172,7 +176,7 @@ func (r *OperatorStepResolver) ResolveSteps(namespace string, _ SourceQuerier) (
172176
bundleLookups = append(bundleLookups, lookup)
173177
}
174178

175-
if existingSubscription == nil {
179+
if len(existingSubscriptions) == 0 {
176180
// explicitly track the resolved CSV as the starting CSV on the resolved subscriptions
177181
op.SourceInfo().StartingCSV = op.Identifier()
178182
subStep, err := NewSubscriptionStepResource(namespace, *op.SourceInfo())
@@ -188,10 +192,13 @@ func (r *OperatorStepResolver) ResolveSteps(namespace string, _ SourceQuerier) (
188192
}
189193

190194
// add steps for subscriptions for bundles that were added through resolution
191-
if existingSubscription != nil && existingSubscription.Status.CurrentCSV != op.Identifier() {
195+
for sub := range existingSubscriptions {
196+
if sub.Status.CurrentCSV == op.Identifier() {
197+
continue
198+
}
192199
// update existing subscription status
193-
existingSubscription.Status.CurrentCSV = op.Identifier()
194-
updatedSubs = append(updatedSubs, existingSubscription)
200+
sub.Status.CurrentCSV = op.Identifier()
201+
updatedSubs = append(updatedSubs, sub)
195202
}
196203
}
197204

pkg/controller/registry/resolver/step_resolver_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,57 @@ func TestResolver(t *testing.T) {
305305
},
306306
},
307307
},
308+
{
309+
name: "ConflictingSubscriptionsToSamePackage",
310+
clusterState: []runtime.Object{
311+
newSub(namespace, "a", "alpha", catalog),
312+
newSub(namespace, "a", "beta", catalog),
313+
},
314+
bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{
315+
catalog: {
316+
bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil),
317+
bundle("a.v2", "a", "beta", "", Provides1, nil, nil, nil),
318+
},
319+
},
320+
out: resolverTestOut{
321+
errAssert: func(t *testing.T, err error) {
322+
fmt.Println(err)
323+
assert.IsType(t, solver.NotSatisfiable{}, err)
324+
},
325+
},
326+
},
327+
{
328+
// No two operators from the same package may run at the same time, but it's possible to have two
329+
// subscriptions to the same package as long as it's possible to find a bundle that satisfies both
330+
// constraints
331+
name: "SatisfiableSubscriptionsToSamePackage",
332+
clusterState: []runtime.Object{
333+
newSub(namespace, "a", "alpha", catalog),
334+
func() (s *v1alpha1.Subscription) {
335+
s = newSub(namespace, "a", "alpha", catalog)
336+
s.Name = s.Name+"-2"
337+
return
338+
}(),
339+
},
340+
bundlesByCatalog: map[registry.CatalogKey][]*api.Bundle{
341+
catalog: {
342+
bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil),
343+
},
344+
},
345+
out: resolverTestOut{
346+
steps: [][]*v1alpha1.Step{
347+
bundleSteps(bundle("a.v1", "a", "alpha", "", Provides1, nil, nil, nil), namespace, "", catalog),
348+
},
349+
subs: []*v1alpha1.Subscription{
350+
updatedSub(namespace, "a.v1", "", "a", "alpha", catalog),
351+
func() (s *v1alpha1.Subscription) {
352+
s = updatedSub(namespace, "a.v1", "", "a", "alpha", catalog)
353+
s.Name = s.Name+"-2"
354+
return
355+
}(),
356+
},
357+
},
358+
},
308359
{
309360
name: "TwoExistingOperatorsWithSameName/NoError",
310361
clusterState: []runtime.Object{

0 commit comments

Comments
 (0)