Skip to content

Commit

Permalink
working at long last
Browse files Browse the repository at this point in the history
  • Loading branch information
grokspawn committed Feb 25, 2024
1 parent 57e4340 commit 71e49b4
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 107 deletions.
93 changes: 47 additions & 46 deletions alpha/template/semver/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"
"fmt"
"io"
"slices"
"sort"

"github.com/operator-framework/operator-registry/alpha/action"
Expand Down Expand Up @@ -224,7 +223,6 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
hwc := highwaterChannel{archetype: archetypesByPriority[0], version: semver.Version{Major: 0, Minor: 0}}

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

for _, archetype := range archetypesByPriority {
bundles := (*semverChannels)[archetype]
Expand Down Expand Up @@ -272,88 +270,91 @@ func (sv *semverTemplate) generateChannels(semverChannels *bundleVersions) []dec
}
}
ch.Entries = append(ch.Entries, declcfg.ChannelEntry{Name: bundleName})
unassociatedEdges = append(unassociatedEdges, entryTuple{arch: archetype, kind: cKey, parent: cName, name: bundleName, version: bundles[bundleName], index: len(ch.Entries) - 1})
}
}
}

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

outChannels = append(outChannels, sv.linkChannels(unlinkedChannels, unassociatedEdges)...)
outChannels = append(outChannels, sv.linkChannels(unlinkedChannels, semverChannels)...)

return outChannels
}

