Skip to content

Commit

Permalink
missing proper last-max-z detection for replaces
Browse files Browse the repository at this point in the history
  • Loading branch information
grokspawn committed Feb 22, 2024
1 parent 0ce6cde commit 57e4340
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 79 deletions.
133 changes: 65 additions & 68 deletions alpha/template/semver/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import (
"context"
"fmt"
"io"
"slices"
"sort"

"github.com/blang/semver/v4"
"k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"sigs.k8s.io/yaml"

"github.com/operator-framework/operator-registry/alpha/action"
"github.com/operator-framework/operator-registry/alpha/declcfg"
"github.com/operator-framework/operator-registry/alpha/property"

"github.com/blang/semver/v4"
"k8s.io/apimachinery/pkg/util/errors"
"sigs.k8s.io/yaml"
)

func (t Template) Render(ctx context.Context) (*declcfg.DeclarativeConfig, error) {
Expand Down Expand Up @@ -288,77 +288,74 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
func (sv *semverTemplate) linkChannels(unlinkedChannels map[string]*declcfg.Channel, entries []entryTuple) []declcfg.Channel {
channels := []declcfg.Channel{}

// sort to force partitioning by archetype --> kind --> semver
sort.Slice(entries, func(i, j int) bool {
if channelPriorities[entries[i].arch] != channelPriorities[entries[j].arch] {
return channelPriorities[entries[i].arch] < channelPriorities[entries[j].arch]
parentChannels := []string{}
channelVersions := make(map[string][]semver.Version)

for e := range entries {
if _, ok := channelVersions[entries[e].parent]; !ok {
channelVersions[entries[e].parent] = []semver.Version{}
} else {
channelVersions[entries[e].parent] = append(channelVersions[entries[e].parent], entries[e].version)
}
if streamTypePriorities[entries[i].kind] != streamTypePriorities[entries[j].kind] {
return streamTypePriorities[entries[i].kind] < streamTypePriorities[entries[j].kind]
if !slices.Contains(parentChannels, entries[e].parent) {
parentChannels = append(parentChannels, entries[e].parent)
}
return entries[i].version.LT(entries[j].version)
})

prevZMax := ""
var curSkips sets.String = sets.NewString()

for index := 1; index < len(entries); index++ {
prevTuple := entries[index-1]
curTuple := entries[index]
prevX := getMajorVersion(prevTuple.version)
prevY := getMinorVersion(prevTuple.version)
curX := getMajorVersion(curTuple.version)
curY := getMinorVersion(curTuple.version)

archChange := curTuple.arch != prevTuple.arch
kindChange := curTuple.kind != prevTuple.kind
xChange := !prevX.EQ(curX)
yChange := !prevY.EQ(curY)

if archChange || kindChange || xChange || yChange {
// if we passed any kind of change besides Z, then we need to set skips/replaces for previous max-Z
prevChannel := unlinkedChannels[prevTuple.parent]
finalEntry := &prevChannel.Entries[prevTuple.index]
finalEntry.Replaces = prevZMax
// don't include replaces in skips list, but they are accumulated in discrete cycles (and maybe useful for later channels) so remove here
if curSkips.Has(finalEntry.Replaces) {
finalEntry.Skips = curSkips.Difference(sets.NewString(finalEntry.Replaces)).List()
} else {
finalEntry.Skips = curSkips.List()
}
sort.Slice(parentChannels, func(i, j int) bool { return parentChannels[i] < parentChannels[j] })
for _, p := range parentChannels {
versions := channelVersions[p]
slices.SortStableFunc(versions, func(a, b semver.Version) int {
if a.LT(b) {
return -1
}
}
if a.GT(b) {
return 1
}
return 0
})

if archChange || kindChange || xChange {
// we don't maintain skips/replaces over these transitions
curSkips = sets.NewString()
prevZMax = ""
} else {
if yChange {
prevZMax = prevTuple.name
// tracking 2 things:
// - the highest Z version before a Y-stream change
// - the beginning of new Y-stream
curEdge, yProbe := 0, 0
zmaxQueue := ""

// tombstones := []semver.Version{}
// t := 1
// for t < len(versions) {
// if getMinorVersion(versions[t]).NE(getMinorVersion(versions[t-1])) {
// tombstones = append(tombstones, versions[t-1])
// }
// }

for yProbe < len(versions) {
for yProbe < len(versions) {
if getMinorVersion(versions[yProbe]).EQ(getMinorVersion(versions[curEdge])) {
yProbe += 1
} else {
break
}
}
if curEdge != yProbe {
if len(zmaxQueue) > 0 {
fmt.Printf(">>> %v -- replaces --> %v\n", unlinkedChannels[p].Entries[yProbe-1].Name, zmaxQueue)
unlinkedChannels[p].Entries[yProbe-1].Replaces = zmaxQueue
zmaxQueue = ""
} else {
fmt.Printf(">>> stashing a max z: %v\n", unlinkedChannels[p].Entries[yProbe-1].Name)
zmaxQueue = unlinkedChannels[p].Entries[yProbe-1].Name
}
}
curSkips.Insert(prevTuple.name)
for curEdge < yProbe {
// add skips edges to y-1 from z < y
unlinkedChannels[p].Entries[yProbe].Skips = append(unlinkedChannels[p].Entries[yProbe].Skips, unlinkedChannels[p].Entries[curEdge].Name)
curEdge += 1
}
yProbe += 2 // seek point for next y change
curEdge += 1
}
}

// last entry accumulation
// for the last entry in
lastTuple := entries[len(entries)-1]
prevTuple := entries[len(entries)-2]
yChange := !getMinorVersion(lastTuple.version).EQ(getMinorVersion(prevTuple.version))
prevChannel := unlinkedChannels[lastTuple.parent]
finalEntry := &prevChannel.Entries[lastTuple.index]

finalEntry.Replaces = prevZMax
if yChange {
curSkips = sets.NewString()
}
// don't include replaces in skips list, but they are accumulated in discrete cycles (and maybe useful for later channels) so remove here
if curSkips.Has(finalEntry.Replaces) {
finalEntry.Skips = curSkips.Difference(sets.NewString(finalEntry.Replaces)).List()
} else {
finalEntry.Skips = curSkips.List()
}

for _, ch := range unlinkedChannels {
channels = append(channels, *ch)
}
Expand Down
22 changes: 11 additions & 11 deletions alpha/template/semver/semver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ func TestLinkChannels(t *testing.T) {
Name: "stable-v1",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: "", Skips: []string{}},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0", Skips: []string{}},
{Name: "a-v1.1.0", Replaces: ""},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0"},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1", Skips: []string{"a-v1.1.0"}},
},
},
Expand Down Expand Up @@ -213,8 +213,8 @@ func TestLinkChannels(t *testing.T) {
Name: "stable-v1",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: "", Skips: []string{}},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0", Skips: []string{}},
{Name: "a-v1.1.0", Replaces: ""},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0"},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1", Skips: []string{"a-v1.1.0"}},
},
},
Expand All @@ -240,7 +240,7 @@ func TestLinkChannels(t *testing.T) {
Name: "candidate-v2",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v2.3.2", Replaces: "", Skips: []string{}},
{Name: "a-v2.3.2", Replaces: ""},
},
},
{
Expand All @@ -250,7 +250,7 @@ func TestLinkChannels(t *testing.T) {
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: ""},
{Name: "a-v1.2.1", Replaces: ""},
{Name: "a-v1.3.1", Replaces: "", Skips: []string{}},
{Name: "a-v1.3.1", Replaces: ""},
},
},
{
Expand All @@ -260,7 +260,7 @@ func TestLinkChannels(t *testing.T) {
Entries: []declcfg.ChannelEntry{
{Name: "a-v2.1.0", Replaces: ""},
{Name: "a-v2.1.1", Replaces: "", Skips: []string{"a-v2.1.0"}},
{Name: "a-v2.3.1", Replaces: "a-v2.1.1", Skips: []string{}},
{Name: "a-v2.3.1", Replaces: "a-v2.1.1"},
},
},
},
Expand Down Expand Up @@ -313,8 +313,8 @@ func TestGenerateChannels(t *testing.T) {
Name: "stable-v1",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: "", Skips: []string{}},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0", Skips: []string{}},
{Name: "a-v1.1.0", Replaces: ""},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0"},
{Name: "a-v1.3.1-alpha", Replaces: ""},
{Name: "a-v1.3.1-beta", Replaces: ""},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1", Skips: []string{"a-v1.1.0", "a-v1.3.1-alpha", "a-v1.3.1-beta"}},
Expand Down Expand Up @@ -360,15 +360,15 @@ func TestGenerateChannels(t *testing.T) {
Name: "stable-v1.1",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: "", Skips: []string{}},
{Name: "a-v1.1.0", Replaces: ""},
},
},
{
Schema: "olm.channel",
Name: "stable-v1.2",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.2.1", Replaces: "a-v1.1.0", Skips: []string{}},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0"},
},
},
{
Expand Down

0 comments on commit 57e4340

Please sign in to comment.