Skip to content

Bug 2017327: Block cross-package skips/replaces on add (#826) #216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions staging/operator-registry/alpha/action/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"

"github.com/blang/semver/v4"
"github.com/sirupsen/logrus"

"github.com/operator-framework/operator-registry/alpha/declcfg"
Expand Down
1 change: 1 addition & 0 deletions staging/operator-registry/alpha/action/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"testing"

"github.com/blang/semver/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package declcfg
import (
"fmt"

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

"github.com/operator-framework/operator-registry/alpha/model"
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/alpha/declcfg/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"sort"
"sync"

"github.com/blang/semver"
"github.com/blang/semver/v4"
"github.com/mitchellh/hashstructure/v2"
"github.com/sirupsen/logrus"

Expand Down
7 changes: 7 additions & 0 deletions staging/operator-registry/alpha/declcfg/diff_include.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package declcfg

import (
"fmt"
"strings"

"github.com/blang/semver/v4"
"github.com/sirupsen/logrus"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/operator-framework/operator-registry/alpha/model"
)

Expand Down
1 change: 1 addition & 0 deletions staging/operator-registry/alpha/declcfg/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package declcfg
import (
"testing"

"github.com/blang/semver/v4"
"github.com/stretchr/testify/require"

"github.com/operator-framework/operator-registry/alpha/model"
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/alpha/model/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"sort"
"strings"

"github.com/blang/semver"
"github.com/blang/semver/v4"
"github.com/h2non/filetype"
"github.com/h2non/filetype/matchers"
"github.com/h2non/filetype/types"
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.16

require (
github.com/Microsoft/hcsshim v0.8.9 // indirect
github.com/blang/semver v3.5.1+incompatible
github.com/blang/semver/v4 v4.0.0
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
github.com/bugsnag/panicwrap v1.2.0 // indirect
github.com/containerd/containerd v1.4.8
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/lib/semver/semver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package semver
import (
"fmt"

"github.com/blang/semver"
"github.com/blang/semver/v4"
)

// BuildIdCompare compares two versions and returns negative one if the first arg is less than the second arg, positive one if it is larger, and zero if they are equal.
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/lib/semver/semver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package semver
import (
"testing"

"github.com/blang/semver"
"github.com/blang/semver/v4"
"github.com/stretchr/testify/require"
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package registry
import (
"fmt"

"github.com/blang/semver"
"github.com/blang/semver/v4"
)

// BundleGraphLoader generates updated graphs by adding bundles to them, updating
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"testing"

"github.com/blang/semver"
"github.com/blang/semver/v4"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/registry/csv.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const (
description = "description"

// The yaml attribute that specifies the version of the ClusterServiceVersion
// expected to be semver and parseable by blang/semver
// expected to be semver and parseable by blang/semver/v4
version = "version"

// The yaml attribute that specifies the related images of the ClusterServiceVersion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"path/filepath"
"sort"

"github.com/blang/semver"
"github.com/blang/semver/v4"
"github.com/onsi/gomega/gstruct/errors"
)

Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/registry/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package registry
import (
"testing"

"github.com/blang/semver"
"github.com/blang/semver/v4"
"github.com/stretchr/testify/require"
)

Expand Down
57 changes: 56 additions & 1 deletion staging/operator-registry/pkg/registry/populator.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import (
"errors"
"fmt"
"os"
"sort"

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

Expand Down Expand Up @@ -142,6 +143,9 @@ func (i *DirectoryPopulator) globalSanityCheck(imagesToAdd []*ImageInput) error
}
}

if err := i.ValidateEdgeBundlePackage(imagesToAdd); err != nil {
errs = append(errs, err)
}
return utilerrors.NewAggregate(errs)
}

Expand Down Expand Up @@ -408,3 +412,54 @@ func DecodeFile(path string, into interface{}) error {

return decoder.Decode(into)
}

// ValidateEdgeBundlePackage ensures that all bundles in the input will only skip or replace bundles in the same package.
func (i *DirectoryPopulator) ValidateEdgeBundlePackage(images []*ImageInput) error {
// track packages for encountered bundles
expectedBundlePackages := map[string]string{}
for _, b := range images {
r, err := b.Bundle.Replaces()
if err != nil {
return fmt.Errorf("failed to validate replaces for bundle %s(%s): %v", b.Bundle.Name, b.Bundle.BundleImage, err)
}

skipped, err := b.Bundle.Skips()
if err != nil {
return fmt.Errorf("failed to validate skipped entries for bundle %s(%s): %v", b.Bundle.Name, b.Bundle.BundleImage, err)
}

for _, bndl := range append(skipped, r, b.Bundle.Name) {
if len(bndl) == 0 {
continue
}

if pkg, ok := expectedBundlePackages[bndl]; ok && pkg != b.Bundle.Package {
pkgs := []string{pkg, b.Bundle.Package}
sort.Strings(pkgs)
return fmt.Errorf("bundle %s must belong to exactly one package, found on: %v", bndl, pkgs)
}
expectedBundlePackages[bndl] = b.Bundle.Package
}
}
if len(expectedBundlePackages) == 0 {
return nil
}

pkgs, err := i.querier.ListPackages(context.TODO())
if err != nil {
return fmt.Errorf("unable to verify bundle packages: %v", err)
}
for _, pkg := range pkgs {
entries, err := i.querier.GetChannelEntriesFromPackage(context.TODO(), pkg)
if err != nil {
return fmt.Errorf("unable to verify bundles for package %v", err)
}
for _, b := range entries {
if bundlePkg, ok := expectedBundlePackages[b.BundleName]; ok && bundlePkg != b.PackageName {
return fmt.Errorf("bundle %s belongs to package %s on index, cannot be added as an edge for package %s", b.BundleName, b.PackageName, bundlePkg)
}
}
}

return nil
}
142 changes: 142 additions & 0 deletions staging/operator-registry/pkg/registry/populator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import (
"testing"
"time"

"github.com/blang/semver/v4"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"k8s.io/apimachinery/pkg/util/errors"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

"github.com/operator-framework/api/pkg/lib/version"
"github.com/operator-framework/api/pkg/operators/v1alpha1"
"github.com/operator-framework/operator-registry/pkg/api"
"github.com/operator-framework/operator-registry/pkg/image"
"github.com/operator-framework/operator-registry/pkg/registry"
Expand Down Expand Up @@ -2621,3 +2624,142 @@ func TestEnableAlpha(t *testing.T) {
})
}
}

func newUnpackedTestBundle(root, dir, name string, csvSpec json.RawMessage, annotations registry.Annotations) (string, func(), error) {
bundleDir := filepath.Join(root, dir)
cleanup := func() {
os.RemoveAll(bundleDir)
}
if err := os.Mkdir(bundleDir, 0755); err != nil {
return bundleDir, cleanup, err
}
if err := os.Mkdir(filepath.Join(bundleDir, bundle.ManifestsDir), 0755); err != nil {
return bundleDir, cleanup, err
}
if err := os.Mkdir(filepath.Join(bundleDir, bundle.MetadataDir), 0755); err != nil {
return bundleDir, cleanup, err
}
if len(csvSpec) == 0 {
csvSpec = json.RawMessage(`{}`)
}

rawCSV, err := json.Marshal(registry.ClusterServiceVersion{
TypeMeta: v1.TypeMeta{
Kind: sqlite.ClusterServiceVersionKind,
},
ObjectMeta: v1.ObjectMeta{
Name: name,
},
Spec: csvSpec,
})
if err != nil {
return bundleDir, cleanup, err
}

rawObj := unstructured.Unstructured{}
if err := json.Unmarshal(rawCSV, &rawObj); err != nil {
return bundleDir, cleanup, err
}
rawObj.SetCreationTimestamp(v1.Time{})

jsonout, err := rawObj.MarshalJSON()
out, err := yaml.JSONToYAML(jsonout)
if err != nil {
return bundleDir, cleanup, err
}
if err := ioutil.WriteFile(filepath.Join(bundleDir, bundle.ManifestsDir, "csv.yaml"), out, 0666); err != nil {
return bundleDir, cleanup, err
}

out, err = yaml.Marshal(registry.AnnotationsFile{Annotations: annotations})
if err != nil {
return bundleDir, cleanup, err
}
if err := ioutil.WriteFile(filepath.Join(bundleDir, bundle.MetadataDir, "annotations.yaml"), out, 0666); err != nil {
return bundleDir, cleanup, err
}
return bundleDir, cleanup, nil
}

func TestValidateEdgeBundlePackage(t *testing.T) {
newInput := func(name, versionString, pkg, defaultChannel, channels, replaces string, skips []string) *registry.ImageInput {
v, err := semver.Parse(versionString)
require.NoError(t, err)

spec := v1alpha1.ClusterServiceVersionSpec{
Replaces: replaces,
Skips: skips,
Version: version.OperatorVersion{v},
}
specJson, err := json.Marshal(&spec)
require.NoError(t, err)

rawCSV, err := json.Marshal(registry.ClusterServiceVersion{
TypeMeta: v1.TypeMeta{
Kind: sqlite.ClusterServiceVersionKind,
},
ObjectMeta: v1.ObjectMeta{
Name: name,
},
Spec: specJson,
})

rawObj := unstructured.Unstructured{}
require.NoError(t, json.Unmarshal(rawCSV, &rawObj))
rawObj.SetCreationTimestamp(v1.Time{})

jsonout, err := rawObj.MarshalJSON()
require.NoError(t, err)

b, err := registry.NewBundleFromStrings(name, versionString, pkg, defaultChannel, channels, string(jsonout))
require.NoError(t, err)
return &registry.ImageInput{Bundle: b}
}

logrus.SetLevel(logrus.DebugLevel)
db, cleanup := CreateTestDb(t)
defer cleanup()

store, err := createAndPopulateDB(db)
require.NoError(t, err)

r := registry.NewDirectoryPopulator(nil, nil, store, nil, nil)

tests := []struct {
name string
args []*registry.ImageInput
wantErr error
}{
{
name: "conflictInAdded",
args: []*registry.ImageInput{
newInput("b1", "0.0.1", "p1", "a", "a", "", []string{}),
newInput("b2", "0.0.2", "p2", "a", "a", "", []string{"b1"}),
},
wantErr: fmt.Errorf("bundle b1 must belong to exactly one package, found on: [p1 p2]"),
},
{
name: "conflictWithExisting",
args: []*registry.ImageInput{
newInput("b1", "0.0.1", "p1", "a", "a", "", []string{"etcdoperator.v0.9.0"}),
},
wantErr: fmt.Errorf("bundle etcdoperator.v0.9.0 belongs to package etcd on index, cannot be added as an edge for package p1"),
},
{
name: "passForNonConflicting",
args: []*registry.ImageInput{
newInput("b1", "0.0.1", "etcd", "a", "a", "", []string{"etcdoperator.v0.9.0"}),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := r.ValidateEdgeBundlePackage(tt.args)
if tt.wantErr == nil {
require.NoError(t, err)
return
}
require.EqualError(t, err, tt.wantErr.Error())
})
}
}
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/registry/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"sort"
"strings"

"github.com/blang/semver"
"github.com/blang/semver/v4"
)

var (
Expand Down
2 changes: 1 addition & 1 deletion staging/operator-registry/pkg/sqlite/load.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"fmt"
"strings"

"github.com/blang/semver"
"github.com/blang/semver/v4"
_ "github.com/mattn/go-sqlite3"
utilerrors "k8s.io/apimachinery/pkg/util/errors"

Expand Down