@@ -6,13 +6,13 @@ import (
6
6
"io"
7
7
"sort"
8
8
9
- "github.com/blang/semver/v4"
10
9
"github.com/operator-framework/operator-registry/alpha/action"
11
10
"github.com/operator-framework/operator-registry/alpha/declcfg"
12
11
"github.com/operator-framework/operator-registry/alpha/property"
12
+
13
+ "github.com/blang/semver/v4"
13
14
"k8s.io/apimachinery/pkg/util/errors"
14
- "k8s.io/apimachinery/pkg/util/sets"
15
- "k8s.io/apimachinery/pkg/util/yaml"
15
+ "sigs.k8s.io/yaml"
16
16
)
17
17
18
18
func (t Template ) Render (ctx context.Context ) (* declcfg.DeclarativeConfig , error ) {
@@ -223,7 +223,6 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
223
223
hwc := highwaterChannel {archetype : archetypesByPriority [0 ], version : semver.Version {Major : 0 , Minor : 0 }}
224
224
225
225
unlinkedChannels := make (map [string ]* declcfg.Channel )
226
- unassociatedEdges := []entryTuple {}
227
226
228
227
for _ , archetype := range archetypesByPriority {
229
228
bundles := (* semverChannels )[archetype ]
@@ -271,88 +270,84 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
271
270
}
272
271
}
273
272
ch .Entries = append (ch .Entries , declcfg.ChannelEntry {Name : bundleName })
274
- unassociatedEdges = append (unassociatedEdges , entryTuple {arch : archetype , kind : cKey , parent : cName , name : bundleName , version : bundles [bundleName ], index : len (ch .Entries ) - 1 })
275
273
}
276
274
}
277
275
}
278
276
279
277
// save off the name of the high-water-mark channel for the default for this package
280
278
sv .defaultChannel = hwc .name
281
279
282
- outChannels = append (outChannels , sv .linkChannels (unlinkedChannels , unassociatedEdges )... )
280
+ outChannels = append (outChannels , sv .linkChannels (unlinkedChannels , semverChannels )... )
283
281
284
282
return outChannels
285
283
}
286
284
287
- func (sv * semverTemplate ) linkChannels (unlinkedChannels map [string ]* declcfg.Channel , entries [] entryTuple ) []declcfg.Channel {
285
+ func (sv * semverTemplate ) linkChannels (unlinkedChannels map [string ]* declcfg.Channel , harvestedVersions * bundleVersions ) []declcfg.Channel {
288
286
channels := []declcfg.Channel {}
289
287
290
- // sort to force partitioning by archetype --> kind --> semver
291
- sort .Slice (entries , func (i , j int ) bool {
292
- if channelPriorities [entries [i ].arch ] != channelPriorities [entries [j ].arch ] {
293
- return channelPriorities [entries [i ].arch ] < channelPriorities [entries [j ].arch ]
294
- }
295
- if streamTypePriorities [entries [i ].kind ] != streamTypePriorities [entries [j ].kind ] {
296
- return streamTypePriorities [entries [i ].kind ] < streamTypePriorities [entries [j ].kind ]
297
- }
298
- return entries [i ].version .LT (entries [j ].version )
299
- })
300
-
301
- prevZMax := ""
302
- var curSkips sets.String = sets .NewString ()
303
-
304
- for index := 1 ; index < len (entries ); index ++ {
305
- prevTuple := entries [index - 1 ]
306
- curTuple := entries [index ]
307
- prevX := getMajorVersion (prevTuple .version )
308
- prevY := getMinorVersion (prevTuple .version )
309
- curX := getMajorVersion (curTuple .version )
310
- curY := getMinorVersion (curTuple .version )
311
-
312
- archChange := curTuple .arch != prevTuple .arch
313
- kindChange := curTuple .kind != prevTuple .kind
314
- xChange := ! prevX .EQ (curX )
315
- yChange := ! prevY .EQ (curY )
316
-
317
- if archChange || kindChange || xChange || yChange {
318
- // if we passed any kind of change besides Z, then we need to set skips/replaces for previous max-Z
319
- prevChannel := unlinkedChannels [prevTuple .parent ]
320
- finalEntry := & prevChannel .Entries [prevTuple .index ]
321
- finalEntry .Replaces = prevZMax
322
- // don't include replaces in skips list, but they are accumulated in discrete cycles (and maybe useful for later channels) so remove here
323
- if curSkips .Has (finalEntry .Replaces ) {
324
- finalEntry .Skips = curSkips .Difference (sets .NewString (finalEntry .Replaces )).List ()
325
- } else {
326
- finalEntry .Skips = curSkips .List ()
327
- }
328
- }
329
-
330
- if archChange || kindChange || xChange {
331
- // we don't maintain skips/replaces over these transitions
332
- curSkips = sets .NewString ()
333
- prevZMax = ""
334
- } else {
335
- if yChange {
336
- prevZMax = prevTuple .name
288
+ // bundle --> version lookup
289
+ bundleVersions := make (map [string ]semver.Version )
290
+ for _ , vs := range * harvestedVersions {
291
+ for b , v := range vs {
292
+ if _ , ok := bundleVersions [b ]; ! ok {
293
+ bundleVersions [b ] = v
337
294
}
338
- curSkips .Insert (prevTuple .name )
339
295
}
340
296
}
341
297
342
- // last entry accumulation
343
- lastTuple := entries [len (entries )- 1 ]
344
- prevChannel := unlinkedChannels [lastTuple .parent ]
345
- finalEntry := & prevChannel .Entries [lastTuple .index ]
346
- finalEntry .Replaces = prevZMax
347
- // don't include replaces in skips list, but they are accumulated in discrete cycles (and maybe useful for later channels) so remove here
348
- if curSkips .Has (finalEntry .Replaces ) {
349
- finalEntry .Skips = curSkips .Difference (sets .NewString (finalEntry .Replaces )).List ()
350
- } else {
351
- finalEntry .Skips = curSkips .List ()
352
- }
298
+ for _ , channel := range unlinkedChannels {
299
+ entries := & channel .Entries
300
+ sort .Slice (* entries , func (i , j int ) bool {
301
+ return bundleVersions [(* entries )[i ].Name ].LT (bundleVersions [(* entries )[j ].Name ])
302
+ })
353
303
354
- for _ , ch := range unlinkedChannels {
355
- channels = append (channels , * ch )
304
+ // "inchworm" through the sorted entries, iterating curEdge but extending yProbe to the next Y-transition
305
+ // then catch up curEdge to yProbe as 'skips', and repeat until we reach the end of the entries
306
+ // finally, because the inchworm will always fail to pick up the last Y-transition, we test for it and link it up as a 'replaces'
307
+ curEdge , yProbe := 0 , 0
308
+ zmaxQueue := ""
309
+ entryCount := len (* entries )
310
+
311
+ for curEdge < entryCount {
312
+ for yProbe < entryCount {
313
+ curVersion := bundleVersions [(* entries )[curEdge ].Name ]
314
+ yProbeVersion := bundleVersions [(* entries )[yProbe ].Name ]
315
+ if getMinorVersion (yProbeVersion ).EQ (getMinorVersion (curVersion )) {
316
+ yProbe += 1
317
+ } else {
318
+ break
319
+ }
320
+ }
321
+ // if yProbe crossed a threshold, the previous entry is the last of the previous Y-stream
322
+ preChangeIndex := yProbe - 1
323
+
324
+ if curEdge != yProbe {
325
+ if zmaxQueue != "" {
326
+ // add skips edge to allow skipping over y iterations within an x stream
327
+ (* entries )[preChangeIndex ].Skips = append ((* entries )[preChangeIndex ].Skips , zmaxQueue )
328
+ (* entries )[preChangeIndex ].Replaces = zmaxQueue
329
+ }
330
+ zmaxQueue = (* entries )[preChangeIndex ].Name
331
+ }
332
+ for curEdge < preChangeIndex {
333
+ // add skips edges to y-1 from z < y
334
+ (* entries )[preChangeIndex ].Skips = append ((* entries )[preChangeIndex ].Skips , (* entries )[curEdge ].Name )
335
+ curEdge += 1
336
+ }
337
+ curEdge += 1
338
+ yProbe = curEdge + 1
339
+ }
340
+ // since probe will always fail to pick up a y-change in the last item, test for it
341
+ if entryCount > 1 {
342
+ penultimateEntry := & (* entries )[len (* entries )- 2 ]
343
+ ultimateEntry := & (* entries )[len (* entries )- 1 ]
344
+ penultimateVersion := bundleVersions [penultimateEntry .Name ]
345
+ ultimateVersion := bundleVersions [ultimateEntry .Name ]
346
+ if ultimateVersion .Minor != penultimateVersion .Minor {
347
+ ultimateEntry .Replaces = penultimateEntry .Name
348
+ }
349
+ }
350
+ channels = append (channels , * channel )
356
351
}
357
352
358
353
return channels
0 commit comments