Skip to content

Commit 1913f58

Browse files
committed
Semver lastedge error (#1169)
* when the last edge in a bundle set is +Y the existing code does not clear the skips list and crosses Y thresholds counter to design intent missing proper last-max-z detection for replaces working at long last * re-add cumulative skips over y changes in an x stream so folks can always skip directly to channel head Signed-off-by: Jordan Keister <[email protected]> --------- Signed-off-by: Jordan Keister <[email protected]> Upstream-repository: operator-registry Upstream-commit: 2096c4f5d0d2cb3ed31767d406afe10786565f01
1 parent c84b6ad commit 1913f58

File tree

5 files changed

+295
-199
lines changed

5 files changed

+295
-199
lines changed

staging/operator-registry/alpha/template/semver/semver.go

Lines changed: 63 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
"io"
77
"sort"
88

9-
"github.com/blang/semver/v4"
109
"github.com/operator-framework/operator-registry/alpha/action"
1110
"github.com/operator-framework/operator-registry/alpha/declcfg"
1211
"github.com/operator-framework/operator-registry/alpha/property"
12+
13+
"github.com/blang/semver/v4"
1314
"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"
1616
)
1717

1818
func (t Template) Render(ctx context.Context) (*declcfg.DeclarativeConfig, error) {
@@ -223,7 +223,6 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
223223
hwc := highwaterChannel{archetype: archetypesByPriority[0], version: semver.Version{Major: 0, Minor: 0}}
224224

225225
unlinkedChannels := make(map[string]*declcfg.Channel)
226-
unassociatedEdges := []entryTuple{}
227226

228227
for _, archetype := range archetypesByPriority {
229228
bundles := (*semverChannels)[archetype]
@@ -271,88 +270,84 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
271270
}
272271
}
273272
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})
275273
}
276274
}
277275
}
278276

279277
// save off the name of the high-water-mark channel for the default for this package
280278
sv.defaultChannel = hwc.name
281279

282-
outChannels = append(outChannels, sv.linkChannels(unlinkedChannels, unassociatedEdges)...)
280+
outChannels = append(outChannels, sv.linkChannels(unlinkedChannels, semverChannels)...)
283281

284282
return outChannels
285283
}
286284

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 {
288286
channels := []declcfg.Channel{}
289287

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
337294
}
338-
curSkips.Insert(prevTuple.name)
339295
}
340296
}
341297

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+
})
353303

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)
356351
}
357352

358353
return channels

0 commit comments

Comments
 (0)