func (sv *semverTemplate) linkChannels(unlinkedChannels map[string]*declcfg.Channel, entries []entryTuple) []declcfg.Channel {
func (sv *semverTemplate) linkChannels(unlinkedChannels map[string]*declcfg.Channel, harvestedVersions *bundleVersions) []declcfg.Channel {
channels := []declcfg.Channel{}

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 !slices.Contains(parentChannels, entries[e].parent) {
parentChannels = append(parentChannels, entries[e].parent)
// bundle --> version lookup
bundleVersions := make(map[string]semver.Version)
for _, vs := range *harvestedVersions {
for b, v := range vs {
if _, ok := bundleVersions[b]; !ok {
bundleVersions[b] = v
}
}
}
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

for _, channel := range unlinkedChannels {
entries := &channel.Entries
sort.Slice(*entries, func(i, j int) bool {
return bundleVersions[(*entries)[i].Name].LT(bundleVersions[(*entries)[j].Name])
})

// tracking 2 things:
// - the highest Z version before a Y-stream change
// - the beginning of new Y-stream
curEdge, yProbe := 0, 0
zmaxQueue := ""
entryCount := len(*entries)

// 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])) {
for curEdge < entryCount {
for yProbe < entryCount {
curVersion := bundleVersions[(*entries)[curEdge].Name]
yProbeVersion := bundleVersions[(*entries)[yProbe].Name]
if getMinorVersion(yProbeVersion).EQ(getMinorVersion(curVersion)) {
yProbe += 1
} else {
break
}
}
// if yProbe crossed a threshold, the previous entry is the last of the previous Y-stream
preChangeIndex := yProbe - 1

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
if zmaxQueue != "" {
fmt.Printf(">>> %v -- replaces --> %v\n", (*entries)[preChangeIndex].Name, zmaxQueue)
(*entries)[preChangeIndex].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
fmt.Printf(">>> stashing a max z: %v\n", (*entries)[preChangeIndex].Name)
zmaxQueue = (*entries)[preChangeIndex].Name
}
}
for curEdge < yProbe {
for curEdge < preChangeIndex {
// 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)
fmt.Printf(">>> %v -- skips --> %v\n", (*entries)[preChangeIndex].Name, (*entries)[curEdge].Name)
if (*entries)[preChangeIndex].Name == (*entries)[curEdge].Name {
fmt.Printf(">>> curEdge: %v yProbe: %v\n", curEdge, yProbe)
}
(*entries)[preChangeIndex].Skips = append((*entries)[preChangeIndex].Skips, (*entries)[curEdge].Name)
curEdge += 1
}
yProbe += 2 // seek point for next y change
curEdge += 1
yProbe = curEdge + 1
}
// since probe will always fail to pick up a y-change in the last item, test for it
if entryCount > 1 {
penultimateEntry := &(*entries)[len(*entries)-2]
ultimateEntry := &(*entries)[len(*entries)-1]
penultimateVersion := bundleVersions[penultimateEntry.Name]
ultimateVersion := bundleVersions[ultimateEntry.Name]
if ultimateVersion.Minor != penultimateVersion.Minor {
fmt.Printf(">>> %v -- replaces --> %v\n", ultimateEntry.Name, penultimateEntry.Name)
ultimateEntry.Replaces = penultimateEntry.Name
}
}
fmt.Printf(">>> end run\n")
}

for _, ch := range unlinkedChannels {
Expand Down
74 changes: 27 additions & 47 deletions alpha/template/semver/semver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,26 @@ import (
)

func TestLinkChannels(t *testing.T) {
// type entryTuple struct {
// arch channelArchetype
// kind streamType
// name string
// parent string
// index int
// version semver.Version
// }

majorChannelEntries := []entryTuple{
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v0.1.0", parent: "stable-v0", index: 0, version: semver.MustParse("0.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v0.1.1", parent: "stable-v0", index: 1, version: semver.MustParse("0.1.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.1.0", parent: "stable-v1", index: 0, version: semver.MustParse("1.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.2.1", parent: "stable-v1", index: 1, version: semver.MustParse("1.2.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.3.1", parent: "stable-v1", index: 2, version: semver.MustParse("1.3.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.1.0", parent: "stable-v2", index: 0, version: semver.MustParse("2.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.1.1", parent: "stable-v2", index: 1, version: semver.MustParse("2.1.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.3.1", parent: "stable-v2", index: 2, version: semver.MustParse("2.3.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.3.2", parent: "stable-v2", index: 3, version: semver.MustParse("2.3.2")},
}

majorChannelEntriesLastXChange := []entryTuple{
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v0.1.0", parent: "stable-v0", index: 0, version: semver.MustParse("0.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v0.1.1", parent: "stable-v0", index: 1, version: semver.MustParse("0.1.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.1.0", parent: "stable-v1", index: 0, version: semver.MustParse("1.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.2.1", parent: "stable-v1", index: 1, version: semver.MustParse("1.2.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.3.1", parent: "stable-v1", index: 2, version: semver.MustParse("1.3.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.1.0", parent: "stable-v2", index: 0, version: semver.MustParse("2.1.0")},
}

majorChannelEntriesLastArchChange := []entryTuple{
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v1.3.1", parent: "stable-v1", index: 2, version: semver.MustParse("1.3.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.1.0", parent: "stable-v2", index: 0, version: semver.MustParse("2.1.0")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.1.1", parent: "stable-v2", index: 1, version: semver.MustParse("2.1.1")},
{arch: stableChannelArchetype, kind: majorStreamType, name: "a-v2.3.1", parent: "stable-v2", index: 2, version: semver.MustParse("2.3.1")},
{arch: candidateChannelArchetype, kind: majorStreamType, name: "a-v2.3.2", parent: "candidate-v2", index: 0, version: semver.MustParse("2.3.2")},
// type bundleVersions map[string]map[string]semver.Version // e.g. d["stable"]["example-operator.v1.0.0"] = 1.0.0
channelOperatorVersions := bundleVersions{
"stable": {
"a-v0.1.0": semver.MustParse("0.1.0"),
"a-v0.1.1": semver.MustParse("0.1.1"),
"a-v1.1.0": semver.MustParse("1.1.0"),
"a-v1.2.1": semver.MustParse("1.2.1"),
"a-v1.3.1": semver.MustParse("1.3.1"),
"a-v2.1.0": semver.MustParse("2.1.0"),
"a-v1.3.1-beta": semver.MustParse(("1.3.1-beta")),
"a-v2.1.1": semver.MustParse("2.1.1"),
"a-v2.3.1": semver.MustParse("2.3.1"),
"a-v2.3.2": semver.MustParse("2.3.2"),
"a-v3.1.0": semver.MustParse("3.1.0"),
"a-v3.1.1": semver.MustParse("3.1.1"),
"a-v1.3.1-alpha": semver.MustParse("1.3.1-alpha"),
"a-v1.4.1": semver.MustParse("1.4.1"),
"a-v1.4.1-beta1": semver.MustParse("1.4.1-beta1"),
"a-v1.4.1-beta2": semver.MustParse("1.4.1-beta2"),
},
}

majorGeneratedUnlinkedChannels := map[string]*declcfg.Channel{
Expand Down Expand Up @@ -148,15 +132,13 @@ func TestLinkChannels(t *testing.T) {
tests := []struct {
name string
unlinkedChannels map[string]*declcfg.Channel
entryTuples []entryTuple
generateMinorChannels bool
generateMajorChannels bool
out []declcfg.Channel
}{
{
name: "No edges between successive major channels",
unlinkedChannels: majorGeneratedUnlinkedChannels,
entryTuples: majorChannelEntries,
generateMinorChannels: false,
generateMajorChannels: true,
out: []declcfg.Channel{
Expand All @@ -176,7 +158,7 @@ func TestLinkChannels(t *testing.T) {
Entries: []declcfg.ChannelEntry{
{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"}},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1"},
},
},
{
Expand All @@ -187,15 +169,14 @@ func TestLinkChannels(t *testing.T) {
{Name: "a-v2.1.0", Replaces: ""},
{Name: "a-v2.1.1", Replaces: "", Skips: []string{"a-v2.1.0"}},
{Name: "a-v2.3.1", Replaces: ""},
{Name: "a-v2.3.2", Replaces: "a-v2.1.1", Skips: []string{"a-v2.1.0", "a-v2.3.1"}},
{Name: "a-v2.3.2", Replaces: "a-v2.1.1", Skips: []string{"a-v2.3.1"}},
},
},
},
},
{
name: "No edges between successive major channels where last edge is X change",
unlinkedChannels: majorGeneratedUnlinkedChannelsLastXChange,
entryTuples: majorChannelEntriesLastXChange,
generateMinorChannels: false,
generateMajorChannels: true,
out: []declcfg.Channel{
Expand All @@ -215,23 +196,22 @@ func TestLinkChannels(t *testing.T) {
Entries: []declcfg.ChannelEntry{
{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"}},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1"},
},
},
{
Schema: "olm.channel",
Name: "stable-v2",
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v2.1.0", Replaces: "", Skips: []string{}},
{Name: "a-v2.1.0", Replaces: ""},
},
},
},
},
{
name: "No edges between successive major channels where last edge is archetype change",
unlinkedChannels: majorGeneratedUnlinkedChannelsLastArchChange,
entryTuples: majorChannelEntriesLastArchChange,
generateMinorChannels: false,
generateMajorChannels: true,
out: []declcfg.Channel{
Expand All @@ -249,8 +229,8 @@ func TestLinkChannels(t *testing.T) {
Package: "a",
Entries: []declcfg.ChannelEntry{
{Name: "a-v1.1.0", Replaces: ""},
{Name: "a-v1.2.1", Replaces: ""},
{Name: "a-v1.3.1", Replaces: ""},
{Name: "a-v1.2.1", Replaces: "a-v1.1.0"},
{Name: "a-v1.3.1", Replaces: "a-v1.2.1"},
},
},
{
Expand All @@ -270,7 +250,7 @@ func TestLinkChannels(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sv := &semverTemplate{pkg: "a", GenerateMajorChannels: tt.generateMajorChannels, GenerateMinorChannels: tt.generateMinorChannels}
require.ElementsMatch(t, tt.out, sv.linkChannels(tt.unlinkedChannels, tt.entryTuples))
require.ElementsMatch(t, tt.out, sv.linkChannels(tt.unlinkedChannels, &channelOperatorVersions))
})
}
}
Expand Down
14 changes: 0 additions & 14 deletions alpha/template/semver/types.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package semver

import (
"fmt"
"io"

"github.com/blang/semver/v4"
Expand Down Expand Up @@ -81,16 +80,3 @@ type highwaterChannel struct {
version semver.Version
name string
}

type entryTuple struct {
arch channelArchetype
kind streamType
name string
parent string
index int
version semver.Version
}

func (t entryTuple) String() string {
return fmt.Sprintf("{ arch: %q, kind: %q, name: %q, parent: %q, index: %d, version: %v }", t.arch, t.kind, t.name, t.parent, t.index, t.version.String())
}

0 comments on commit 71e49b4

Please sign in to comment.