Skip to content

Commit 3085d76

Browse files
committed
handle deprecating branch points (#767)
* do not add skips/replaces entries for deprecated bundles Signed-off-by: Ankita Thomas <[email protected]> * remove overwritten channel head before add in overwrite-latest mode - preserve upgrade graph after add Signed-off-by: Ankita Thomas <[email protected]> * handle branchpoint deprecation Remove deprecated bundles from channels of the deprecated bundles, truncating only when a bundle is not on any channel. Branchpoints (i.e, branches that have two or more different replacements on different channels) are removed from any channel that the deprecated bundle was on, while everything till the first branch point is truncated Signed-off-by: Ankita Thomas <[email protected]> * fix unit tests, add function documentation Signed-off-by: Ankita Thomas <[email protected]> * remove deprecated channel heads on package delete, simplify populator signature Signed-off-by: Ankita Thomas <[email protected]> * optional interface for RemoveOverwrittenChannelHead Signed-off-by: Ankita Thomas <[email protected]> * comment changes Signed-off-by: Ankita Thomas <[email protected]> Upstream-repository: operator-registry Upstream-commit: 38994ece779c79a6b238cff667810892c6df0d4e
1 parent dde7292 commit 3085d76

File tree

16 files changed

+2048
-721
lines changed

16 files changed

+2048
-721
lines changed

staging/operator-registry/alpha/action/render_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,7 +792,7 @@ func generateSqliteFile(path string, imageMap map[image.Reference]string) error
792792
return err
793793
}
794794

795-
populator := registry.NewDirectoryPopulator(loader, graphLoader, dbQuerier, imageMap, nil, false)
795+
populator := registry.NewDirectoryPopulator(loader, graphLoader, dbQuerier, imageMap, nil)
796796
if err := populator.Populate(registry.ReplacesMode); err != nil {
797797
return err
798798
}

staging/operator-registry/pkg/lib/registry/registry.go

