Skip to content

Commit 360cf6f

Browse files
committed
Clarify subscription constraint strings in resolution failures.
ResolutionFailed events exposed internal terminology (i.e. "mandatory" and "dependency") directly to users. Common constraints applied to subscriptions appeared as "PACKAGE is mandatory" or "PACKAGE has a dependency without any candidates to satisfy it", which in practice meant "a subscription to PACKAGE exists in the namespace" and "no operators found matching the criteria of the subscription to PACKAGE", respectively. The language of these Subscription-related constraint strings now better reflects what they mean to users. Signed-off-by: Ben Luddy <[email protected]>
1 parent 0373c9b commit 360cf6f

File tree

6 files changed

+74
-19
lines changed

6 files changed

+74
-19
lines changed

pkg/controller/registry/resolver/installabletypes.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ func NewBundleInstallable(bundle, channel string, catalog registry.CatalogKey, c
6464

6565
func NewSubscriptionInstallable(pkg string) SubscriptionInstallable {
6666
return SubscriptionInstallable{
67-
identifier: solver.Identifier(pkg),
68-
constraints: []solver.Constraint{solver.Mandatory()},
67+
identifier: solver.Identifier(pkg),
68+
constraints: []solver.Constraint{
69+
ConstraintSubscriptionExists,
70+
},
6971
}
7072
}
7173

@@ -83,5 +85,35 @@ func (r SubscriptionInstallable) Constraints() []solver.Constraint {
8385
}
8486

8587
func (r *SubscriptionInstallable) AddDependency(dependencies []solver.Identifier) {
86-
r.constraints = append(r.constraints, solver.Dependency(dependencies...))
88+
if len(dependencies) == 0 {
89+
r.constraints = append(r.constraints, ConstraintSubscriptionWithoutCandidates)
90+
return
91+
}
92+
93+
s := make([]string, len(dependencies))
94+
for i, each := range dependencies {
95+
s[i] = string(each)
96+
}
97+
r.constraints = append(r.constraints, PrettyConstraint(solver.Dependency(dependencies...), fmt.Sprintf("subscription to %%s requires at least one of %s", strings.Join(s, ", "))))
98+
}
99+
100+
func PrettyConstraint(c solver.Constraint, format string) solver.Constraint {
101+
return prettyConstraint{
102+
Constraint: c,
103+
format: format,
104+
}
87105
}
106+
107+
type prettyConstraint struct {
108+
solver.Constraint
109+
format string
110+
}
111+
112+
func (pc prettyConstraint) String(subject solver.Identifier) string {
113+
return fmt.Sprintf(pc.format, subject)
114+
}
115+
116+
var (
117+
ConstraintSubscriptionExists = PrettyConstraint(solver.Mandatory(), "a subscription to package %s exists in the namespace")
118+
ConstraintSubscriptionWithoutCandidates = PrettyConstraint(solver.Dependency(), "no operators found matching the subscription criteria for %s")
119+
)

pkg/controller/registry/resolver/solver/constraints.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type Constraint interface {
1414
String(subject Identifier) string
1515
apply(c *logic.C, lm *litMapping, subject Identifier) z.Lit
1616
order() []Identifier
17+
anchor() bool
1718
}
1819

1920
// zeroConstraint is returned by ConstraintOf in error cases.
@@ -33,6 +34,10 @@ func (zeroConstraint) order() []Identifier {
3334
return nil
3435
}
3536

37+
func (zeroConstraint) anchor() bool {
38+
return false
39+
}
40+
3641
// AppliedConstraint values compose a single Constraint with the
3742
// Installable it applies to.
3843
type AppliedConstraint struct {
@@ -60,6 +65,10 @@ func (constraint mandatory) order() []Identifier {
6065
return nil
6166
}
6267

68+
func (constraint mandatory) anchor() bool {
69+
return true
70+
}
71+
6372
// Mandatory returns a Constraint that will permit only solutions that
6473
// contain a particular Installable.
6574
func Mandatory() Constraint {
@@ -80,6 +89,10 @@ func (constraint prohibited) order() []Identifier {
8089
return nil
8190
}
8291

92+
func (constraint prohibited) anchor() bool {
93+
return false
94+
}
95+
8396
// Prohibited returns a Constraint that will reject any solution that
8497
// contains a particular Installable. Callers may also decide to omit
8598
// an Installable from input to Solve rather than apply such a
@@ -113,6 +126,10 @@ func (constraint dependency) order() []Identifier {
113126
return constraint
114127
}
115128

129+
func (constraint dependency) anchor() bool {
130+
return false
131+
}
132+
116133
// Dependency returns a Constraint that will only permit solutions
117134
// containing a given Installable on the condition that at least one
118135
// of the Installables identified by the given Identifiers also
@@ -136,6 +153,10 @@ func (constraint conflict) order() []Identifier {
136153
return nil
137154
}
138155

156+
func (constraint conflict) anchor() bool {
157+
return false
158+
}
159+
139160
// Conflict returns a Constraint that will permit solutions containing
140161
// either the constrained Installable, the Installable identified by
141162
// the given Identifier, or neither, but not both.
@@ -168,6 +189,10 @@ func (constraint leq) order() []Identifier {
168189
return nil
169190
}
170191

192+
func (constraint leq) anchor() bool {
193+
return false
194+
}
195+
171196
// AtMost returns a Constraint that forbids solutions that contain
172197
// more than n of the Installables identified by the given
173198
// Identifiers.

pkg/controller/registry/resolver/solver/lit_mapping.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -158,14 +158,14 @@ func (d *litMapping) CardinalityConstrainer(g inter.Adder, ms []z.Lit) *logic.Ca
158158
return cs
159159
}
160160

161-
// MandatoryIdentifiers returns a slice containing the Identifiers of
162-
// every Installable with at least one "Mandatory" constraint, in the
161+
// AnchorIdentifiers returns a slice containing the Identifiers of
162+
// every Installable with at least one "anchor" constraint, in the
163163
// order they appear in the input.
164-
func (d *litMapping) MandatoryIdentifiers() []Identifier {
164+
func (d *litMapping) AnchorIdentifiers() []Identifier {
165165
var ids []Identifier
166166
for _, installable := range d.inorder {
167167
for _, constraint := range installable.Constraints() {
168-
if _, ok := constraint.(mandatory); ok {
168+
if constraint.anchor() {
169169
ids = append(ids, installable.Identifier())
170170
break
171171
}

pkg/controller/registry/resolver/solver/search_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func TestSearch(t *testing.T) {
8888
}
8989

9090
var anchors []z.Lit
91-
for _, id := range h.lits.MandatoryIdentifiers() {
91+
for _, id := range h.lits.AnchorIdentifiers() {
9292
anchors = append(anchors, h.lits.LitOf(id))
9393
}
9494

pkg/controller/registry/resolver/solver/solve.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func (s *solver) Solve(ctx context.Context) (result []Installable, err error) {
6565

6666
// collect literals of all mandatory installables to assume as a baseline
6767
var assumptions []z.Lit
68-
for _, anchor := range s.litMap.MandatoryIdentifiers() {
68+
for _, anchor := range s.litMap.AnchorIdentifiers() {
6969
assumptions = append(assumptions, s.litMap.LitOf(anchor))
7070
}
7171

pkg/controller/registry/resolver/step_resolver_test.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func SharedResolverSpecs() []resolverTest {
105105
solver.Dependency(),
106106
},
107107
},
108-
Constraint: solver.Mandatory(),
108+
Constraint: ConstraintSubscriptionExists,
109109
},
110110
{
111111
Installable: &SubscriptionInstallable{
@@ -115,7 +115,7 @@ func SharedResolverSpecs() []resolverTest {
115115
solver.Dependency(),
116116
},
117117
},
118-
Constraint: solver.Dependency(),
118+
Constraint: ConstraintSubscriptionWithoutCandidates,
119119
},
120120
},
121121
},
@@ -270,12 +270,11 @@ func SharedResolverSpecs() []resolverTest {
270270
Installable: &SubscriptionInstallable{
271271
identifier: "a",
272272
constraints: []solver.Constraint{
273-
solver.Mandatory(),
274-
solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"),
275-
solver.AtMost(1, "catsrc/catsrc-namespace/alpha/a.v1"),
273+
ConstraintSubscriptionExists,
274+
PrettyConstraint(solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"), "subscription to %s requires at least one of catsrc/catsrc-namespace/alpha/a.v1"),
276275
},
277276
},
278-
Constraint: solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"),
277+
Constraint: PrettyConstraint(solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"), "subscription to %s requires at least one of catsrc/catsrc-namespace/alpha/a.v1"),
279278
},
280279
{
281280
Installable: &BundleInstallable{
@@ -288,12 +287,11 @@ func SharedResolverSpecs() []resolverTest {
288287
Installable: &SubscriptionInstallable{
289288
identifier: "a",
290289
constraints: []solver.Constraint{
291-
solver.Mandatory(),
292-
solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"),
293-
solver.AtMost(1, "catsrc/catsrc-namespace/alpha/a.v1"),
290+
ConstraintSubscriptionExists,
291+
PrettyConstraint(solver.Dependency("catsrc/catsrc-namespace/alpha/a.v1"), "subscription to %s requires at least one of catsrc/catsrc-namespace/alpha/a.v1"),
294292
},
295293
},
296-
Constraint: solver.Mandatory(),
294+
Constraint: ConstraintSubscriptionExists,
297295
},
298296
}),
299297
},

0 commit comments

Comments
 (0)