Skip to content

Commit 6c8e86c

Browse files
grokspawntimflannagan
authored andcommitted
modify to validate bundle versions differ by more than build metadata, or fail (openshift#988)
Signed-off-by: Jordan Keister <[email protected]> Upstream-repository: operator-registry Upstream-commit: 05b1edcf7992c08cf46b87fc874df242a92f61ce
1 parent 2731812 commit 6c8e86c

File tree

3 files changed

+111
-9
lines changed
  • staging/operator-registry/alpha/veneer/semver
  • vendor/github.com/operator-framework/operator-registry/alpha/veneer/semver

3 files changed

+111
-9
lines changed

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

+53-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/operator-framework/operator-registry/alpha/declcfg"
1414
"github.com/operator-framework/operator-registry/alpha/property"
1515
"github.com/operator-framework/operator-registry/pkg/image"
16+
"k8s.io/apimachinery/pkg/util/errors"
1617
"k8s.io/apimachinery/pkg/util/sets"
1718
"k8s.io/apimachinery/pkg/util/yaml"
1819
)
@@ -140,18 +141,27 @@ func (sv *semverVeneer) getVersionsFromStandardChannels(cfg *declcfg.Declarative
140141
if err != nil {
141142
return nil, err
142143
}
144+
if err = validateVersions(&bdm); err != nil {
145+
return nil, err
146+
}
143147
versions[candidateChannelName] = bdm
144148

145149
bdm, err = sv.getVersionsFromChannel(sv.Fast.Bundles, cfg)
146150
if err != nil {
147151
return nil, err
148152
}
153+
if err = validateVersions(&bdm); err != nil {
154+
return nil, err
155+
}
149156
versions[fastChannelName] = bdm
150157

151158
bdm, err = sv.getVersionsFromChannel(sv.Stable.Bundles, cfg)
152159
if err != nil {
153160
return nil, err
154161
}
162+
if err = validateVersions(&bdm); err != nil {
163+
return nil, err
164+
}
155165
versions[stableChannelName] = bdm
156166

157167
return &versions, nil
@@ -189,9 +199,6 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
189199
if err != nil {
190200
return nil, fmt.Errorf("bundle %q has invalid version %q: %v", b.Name, props.Packages[0].Version, err)
191201
}
192-
if len(v.Build) > 0 {
193-
return nil, fmt.Errorf("bundle %q uses build metadata in its versioning, which is not sortable: %v", b.Name, v.String())
194-
}
195202

196203
// package name detection
197204
if sv.pkg != "" {
@@ -206,6 +213,10 @@ func (sv *semverVeneer) getVersionsFromChannel(semverBundles []semverVeneerBundl
206213
sv.pkg = props.Packages[0].PackageName
207214
}
208215

216+
if _, ok := entries[b.Name]; ok {
217+
return nil, fmt.Errorf("duplicate bundle name %q", b.Name)
218+
}
219+
209220
entries[b.Name] = v
210221
}
211222

@@ -255,7 +266,7 @@ func (sv *semverVeneer) generateChannels(semverChannels *semverRenderedChannelVe
255266

256267
// sort the bundle names according to their semver, so we can walk in ascending order
257268
bundleNamesByVersion := []string{}
258-
for b, _ := range bundles {
269+
for b := range bundles {
259270
bundleNamesByVersion = append(bundleNamesByVersion, b)
260271
}
261272
sort.Slice(bundleNamesByVersion, func(i, j int) bool {
@@ -434,3 +445,41 @@ func MermaidChannelWriter(cfg declcfg.DeclarativeConfig, out io.Writer) error {
434445
}
435446
return nil
436447
}
448+
449+
func withoutBuildMetadataConflict(versions *map[string]semver.Version) error {
450+
errs := []error{}
451+
452+
// using the stringified semver because the semver package generates deterministic representations,
453+
// and because the semver.Version contains slice fields which make it unsuitable as a map key
454+
// stringified-semver.Version ==> incidence count
455+
seen := make(map[string]int)
456+
for b := range *versions {
457+
stripped := stripBuildMetadata((*versions)[b])
458+
if _, ok := seen[stripped]; !ok {
459+
seen[stripped] = 1
460+
} else {
461+
seen[stripped] = seen[stripped] + 1
462+
errs = append(errs, fmt.Errorf("bundle version %q cannot be compared to %q", (*versions)[b].String(), stripped))
463+
}
464+
}
465+
466+
if len(errs) != 0 {
467+
return fmt.Errorf("encountered bundle versions which differ only by build metadata, which cannot be ordered: %v", errors.NewAggregate(errs))
468+
}
469+
470+
return nil
471+
}
472+
473+
func validateVersions(versions *map[string]semver.Version) error {
474+
// short-circuit if empty, since that is not an error
475+
if len(*versions) == 0 {
476+
return nil
477+
}
478+
return withoutBuildMetadataConflict(versions)
479+
}
480+
481+
// strips out the build metadata from a semver.Version and then stringifies it to make it suitable for collision detection
482+
func stripBuildMetadata(v semver.Version) string {
483+
v.Build = nil
484+
return v.String()
485+
}

staging/operator-registry/alpha/veneer/semver/semver_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,8 @@ func TestBailOnVersionBuildMetadata(t *testing.T) {
581581
{Image: "repo/origin/a-v2.3.1"},
582582
{Image: "repo/origin/a-v2.3.2"},
583583
{Image: "repo/origin/a-v1.3.1-alpha"},
584+
{Image: "repo/origin/a-v1.3.1-alpha+2001Jan21"},
585+
{Image: "repo/origin/a-v1.3.1-alpha+2003May06"},
584586
},
585587
},
586588
}
@@ -597,16 +599,18 @@ func TestBailOnVersionBuildMetadata(t *testing.T) {
597599
{Schema: "olm.bundle", Image: "repo/origin/a-v0.1.1", Name: "a-v0.1.1", Properties: []property.Property{property.MustBuildPackage("a", "0.1.1")}},
598600
{Schema: "olm.bundle", Image: "repo/origin/a-v1.1.0", Name: "a-v1.1.0", Properties: []property.Property{property.MustBuildPackage("a", "1.1.0")}},
599601
{Schema: "olm.bundle", Image: "repo/origin/a-v1.2.1", Name: "a-v1.2.1", Properties: []property.Property{property.MustBuildPackage("a", "1.2.1")}},
602+
{Schema: "olm.bundle", Image: "repo/origin/a-v1.3.1-alpha", Name: "a-v1.3.1-alpha", Properties: []property.Property{property.MustBuildPackage("a", "1.3.1-alpha")}},
600603
{Schema: "olm.bundle", Image: "repo/origin/a-v1.3.1-alpha+2001Jan21", Name: "a-v1.3.1-alpha+2001Jan21", Properties: []property.Property{property.MustBuildPackage("a", "1.3.1-alpha+2001Jan21")}},
601604
{Schema: "olm.bundle", Image: "repo/origin/a-v1.3.1", Name: "a-v1.3.1", Properties: []property.Property{property.MustBuildPackage("a", "1.3.1")}},
602605
{Schema: "olm.bundle", Image: "repo/origin/a-v2.1.0", Name: "a-v2.1.0", Properties: []property.Property{property.MustBuildPackage("a", "2.1.0")}},
603606
{Schema: "olm.bundle", Image: "repo/origin/a-v2.1.1", Name: "a-v2.1.1", Properties: []property.Property{property.MustBuildPackage("a", "2.1.1")}},
604607
{Schema: "olm.bundle", Image: "repo/origin/a-v2.3.1", Name: "a-v2.3.1", Properties: []property.Property{property.MustBuildPackage("a", "2.3.1")}},
605608
{Schema: "olm.bundle", Image: "repo/origin/a-v2.3.2", Name: "a-v2.3.2", Properties: []property.Property{property.MustBuildPackage("a", "2.3.2")}},
609+
{Schema: "olm.bundle", Image: "repo/origin/a-v1.3.1-alpha+2003May06", Name: "a-v1.3.1-alpha+2003May06", Properties: []property.Property{property.MustBuildPackage("a", "1.3.1-alpha+2003May06")}},
606610
},
607611
}
608612

609-
t.Run("Abort on detected build metadata version data", func(t *testing.T) {
613+
t.Run("Abort on unorderable build metadata version data", func(t *testing.T) {
610614
_, err := sv.getVersionsFromStandardChannels(&dc)
611615
require.Error(t, err)
612616
})

vendor/github.com/operator-framework/operator-registry/alpha/veneer/semver/semver.go

+53-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)