1
1
package declcfg
2
2
3
3
import (
4
+ "fmt"
4
5
"reflect"
5
6
"sort"
6
7
"sync"
@@ -167,7 +168,7 @@ func bundlesEqual(b1, b2 *model.Bundle) (bool, error) {
167
168
168
169
func addAllDependencies (newModel , oldModel , outputModel model.Model ) error {
169
170
// Get every oldModel's bundle's dependencies, and their dependencies, etc. by BFS.
170
- allProvidingBundles := []* model.Bundle {}
171
+ providingBundlesByPackage := map [ string ] []* model.Bundle {}
171
172
for curr := getBundles (outputModel ); len (curr ) != 0 ; {
172
173
reqGVKs , reqPkgs , err := findDependencies (curr )
173
174
if err != nil {
@@ -183,36 +184,85 @@ func addAllDependencies(newModel, oldModel, outputModel model.Model) error {
183
184
for _ , pkg := range newModel {
184
185
providingBundles := getBundlesThatProvide (pkg , reqGVKs , reqPkgs )
185
186
curr = append (curr , providingBundles ... )
186
- allProvidingBundles = append (allProvidingBundles , providingBundles ... )
187
+
188
+ oldPkg , oldHasPkg := oldModel [pkg .Name ]
189
+ for _ , b := range providingBundles {
190
+ // If the bundle is not in oldModel, add it to the set.
191
+ // outputModel is checked below.
192
+ add := true
193
+ if oldHasPkg {
194
+ if oldCh , oldHasCh := oldPkg .Channels [b .Channel .Name ]; oldHasCh {
195
+ _ , oldHasBundle := oldCh .Bundles [b .Name ]
196
+ add = ! oldHasBundle
197
+ }
198
+ }
199
+ if add {
200
+ providingBundlesByPackage [b .Package .Name ] = append (providingBundlesByPackage [b .Package .Name ], b )
201
+ }
202
+ }
187
203
}
188
204
}
189
205
190
206
// Add the diff between an oldModel dependency package and its new counterpart
191
207
// or the entire package if oldModel does not have it.
192
- //
193
- // TODO(estroz): add bundles then fill in dependency upgrade graph
194
- // by selecting latest versions, as the EP specifies.
195
- dependencyPkgs := map [string ]* model.Package {}
196
- for _ , b := range allProvidingBundles {
197
- if _ , copied := dependencyPkgs [b .Package .Name ]; ! copied {
198
- dependencyPkgs [b .Package .Name ] = copyPackage (b .Package )
199
- }
200
- }
201
- for _ , newDepPkg := range dependencyPkgs {
202
- // newDepPkg is a copy of a newModel pkg, so running diffPackages
203
- // on it and oldPkg, which may have some but not all bundles,
204
- // will produce a set of all bundles that outputModel doesn't have.
205
- // Otherwise, just add the whole package.
206
- if oldPkg , oldHasPkg := oldModel [newDepPkg .Name ]; oldHasPkg {
207
- if err := diffPackages (oldPkg , newDepPkg ); err != nil {
208
+ for pkgName , bundles := range providingBundlesByPackage {
209
+ newPkg := newModel [pkgName ]
210
+ heads := make (map [string ]* model.Bundle , len (newPkg .Channels ))
211
+ for _ , ch := range newPkg .Channels {
212
+ var err error
213
+ if heads [ch .Name ], err = ch .Head (); err != nil {
208
214
return err
209
215
}
210
- if len (newDepPkg .Channels ) == 0 {
211
- // Skip empty packages.
212
- continue
216
+ }
217
+
218
+ // Sort by version then channel so bundles lower in the full graph are more likely
219
+ // to be included in previous loops.
220
+ sort .Slice (bundles , func (i , j int ) bool {
221
+ if bundles [i ].Channel .Name == bundles [j ].Channel .Name {
222
+ return bundles [i ].Version .LT (bundles [j ].Version )
223
+ }
224
+ return bundles [i ].Channel .Name < bundles [j ].Channel .Name
225
+ })
226
+
227
+ for _ , b := range bundles {
228
+ newCh := b .Channel
229
+
230
+ // Continue if b was added in a previous loop iteration.
231
+ // Otherwise create a new package/channel for b if they do not exist.
232
+ var (
233
+ outputPkg * model.Package
234
+ outputCh * model.Channel
235
+
236
+ outHasPkg , outHasCh bool
237
+ )
238
+ if outputPkg , outHasPkg = outputModel [b .Package .Name ]; outHasPkg {
239
+ if outputCh , outHasCh = outputPkg .Channels [b .Channel .Name ]; outHasCh {
240
+ if _ , outputHasBundle := outputCh .Bundles [b .Name ]; outputHasBundle {
241
+ continue
242
+ }
243
+ }
244
+ } else {
245
+ outputPkg = copyPackageNoChannels (newPkg )
246
+ outputModel [outputPkg .Name ] = outputPkg
247
+ }
248
+ if ! outHasCh {
249
+ outputCh = copyChannelNoBundles (newCh , outputPkg )
250
+ outputPkg .Channels [outputCh .Name ] = outputCh
251
+ }
252
+
253
+ head := heads [newCh .Name ]
254
+ graph := makeUpgradeGraph (newCh )
255
+ intersectingBundles , intersectionFound := findIntersectingBundles (newCh , b , head , graph )
256
+ if ! intersectionFound {
257
+ // This should never happen, since b and head are from the same model.
258
+ return fmt .Errorf ("channel %s: head %q not reachable from bundle %q" , newCh .Name , head .Name , b .Name )
259
+ }
260
+ for _ , ib := range intersectingBundles {
261
+ if _ , outHasBundle := outputCh .Bundles [ib .Name ]; ! outHasBundle {
262
+ outputCh .Bundles [ib .Name ] = copyBundle (ib , outputCh , outputPkg )
263
+ }
213
264
}
214
265
}
215
- outputModel [newDepPkg .Name ] = newDepPkg
216
266
}
217
267
218
268
return nil
0 commit comments