Lines changed: 95 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -128,23 +128,23 @@ func unpackImage(ctx context.Context, reg image.Registry, ref image.Reference) (
128128

129129
func populate(ctx context.Context, loader registry.Load, graphLoader registry.GraphLoader, querier registry.Query, reg image.Registry, refs []image.Reference, mode registry.Mode, overwrite bool) error {
130130
unpackedImageMap := make(map[image.Reference]string, 0)
131+
overwrittenBundles := map[string][]string{}
132+
var imagesToAdd []*registry.Bundle
131133
for _, ref := range refs {
132134
to, from, cleanup, err := unpackImage(ctx, reg, ref)
133135
if err != nil {
134136
return err
135137
}
136138
unpackedImageMap[to] = from
137139
defer cleanup()
138-
}
139140

140-
overwriteImageMap := make(map[string]map[image.Reference]string, 0)
141-
if overwrite {
142-
// find all bundles that are attempting to overwrite
143-
for to, from := range unpackedImageMap {
144-
img, err := registry.NewImageInput(to, from)
145-
if err != nil {
146-
return err
147-
}
141+
img, err := registry.NewImageInput(to, from)
142+
if err != nil {
143+
return err
144+
}
145+
imagesToAdd = append(imagesToAdd, img.Bundle)
146+
147+
if overwrite {
148148
overwritten, err := querier.GetBundlePathIfExists(ctx, img.Bundle.Name)
149149
if err != nil {
150150
if err == registry.ErrBundleImageNotInDatabase {
@@ -155,57 +155,24 @@ func populate(ctx context.Context, loader registry.Load, graphLoader registry.Gr
155155
if overwritten == "" {
156156
return fmt.Errorf("index add --overwrite-latest is only supported when using bundle images")
157157
}
158-
// get all bundle paths for that package - we will re-add these to regenerate the graph
159-
bundles, err := querier.GetBundlesForPackage(ctx, img.Bundle.Package)
160-
if err != nil {
161-
return err
162-
}
163-
type unpackedImage struct {
164-
to image.Reference
165-
from string
166-
cleanup func()
167-
err error
168-
}
169-
unpacked := make(chan unpackedImage)
170-
for bundle := range bundles {
171-
// parallelize image pulls
172-
go func(bundle registry.BundleKey, img *registry.ImageInput) {
173-
if bundle.CsvName != img.Bundle.Name {
174-
to, from, cleanup, err := unpackImage(ctx, reg, image.SimpleReference(bundle.BundlePath))
175-
unpacked <- unpackedImage{to: to, from: from, cleanup: cleanup, err: err}
176-
} else {
177-
unpacked <- unpackedImage{to: to, from: from, cleanup: func() { return }, err: nil}
178-
}
179-
}(bundle, img)
180-
}
181-
if _, ok := overwriteImageMap[img.Bundle.Package]; !ok {
182-
overwriteImageMap[img.Bundle.Package] = make(map[image.Reference]string, 0)
183-
}
184-
for i := 0; i < len(bundles); i++ {
185-
unpack := <-unpacked
186-
if unpack.err != nil {
187-
return unpack.err
188-
}
189-
overwriteImageMap[img.Bundle.Package][unpack.to] = unpack.from
190-
if _, ok := unpackedImageMap[unpack.to]; ok {
191-
delete(unpackedImageMap, unpack.to)
192-
}
193-
defer unpack.cleanup()
194-
}
158+
overwrittenBundles[img.Bundle.Package] = append(overwrittenBundles[img.Bundle.Package], img.Bundle.Name)
195159
}
196160
}
197161

198-
populator := registry.NewDirectoryPopulator(loader, graphLoader, querier, unpackedImageMap, overwriteImageMap, overwrite)
199-
if err := populator.Populate(mode); err != nil {
162+
expectedBundles, err := expectedGraphBundles(imagesToAdd, graphLoader, overwrite)
163+
if err != nil {
164+
200165
return err
166+
201167
}
168+
populator := registry.NewDirectoryPopulator(loader, graphLoader, querier, unpackedImageMap, overwrittenBundles)
169+
170+
if err := populator.Populate(mode); err != nil {
171+
172+
return err
202173

203-
for _, imgMap := range overwriteImageMap {
204-
for to, from := range imgMap {
205-
unpackedImageMap[to] = from
206-
}
207174
}
208-
return checkForBundles(ctx, querier.(*sqlite.SQLQuerier), graphLoader, unpackedImageMap)
175+
return checkForBundles(ctx, querier.(*sqlite.SQLQuerier), graphLoader, expectedBundles)
209176
}
210177

211178
type DeleteFromRegistryRequest struct {
@@ -291,7 +258,7 @@ func (r RegistryUpdater) PruneFromRegistry(request PruneFromRegistryRequest) err
291258
}
292259
defer db.Close()
293260

294-
dbLoader, err := sqlite.NewSQLLiteLoader(db)
261+
dbLoader, err := sqlite.NewDeprecationAwareLoader(db)
295262
if err != nil {
296263
return err
297264
}
@@ -344,7 +311,7 @@ func (r RegistryUpdater) DeprecateFromRegistry(request DeprecateFromRegistryRequ
344311
}
345312
defer db.Close()
346313

347-
dbLoader, err := sqlite.NewSQLLiteLoader(db)
314+
dbLoader, err := sqlite.NewDeprecationAwareLoader(db)
348315
if err != nil {
349316
return err
350317
}
@@ -430,55 +397,10 @@ func checkForBundlePaths(querier registry.GRPCQuery, bundlePaths []string) ([]st
430397
return found, missing, nil
431398
}
432399

433-
// packagesFromUnpackedRefs creates packages from a set of unpacked ref dirs without their upgrade edges.
434-
func packagesFromUnpackedRefs(bundles map[image.Reference]string) (map[string]registry.Package, error) {
435-
graph := map[string]registry.Package{}
436-
for to, from := range bundles {
437-
b, err := registry.NewImageInput(to, from)
438-
if err != nil {
439-
return nil, fmt.Errorf("failed to parse unpacked bundle image %s: %v", to, err)
440-
}
441-
v, err := b.Bundle.Version()
442-
if err != nil {
443-
return nil, fmt.Errorf("failed to parse version for %s (%s): %v", b.Bundle.Name, b.Bundle.BundleImage, err)
444-
}
445-
key := registry.BundleKey{
446-
CsvName: b.Bundle.Name,
447-
Version: v,
448-
BundlePath: b.Bundle.BundleImage,
449-
}
450-
if _, ok := graph[b.Bundle.Package]; !ok {
451-
graph[b.Bundle.Package] = registry.Package{
452-
Name: b.Bundle.Package,
453-
Channels: map[string]registry.Channel{},
454-
}
455-
}
456-
for _, c := range b.Bundle.Channels {
457-
if _, ok := graph[b.Bundle.Package].Channels[c]; !ok {
458-
graph[b.Bundle.Package].Channels[c] = registry.Channel{
459-
Nodes: map[registry.BundleKey]map[registry.BundleKey]struct{}{},
460-
}
461-
}
462-
graph[b.Bundle.Package].Channels[c].Nodes[key] = nil
463-
}
464-
}
465-
466-
return graph, nil
467-
}
468-
469400
// replaces mode selects highest version as channel head and
470401
// prunes any bundles in the upgrade chain after the channel head.
471402
// check for the presence of all bundles after a replaces-mode add.
472-
func checkForBundles(ctx context.Context, q *sqlite.SQLQuerier, g registry.GraphLoader, bundles map[image.Reference]string) error {
473-
if len(bundles) == 0 {
474-
return nil
475-
}
476-
477-
required, err := packagesFromUnpackedRefs(bundles)
478-
if err != nil {
479-
return err
480-
}
481-
403+
func checkForBundles(ctx context.Context, q *sqlite.SQLQuerier, g registry.GraphLoader, required map[string]*registry.Package) error {
482404
var errs []error
483405
for _, pkg := range required {
484406
graph, err := g.Generate(pkg.Name)
@@ -523,3 +445,75 @@ func isDeprecated(ctx context.Context, q *sqlite.SQLQuerier, bundle registry.Bun
523445
}
524446
return false, nil
525447
}
448+
449+
// expectedGraphBundles returns a set of package-channel-bundle tuples that MUST be present following an add.
450+
/* opm index add drops bundles that replace a channel head, and since channel head selection heuristics
451+
* choose the bundle with the greatest semver as the channel head, any bundle that replaces such a bundle
452+
* will be dropped from the graph following an add.
453+
* eg: 1.0.1 <- 1.0.1-new
454+
*
455+
* 1.0.1-new replaces 1.0.1 but will not be chosen as the channel head because of its non-empty pre-release version.
456+
* expectedGraphBundles gives a set of bundles (old bundles from the graphLoader and the newly added set of bundles from
457+
* imagesToAdd) that must be present following an add to ensure no bundle is dropped.
458+
*
459+
* Overwritten bundles will only be verified on the channels of the newly added version.
460+
* Any inherited channels due to addition of a new bundle on its tail bundles may not be verified
461+
* eg: 1.0.1 (alpha) <-[1.0.2 (alpha, stable)]
462+
* When 1.0.2 in alpha and stable channels is added replacing 1.0.1, 1.0.1's presence will only be marked as expected on
463+
* the alpha channel, not on the inherited stable channel.
464+
*/
465+
func expectedGraphBundles(imagesToAdd []*registry.Bundle, graphLoader registry.GraphLoader, overwrite bool) (map[string]*registry.Package, error) {
466+
expectedBundles := map[string]*registry.Package{}
467+
for _, bundle := range imagesToAdd {
468+
version, err := bundle.Version()
469+
if err != nil {
470+
return nil, err
471+
}
472+
newBundleKey := registry.BundleKey{
473+
BundlePath: bundle.BundleImage,
474+
Version: version,
475+
CsvName: bundle.Name,
476+
}
477+
var pkg *registry.Package
478+
var ok bool
479+
if pkg, ok = expectedBundles[bundle.Package]; !ok {
480+
var err error
481+
if pkg, err = graphLoader.Generate(bundle.Package); err != nil {
482+
if err != registry.ErrPackageNotInDatabase {
483+
return nil, err
484+
}
485+
pkg = &registry.Package{
486+
Name: bundle.Package,
487+
Channels: map[string]registry.Channel{},
488+
}
489+
}
490+
}
491+
for c, channelEntries := range pkg.Channels {
492+
for oldBundle := range channelEntries.Nodes {
493+
if oldBundle.CsvName == bundle.Name {
494+
if overwrite {
495+
delete(pkg.Channels[c].Nodes, oldBundle)
496+
if len(pkg.Channels[c].Nodes) == 0 {
497+
delete(pkg.Channels, c)
498+
}
499+
} else {
500+
return nil, registry.BundleImageAlreadyAddedErr{ErrorString: fmt.Sprintf("Bundle %s already exists", bundle.BundleImage)}
501+
}
502+
}
503+
}
504+
}
505+
for _, c := range bundle.Channels {
506+
if _, ok := pkg.Channels[c]; !ok {
507+
pkg.Channels[c] = registry.Channel{
508+
Nodes: map[registry.BundleKey]map[registry.BundleKey]struct{}{},
509+
}
510+
}
511+
// This can miss out on some channels, when a new bundle has channels that the one it replaces does not.
512+
// eg: When bundle A in channel A replaces bundle B in channel B is added, bundle B is also added to channel A
513+
// but it is only expected to be in channel B, presence in channel A will be ignored
514+
pkg.Channels[c].Nodes[newBundleKey] = nil
515+
}
516+
expectedBundles[bundle.Package] = pkg
517+
}
518+
return expectedBundles, nil
519+
}

0 commit comments

Comments
 (0)