@@ -128,23 +128,23 @@ func unpackImage(ctx context.Context, reg image.Registry, ref image.Reference) (
128128
129129func 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
211178type 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