Skip to content

Commit fd38fe6

Browse files
committed
fix(index): add build id ordering to channel head selection
Add a semver version preference to [build IDs](https://semver.org/#spec-item-10) to accommodate existing published content making use of them to differentiate between successive releases of the same version. Rule: when two versions are semver equivalent, compare their build IDs as if they were [prereleases](https://semver.org/#spec-item-9). e.g. `1.2.3+1.2.3` and `1.2.3+1.1.55` are evaluated to `1.0.0-1.2.3` and `1.0.0-1.1.55` respectively and compared. This yields `1.0.0-1.2.3 < 1.0.0-1.1.55` which is then mapped back to `1.2.3+1.2.3 < 1.2.3+1.1.55` Additionally, split bundle addition and upgrade graph generation into two distinct steps. This reduces the number of times opm needs to generate the graph on index add, from once-per-bundle added to a package, to once-per-add operation (for a package). This also prevents opm from choking on existing, intermediate, bundles using semver incorrectly; e.g. `1.0.0-1` replaces `1.0.0-2` when `2.0.0` is being overwritten. Signed-off-by: Nick Hale <[email protected]>
1 parent a21f34e commit fd38fe6

File tree

8 files changed

+402
-768
lines changed

8 files changed

+402
-768
lines changed

pkg/registry/graph.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package registry
22

33
import (
44
"fmt"
5+
"strings"
56
)
67

78
type Package struct {
@@ -10,11 +11,38 @@ type Package struct {
1011
Channels map[string]Channel
1112
}
1213

14+
func (p *Package) String() string {
15+
var b strings.Builder
16+
b.WriteString("name: ")
17+
b.WriteString(p.Name)
18+
b.WriteString("\ndefault channel: ")
19+
b.WriteString(p.DefaultChannel)
20+
b.WriteString("\nchannels:\n")
21+
22+
for n, c := range p.Channels {
23+
b.WriteString(n)
24+
b.WriteString("\n")
25+
b.WriteString(c.String())
26+
}
27+
28+
return b.String()
29+
}
30+
1331
type Channel struct {
1432
Head BundleKey
1533
Nodes map[BundleKey]map[BundleKey]struct{}
1634
}
1735

36+
func (c *Channel) String() string {
37+
var b strings.Builder
38+
for node, _ := range c.Nodes {
39+
b.WriteString(node.String())
40+
b.WriteString("\n")
41+
}
42+
43+
return b.String()
44+
}
45+
1846
type BundleKey struct {
1947
BundlePath string
2048
Version string //semver string

pkg/registry/helper_test.go

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
package registry
2+
3+
import (
4+
"testing"
5+
6+
"github.com/blang/semver"
7+
"github.com/stretchr/testify/require"
8+
)
9+
10+
func TestBundleVersionCompare(t *testing.T) {
11+
type fields struct {
12+
version string
13+
}
14+
type args struct {
15+
version string
16+
}
17+
18+
type order func(t *testing.T, val int)
19+
var (
20+
lt order = func(t *testing.T, val int) {
21+
require.Less(t, val, 0)
22+
}
23+
gt order = func(t *testing.T, val int) {
24+
require.Greater(t, val, 0)
25+
}
26+
eq order = func(t *testing.T, val int) {
27+
require.Equal(t, val, 0)
28+
}
29+
)
30+
type expect struct {
31+
order order
32+
}
33+
34+
for _, tt := range []struct {
35+
description string
36+
fields fields
37+
args args
38+
expect expect
39+
}{
40+
{
41+
description: "BuildMetaAscendingLT",
42+
fields: fields{
43+
version: "1.0.0+1",
44+
},
45+
args: args{
46+
version: "1.0.0+2",
47+
},
48+
expect: expect{
49+
order: lt,
50+
},
51+
},
52+
{
53+
description: "BuildMetaDescendingGT",
54+
fields: fields{
55+
version: "1.0.0+2",
56+
},
57+
args: args{
58+
version: "1.0.0+1",
59+
},
60+
expect: expect{
61+
order: gt,
62+
},
63+
},
64+
{
65+
description: "BuildMetaGT",
66+
fields: fields{
67+
version: "1.0.0+1",
68+
},
69+
args: args{
70+
version: "1.0.0",
71+
},
72+
expect: expect{
73+
order: gt,
74+
},
75+
},
76+
{
77+
description: "BuildMetaEQ",
78+
fields: fields{
79+
version: "1.0.0+1",
80+
},
81+
args: args{
82+
version: "1.0.0+1",
83+
},
84+
expect: expect{
85+
order: eq,
86+
},
87+
},
88+
{
89+
description: "BuildMetaZeroGT",
90+
fields: fields{
91+
version: "1.0.0+0",
92+
},
93+
args: args{
94+
version: "1.0.0",
95+
},
96+
expect: expect{
97+
order: gt,
98+
},
99+
},
100+
{
101+
description: "BuildMetaMultipartLT",
102+
fields: fields{
103+
version: "1.0.0+0",
104+
},
105+
args: args{
106+
version: "1.0.0+1.2.3",
107+
},
108+
expect: expect{
109+
order: lt,
110+
},
111+
},
112+
{
113+
description: "PatchAscendingLT",
114+
fields: fields{
115+
version: "1.0.0",
116+
},
117+
args: args{
118+
version: "1.0.1",
119+
},
120+
expect: expect{
121+
order: lt,
122+
},
123+
},
124+
{
125+
description: "PatchDescendingGT",
126+
fields: fields{
127+
version: "1.0.1",
128+
},
129+
args: args{
130+
version: "1.0.0",
131+
},
132+
expect: expect{
133+
order: gt,
134+
},
135+
},
136+
{
137+
description: "MinorAscendingLT",
138+
fields: fields{
139+
version: "1.0.0",
140+
},
141+
args: args{
142+
version: "1.1.0",
143+
},
144+
expect: expect{
145+
order: lt,
146+
},
147+
},
148+
{
149+
description: "MinorDescendingGT",
150+
fields: fields{
151+
version: "1.1.0",
152+
},
153+
args: args{
154+
version: "1.0.0",
155+
},
156+
expect: expect{
157+
order: gt,
158+
},
159+
},
160+
{
161+
description: "MajorAscendingLT",
162+
fields: fields{
163+
version: "1.0.0",
164+
},
165+
args: args{
166+
version: "2.0.0",
167+
},
168+
expect: expect{
169+
order: lt,
170+
},
171+
},
172+
{
173+
description: "MajorDescendingGT",
174+
fields: fields{
175+
version: "2.0.0",
176+
},
177+
args: args{
178+
version: "1.0.0",
179+
},
180+
expect: expect{
181+
order: gt,
182+
},
183+
},
184+
{
185+
description: "PrereleaseLT",
186+
fields: fields{
187+
version: "1.0.0-pre",
188+
},
189+
args: args{
190+
version: "1.0.0",
191+
},
192+
expect: expect{
193+
order: lt,
194+
},
195+
},
196+
{
197+
description: "PrereleaseGT",
198+
fields: fields{
199+
version: "2.0.0-pre",
200+
},
201+
args: args{
202+
version: "1.0.0",
203+
},
204+
expect: expect{
205+
order: gt,
206+
},
207+
},
208+
} {
209+
t.Run(tt.description, func(t *testing.T) {
210+
fieldVersion, err := semver.Parse(tt.fields.version)
211+
require.NoErrorf(t, err, "bad testcase")
212+
213+
argVersion, err := semver.Parse(tt.args.version)
214+
require.NoErrorf(t, err, "bad testcase")
215+
216+
c, err := bundleVersion{version: fieldVersion}.compare(bundleVersion{version: argVersion})
217+
require.NoError(t, err)
218+
219+
tt.expect.order(t, c)
220+
})
221+
}
222+
}

0 commit comments

Comments
 (0)