Skip to content

Commit 454ee08

Browse files
Merge pull request #1689 from ecordell/requiredapi-downgrade-allpermuations
[release-4.5] Bug 1862407: fix(resolver): be smarter about the way downgrades are attempted
2 parents c83c984 + a58a338 commit 454ee08

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

pkg/controller/registry/resolver/evolver.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type Evolver interface {
1717
type NamespaceGenerationEvolver struct {
1818
querier SourceQuerier
1919
gen Generation
20+
replacements map[OperatorSurface]OperatorSurface
2021
}
2122

2223
func NewNamespaceGenerationEvolver(querier SourceQuerier, gen Generation) Evolver {
@@ -44,13 +45,20 @@ func (e *NamespaceGenerationEvolver) Evolve(add map[OperatorSourceInfo]struct{})
4445
return err
4546
}
4647

48+
// if apis are missing, attempt to contract back to a good set by trying all combinations of rollbacks to updates
49+
if err := e.downgradeUpdates(); err != nil {
50+
return err
51+
}
52+
4753
// for any remaining missing APIs, attempt to downgrade the operator that required them
4854
// this may contract the generation back to the original set!
4955
e.downgradeAPIs()
5056
return nil
5157
}
5258

5359
func (e *NamespaceGenerationEvolver) checkForUpdates() error {
60+
e.replacements = make(map[OperatorSurface]OperatorSurface)
61+
5462
// maps the old operator identifier to the new operator
5563
updates := EmptyOperatorSet()
5664

@@ -74,6 +82,7 @@ func (e *NamespaceGenerationEvolver) checkForUpdates() error {
7482
}
7583
o.SetReplaces(op.Identifier())
7684
updates[op.Identifier()] = o
85+
e.replacements[op] = o
7786
}
7887

7988
// remove any operators we found updates for
@@ -153,7 +162,68 @@ func (e *NamespaceGenerationEvolver) queryForRequiredAPIs() error {
153162
return nil
154163
}
155164

165+
func (e *NamespaceGenerationEvolver) downgradeUpdates() error {
166+
// no need to attempt downgrades
167+
if len(e.gen.MissingAPIs()) == 0 {
168+
return nil
169+
}
170+
171+
// smart downgrades are only supported if fewer than 64 updates are resolved at the same time
172+
// (this should be all but pathological cases)
173+
if len(e.replacements) > 64 {
174+
return nil
175+
}
176+
177+
old := make([]OperatorSurface, 0)
178+
new := make([]OperatorSurface, 0)
179+
for o, n := range e.replacements {
180+
old = append(old, o)
181+
new = append(new, n)
182+
}
183+
flagToIndex := make(map[uint64]int)
184+
flags := make([]uint64, 0)
185+
var max uint64
186+
for i := 0; i < len(e.replacements); i++ {
187+
var f uint64 = 1 << i
188+
flags = append(flags, f)
189+
max += f
190+
flagToIndex[f] = i
191+
}
192+
193+
var i uint64
194+
var g Generation
195+
for i = 0; i <= max; i++ {
196+
g = NewEmptyGeneration()
197+
for _, f := range flags {
198+
idx := flagToIndex[f]
199+
// if toggled, pick old
200+
if f&i != 0 {
201+
_ = g.AddOperator(old[idx])
202+
} else {
203+
_ = g.AddOperator(new[idx])
204+
}
205+
}
206+
// we found a good set, update the real generation and quit
207+
if len(g.MissingAPIs()) == 0 {
208+
for _, f := range flags {
209+
idx := flagToIndex[f]
210+
// if toggled, remove new and add old
211+
if f&i != 0 {
212+
e.gen.RemoveOperator(new[idx])
213+
if err := e.gen.AddOperator(old[idx]); err != nil {
214+
return err
215+
}
216+
}
217+
}
218+
return nil
219+
}
220+
}
221+
222+
return nil
223+
}
224+
156225
func (e *NamespaceGenerationEvolver) downgradeAPIs() {
226+
// remove anything we can't satisfy after trying to downgrade all possible combinations
157227
e.gen.ResetUnchecked()
158228
for missingAPIs := e.gen.MissingAPIs(); len(missingAPIs) > 0; {
159229
requirers := missingAPIs.PopAPIRequirers()

pkg/controller/registry/resolver/evolver_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,28 @@ func TestNamespaceGenerationEvolver(t *testing.T) {
451451
NewFakeOperatorSurface("depender.v1", "depender", "channel", "", "catsrc", "", nil,[]opregistry.APIKey{{"g", "v", "k", "ks"}}, nil, nil),
452452
),
453453
},
454+
{
455+
name: "NoProviderInUpdateSet",
456+
fields: fields{
457+
querier: NewFakeSourceQuerier(map[CatalogKey][]*api.Bundle{
458+
CatalogKey{"catsrc", "catsrc-namespace"}: {
459+
bundle("original", "o", "c", "", APISet{opregistry.APIKey{"g", "v", "k", "ks"}: {}}, EmptyAPISet(), EmptyAPISet(), EmptyAPISet()),
460+
bundle("original.v2", "o", "c", "original", EmptyAPISet(), EmptyAPISet(), EmptyAPISet(), EmptyAPISet()),
461+
bundle("depender", "depender", "channel", "", EmptyAPISet(), APISet{opregistry.APIKey{"g", "v", "k", "ks"}: {}}, EmptyAPISet(), EmptyAPISet()),
462+
bundle("depender.v2", "depender", "channel", "depender", EmptyAPISet(), APISet{opregistry.APIKey{"g", "v", "k", "ks"}: {}}, EmptyAPISet(), EmptyAPISet()),
463+
},
464+
}),
465+
gen: NewGenerationFromOperators(
466+
NewFakeOperatorSurface("original", "o", "c", "", "catsrc", "", []opregistry.APIKey{{"g", "v", "k", "ks"}}, nil, nil, nil),
467+
NewFakeOperatorSurface("depender", "depender", "channel", "", "catsrc", "", nil, []opregistry.APIKey{{"g", "v", "k", "ks"}}, nil, nil),
468+
),
469+
},
470+
args: args{},
471+
wantGen: NewGenerationFromOperators(
472+
NewFakeOperatorSurface("original", "o", "c", "", "catsrc", "", []opregistry.APIKey{{"g", "v", "k", "ks"}},nil, nil, nil),
473+
NewFakeOperatorSurface("depender.v2", "depender", "channel", "depender", "catsrc", "", nil, []opregistry.APIKey{{"g", "v", "k", "ks"}}, nil, nil),
474+
),
475+
},
454476
}
455477
for _, tt := range tests {
456478
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)