From 233bb1e878ce17220cea872865eed78d9c2dbd4b Mon Sep 17 00:00:00 2001 From: Oskar Pawica Date: Tue, 9 Aug 2022 15:54:15 +0200 Subject: [PATCH 1/2] Removed diff logic (migration to oc-mirror) Signed-off-by: Oskar Pawica --- alpha/action/diff.go | 222 -- alpha/action/diff_test.go | 500 ---- alpha/declcfg/declcfg.go | 6 +- alpha/declcfg/diff.go | 581 ----- alpha/declcfg/diff_include.go | 444 ---- alpha/declcfg/diff_include_test.go | 183 -- alpha/declcfg/diff_test.go | 3436 ---------------------------- alpha/declcfg/helpers_test.go | 6 +- alpha/declcfg/load.go | 6 +- alpha/declcfg/model_to_declcfg.go | 10 +- cmd/opm/alpha/cmd.go | 2 - cmd/opm/alpha/diff/cmd.go | 239 -- 12 files changed, 14 insertions(+), 5621 deletions(-) delete mode 100644 alpha/action/diff.go delete mode 100644 alpha/action/diff_test.go delete mode 100644 alpha/declcfg/diff.go delete mode 100644 alpha/declcfg/diff_include.go delete mode 100644 alpha/declcfg/diff_include_test.go delete mode 100644 alpha/declcfg/diff_test.go delete mode 100644 cmd/opm/alpha/diff/cmd.go diff --git a/alpha/action/diff.go b/alpha/action/diff.go deleted file mode 100644 index 184428f13..000000000 --- a/alpha/action/diff.go +++ /dev/null @@ -1,222 +0,0 @@ -package action - -import ( - "context" - "errors" - "fmt" - "io" - - "github.com/blang/semver/v4" - "github.com/sirupsen/logrus" - utilerrors "k8s.io/apimachinery/pkg/util/errors" - "k8s.io/apimachinery/pkg/util/yaml" - - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-registry/alpha/model" - "github.com/operator-framework/operator-registry/pkg/image" -) - -type Diff struct { - Registry image.Registry - - OldRefs []string - NewRefs []string - // SkipDependencies directs Run() to not include dependencies - // of bundles included in the diff if true. - SkipDependencies bool - - IncludeConfig DiffIncludeConfig - // IncludeAdditively catalog objects specified in IncludeConfig. - IncludeAdditively bool - // HeadsOnly is the mode that selects the head of the channels only. - HeadsOnly bool - - Logger *logrus.Entry -} - -func (diff Diff) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) { - if err := diff.validate(); err != nil { - return nil, err - } - - // Disallow bundle refs. - mask := RefDCDir | RefDCImage | RefSqliteFile | RefSqliteImage - - // Heads-only mode does not require an old ref, so there may be nothing to render. - var oldModel model.Model - if len(diff.OldRefs) != 0 { - oldRender := Render{Refs: diff.OldRefs, Registry: diff.Registry, AllowedRefMask: mask} - oldCfg, err := oldRender.Run(ctx) - if err != nil { - if errors.Is(err, ErrNotAllowed) { - return nil, fmt.Errorf("%w (diff does not permit direct bundle references)", err) - } - return nil, fmt.Errorf("error rendering old refs: %v", err) - } - oldModel, err = declcfg.ConvertToModel(*oldCfg) - if err != nil { - return nil, fmt.Errorf("error converting old declarative config to model: %v", err) - } - } - - newRender := Render{Refs: diff.NewRefs, Registry: diff.Registry, AllowedRefMask: mask} - newCfg, err := newRender.Run(ctx) - if err != nil { - if errors.Is(err, ErrNotAllowed) { - return nil, fmt.Errorf("%w (diff does not permit direct bundle references)", err) - } - return nil, fmt.Errorf("error rendering new refs: %v", err) - } - newModel, err := declcfg.ConvertToModel(*newCfg) - if err != nil { - return nil, fmt.Errorf("error converting new declarative config to model: %v", err) - } - - g := &declcfg.DiffGenerator{ - Logger: diff.Logger, - SkipDependencies: diff.SkipDependencies, - Includer: convertIncludeConfigToIncluder(diff.IncludeConfig), - IncludeAdditively: diff.IncludeAdditively, - HeadsOnly: diff.HeadsOnly, - } - diffModel, err := g.Run(oldModel, newModel) - if err != nil { - return nil, fmt.Errorf("error generating diff: %v", err) - } - - cfg := declcfg.ConvertFromModel(diffModel) - return &cfg, nil -} - -func (p Diff) validate() error { - if len(p.NewRefs) == 0 { - return fmt.Errorf("no new refs to diff") - } - return nil -} - -// DiffIncludeConfig configures Diff.Run() to include a set of packages, -// channels, and/or bundles/versions in the output DeclarativeConfig. -// These override other diff mechanisms. For example, if running in -// heads-only mode but package "foo" channel "stable" is specified, -// the entire "stable" channel (all channel bundles) is added to the output. -type DiffIncludeConfig struct { - // Packages to include. - Packages []DiffIncludePackage `json:"packages" yaml:"packages"` -} - -// DiffIncludePackage contains a name (required) and channels and/or versions -// (optional) to include in the diff. The full package is only included if no channels -// or versions are specified. -type DiffIncludePackage struct { - // Name of package. - Name string `json:"name" yaml:"name"` - // Channels to include. - Channels []DiffIncludeChannel `json:"channels,omitempty" yaml:"channels,omitempty"` - // Versions to include. All channels containing these versions - // are parsed for an upgrade graph. - Versions []semver.Version `json:"versions,omitempty" yaml:"versions,omitempty"` - // Bundles are bundle names to include. All channels containing these bundles - // are parsed for an upgrade graph. - // Set this field only if the named bundle has no semantic version metadata. - Bundles []string `json:"bundles,omitempty" yaml:"bundles,omitempty"` - // Semver range of versions to include. All channels containing these versions - // are parsed for an upgrade graph. If the channels don't contain these versions, - // they will be ignored. This range can only be used with package exclusively - // and cannot combined with `Range` in `DiffIncludeChannel`. - // Range setting is mutually exclusive with channel versions/bundles/range settings. - Range string `json:"range,omitempty" yaml:"range,omitempty"` -} - -// DiffIncludeChannel contains a name (required) and versions (optional) -// to include in the diff. The full channel is only included if no versions are specified. -type DiffIncludeChannel struct { - // Name of channel. - Name string `json:"name" yaml:"name"` - // Versions to include. - Versions []semver.Version `json:"versions,omitempty" yaml:"versions,omitempty"` - // Bundles are bundle names to include. - // Set this field only if the named bundle has no semantic version metadata. - Bundles []string `json:"bundles,omitempty" yaml:"bundles,omitempty"` - // Semver range of versions to include in the channel. If the channel don't contain - // these versions, an error will be raised. This range can only be used with - // channel exclusively and cannot combined with `Range` in `DiffIncludePackage`. - // Range setting is mutually exclusive with Versions and Bundles settings. - Range string `json:"range,omitempty" yaml:"range,omitempty"` -} - -// LoadDiffIncludeConfig loads a (YAML or JSON) DiffIncludeConfig from r. -func LoadDiffIncludeConfig(r io.Reader) (c DiffIncludeConfig, err error) { - dec := yaml.NewYAMLOrJSONDecoder(r, 8) - if err := dec.Decode(&c); err != nil { - return DiffIncludeConfig{}, err - } - - if len(c.Packages) == 0 { - return c, fmt.Errorf("must specify at least one package in include config") - } - - var errs []error - for pkgI, pkg := range c.Packages { - if pkg.Name == "" { - errs = append(errs, fmt.Errorf("package at index %v requires a name", pkgI)) - continue - } - if pkg.Range != "" && (len(pkg.Versions) != 0 || len(pkg.Bundles) != 0) { - errs = append(errs, fmt.Errorf("package %q contains invalid settings: range and versions and/or bundles are mutually exclusive", pkg.Name)) - } - if pkg.Range != "" { - _, err := semver.ParseRange(pkg.Range) - if err != nil { - errs = append(errs, fmt.Errorf("package %q has an invalid version range %s", pkg.Name, pkg.Range)) - } - } - for chI, ch := range pkg.Channels { - if ch.Name == "" { - errs = append(errs, fmt.Errorf("package %s: channel at index %v requires a name", pkg.Name, chI)) - continue - } - if ch.Range == "" { - continue - } - if ch.Range != "" && (len(ch.Versions) != 0 || len(ch.Bundles) != 0) { - errs = append(errs, fmt.Errorf("package %q: channel %q contains invalid settings: range and versions and/or bundles are mutually exclusive", pkg.Name, ch.Name)) - } - if pkg.Range != "" && ch.Range != "" { - errs = append(errs, fmt.Errorf("version range settings in package %q and in channel %q must be mutually exclusive", pkg.Name, ch.Name)) - } - _, err := semver.ParseRange(ch.Range) - if err != nil { - errs = append(errs, fmt.Errorf("package %s: channel %q has an invalid version range %s", pkg.Name, ch.Name, pkg.Range)) - } - } - } - return c, utilerrors.NewAggregate(errs) -} - -func convertIncludeConfigToIncluder(c DiffIncludeConfig) (includer declcfg.DiffIncluder) { - includer.Packages = make([]declcfg.DiffIncludePackage, len(c.Packages)) - for pkgI, cpkg := range c.Packages { - pkg := &includer.Packages[pkgI] - pkg.Name = cpkg.Name - pkg.AllChannels.Versions = cpkg.Versions - pkg.AllChannels.Bundles = cpkg.Bundles - if cpkg.Range != "" { - pkg.Range, _ = semver.ParseRange(cpkg.Range) - } - - if len(cpkg.Channels) != 0 { - pkg.Channels = make([]declcfg.DiffIncludeChannel, len(cpkg.Channels)) - for chI, cch := range cpkg.Channels { - ch := &pkg.Channels[chI] - ch.Name = cch.Name - ch.Versions = cch.Versions - ch.Bundles = cch.Bundles - if cch.Range != "" { - ch.Range, _ = semver.ParseRange(cch.Range) - } - } - } - } - return includer -} diff --git a/alpha/action/diff_test.go b/alpha/action/diff_test.go deleted file mode 100644 index e3903b616..000000000 --- a/alpha/action/diff_test.go +++ /dev/null @@ -1,500 +0,0 @@ -package action - -import ( - "bytes" - "context" - "embed" - "errors" - "io/fs" - "path" - "path/filepath" - "strings" - "testing" - - "github.com/blang/semver/v4" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-registry/pkg/containertools" - "github.com/operator-framework/operator-registry/pkg/image" - "github.com/operator-framework/operator-registry/pkg/lib/bundle" -) - -func TestDiff(t *testing.T) { - type spec struct { - name string - diff Diff - expectedCfg *declcfg.DeclarativeConfig - assertion require.ErrorAssertionFunc - } - - registry, err := newDiffRegistry() - require.NoError(t, err) - - specs := []spec{ - { - name: "Success/Latest", - diff: Diff{ - Registry: registry, - OldRefs: []string{filepath.Join("testdata", "index-declcfgs", "old")}, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-latest")), - assertion: require.NoError, - }, - { - name: "Success/HeadsOnly", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-headsonly")), - assertion: require.NoError, - }, - { - name: "Success/IncludePackage", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - IncludeConfig: DiffIncludeConfig{ - Packages: []DiffIncludePackage{{Name: "baz"}}, - }, - IncludeAdditively: true, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-pkg")), - assertion: require.NoError, - }, - { - name: "Success/IncludeChannel", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - IncludeConfig: DiffIncludeConfig{ - Packages: []DiffIncludePackage{ - { - Name: "baz", - Channels: []DiffIncludeChannel{{Name: "stable"}}, - }, - }, - }, - IncludeAdditively: true, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")), - assertion: require.NoError, - }, - { - name: "Success/IncludeVersion", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - IncludeConfig: DiffIncludeConfig{ - Packages: []DiffIncludePackage{ - { - Name: "baz", - Versions: []semver.Version{semver.MustParse("1.0.0")}, - }, - }, - }, - IncludeAdditively: true, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")), - assertion: require.NoError, - }, - { - name: "Success/IncludeBundle", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - IncludeConfig: DiffIncludeConfig{ - Packages: []DiffIncludePackage{ - { - Name: "baz", - Bundles: []string{"baz.v1.0.0"}, - }, - }, - }, - IncludeAdditively: true, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")), - assertion: require.NoError, - }, - { - name: "Success/IncludeSameVersionAndBundle", - diff: Diff{ - Registry: registry, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - IncludeConfig: DiffIncludeConfig{ - Packages: []DiffIncludePackage{ - { - Name: "baz", - Versions: []semver.Version{semver.MustParse("1.0.0")}, - Bundles: []string{"baz.v1.0.0"}, - }, - }, - }, - IncludeAdditively: true, - HeadsOnly: true, - }, - expectedCfg: loadDirFS(t, indicesDir, filepath.Join("testdata", "index-declcfgs", "exp-include-channel")), - assertion: require.NoError, - }, - { - name: "Fail/NewBundleImage", - diff: Diff{ - Registry: registry, - NewRefs: []string{"test.registry/foo-operator/foo-bundle:v0.1.0"}, - HeadsOnly: true, - }, - assertion: func(t require.TestingT, err error, _ ...interface{}) { - if !assert.Error(t, err) { - require.Fail(t, "expected an error") - } - if !errors.Is(err, ErrNotAllowed) { - require.Fail(t, "err is not ErrNotAllowed", err) - } - }, - }, - { - name: "Fail/OldBundleImage", - diff: Diff{ - Registry: registry, - OldRefs: []string{"test.registry/foo-operator/foo-bundle:v0.1.0"}, - NewRefs: []string{filepath.Join("testdata", "index-declcfgs", "latest")}, - }, - assertion: func(t require.TestingT, err error, _ ...interface{}) { - if !assert.Error(t, err) { - require.Fail(t, "expected an error") - } - if !errors.Is(err, ErrNotAllowed) { - require.Fail(t, "err is not ErrNotAllowed", err) - } - }, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - actualCfg, actualErr := s.diff.Run(context.Background()) - s.assertion(t, actualErr) - require.Equal(t, s.expectedCfg, actualCfg) - }) - } -} - -func TestLoadDiffIncludeConfig(t *testing.T) { - type spec struct { - name string - input string - expectedCfg DiffIncludeConfig - expectedIncluder declcfg.DiffIncluder - assertion require.ErrorAssertionFunc - } - - specs := []spec{ - { - name: "Success/Basic", - input: ` -packages: -- name: foo -`, - expectedCfg: DiffIncludeConfig{ - Packages: []DiffIncludePackage{{Name: "foo"}}, - }, - expectedIncluder: declcfg.DiffIncluder{ - Packages: []declcfg.DiffIncludePackage{{Name: "foo"}}, - }, - assertion: require.NoError, - }, - { - name: "Success/MultiPackage", - input: ` -packages: -- name: foo - channels: - - name: stable - bundles: - - foo.v0.3.0 - versions: - - 0.1.0 - - 0.2.0 - versions: - - 1.0.0 -- name: bar - channels: - - name: stable - versions: - - 0.1.0 - versions: - - 1.0.0 - bundles: - - bar.v1.2.0 -`, - expectedCfg: DiffIncludeConfig{ - Packages: []DiffIncludePackage{ - { - Name: "foo", - Channels: []DiffIncludeChannel{ - { - Name: "stable", - Versions: []semver.Version{semver.MustParse("0.1.0"), semver.MustParse("0.2.0")}, - Bundles: []string{"foo.v0.3.0"}, - }, - }, - Versions: []semver.Version{semver.MustParse("1.0.0")}, - }, - { - Name: "bar", - Channels: []DiffIncludeChannel{ - {Name: "stable", Versions: []semver.Version{ - semver.MustParse("0.1.0"), - }}, - }, - Versions: []semver.Version{semver.MustParse("1.0.0")}, - Bundles: []string{"bar.v1.2.0"}, - }, - }, - }, - expectedIncluder: declcfg.DiffIncluder{ - Packages: []declcfg.DiffIncludePackage{ - { - Name: "foo", - Channels: []declcfg.DiffIncludeChannel{ - { - Name: "stable", - Versions: []semver.Version{semver.MustParse("0.1.0"), semver.MustParse("0.2.0")}, - Bundles: []string{"foo.v0.3.0"}, - }, - }, - AllChannels: declcfg.DiffIncludeChannel{ - Versions: []semver.Version{semver.MustParse("1.0.0")}, - }, - }, - { - Name: "bar", - Channels: []declcfg.DiffIncludeChannel{ - {Name: "stable", Versions: []semver.Version{ - semver.MustParse("0.1.0"), - }}, - }, - AllChannels: declcfg.DiffIncludeChannel{ - Versions: []semver.Version{semver.MustParse("1.0.0")}, - Bundles: []string{"bar.v1.2.0"}, - }, - }, - }, - }, - assertion: require.NoError, - }, - { - name: "Fail/Empty", - input: ``, - assertion: require.Error, - }, - { - name: "Fail/NoPackageName", - input: ` -packages: -- channels: - - name: stable - versions: - - 0.1.0 -`, - assertion: require.Error, - }, - { - name: "Fail/NoChannelName", - input: ` -packages: -- name: foo - channels: - - versions: - - 0.1.0 -`, - assertion: require.Error, - }, - { - name: "Fail/InvalidPackageRange", - input: ` - { - "packages": [ - { - "name": "foo", - "range": "test" - } - ] - }`, - assertion: require.Error, - }, - { - name: "Fail/InvalidChannelRange", - input: ` - { - "packages": [ - { - "name": "foo", - "channels": [ - { - "name": "stable", - "range": "test" - } - ] - } - ] - }`, - assertion: require.Error, - }, - { - name: "Fail/InvalidRangeSetting/MixedRange&ChannelRange", - input: ` - { - "packages": [ - { - "name": "foo", - "range": "test", - "channels": [ - { - "name": "stable", - "range": "test" - } - ] - } - ] - }`, - assertion: require.Error, - }, - { - name: "Fail/InvalidRangeSetting/MixedRange&OtherVersions", - input: ` - { - "packages": [ - { - "name": "foo", - "channels": [ - { - "name": "stable", - "range": ">0.1.0", - "versions": [ - "0.1.0" - ] - } - ] - } - ] - }`, - assertion: require.Error, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - actualCfg, err := LoadDiffIncludeConfig(bytes.NewBufferString(s.input)) - s.assertion(t, err) - if err == nil { - require.Equal(t, s.expectedCfg, actualCfg) - require.Equal(t, s.expectedIncluder, convertIncludeConfigToIncluder(actualCfg)) - } - }) - } -} - -var ( - //go:embed testdata/foo-bundle-v0.1.0/manifests/* - //go:embed testdata/foo-bundle-v0.1.0/metadata/* - fooBundlev010 embed.FS - //go:embed testdata/foo-bundle-v0.2.0/manifests/* - //go:embed testdata/foo-bundle-v0.2.0/metadata/* - fooBundlev020 embed.FS - //go:embed testdata/foo-bundle-v0.3.0/manifests/* - //go:embed testdata/foo-bundle-v0.3.0/metadata/* - fooBundlev030 embed.FS - //go:embed testdata/foo-bundle-v0.3.1/manifests/* - //go:embed testdata/foo-bundle-v0.3.1/metadata/* - fooBundlev031 embed.FS - //go:embed testdata/bar-bundle-v0.1.0/manifests/* - //go:embed testdata/bar-bundle-v0.1.0/metadata/* - barBundlev010 embed.FS - //go:embed testdata/bar-bundle-v0.2.0/manifests/* - //go:embed testdata/bar-bundle-v0.2.0/metadata/* - barBundlev020 embed.FS - //go:embed testdata/bar-bundle-v1.0.0/manifests/* - //go:embed testdata/bar-bundle-v1.0.0/metadata/* - barBundlev100 embed.FS - //go:embed testdata/baz-bundle-v1.0.0/manifests/* - //go:embed testdata/baz-bundle-v1.0.0/metadata/* - bazBundlev100 embed.FS - //go:embed testdata/baz-bundle-v1.0.1/manifests/* - //go:embed testdata/baz-bundle-v1.0.1/metadata/* - bazBundlev101 embed.FS - //go:embed testdata/baz-bundle-v1.1.0/manifests/* - //go:embed testdata/baz-bundle-v1.1.0/metadata/* - bazBundlev110 embed.FS -) - -var bundleToFS = map[string]embed.FS{ - "test.registry/foo-operator/foo-bundle:v0.1.0": fooBundlev010, - "test.registry/foo-operator/foo-bundle:v0.2.0": fooBundlev020, - "test.registry/foo-operator/foo-bundle:v0.3.0": fooBundlev030, - "test.registry/foo-operator/foo-bundle:v0.3.1": fooBundlev031, - "test.registry/bar-operator/bar-bundle:v0.1.0": barBundlev010, - "test.registry/bar-operator/bar-bundle:v0.2.0": barBundlev020, - "test.registry/bar-operator/bar-bundle:v1.0.0": barBundlev100, - "test.registry/baz-operator/baz-bundle:v1.0.0": bazBundlev100, - "test.registry/baz-operator/baz-bundle:v1.0.1": bazBundlev101, - "test.registry/baz-operator/baz-bundle:v1.1.0": bazBundlev110, -} - -//go:embed testdata/index-declcfgs -var indicesDir embed.FS - -func newDiffRegistry() (image.Registry, error) { - subDeclcfgImage, err := fs.Sub(indicesDir, "testdata/index-declcfgs") - if err != nil { - return nil, err - } - reg := &image.MockRegistry{ - RemoteImages: map[image.Reference]*image.MockImage{ - image.SimpleReference("test.registry/catalog/index-declcfg:latest"): { - Labels: map[string]string{containertools.ConfigsLocationLabel: "/latest/index.yaml"}, - FS: subDeclcfgImage, - }, - image.SimpleReference("test.registry/catalog/index-declcfg:old"): { - Labels: map[string]string{containertools.ConfigsLocationLabel: "/old/index.yaml"}, - FS: subDeclcfgImage, - }, - }, - } - - for name, bfs := range bundleToFS { - base := filepath.Base(name) - pkg := base[:strings.Index(base, ":")] - base = strings.ReplaceAll(base, ":", "-") - subImage, err := fs.Sub(bfs, path.Join("testdata", base)) - if err != nil { - return nil, err - } - reg.RemoteImages[image.SimpleReference(name)] = &image.MockImage{ - Labels: map[string]string{bundle.PackageLabel: pkg}, - FS: subImage, - } - } - - return reg, nil -} - -func loadDirFS(t *testing.T, parent fs.FS, dir string) *declcfg.DeclarativeConfig { - sub, err := fs.Sub(parent, dir) - if err != nil { - t.Fatal(err) - } - cfg, err := declcfg.LoadFS(sub) - if err != nil { - t.Fatal(err) - } - return cfg -} diff --git a/alpha/declcfg/declcfg.go b/alpha/declcfg/declcfg.go index 7579c97e3..a4169c28c 100644 --- a/alpha/declcfg/declcfg.go +++ b/alpha/declcfg/declcfg.go @@ -7,9 +7,9 @@ import ( ) const ( - schemaPackage = "olm.package" - schemaChannel = "olm.channel" - schemaBundle = "olm.bundle" + SchemaPackage = "olm.package" + SchemaChannel = "olm.channel" + SchemaBundle = "olm.bundle" ) type DeclarativeConfig struct { diff --git a/alpha/declcfg/diff.go b/alpha/declcfg/diff.go deleted file mode 100644 index d4cbfe8b1..000000000 --- a/alpha/declcfg/diff.go +++ /dev/null @@ -1,581 +0,0 @@ -package declcfg - -import ( - "fmt" - "reflect" - "sort" - "sync" - - "github.com/blang/semver/v4" - "github.com/mitchellh/hashstructure/v2" - "github.com/sirupsen/logrus" - - "github.com/operator-framework/operator-registry/alpha/model" - "github.com/operator-framework/operator-registry/alpha/property" -) - -// DiffGenerator configures how diffs are created via Run(). -type DiffGenerator struct { - Logger *logrus.Entry - - // SkipDependencies directs Run() to not include dependencies - // of bundles included in the diff if true. - SkipDependencies bool - // Includer for adding catalog objects to Run() output. - Includer DiffIncluder - // IncludeAdditively catalog objects specified in Includer in headsOnly mode. - IncludeAdditively bool - // HeadsOnly is the mode that selects the head of the channels only. - HeadsOnly bool - - initOnce sync.Once -} - -func (g *DiffGenerator) init() { - g.initOnce.Do(func() { - if g.Logger == nil { - g.Logger = &logrus.Entry{} - } - if g.Includer.Logger == nil { - g.Includer.Logger = g.Logger - } - // Inject headsOnly setting into DiffIncluder from command line setting - g.Includer.HeadsOnly = g.HeadsOnly - }) -} - -// Run returns a Model containing a subset of catalog objects in newModel: -// - If g.Includer contains objects: -// - If g.IncludeAdditively is false, a diff will be generated only on those objects, -// depending on the mode. -// - If g.IncludeAdditionally is true, the diff will contain included objects, -// plus those added by the mode. -// - If in heads-only mode (oldModel == nil), then the heads of channels are added to the output. -// - If in latest mode, a diff between old and new Models is added to the output. -// - Dependencies are added in all modes if g.SkipDependencies is false. -func (g *DiffGenerator) Run(oldModel, newModel model.Model) (model.Model, error) { - g.init() - - // TODO(estroz): loading both oldModel and newModel into memory may - // exceed process/hardware limits. Instead, store models on-disk then - // load by package. - - outputModel := model.Model{} - - // Prunes old objects from outputModel if they exist. - latestPruneFromOutput := func() error { - - for _, outputPkg := range outputModel { - oldPkg, oldHasPkg := oldModel[outputPkg.Name] - if !oldHasPkg { - // outputPkg was already copied to outputModel above. - continue - } - if err := pruneOldFromNewPackage(oldPkg, outputPkg); err != nil { - return err - } - if len(outputPkg.Channels) == 0 { - // Remove empty packages. - delete(outputModel, outputPkg.Name) - } - } - - return nil - } - - headsOnlyMode := g.HeadsOnly - latestMode := !headsOnlyMode - isInclude := len(g.Includer.Packages) != 0 - - switch { - case !g.IncludeAdditively && isInclude: // Only diff between included objects. - - // Add included packages/channels/bundles from newModel to outputModel. - if err := g.Includer.Run(newModel, outputModel); err != nil { - return nil, err - } - - if latestMode { - if err := latestPruneFromOutput(); err != nil { - return nil, err - } - } - - for _, outputPkg := range outputModel { - for _, ch := range outputPkg.Channels { - if len(ch.Bundles) == 0 { - delete(outputPkg.Channels, ch.Name) - } - } - if len(outputPkg.Channels) == 0 { - // Remove empty packages. - delete(outputModel, outputPkg.Name) - } - } - case isInclude: // Add included objects to outputModel. - - // Assume heads-only is false for include additively since we already have the channel heads - // in the output model. - g.Includer.HeadsOnly = false - // Add included packages/channels/bundles from newModel to outputModel. - if err := g.Includer.Run(newModel, outputModel); err != nil { - return nil, err - } - - fallthrough - default: - - if headsOnlyMode { // Net-new diff of heads only. - - // Make shallow copies of packages and channels that are only - // filled with channel heads. - for _, newPkg := range newModel { - // This package may have been created in the include step. - outputPkg, pkgIncluded := outputModel[newPkg.Name] - if !pkgIncluded { - outputPkg = copyPackageNoChannels(newPkg) - outputModel[outputPkg.Name] = outputPkg - } - for _, newCh := range newPkg.Channels { - if _, chIncluded := outputPkg.Channels[newCh.Name]; chIncluded { - // Head (and other bundles) were added in the include step. - continue - } - outputCh := copyChannelNoBundles(newCh, outputPkg) - outputPkg.Channels[outputCh.Name] = outputCh - head, err := newCh.Head() - if err != nil { - return nil, err - } - outputBundle := copyBundle(head, outputCh, outputPkg) - outputModel.AddBundle(*outputBundle) - } - } - - } else { // Diff between old and new Model. - - // Copy newModel to create an output model by deletion, - // which is more succinct than by addition. - for _, newPkg := range newModel { - if _, pkgIncluded := outputModel[newPkg.Name]; pkgIncluded { - // The user has specified the state they want this package to have in the diff - // via an inclusion entry, so the package created above should not be changed. - continue - } - outputModel[newPkg.Name] = copyPackage(newPkg) - } - - if err := latestPruneFromOutput(); err != nil { - return nil, err - } - - } - - } - - if !g.SkipDependencies { - // Add dependencies to outputModel not already present in oldModel. - if err := addAllDependencies(newModel, oldModel, outputModel); err != nil { - return nil, err - } - } - - // Default channel may not have been copied, so set it to the new default channel here. - for _, outputPkg := range outputModel { - newPkg := newModel[outputPkg.Name] - var outputHasDefault bool - outputPkg.DefaultChannel, outputHasDefault = outputPkg.Channels[newPkg.DefaultChannel.Name] - if !outputHasDefault { - // Create a name-only channel since oldModel contains the channel already. - outputPkg.DefaultChannel = copyChannelNoBundles(newPkg.DefaultChannel, outputPkg) - } - } - - return outputModel, nil -} - -// pruneOldFromNewPackage prune any bundles and channels from newPkg that -// are in oldPkg, but not those that differ in any way. -func pruneOldFromNewPackage(oldPkg, newPkg *model.Package) error { - for _, newCh := range newPkg.Channels { - oldCh, oldHasCh := oldPkg.Channels[newCh.Name] - if !oldHasCh { - // newCh is assumed to have been copied to outputModel by the caller. - continue - } - - for _, newBundle := range newCh.Bundles { - oldBundle, oldHasBundle := oldCh.Bundles[newBundle.Name] - if !oldHasBundle { - // newBundle is copied to outputModel by the caller if it is a channel head. - continue - } - equal, err := bundlesEqual(oldBundle, newBundle) - if err != nil { - return err - } - if equal { - delete(newCh.Bundles, newBundle.Name) - } - } - if len(newCh.Bundles) == 0 { - // Remove empty channels. - delete(newPkg.Channels, newCh.Name) - } - } - - return nil -} - -// bundlesEqual computes then compares the hashes of b1 and b2 for equality. -func bundlesEqual(b1, b2 *model.Bundle) (bool, error) { - // Use a declarative config bundle type to avoid infinite recursion. - dcBundle1 := convertFromModelBundle(b1) - dcBundle2 := convertFromModelBundle(b2) - - hash1, err := hashstructure.Hash(dcBundle1, hashstructure.FormatV2, nil) - if err != nil { - return false, err - } - hash2, err := hashstructure.Hash(dcBundle2, hashstructure.FormatV2, nil) - if err != nil { - return false, err - } - // CsvJSON and Objects are ignored by Hash, so they must be compared separately. - return hash1 == hash2 && b1.CsvJSON == b2.CsvJSON && reflect.DeepEqual(b1.Objects, b2.Objects), nil -} - -func addAllDependencies(newModel, oldModel, outputModel model.Model) error { - // Get every oldModel's bundle's dependencies, and their dependencies, etc. by BFS. - providingBundlesByPackage := map[string][]*model.Bundle{} - var visitedBundles []*model.Bundle - for currentList := getBundles(outputModel); len(currentList) != 0; { - visitedBundles = append(visitedBundles, currentList...) - reqGVKs, reqPkgs, err := findDependencies(currentList) - if err != nil { - return err - } - // Break early so the entire source model is not iterated through unnecessarily. - if len(reqGVKs) == 0 && len(reqPkgs) == 0 { - break - } - currentList = nil - // Get bundles that provide dependencies from newModel, which should have - // the latest bundles of each dependency package. - for _, pkg := range newModel { - providingBundles := getBundlesThatProvide(pkg, reqGVKs, reqPkgs) - unvisitedProvidingBundles := difference(visitedBundles, providingBundles) - currentList = append(currentList, unvisitedProvidingBundles...) - - oldPkg, oldHasPkg := oldModel[pkg.Name] - for _, b := range unvisitedProvidingBundles { - // If the bundle is not in oldModel, add it to the set. - // outputModel is checked below. - add := true - if oldHasPkg { - if oldCh, oldHasCh := oldPkg.Channels[b.Channel.Name]; oldHasCh { - _, oldHasBundle := oldCh.Bundles[b.Name] - add = !oldHasBundle - } - } - if add { - providingBundlesByPackage[b.Package.Name] = append(providingBundlesByPackage[b.Package.Name], b) - } - } - } - } - - // Add the diff between an oldModel dependency package and its new counterpart - // or the entire package if oldModel does not have it. - for pkgName, bundles := range providingBundlesByPackage { - newPkg := newModel[pkgName] - heads := make(map[string]*model.Bundle, len(newPkg.Channels)) - for _, ch := range newPkg.Channels { - var err error - if heads[ch.Name], err = ch.Head(); err != nil { - return err - } - } - - // Sort by version then channel so bundles lower in the full graph are more likely - // to be included in previous loops. - sort.Slice(bundles, func(i, j int) bool { - if bundles[i].Channel.Name == bundles[j].Channel.Name { - return bundles[i].Version.LT(bundles[j].Version) - } - return bundles[i].Channel.Name < bundles[j].Channel.Name - }) - - for _, b := range bundles { - newCh := b.Channel - - // Continue if b was added in a previous loop iteration. - // Otherwise create a new package/channel for b if they do not exist. - var ( - outputPkg *model.Package - outputCh *model.Channel - - outHasPkg, outHasCh bool - ) - if outputPkg, outHasPkg = outputModel[b.Package.Name]; outHasPkg { - if outputCh, outHasCh = outputPkg.Channels[b.Channel.Name]; outHasCh { - if _, outputHasBundle := outputCh.Bundles[b.Name]; outputHasBundle { - continue - } - } - } else { - outputPkg = copyPackageNoChannels(newPkg) - outputModel[outputPkg.Name] = outputPkg - } - if !outHasCh { - outputCh = copyChannelNoBundles(newCh, outputPkg) - outputPkg.Channels[outputCh.Name] = outputCh - } - - head := heads[newCh.Name] - graph := makeUpgradeGraph(newCh) - intersectingBundles, intersectionFound := findIntersectingBundles(newCh, b, head, graph) - if !intersectionFound { - // This should never happen, since b and head are from the same model. - return fmt.Errorf("channel %s: head %q not reachable from bundle %q", newCh.Name, head.Name, b.Name) - } - for _, ib := range intersectingBundles { - if _, outHasBundle := outputCh.Bundles[ib.Name]; !outHasBundle { - outputCh.Bundles[ib.Name] = copyBundle(ib, outputCh, outputPkg) - } - } - } - } - - return nil -} - -func difference(a, b []*model.Bundle) []*model.Bundle { - aMap := make(map[*model.Bundle]struct{}) - for _, bd := range a { - aMap[bd] = struct{}{} - } - uniqueBundles := make([]*model.Bundle, 0) - for _, bd := range b { - if _, present := aMap[bd]; !present { - uniqueBundles = append(uniqueBundles, bd) - } - } - return uniqueBundles -} - -// getBundles collects all bundles specified by m. Since each bundle -// references its package, their uniqueness property holds in a flat list. -func getBundles(m model.Model) (bundles []*model.Bundle) { - for _, pkg := range m { - for _, ch := range pkg.Channels { - for _, b := range ch.Bundles { - bundles = append(bundles, b) - } - } - } - return bundles -} - -// findDependencies finds all GVK and package dependencies and indexes them -// by the apropriate key for lookups. -func findDependencies(bundles []*model.Bundle) (map[property.GVK]struct{}, map[string][]semver.Range, error) { - // Find all dependencies of bundles in the output model. - reqGVKs := map[property.GVK]struct{}{} - reqPkgs := map[string][]semver.Range{} - for _, b := range bundles { - - for _, gvkReq := range b.PropertiesP.GVKsRequired { - gvk := property.GVK{ - Group: gvkReq.Group, - Version: gvkReq.Version, - Kind: gvkReq.Kind, - } - reqGVKs[gvk] = struct{}{} - } - - for _, pkgReq := range b.PropertiesP.PackagesRequired { - var inRange semver.Range - if pkgReq.VersionRange != "" { - var err error - if inRange, err = semver.ParseRange(pkgReq.VersionRange); err != nil { - // Should never happen since model has been validated. - return nil, nil, err - } - } else { - // Any bundle in this package will satisfy a range-less package requirement. - inRange = func(semver.Version) bool { return true } - } - reqPkgs[pkgReq.PackageName] = append(reqPkgs[pkgReq.PackageName], inRange) - } - } - - return reqGVKs, reqPkgs, nil -} - -// getBundlesThatProvide returns the latest-version bundles in pkg that provide -// a GVK or version in reqGVKs or reqPkgs, respectively. -func getBundlesThatProvide(pkg *model.Package, reqGVKs map[property.GVK]struct{}, reqPkgs map[string][]semver.Range) (providingBundles []*model.Bundle) { - // Pre-allocate the amount of space needed for all ranges - // specified by requiring bundles. - var bundlesByRange [][]*model.Bundle - ranges, isPkgRequired := reqPkgs[pkg.Name] - if isPkgRequired { - bundlesByRange = make([][]*model.Bundle, len(ranges)) - } - // Collect package bundles that provide a GVK or are in a range. - bundlesProvidingGVK := make(map[property.GVK][]*model.Bundle) - for _, ch := range pkg.Channels { - for _, b := range ch.Bundles { - for _, gvk := range b.PropertiesP.GVKs { - if _, hasGVK := reqGVKs[gvk]; hasGVK { - bundlesProvidingGVK[gvk] = append(bundlesProvidingGVK[gvk], b) - } - } - for i, inRange := range ranges { - if inRange(b.Version) { - bundlesByRange[i] = append(bundlesByRange[i], b) - } - } - } - } - - // Sort bundles providing a GVK by version and use the latest version. - latestBundles := make(map[string]*model.Bundle) - for gvk, bundles := range bundlesProvidingGVK { - sort.Slice(bundles, func(i, j int) bool { - // sort by version - sortedByVersion := bundles[i].Version.LT(bundles[j].Version) - - // sort by channel - // prioritize default channel bundles - if bundles[i].Version.EQ(bundles[j].Version) { - return bundles[i].Channel != pkg.DefaultChannel - } - return sortedByVersion - }) - lb := bundles[len(bundles)-1] - latestBundles[lb.Version.String()] = lb - delete(reqGVKs, gvk) - } - - // Sort bundles in a range by version and use the latest version. - unsatisfiedRanges := []semver.Range{} - for i, bundlesInRange := range bundlesByRange { - if len(bundlesInRange) == 0 { - unsatisfiedRanges = append(unsatisfiedRanges, ranges[i]) - continue - } - sort.Slice(bundlesInRange, func(i, j int) bool { - // sort by version - sortedByVersion := bundlesInRange[i].Version.LT(bundlesInRange[j].Version) - - // sort by channel - // prioritize default channel bundles - if bundlesInRange[i].Version.EQ(bundlesInRange[j].Version) { - return bundlesInRange[i].Channel != pkg.DefaultChannel - } - return sortedByVersion - }) - lb := bundlesInRange[len(bundlesInRange)-1] - latestBundles[lb.Version.String()] = lb - } - if isPkgRequired && len(unsatisfiedRanges) == 0 { - delete(reqPkgs, pkg.Name) - } - // TODO(estroz): handle missed ranges with logs. - - // Return deduplicated bundles that provide GVKs/versions. - for _, b := range latestBundles { - providingBundles = append(providingBundles, b) - } - return providingBundles -} - -func convertFromModelBundle(b *model.Bundle) Bundle { - return Bundle{ - Schema: schemaBundle, - Name: b.Name, - Package: b.Package.Name, - Image: b.Image, - RelatedImages: modelRelatedImagesToRelatedImages(b.RelatedImages), - CsvJSON: b.CsvJSON, - Objects: b.Objects, - Properties: b.Properties, - } -} - -func copyPackageNoChannels(in *model.Package) *model.Package { - cp := &model.Package{ - Name: in.Name, - Description: in.Description, - Channels: make(map[string]*model.Channel, len(in.Channels)), - } - if in.Icon != nil { - cp.Icon = &model.Icon{ - Data: make([]byte, len(in.Icon.Data)), - MediaType: in.Icon.MediaType, - } - copy(cp.Icon.Data, in.Icon.Data) - } - return cp -} - -func copyPackage(in *model.Package) *model.Package { - cp := copyPackageNoChannels(in) - for _, ch := range in.Channels { - cp.Channels[ch.Name] = copyChannel(ch, cp) - } - return cp -} - -func copyChannelNoBundles(in *model.Channel, pkg *model.Package) *model.Channel { - cp := &model.Channel{ - Name: in.Name, - Package: pkg, - Bundles: make(map[string]*model.Bundle, len(in.Bundles)), - } - return cp -} - -func copyChannel(in *model.Channel, pkg *model.Package) *model.Channel { - cp := copyChannelNoBundles(in, pkg) - for _, b := range in.Bundles { - cp.Bundles[b.Name] = copyBundle(b, cp, pkg) - } - return cp -} - -func copyBundle(in *model.Bundle, ch *model.Channel, pkg *model.Package) *model.Bundle { - cp := &model.Bundle{ - Name: in.Name, - Channel: ch, - Package: pkg, - Image: in.Image, - Replaces: in.Replaces, - Version: semver.MustParse(in.Version.String()), - CsvJSON: in.CsvJSON, - SkipRange: in.SkipRange, - } - if in.PropertiesP != nil { - cp.PropertiesP = new(property.Properties) - *cp.PropertiesP = *in.PropertiesP - } - if len(in.Skips) != 0 { - cp.Skips = make([]string, len(in.Skips)) - copy(cp.Skips, in.Skips) - } - if len(in.Properties) != 0 { - cp.Properties = make([]property.Property, len(in.Properties)) - copy(cp.Properties, in.Properties) - } - if len(in.RelatedImages) != 0 { - cp.RelatedImages = make([]model.RelatedImage, len(in.RelatedImages)) - copy(cp.RelatedImages, in.RelatedImages) - } - if len(in.Objects) != 0 { - cp.Objects = make([]string, len(in.Objects)) - copy(cp.Objects, in.Objects) - } - return cp -} diff --git a/alpha/declcfg/diff_include.go b/alpha/declcfg/diff_include.go deleted file mode 100644 index d92f76877..000000000 --- a/alpha/declcfg/diff_include.go +++ /dev/null @@ -1,444 +0,0 @@ -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" -) - -// DiffIncluder knows how to add packages, channels, and bundles -// from a source to a destination model.Model. -type DiffIncluder struct { - // Packages to add. - Packages []DiffIncludePackage - Logger *logrus.Entry - // HeadsOnly is the mode that selects the head of the channels only. - // This setting will be overridden by any versions or bundles in the channels. - HeadsOnly bool -} - -// DiffIncludePackage specifies a package, and optionally channels -// or a set of bundles from all channels (wrapped by a DiffIncludeChannel), -// to include. -type DiffIncludePackage struct { - // Name of package. - Name string - // Channels in package. - Channels []DiffIncludeChannel - // AllChannels contains bundle versions in package. - // Upgrade graphs from all channels in the named package containing a version - // from this field are included. - AllChannels DiffIncludeChannel - // The semver range of bundle versions. - // Package range setting is mutually exclusive with channel range/bundles/version - // settings. - Range semver.Range - // HeadsOnly is the mode that selects the head of the channels only. - // This setting will be overridden by any versions or bundles in the channels. - HeadsOnly bool -} - -// DiffIncludeChannel specifies a channel, and optionally bundles and bundle versions -// (or version range) to include. -type DiffIncludeChannel struct { - // Name of channel. - Name string - // Versions of bundles. - Versions []semver.Version - // Bundles are bundle names to include. - // Set this field only if the named bundle has no semantic version metadata. - Bundles []string - // The semver range of bundle versions. - // Range setting is mutually exclusive with Versions and Bundles settings. - Range semver.Range -} - -func (dip DiffIncludePackage) Validate() error { - var errs []error - if dip.Name == "" { - errs = append(errs, fmt.Errorf("missing package name")) - } - - var isChannelSet bool - for _, ch := range dip.Channels { - isChannelSet = ch.isChannelSet() - err := ch.Validate() - if err != nil { - errs = append(errs, err) - } - } - - isChannelSet = dip.AllChannels.isChannelSet() - if isChannelSet && dip.Range != nil { - errs = append(errs, fmt.Errorf("package range setting is mutually exclusive with channel versions/bundles/range settings")) - } - - if len(errs) != 0 { - return fmt.Errorf("invalid DiffIncludePackage config for package %q:\n%v", dip.Name, utilerrors.NewAggregate(errs)) - } - return nil -} - -// isChannelSet returns true if at least one of Range/Bundles/Versions is set -func (dic DiffIncludeChannel) isChannelSet() bool { - return dic.Range != nil || len(dic.Versions) != 0 || len(dic.Bundles) != 0 -} - -func (dic DiffIncludeChannel) Validate() error { - var errs []error - if dic.Name == "" { - errs = append(errs, fmt.Errorf("missing channel name")) - } - - if dic.Range != nil && (len(dic.Versions) != 0 || len(dic.Bundles) != 0) { - errs = append(errs, fmt.Errorf("Channel %q: range and versions/bundles are mutually exclusive", dic.Name)) - } - - if len(errs) != 0 { - return fmt.Errorf("invalid DiffIncludeChannel config for channel %q:\n%v", dic.Name, utilerrors.NewAggregate(errs)) - } - return nil -} - -func (i DiffIncluder) Validate() error { - var errs []error - for _, pkg := range i.Packages { - err := pkg.Validate() - if err != nil { - errs = append(errs, err) - } - } - - if len(errs) != 0 { - return fmt.Errorf("invalid DiffIncluder config:\n%v", utilerrors.NewAggregate(errs)) - } - return nil -} - -// Run adds all packages and channels in DiffIncluder with matching names -// directly, and all versions plus their upgrade graphs to channel heads, -// from newModel to outputModel. -func (i DiffIncluder) Run(newModel, outputModel model.Model) error { - var includeErrs []error - if err := i.Validate(); err != nil { - includeErrs = append(includeErrs, err) - return fmt.Errorf("invalid DiffIncluder config:\n%v", utilerrors.NewAggregate(includeErrs)) - } - - for _, ipkg := range i.Packages { - pkgLog := i.Logger.WithField("package", ipkg.Name) - ipkg.HeadsOnly = i.HeadsOnly - includeErrs = append(includeErrs, ipkg.includeNewInOutputModel(newModel, outputModel, pkgLog)...) - } - if len(includeErrs) != 0 { - return fmt.Errorf("error including items:\n%v", utilerrors.NewAggregate(includeErrs)) - } - return nil -} - -// includeNewInOutputModel adds all packages, channels, and range (or versions/bundles) -// specified by ipkg that exist in newModel to outputModel. Any package, channel, -// or version in ipkg not satisfied by newModel is an error. -func (ipkg DiffIncludePackage) includeNewInOutputModel(newModel, outputModel model.Model, logger *logrus.Entry) (ierrs []error) { - - newPkg, newHasPkg := newModel[ipkg.Name] - if !newHasPkg { - ierrs = append(ierrs, fmt.Errorf("[package=%q] package does not exist in new model", ipkg.Name)) - return ierrs - } - pkgLog := logger.WithField("package", newPkg.Name) - - // No range, channels or versions were specified - if len(ipkg.Channels) == 0 && len(ipkg.AllChannels.Versions) == 0 && len(ipkg.AllChannels.Bundles) == 0 && ipkg.Range == nil { - // heads-only false, meaning "include the full package". - if !ipkg.HeadsOnly { - outputModel[ipkg.Name] = newPkg - return nil - } - // heads-only true, get the head of every channel in the package - for _, c := range newPkg.Channels { - newCh := DiffIncludeChannel{ - Name: c.Name, - } - ipkg.Channels = append(ipkg.Channels, newCh) - } - } - - outputPkg := copyPackageNoChannels(newPkg) - outputModel[outputPkg.Name] = outputPkg - skipMissingBundleForChannels := map[string]bool{} - if ipkg.Range != nil { - if len(ipkg.Channels) != 0 { - for _, ich := range ipkg.Channels { - if ich.Range != nil { - ierrs = append(ierrs, fmt.Errorf("[package=%q channel=%q] range setting is mutually exclusive between package and channel", newPkg.Name, ich.Name)) - } - } - } else { - // Add package range setting to all existing channels if there is no - // channel setting in the config - for newChName := range newPkg.Channels { - ipkg.Channels = append(ipkg.Channels, DiffIncludeChannel{ - Name: newChName, - Range: ipkg.Range, - }) - } - } - } else { - // Add all channels to ipkg.Channels if bundles or versions were specified to include across all channels. - // skipMissingBundleForChannels's value for a channel will be true IFF at least one version is specified, - // since some other channel may contain that version. - if len(ipkg.AllChannels.Versions) != 0 || len(ipkg.AllChannels.Bundles) != 0 { - for newChName := range newPkg.Channels { - ipkg.Channels = append(ipkg.Channels, DiffIncludeChannel{ - Name: newChName, - Versions: ipkg.AllChannels.Versions, - Bundles: ipkg.AllChannels.Bundles, - }) - skipMissingBundleForChannels[newChName] = true - } - } - } - - for _, ich := range ipkg.Channels { - newCh, pkgHasCh := newPkg.Channels[ich.Name] - if !pkgHasCh { - ierrs = append(ierrs, fmt.Errorf("[package=%q channel=%q] channel does not exist in new model", newPkg.Name, ich.Name)) - continue - } - chLog := pkgLog.WithField("channel", newCh.Name) - - var bundles []*model.Bundle - var head *model.Bundle - var err error - // No versions have been specified, but heads-only set to true, get the channel head only. - switch { - case ipkg.HeadsOnly && len(ich.Versions) == 0 && len(ich.Bundles) == 0 && ich.Range == nil: - head, err = newCh.Head() - bundles = append(bundles, head) - case ich.Range != nil: - bundles, err = getBundlesForRange(newCh, ich.Range, chLog) - default: - bundles, err = getBundlesForVersions(newCh, ich.Versions, ich.Bundles, chLog, skipMissingBundleForChannels[newCh.Name]) - } - - if err != nil { - ierrs = append(ierrs, fmt.Errorf("[package=%q channel=%q] %v", newPkg.Name, newCh.Name, err)) - continue - } - - outputCh := copyChannelNoBundles(newCh, outputPkg) - outputPkg.Channels[outputCh.Name] = outputCh - for _, b := range bundles { - tb := copyBundle(b, outputCh, outputPkg) - outputCh.Bundles[tb.Name] = tb - } - } - - return ierrs -} - -// getBundlesForVersions returns all bundles matching a version in vers -// and their upgrade graph(s) to ch.Head(). -// If skipMissingBundles is true, bundle names and versions not satisfied by bundles in ch -// will not result in errors. -func getBundlesForVersions(ch *model.Channel, vers []semver.Version, names []string, logger *logrus.Entry, skipMissingBundles bool) (bundles []*model.Bundle, err error) { - - // Short circuit when no versions were specified, meaning "include the whole channel". - if len(vers) == 0 { - for _, b := range ch.Bundles { - bundles = append(bundles, b) - } - return bundles, nil - } - - // Add every bundle with a specified bundle name or directly satisfying a bundle version to bundles. - versionsToInclude := make(map[string]struct{}, len(vers)) - for _, ver := range vers { - versionsToInclude[ver.String()] = struct{}{} - } - namesToInclude := make(map[string]struct{}, len(vers)) - for _, name := range names { - namesToInclude[name] = struct{}{} - } - for _, b := range ch.Bundles { - _, includeVersionedBundle := versionsToInclude[b.Version.String()] - _, includeNamedBundle := namesToInclude[b.Name] - if includeVersionedBundle || includeNamedBundle { - bundles = append(bundles, b) - } - } - - // Some version was not satisfied by this channel. - if len(bundles) != len(versionsToInclude)+len(namesToInclude) && !skipMissingBundles { - for _, b := range bundles { - delete(versionsToInclude, b.Version.String()) - delete(namesToInclude, b.Name) - } - var verStrs, nameStrs []string - for verStr := range versionsToInclude { - verStrs = append(verStrs, verStr) - } - for nameStr := range namesToInclude { - nameStrs = append(nameStrs, nameStr) - } - sb := strings.Builder{} - if len(verStrs) != 0 { - sb.WriteString(fmt.Sprintf("versions=%+q ", verStrs)) - } - if len(nameStrs) != 0 { - sb.WriteString(fmt.Sprintf("names=%+q", nameStrs)) - } - return nil, fmt.Errorf("bundles do not exist in channel: %s", strings.TrimSpace(sb.String())) - } - - bundles, err = fillUpgradeGraph(ch, bundles, logger) - if err != nil { - return nil, err - } - return bundles, nil -} - -// getBundlesForRange returns all bundles matching the version range in vers -// If the range is nil, return all bundles in the channel -func getBundlesForRange(ch *model.Channel, vers semver.Range, logger *logrus.Entry) (bundles []*model.Bundle, err error) { - // Short circuit when an empty range was specified, meaning "include the whole channel" - if vers == nil { - for _, b := range ch.Bundles { - bundles = append(bundles, b) - } - return bundles, nil - } - - for _, b := range ch.Bundles { - v, err := semver.Parse(b.Version.String()) - if err != nil { - return nil, fmt.Errorf("unable to parse bunble version: %s", err.Error()) - } - if vers(v) { - bundles = append(bundles, b) - } - } - - return bundles, nil -} - -// fillUpgradeGraph fills in the upgrade graph between each bundle and head. -// Regardless of semver order, this step needs to be performed -// for each included bundle because there might be leaf nodes -// in the upgrade graph for a particular version not captured -// by any other fill due to skips not being honored here. -func fillUpgradeGraph(ch *model.Channel, bundles []*model.Bundle, logger *logrus.Entry) (bd []*model.Bundle, err error) { - head, err := ch.Head() - if err != nil { - return nil, err - } - graph := makeUpgradeGraph(ch) - bundleSet := map[string]*model.Bundle{} - for _, ib := range bundles { - if _, addedBundle := bundleSet[ib.Name]; addedBundle { - // A prior graph traverse already included this bundle. - continue - } - intersectingBundles, intersectionFound := findIntersectingBundles(ch, ib, head, graph) - if !intersectionFound { - logger.Debugf("channel head %q not reachable from bundle %q, adding without upgrade graph", head.Name, ib.Name) - bundleSet[ib.Name] = ib - } - - for _, rb := range intersectingBundles { - bundleSet[rb.Name] = rb - } - } - - for _, b := range bundleSet { - bundles = append(bundles, b) - } - return bundles, nil -} - -// makeUpgradeGraph creates a DAG of bundles with map key Bundle.Replaces. -func makeUpgradeGraph(ch *model.Channel) map[string][]*model.Bundle { - graph := map[string][]*model.Bundle{} - for _, b := range ch.Bundles { - if b.Replaces != "" { - graph[b.Replaces] = append(graph[b.Replaces], b) - } - } - return graph -} - -// findIntersectingBundles finds the intersecting bundle of start and end in the -// replaces upgrade graph graph by traversing down to the lowest graph node, -// then returns every bundle higher than the intersection. It is possible -// to find no intersection; this should only happen when start and end -// are not part of the same upgrade graph. -// Output bundle order is not guaranteed. -// Precondition: start must be a bundle in ch. -// Precondition: end must be ch's head. -func findIntersectingBundles(ch *model.Channel, start, end *model.Bundle, graph map[string][]*model.Bundle) ([]*model.Bundle, bool) { - // The intersecting set is equal to end if start is end. - if start.Name == end.Name { - return []*model.Bundle{end}, true - } - - // Construct start's replaces chain for comparison against end's. - startChain := map[string]*model.Bundle{start.Name: nil} - for curr := start; curr != nil && curr.Replaces != ""; curr = ch.Bundles[curr.Replaces] { - startChain[curr.Replaces] = curr - } - - // Trace end's replaces chain until it intersects with start's, or the root is reached. - var intersection string - if _, inChain := startChain[end.Name]; inChain { - intersection = end.Name - } else { - for curr := end; curr != nil && curr.Replaces != ""; curr = ch.Bundles[curr.Replaces] { - if _, inChain := startChain[curr.Replaces]; inChain { - intersection = curr.Replaces - break - } - } - } - - // No intersection is found, delegate behavior to caller. - if intersection == "" { - return nil, false - } - - // Find all bundles that replace the intersection via BFS, - // i.e. the set of bundles that fill the update graph between start and end. - replacesIntersection := graph[intersection] - replacesSet := map[string]*model.Bundle{} - for _, b := range replacesIntersection { - currName := "" - for next := []*model.Bundle{b}; len(next) > 0; next = next[1:] { - currName = next[0].Name - if _, hasReplaces := replacesSet[currName]; !hasReplaces { - replacers := graph[currName] - next = append(next, replacers...) - replacesSet[currName] = ch.Bundles[currName] - } - } - } - - // Remove every bundle between start and intersection exclusively, - // since these bundles must already exist in the destination channel. - for rep := start; rep != nil && rep.Name != intersection; rep = ch.Bundles[rep.Replaces] { - delete(replacesSet, rep.Name) - } - - // Ensure both start and end are added to the output. - replacesSet[start.Name] = start - replacesSet[end.Name] = end - var intersectingBundles []*model.Bundle - for _, b := range replacesSet { - intersectingBundles = append(intersectingBundles, b) - } - return intersectingBundles, true -} diff --git a/alpha/declcfg/diff_include_test.go b/alpha/declcfg/diff_include_test.go deleted file mode 100644 index 2a5467b0b..000000000 --- a/alpha/declcfg/diff_include_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package declcfg - -import ( - "fmt" - "sort" - "strings" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/operator-framework/operator-registry/alpha/model" - "github.com/operator-framework/operator-registry/alpha/property" -) - -func TestFindIntersectingBundles(t *testing.T) { - type bundleSpec struct { - name, replaces string - skips []string - } - - inputBundles1 := []bundleSpec{ - {"foo.v0.1.0", "", nil}, - {"foo.v0.1.1", "foo.v0.1.0", nil}, - {"foo.v0.2.0", "foo.v0.1.1", nil}, - {"foo.v0.3.0", "foo.v0.2.0", nil}, - {"foo.v1.0.0", "foo.v0.1.0", []string{"foo.v0.1.1", "foo.v0.2.0", "foo.v0.3.0"}}, - {"foo.v1.1.0", "foo.v1.0.0", nil}, - {"foo.v2.0.0", "foo.v0.1.0", []string{"foo.v0.1.1", "foo.v0.2.0", "foo.v0.3.0", "foo.v1.0.0", "foo.v1.1.0"}}, - {"foo.v2.1.0", "foo.v2.0.0", nil}, - {"foo.v3.0.0", "foo.v1.1.0", []string{"foo.v2.0.0", "foo.v2.1.0"}}, - } - - type spec struct { - name string - pkgName string - channelName string - inputBundles []bundleSpec - start, end bundleSpec - headName string - assertion require.BoolAssertionFunc - expIntersecting []bundleSpec - } - - specs := []spec{ - { - name: "Success/StartEndEqual", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.2.0", "foo.v0.1.1", nil}, - end: bundleSpec{"foo.v0.2.0", "foo.v0.1.1", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: []bundleSpec{{"foo.v0.2.0", "foo.v0.1.1", nil}}, - }, - { - name: "Success/FullGraph", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.1.0", "", nil}, - end: bundleSpec{"foo.v3.0.0", "foo.v1.1.0", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: inputBundles1, - }, - { - name: "Success/SubGraph1", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.2.0", "foo.v0.1.1", nil}, - end: bundleSpec{"foo.v3.0.0", "foo.v1.1.0", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: []bundleSpec{ - {"foo.v0.2.0", "foo.v0.1.1", nil}, - {"foo.v0.3.0", "foo.v0.2.0", nil}, - {"foo.v1.0.0", "foo.v0.1.0", []string{"foo.v0.1.1", "foo.v0.2.0", "foo.v0.3.0"}}, - {"foo.v1.1.0", "foo.v1.0.0", nil}, - {"foo.v2.0.0", "foo.v0.1.0", []string{"foo.v0.1.1", "foo.v0.2.0", "foo.v0.3.0", "foo.v1.0.0", "foo.v1.1.0"}}, - {"foo.v2.1.0", "foo.v2.0.0", nil}, - {"foo.v3.0.0", "foo.v1.1.0", []string{"foo.v2.0.0", "foo.v2.1.0"}}, - }, - }, - { - name: "Success/SubGraph2", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.1.1", "foo.v0.1.0", nil}, - end: bundleSpec{"foo.v0.3.0", "foo.v0.2.0", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: []bundleSpec{ - {"foo.v0.1.1", "foo.v0.1.0", nil}, - {"foo.v0.2.0", "foo.v0.1.1", nil}, - {"foo.v0.3.0", "foo.v0.2.0", nil}, - }, - }, - { - // This case returns inputBundles1 minus foo.v0.1.0, which is the intersection, - // because foo.v2.0.0 is a leaf node (disregarding skips). - name: "Success/SubGraph3", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.1.1", "foo.v0.1.0", nil}, - end: bundleSpec{"foo.v2.0.0", "foo.v0.1.0", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: inputBundles1[1:], - }, - { - // Even though foo.v0.4.0 is not in the channel, it's replaces (foo.v0.1.1) is. - name: "Success/ReplacesInChannel", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.2.0", "foo.v0.1.1", nil}, - end: bundleSpec{"foo.v0.4.0", "foo.v0.1.1", nil}, - headName: "foo.v3.0.0", - assertion: require.True, - expIntersecting: []bundleSpec{ - {"foo.v0.2.0", "foo.v0.1.1", nil}, - {"foo.v0.3.0", "foo.v0.2.0", nil}, - {"foo.v0.4.0", "foo.v0.1.1", nil}, - }, - }, - { - name: "Fail/ReplacesNotInChannel", - inputBundles: inputBundles1, - start: bundleSpec{"foo.v0.2.0", "foo.v0.1.1", nil}, - end: bundleSpec{"foo.v0.4.0", "foo.v0.1.2", nil}, - headName: "foo.v3.0.0", - assertion: require.False, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - // Construct test - pkg := &model.Package{Name: "foo"} - ch := &model.Channel{Name: "stable", Bundles: make(map[string]*model.Bundle, len(s.inputBundles))} - ch.Package = pkg - for _, b := range s.inputBundles { - ch.Bundles[b.name] = newReplacingBundle(b.name, b.replaces, b.skips, ch, pkg) - } - expIntersecting := make([]*model.Bundle, len(s.expIntersecting)) - for i, b := range s.expIntersecting { - expIntersecting[i] = newReplacingBundle(b.name, b.replaces, b.skips, ch, pkg) - } - - // Ensure the channel is valid and has the correct head. - require.NoError(t, ch.Validate()) - head, err := ch.Head() - require.NoError(t, err) - require.Equal(t, ch.Bundles[s.headName], head) - - start := newReplacingBundle(s.start.name, s.start.replaces, s.start.skips, ch, pkg) - end := newReplacingBundle(s.end.name, s.end.replaces, s.end.skips, ch, pkg) - graph := makeUpgradeGraph(ch) - intersecting, found := findIntersectingBundles(ch, start, end, graph) - s.assertion(t, found) - // Compare bundle names only, since mismatch output is too verbose. - require.ElementsMatch(t, getBundleNames(expIntersecting), getBundleNames(intersecting)) - }) - } - -} - -func newReplacingBundle(name, replaces string, skips []string, ch *model.Channel, pkg *model.Package) *model.Bundle { - split := strings.SplitN(name, ".", 2) - nameStr, verStr := split[0], split[1] - b := &model.Bundle{ - Name: name, - Replaces: replaces, - Skips: skips, - Channel: ch, - Package: pkg, - Image: fmt.Sprintf("namespace/%s:%s", nameStr, verStr), - Properties: []property.Property{ - property.MustBuildPackage(ch.Package.Name, verStr), - }, - } - return b -} - -func getBundleNames(bundles []*model.Bundle) (names []string) { - for _, b := range bundles { - names = append(names, b.Name) - } - sort.Strings(names) - return names -} diff --git a/alpha/declcfg/diff_test.go b/alpha/declcfg/diff_test.go deleted file mode 100644 index 19d7104f6..000000000 --- a/alpha/declcfg/diff_test.go +++ /dev/null @@ -1,3436 +0,0 @@ -package declcfg - -import ( - "testing" - - "github.com/blang/semver/v4" - "github.com/stretchr/testify/require" - - "github.com/operator-framework/operator-registry/alpha/model" - "github.com/operator-framework/operator-registry/alpha/property" -) - -type deprecated struct{} - -const deprecatedType = "olm.deprecated" - -func init() { - property.AddToScheme(deprecatedType, &deprecated{}) -} - -func TestDiffLatest(t *testing.T) { - type spec struct { - name string - g *DiffGenerator - oldCfg DeclarativeConfig - newCfg DeclarativeConfig - expCfg DeclarativeConfig - assertion require.ErrorAssertionFunc - } - - specs := []spec{ - { - name: "NoDiff/Empty", - oldCfg: DeclarativeConfig{}, - newCfg: DeclarativeConfig{}, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{}, - }, - { - name: "NoDiff/OneEqualBundle", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{}, - }, - { - name: "NoDiff/UnsortedBundleProps", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{}, - }, - { - name: "HasDiff/EmptyChannel", - oldCfg: DeclarativeConfig{}, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "v1", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "foo", - AllChannels: DiffIncludeChannel{ - Versions: []semver.Version{semver.MustParse("0.2.0")}, - }, - }, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/OneModifiedBundle", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("bar", ">=1.0.0"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("bar", ">=1.0.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/ManyBundlesAndChannels", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0-alpha.0"}, - {Name: "foo.v0.2.0-alpha.1", Replaces: "foo.v0.2.0-alpha.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.1", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.1"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Skips: []string{"foo.v0.1.0"}}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0-alpha.0"}, - {Name: "foo.v0.2.0-alpha.1", Replaces: "foo.v0.2.0-alpha.0"}, - }}, - {Schema: schemaChannel, Name: "clusterwide", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-clusterwide"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuild(&deprecated{}), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.1", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.1"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-clusterwide", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0-clusterwide"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "clusterwide", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-clusterwide"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Skips: []string{"foo.v0.1.0"}}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuild(&deprecated{}), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-clusterwide", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0-clusterwide"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/OldBundleUpdatedDependencyRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/BundleNewDependencyRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/NewBundleNewDependencyRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "clusterwide", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-clusterwide"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-clusterwide", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0-clusterwide"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "clusterwide", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-clusterwide"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-clusterwide", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0-clusterwide"), - property.MustBuildPackageRequired("etcd", ">=0.9.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/OneNewDependencyRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - }, - }, - }, - { - name: "HasDiff/TwoDependencyRanges", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0 <0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.0 <0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - property.MustBuildPackageRequired("etcd", ">=0.9.2"), - }, - }, - }, - }, - }, - { - name: "HasDiff/BundleNewDependencyGVK", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/IncludePackage", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0"}}}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{{Name: "bar.v0.1.0"}}}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.1.0")}, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0"}}}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "bar"}}, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeChannel", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0"}}}, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0-alpha.0"}}}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0-alpha.0")}, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, // Make sure the default channel is still updated. - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-alpha.0"}, {Name: "foo.v0.2.0-alpha.0", Replaces: "foo.v0.1.0-alpha.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0-alpha.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0-alpha.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "foo", Channels: []DiffIncludeChannel{{Name: "stable"}}}}, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeVersion", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.1.1", Replaces: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.1"}, {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.1", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.1")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.3.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - {Name: "foo", Channels: []DiffIncludeChannel{ - {Name: "stable", Versions: []semver.Version{{Major: 0, Minor: 2, Patch: 0}}}}, - }}, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.3.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.3.0")}, - }, - }, - }, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - if s.assertion == nil { - s.assertion = require.NoError - } - - oldModel, err := ConvertToModel(s.oldCfg) - require.NoError(t, err) - - newModel, err := ConvertToModel(s.newCfg) - require.NoError(t, err) - - outputModel, err := s.g.Run(oldModel, newModel) - s.assertion(t, err) - - outputCfg := ConvertFromModel(outputModel) - require.Equal(t, s.expCfg, outputCfg) - }) - } -} - -func TestDiffHeadsOnly(t *testing.T) { - type spec struct { - name string - g *DiffGenerator - newCfg DeclarativeConfig - expCfg DeclarativeConfig - assertion require.ErrorAssertionFunc - } - - specs := []spec{ - { - name: "NoDiff/Empty", - newCfg: DeclarativeConfig{}, - g: &DiffGenerator{}, - expCfg: DeclarativeConfig{}, - }, - { - name: "NoDiff/EmptyBundleWithInclude", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - }}, - {Schema: schemaChannel, Name: "clusterwide", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1-clusterwide"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1-clusterwide", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1-clusterwide"), - }, - }, - }, - }, - g: &DiffGenerator{ - IncludeAdditively: false, - HeadsOnly: true, - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "etcd", - AllChannels: DiffIncludeChannel{ - Versions: []semver.Version{{Major: 0, Minor: 9, Patch: 2}}, - }, - }, - }, - }, - }, - expCfg: DeclarativeConfig{}, - }, - { - name: "HasDiff/EmptyChannel", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "v1", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - HeadsOnly: true, - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "foo", - AllChannels: DiffIncludeChannel{ - Versions: []semver.Version{semver.MustParse("0.2.0")}, - }, - }, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/OneBundle", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/Graph", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - }}, - {Schema: schemaChannel, Name: "clusterwide", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1-clusterwide"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0-alpha.0"}, - {Name: "foo.v0.2.0-alpha.1", Replaces: "foo.v0.2.0-alpha.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.2.0-alpha.1"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.1", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0-alpha.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1-clusterwide", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1-clusterwide"), - }, - }, - }, - }, - g: &DiffGenerator{ - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "clusterwide", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1-clusterwide"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - }}, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.2.0-alpha.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1-clusterwide", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1-clusterwide"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/CyclicDependencyGraph", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v4.9.3"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v4.9.3"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v4.9.3", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildGVK("foo", "v1alpha1", "Foo"), - property.MustBuildGVKRequired("bar", "v1alpha1", "Bar"), - }, - }, - { - Schema: schemaBundle, - Name: "bar.v4.9.3", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildPackage("bar", "4.9.3"), - property.MustBuildGVK("bar", "v1alpha1", "Bar"), - property.MustBuildGVKRequired("foo", "v1alpha1", "Foo"), - }, - }, - }, - }, - g: &DiffGenerator{ - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v4.9.3"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v4.9.3"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v4.9.3", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVK("bar", "v1alpha1", "Bar"), - property.MustBuildGVKRequired("foo", "v1alpha1", "Foo"), - property.MustBuildPackage("bar", "4.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v4.9.3", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildGVK("foo", "v1alpha1", "Foo"), - property.MustBuildGVKRequired("bar", "v1alpha1", "Bar"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - }, - }, - }, - { - // Testing SkipDependencies only really makes sense in heads-only mode, - // since new dependencies are always added. - name: "HasDiff/SkipDependencies", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackageRequired("etcd", "<=0.9.1"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - }, - }, - g: &DiffGenerator{ - SkipDependencies: true, - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", "<=0.9.1"), - }, - }, - }, - }, - }, - { - name: "HasDiff/SelectDependencies", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackageRequired("etcd", "<0.9.2"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", "<0.9.2"), - }, - }, - }, - }, - }, - { - name: "HasDiff/SelectDependenciesInclude", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "alpha", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackageRequired("etcd", "<0.9.2"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - IncludeAdditively: false, - HeadsOnly: true, - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "bar", - Channels: []DiffIncludeChannel{{Name: "stable"}}, - }, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - }, - { - name: "HasDiff/IncludeAdditive", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackageRequired("etcd", "<0.9.2"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - IncludeAdditively: true, - HeadsOnly: true, - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "etcd", - Channels: []DiffIncludeChannel{{ - Name: "stable", - Versions: []semver.Version{{Major: 0, Minor: 9, Patch: 2}}}, - }}, - { - Name: "bar", - Channels: []DiffIncludeChannel{{Name: "stable"}}, - }, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - property.MustBuildPackageRequired("etcd", "<0.9.2"), - }, - }, - }, - }, - }, - { - name: "HasDiff/IncludePackage", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0"}}}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "bar"}}, - }, - HeadsOnly: false, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.1.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeChannel", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, // Make sure the default channel is still updated. - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-alpha.0"}, {Name: "foo.v0.2.0-alpha.0", Replaces: "foo.v0.1.0-alpha.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0-alpha.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0-alpha.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "foo", Channels: []DiffIncludeChannel{{Name: "stable"}}}}, - }, - HeadsOnly: false, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludePackageHeadsOnly", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{{Name: "foo.v0.1.0"}}}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "bar"}}, - }, - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.2.0", Replaces: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.2.0", Package: "bar", Image: "reg/bar:latest", - Properties: []property.Property{property.MustBuildPackage("bar", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeChannelHeadsOnly", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, // Make sure the default channel is still updated. - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - {Schema: schemaChannel, Name: "alpha", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0-alpha.0"}, {Name: "foo.v0.2.0-alpha.0", Replaces: "foo.v0.1.0-alpha.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0-alpha.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0-alpha.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0-alpha.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{{Name: "foo", Channels: []DiffIncludeChannel{{Name: "stable"}}}}, - }, - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "alpha"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeVersion", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, {Name: "foo.v0.1.1", Replaces: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.1"}, {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.1.1", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.1.1")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.3.0")}, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - {Name: "foo", Channels: []DiffIncludeChannel{ - {Name: "stable", Versions: []semver.Version{{Major: 0, Minor: 2, Patch: 0}}}}, - }}, - }, - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.1"}, {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}}, - }, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.2.0")}, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", Package: "foo", Image: "reg/foo:latest", - Properties: []property.Property{property.MustBuildPackage("foo", "0.3.0")}, - }, - }, - }, - }, - { - name: "HasDiff/IncludeNonAdditive", - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackageRequired("etcd", "<0.9.2"), - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "etcd", - Channels: []DiffIncludeChannel{{ - Name: "stable", - Versions: []semver.Version{{Major: 0, Minor: 9, Patch: 3}}}, - }}, - { - Name: "bar", - Channels: []DiffIncludeChannel{{Name: "stable"}}, - }, - }, - }, - HeadsOnly: true, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "bar", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "bar", Entries: []ChannelEntry{ - {Name: "bar.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - {Name: "etcd.v1.0.0", Replaces: "etcd.v0.9.3", Skips: []string{"etcd.v0.9.1", "etcd.v0.9.2", "etcd.v0.9.3"}}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "bar.v0.1.0", - Package: "bar", - Image: "reg/bar:latest", - Properties: []property.Property{ - property.MustBuildGVKRequired("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildPackage("bar", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v1.0.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildGVK("etcd.database.coreos.com", "v1", "EtcdBackup"), - property.MustBuildGVK("etcd.database.coreos.com", "v1beta2", "EtcdBackup"), - property.MustBuildPackage("etcd", "1.0.0"), - }, - }, - }, - }, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - if s.assertion == nil { - s.assertion = require.NoError - } - - newModel, err := ConvertToModel(s.newCfg) - require.NoError(t, err) - - outputModel, err := s.g.Run(model.Model{}, newModel) - s.assertion(t, err) - - outputCfg := ConvertFromModel(outputModel) - require.Equal(t, s.expCfg, outputCfg) - }) - } -} - -func TestDiffRange(t *testing.T) { - type spec struct { - name string - g *DiffGenerator - oldCfg DeclarativeConfig - newCfg DeclarativeConfig - expCfg DeclarativeConfig - assertion require.ErrorAssertionFunc - } - - specs := []spec{ - { - name: "OnlyPackageRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "foo", - Range: semver.MustParseRange("<=0.3.0"), - }, - { - Name: "etcd", - Range: semver.MustParseRange(">0.9.0 <0.9.3"), - }, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - }, - }, - }, - { - name: "OnlyChannelRange", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.3.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.3.0"}, - {Name: "foo.v0.4.0", Replaces: "foo.v0.3.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.4.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.4.0"), - }, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - {Name: "foo", Channels: []DiffIncludeChannel{ - {Name: "stable", Range: semver.MustParseRange("0.2.0")}, - {Name: "fast", Range: semver.MustParseRange("0.4.0")}, - }}, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.4.0", Replaces: "foo.v0.3.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0", Replaces: "foo.v0.1.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.4.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.4.0"), - }, - }, - }, - }, - }, - { - name: "CombinationPackageChannelRange/Valid", - oldCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - }, - }, - newCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.1.0"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.2.0"}, - {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}, - }}, - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.1", Replaces: "etcd.v0.9.0"}, - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "foo.v0.1.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.1.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.2.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.2.0"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.0", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.0"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.1", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.1"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - }, - }, - g: &DiffGenerator{ - Includer: DiffIncluder{ - Packages: []DiffIncludePackage{ - { - Name: "foo", - Range: semver.MustParseRange("<=0.3.0"), - }, - {Name: "etcd", Channels: []DiffIncludeChannel{ - {Name: "stable", Range: semver.MustParseRange(">0.9.1")}, - }}, - }, - }, - }, - expCfg: DeclarativeConfig{ - Packages: []Package{ - {Schema: schemaPackage, Name: "etcd", DefaultChannel: "stable"}, - {Schema: schemaPackage, Name: "foo", DefaultChannel: "stable"}, - }, - Channels: []Channel{ - {Schema: schemaChannel, Name: "stable", Package: "etcd", Entries: []ChannelEntry{ - {Name: "etcd.v0.9.2", Replaces: "etcd.v0.9.1"}, - {Name: "etcd.v0.9.3", Replaces: "etcd.v0.9.2"}, - }}, - {Schema: schemaChannel, Name: "fast", Package: "foo", Entries: []ChannelEntry{ - {Name: "foo.v0.3.0", Replaces: "foo.v0.2.0"}, - }}, - }, - Bundles: []Bundle{ - { - Schema: schemaBundle, - Name: "etcd.v0.9.2", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.2"), - }, - }, - { - Schema: schemaBundle, - Name: "etcd.v0.9.3", - Package: "etcd", - Image: "reg/etcd:latest", - Properties: []property.Property{ - property.MustBuildPackage("etcd", "0.9.3"), - }, - }, - { - Schema: schemaBundle, - Name: "foo.v0.3.0", - Package: "foo", - Image: "reg/foo:latest", - Properties: []property.Property{ - property.MustBuildPackage("foo", "0.3.0"), - }, - }, - }, - }, - }, - } - - for _, s := range specs { - t.Run(s.name, func(t *testing.T) { - if s.assertion == nil { - s.assertion = require.NoError - } - - oldModel, err := ConvertToModel(s.oldCfg) - require.NoError(t, err) - - newModel, err := ConvertToModel(s.newCfg) - require.NoError(t, err) - - outputModel, err := s.g.Run(oldModel, newModel) - s.assertion(t, err) - - outputCfg := ConvertFromModel(outputModel) - require.EqualValues(t, s.expCfg, outputCfg) - }) - } -} diff --git a/alpha/declcfg/helpers_test.go b/alpha/declcfg/helpers_test.go index cb1a10e2e..cfeb41983 100644 --- a/alpha/declcfg/helpers_test.go +++ b/alpha/declcfg/helpers_test.go @@ -110,7 +110,7 @@ func withNoBundleData() func(*Bundle) { func newTestBundle(packageName, version string, opts ...bundleOpt) Bundle { csvJson := fmt.Sprintf(`{"kind": "ClusterServiceVersion", "apiVersion": "operators.coreos.com/v1alpha1", "metadata":{"name":%q}}`, testBundleName(packageName, version)) b := Bundle{ - Schema: schemaBundle, + Schema: SchemaBundle, Name: testBundleName(packageName, version), Package: packageName, Image: testBundleImage(packageName, version), @@ -150,7 +150,7 @@ const ( func newTestPackage(packageName, defaultChannel, svgData string) Package { p := Package{ - Schema: schemaPackage, + Schema: SchemaPackage, Name: packageName, DefaultChannel: defaultChannel, Icon: &Icon{Data: []byte(svgData), MediaType: "image/svg+xml"}, @@ -166,7 +166,7 @@ func addPackageProperties(in Package, p []property.Property) Package { func newTestChannel(packageName, channelName string, entries ...ChannelEntry) Channel { return Channel{ - Schema: schemaChannel, + Schema: SchemaChannel, Name: channelName, Package: packageName, Entries: entries, diff --git a/alpha/declcfg/load.go b/alpha/declcfg/load.go index 15a5259de..0729c751a 100644 --- a/alpha/declcfg/load.go +++ b/alpha/declcfg/load.go @@ -137,19 +137,19 @@ func readYAMLOrJSON(r io.Reader) (*DeclarativeConfig, error) { } switch in.Schema { - case schemaPackage: + case SchemaPackage: var p Package if err := json.Unmarshal(doc, &p); err != nil { return nil, fmt.Errorf("parse package: %v", err) } cfg.Packages = append(cfg.Packages, p) - case schemaChannel: + case SchemaChannel: var c Channel if err := json.Unmarshal(doc, &c); err != nil { return nil, fmt.Errorf("parse channel: %v", err) } cfg.Channels = append(cfg.Channels, c) - case schemaBundle: + case SchemaBundle: var b Bundle if err := json.Unmarshal(doc, &b); err != nil { return nil, fmt.Errorf("parse bundle: %v", err) diff --git a/alpha/declcfg/model_to_declcfg.go b/alpha/declcfg/model_to_declcfg.go index 9d6651e8b..5f3728fa0 100644 --- a/alpha/declcfg/model_to_declcfg.go +++ b/alpha/declcfg/model_to_declcfg.go @@ -24,7 +24,7 @@ func ConvertFromModel(mpkgs model.Model) DeclarativeConfig { defaultChannel = mpkg.DefaultChannel.Name } cfg.Packages = append(cfg.Packages, Package{ - Schema: schemaPackage, + Schema: SchemaPackage, Name: mpkg.Name, DefaultChannel: defaultChannel, Icon: i, @@ -60,7 +60,7 @@ func traverseModelChannels(mpkg model.Package) ([]Channel, []Bundle) { for _, ch := range mpkg.Channels { // initialize channel c := Channel{ - Schema: schemaChannel, + Schema: SchemaChannel, Name: ch.Name, Package: ch.Package.Name, Entries: []ChannelEntry{}, @@ -79,11 +79,11 @@ func traverseModelChannels(mpkg model.Package) ([]Channel, []Bundle) { b, ok := bundleMap[chb.Name] if !ok { b = &Bundle{ - Schema: schemaBundle, + Schema: SchemaBundle, Name: chb.Name, Package: chb.Package.Name, Image: chb.Image, - RelatedImages: modelRelatedImagesToRelatedImages(chb.RelatedImages), + RelatedImages: ModelRelatedImagesToRelatedImages(chb.RelatedImages), CsvJSON: chb.CsvJSON, Objects: chb.Objects, } @@ -115,7 +115,7 @@ func traverseModelChannels(mpkg model.Package) ([]Channel, []Bundle) { return channels, bundles } -func modelRelatedImagesToRelatedImages(relatedImages []model.RelatedImage) []RelatedImage { +func ModelRelatedImagesToRelatedImages(relatedImages []model.RelatedImage) []RelatedImage { var out []RelatedImage for _, ri := range relatedImages { out = append(out, RelatedImage{ diff --git a/cmd/opm/alpha/cmd.go b/cmd/opm/alpha/cmd.go index 49699a06f..6651b3b88 100644 --- a/cmd/opm/alpha/cmd.go +++ b/cmd/opm/alpha/cmd.go @@ -4,7 +4,6 @@ import ( "github.com/spf13/cobra" "github.com/operator-framework/operator-registry/cmd/opm/alpha/bundle" - "github.com/operator-framework/operator-registry/cmd/opm/alpha/diff" "github.com/operator-framework/operator-registry/cmd/opm/alpha/list" "github.com/operator-framework/operator-registry/cmd/opm/alpha/veneer" ) @@ -20,7 +19,6 @@ func NewCmd() *cobra.Command { runCmd.AddCommand( bundle.NewCmd(), list.NewCmd(), - diff.NewCmd(), veneer.NewCmd(), ) return runCmd diff --git a/cmd/opm/alpha/diff/cmd.go b/cmd/opm/alpha/diff/cmd.go deleted file mode 100644 index cbc2a96b1..000000000 --- a/cmd/opm/alpha/diff/cmd.go +++ /dev/null @@ -1,239 +0,0 @@ -package diff - -import ( - "context" - "fmt" - "io" - "os" - "strings" - "time" - - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "k8s.io/kubectl/pkg/util/templates" - - "github.com/operator-framework/operator-registry/alpha/action" - "github.com/operator-framework/operator-registry/alpha/declcfg" - "github.com/operator-framework/operator-registry/cmd/opm/internal/util" - containerd "github.com/operator-framework/operator-registry/pkg/image/containerdregistry" - "github.com/operator-framework/operator-registry/pkg/lib/certs" -) - -const ( - retryInterval = time.Second * 5 - timeout = time.Minute * 1 -) - -type input struct { - oldRefs []string - newRefs []string - skipDeps bool - includeAdditive bool - headsOnly bool - includeFile string - - output string - caFile string - - debug bool - logger *logrus.Entry -} - -// Example include file needs to be formatted separately so indentation is not messed up. -var includeFileExample = fmt.Sprintf(`packages: -%[1]s- name: foo -%[1]s- name: bar -%[1]s channels: -%[1]s - name: stable -%[1]s- name: baz -%[1]s channels: -%[1]s - name: alpha -%[1]s versions: -%[1]s - 0.2.0-alpha.0`, templates.Indentation) - -func NewCmd() *cobra.Command { - in := input{ - logger: logrus.NewEntry(logrus.New()), - } - cmd := &cobra.Command{ - Use: "diff [old-refs]... new-refs...", - Short: "Diff old and new catalog references into a declarative config", - Long: templates.LongDesc(` -'diff' returns a declarative config containing packages, channels, and versions -from new-refs, optionally removing those in old-refs or those omitted by an include config file. - -Each set of refs is passed to 'opm render ' to produce a single, normalized delcarative config. - -Depending on what arguments are provided to the command, a particular "mode" is invoked to produce a diff: - -- If in heads-only mode (old-refs is not specified), then the heads of channels in new-refs are added to the output. -- If in latest mode (old-refs is specified), a diff between old-refs and new-refs is added to the output. -- If --include-file is set, items from that file will be added to the diff: - - If --include-additive is false (the default), a diff will be generated only on those objects, depending on the mode. - - If --include-additive is true, the diff will contain included objects, plus those added by the mode's invocation. - -Dependencies are added in all modes if --skip-deps is false (the default). -Dependencies are assumed to be provided by either an old-ref, in which case they are not included in the diff, -or a new-ref, in which case they are included. -Dependencies provided by some catalog unknown to 'diff' will not cause the command to error, -but an error will occur if that catalog is not serving these dependencies at runtime. -While dependency inclusion can be turned off with --skip-deps, doing so is not recommended -unless you are certain some in-cluster catalog satisfies all dependencies. -`), - Example: fmt.Sprintf(templates.Examples(` -# Create a directory for your declarative config diff. -mkdir -p my-catalog-index - -# THEN: -# Create a new catalog from a diff between an old and the latest -# state of a catalog as a declarative config index. -opm alpha diff registry.org/my-catalog:abc123 registry.org/my-catalog:def456 -o yaml > ./my-catalog-index/index.yaml - -# OR: -# Create a new catalog from the heads of an existing catalog. -opm alpha diff registry.org/my-catalog:def456 -o yaml > my-catalog-index/index.yaml - -# OR: -# Only include all of package "foo", package "bar" channel "stable", -# and package "baz" channel "alpha" version "0.2.0-alpha.0" (and its upgrade graph) in the diff. -cat < include.yaml -%s -EOF -opm alpha diff registry.org/my-catalog:def456 -i include.yaml -o yaml > pruned-index/index.yaml - -# OR: -# Include all of package "foo", package "bar" channel "stable", -# and package "baz" channel "alpha" version "0.2.0-alpha.0" in the diff -# on top of heads of all other channels in all packages (using the above include.yaml). -opm alpha diff registry.org/my-catalog:def456 -i include.yaml --include-additive -o yaml > pruned-index/index.yaml - -# FINALLY: -# Build an index image containing the diff-ed declarative config, -# then tag and push it. -opm alpha generate dockerfile ./my-catalog-index -docker build -t registry.org/my-catalog:diff-latest -f index.Dockerfile . -docker push registry.org/my-catalog:diff-latest -`), includeFileExample), - Args: cobra.RangeArgs(1, 2), - PreRunE: func(cmd *cobra.Command, args []string) error { - if in.debug { - in.logger.Logger.SetLevel(logrus.DebugLevel) - } - in.logger.Logger.SetOutput(os.Stderr) - return nil - }, - RunE: in.diffFunc, - } - - cmd.Flags().BoolVar(&in.skipDeps, "skip-deps", false, "do not include bundle dependencies in the output catalog") - - cmd.Flags().StringVarP(&in.output, "output", "o", "yaml", "Output format (json|yaml)") - cmd.Flags().StringVar(&in.caFile, "ca-file", "", "the root Certificates to use with this command") - cmd.Flags().StringVarP(&in.includeFile, "include-file", "i", "", - "YAML defining packages, channels, and/or bundles/versions to extract from the new refs. "+ - "Upgrade graphs from individual bundles/versions to their channel's head are also included") - cmd.Flags().BoolVar(&in.includeAdditive, "include-additive", false, - "Ref objects from --include-file are returned on top of 'heads-only' or 'latest' output") - cmd.Flags().BoolVar(&in.headsOnly, "headsOnly", false, - "Using `headsOnly` mode where head(s) of the channel(s) in the package are selected") - - cmd.Flags().BoolVar(&in.debug, "debug", false, "enable debug logging") - return cmd -} - -func (in *input) diffFunc(cmd *cobra.Command, args []string) error { - in.parseArgs(args) - - if cmd.Flags().Changed("include-additive") && in.includeFile == "" { - in.logger.Fatal("must set --include-file if --include-additive is set") - } - - var write func(declcfg.DeclarativeConfig, io.Writer) error - switch in.output { - case "yaml": - write = declcfg.WriteYAML - case "json": - write = declcfg.WriteJSON - default: - return fmt.Errorf("invalid --output value: %q", in.output) - } - - skipTLSVerify, useHTTP, err := util.GetTLSOptions(cmd) - if err != nil { - return err - } - - rootCAs, err := certs.RootCAs(in.caFile) - if err != nil { - in.logger.Fatalf("error getting root CAs: %v", err) - } - reg, err := containerd.NewRegistry( - containerd.SkipTLSVerify(skipTLSVerify), - containerd.WithLog(in.logger), - containerd.WithRootCAs(rootCAs), - containerd.WithPlainHTTP(useHTTP), - ) - if err != nil { - in.logger.Fatalf("error creating containerd registry: %v", err) - } - defer func() { - if err := reg.Destroy(); err != nil { - in.logger.Errorf("error destroying local cache: %v", err) - } - }() - - diff := action.Diff{ - Registry: reg, - OldRefs: in.oldRefs, - NewRefs: in.newRefs, - SkipDependencies: in.skipDeps, - IncludeAdditively: in.includeAdditive, - HeadsOnly: in.headsOnly, - Logger: in.logger, - } - - if in.includeFile != "" { - f, err := os.Open(in.includeFile) - if err != nil { - in.logger.Fatalf("error opening include file: %v", err) - } - defer func() { - if cerr := f.Close(); cerr != nil { - in.logger.Error(cerr) - } - }() - if diff.IncludeConfig, err = action.LoadDiffIncludeConfig(f); err != nil { - in.logger.Fatalf("error loading include file: %v", err) - } - } - - ctx, cancel := context.WithTimeout(context.TODO(), timeout) - defer cancel() - - cfg, err := diff.Run(ctx) - if err != nil { - in.logger.Fatalf("error generating diff: %v", err) - } - - if err := write(*cfg, os.Stdout); err != nil { - in.logger.Fatalf("error writing diff: %v", err) - } - - return nil -} - -func (in *input) parseArgs(args []string) { - var old, new string - switch len(args) { - case 1: - new = args[0] - case 2: - old, new = args[0], args[1] - default: - logrus.Panic("should never be here, CLI must enforce arg size") - } - if old != "" { - in.oldRefs = strings.Split(old, ",") - } - in.newRefs = strings.Split(new, ",") -} From 1096598a7baf4112328f4f1626cfdbebac7e6f09 Mon Sep 17 00:00:00 2001 From: Oskar Pawica Date: Fri, 19 Aug 2022 13:13:26 +0200 Subject: [PATCH 2/2] updated vendor for removed diff logic Signed-off-by: Oskar Pawica --- go.mod | 1 - go.sum | 2 - .../mitchellh/hashstructure/v2/LICENSE | 21 - .../mitchellh/hashstructure/v2/README.md | 76 --- .../mitchellh/hashstructure/v2/errors.go | 22 - .../hashstructure/v2/hashstructure.go | 482 ------------------ .../mitchellh/hashstructure/v2/include.go | 22 - vendor/modules.txt | 3 - 8 files changed, 629 deletions(-) delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/LICENSE delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/README.md delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/errors.go delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go delete mode 100644 vendor/github.com/mitchellh/hashstructure/v2/include.go diff --git a/go.mod b/go.mod index bfef9504b..e26fa6cf1 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,6 @@ require ( github.com/joelanford/ignore v0.0.0-20210607151042-0d25dc18b62d github.com/mattn/go-sqlite3 v1.14.10 github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 - github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/onsi/ginkgo/v2 v2.1.3 github.com/onsi/gomega v1.18.1 github.com/opencontainers/go-digest v1.0.0 diff --git a/go.sum b/go.sum index 03ea96829..27ac35590 100644 --- a/go.sum +++ b/go.sum @@ -567,8 +567,6 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/vendor/github.com/mitchellh/hashstructure/v2/LICENSE b/vendor/github.com/mitchellh/hashstructure/v2/LICENSE deleted file mode 100644 index a3866a291..000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Mitchell Hashimoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/mitchellh/hashstructure/v2/README.md b/vendor/github.com/mitchellh/hashstructure/v2/README.md deleted file mode 100644 index 21f36be19..000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# hashstructure [![GoDoc](https://godoc.org/github.com/mitchellh/hashstructure?status.svg)](https://godoc.org/github.com/mitchellh/hashstructure) - -hashstructure is a Go library for creating a unique hash value -for arbitrary values in Go. - -This can be used to key values in a hash (for use in a map, set, etc.) -that are complex. The most common use case is comparing two values without -sending data across the network, caching values locally (de-dup), and so on. - -## Features - - * Hash any arbitrary Go value, including complex types. - - * Tag a struct field to ignore it and not affect the hash value. - - * Tag a slice type struct field to treat it as a set where ordering - doesn't affect the hash code but the field itself is still taken into - account to create the hash value. - - * Optionally, specify a custom hash function to optimize for speed, collision - avoidance for your data set, etc. - - * Optionally, hash the output of `.String()` on structs that implement fmt.Stringer, - allowing effective hashing of time.Time - - * Optionally, override the hashing process by implementing `Hashable`. - -## Installation - -Standard `go get`: - -``` -$ go get github.com/mitchellh/hashstructure/v2 -``` - -**Note on v2:** It is highly recommended you use the "v2" release since this -fixes some significant hash collisions issues from v1. In practice, we used -v1 for many years in real projects at HashiCorp and never had issues, but it -is highly dependent on the shape of the data you're hashing and how you use -those hashes. - -When using v2+, you can still generate weaker v1 hashes by using the -`FormatV1` format when calling `Hash`. - -## Usage & Example - -For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/hashstructure). - -A quick code example is shown below: - -```go -type ComplexStruct struct { - Name string - Age uint - Metadata map[string]interface{} -} - -v := ComplexStruct{ - Name: "mitchellh", - Age: 64, - Metadata: map[string]interface{}{ - "car": true, - "location": "California", - "siblings": []string{"Bob", "John"}, - }, -} - -hash, err := hashstructure.Hash(v, hashstructure.FormatV2, nil) -if err != nil { - panic(err) -} - -fmt.Printf("%d", hash) -// Output: -// 2307517237273902113 -``` diff --git a/vendor/github.com/mitchellh/hashstructure/v2/errors.go b/vendor/github.com/mitchellh/hashstructure/v2/errors.go deleted file mode 100644 index 44b895147..000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/errors.go +++ /dev/null @@ -1,22 +0,0 @@ -package hashstructure - -import ( - "fmt" -) - -// ErrNotStringer is returned when there's an error with hash:"string" -type ErrNotStringer struct { - Field string -} - -// Error implements error for ErrNotStringer -func (ens *ErrNotStringer) Error() string { - return fmt.Sprintf("hashstructure: %s has hash:\"string\" set, but does not implement fmt.Stringer", ens.Field) -} - -// ErrFormat is returned when an invalid format is given to the Hash function. -type ErrFormat struct{} - -func (*ErrFormat) Error() string { - return "format must be one of the defined Format values in the hashstructure library" -} diff --git a/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go b/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go deleted file mode 100644 index 3dc0eb74e..000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/hashstructure.go +++ /dev/null @@ -1,482 +0,0 @@ -package hashstructure - -import ( - "encoding/binary" - "fmt" - "hash" - "hash/fnv" - "reflect" - "time" -) - -// HashOptions are options that are available for hashing. -type HashOptions struct { - // Hasher is the hash function to use. If this isn't set, it will - // default to FNV. - Hasher hash.Hash64 - - // TagName is the struct tag to look at when hashing the structure. - // By default this is "hash". - TagName string - - // ZeroNil is flag determining if nil pointer should be treated equal - // to a zero value of pointed type. By default this is false. - ZeroNil bool - - // IgnoreZeroValue is determining if zero value fields should be - // ignored for hash calculation. - IgnoreZeroValue bool - - // SlicesAsSets assumes that a `set` tag is always present for slices. - // Default is false (in which case the tag is used instead) - SlicesAsSets bool - - // UseStringer will attempt to use fmt.Stringer always. If the struct - // doesn't implement fmt.Stringer, it'll fall back to trying usual tricks. - // If this is true, and the "string" tag is also set, the tag takes - // precedence (meaning that if the type doesn't implement fmt.Stringer, we - // panic) - UseStringer bool -} - -// Format specifies the hashing process used. Different formats typically -// generate different hashes for the same value and have different properties. -type Format uint - -const ( - // To disallow the zero value - formatInvalid Format = iota - - // FormatV1 is the format used in v1.x of this library. This has the - // downsides noted in issue #18 but allows simultaneous v1/v2 usage. - FormatV1 - - // FormatV2 is the current recommended format and fixes the issues - // noted in FormatV1. - FormatV2 - - formatMax // so we can easily find the end -) - -// Hash returns the hash value of an arbitrary value. -// -// If opts is nil, then default options will be used. See HashOptions -// for the default values. The same *HashOptions value cannot be used -// concurrently. None of the values within a *HashOptions struct are -// safe to read/write while hashing is being done. -// -// The "format" is required and must be one of the format values defined -// by this library. You should probably just use "FormatV2". This allows -// generated hashes uses alternate logic to maintain compatibility with -// older versions. -// -// Notes on the value: -// -// * Unexported fields on structs are ignored and do not affect the -// hash value. -// -// * Adding an exported field to a struct with the zero value will change -// the hash value. -// -// For structs, the hashing can be controlled using tags. For example: -// -// struct { -// Name string -// UUID string `hash:"ignore"` -// } -// -// The available tag values are: -// -// * "ignore" or "-" - The field will be ignored and not affect the hash code. -// -// * "set" - The field will be treated as a set, where ordering doesn't -// affect the hash code. This only works for slices. -// -// * "string" - The field will be hashed as a string, only works when the -// field implements fmt.Stringer -// -func Hash(v interface{}, format Format, opts *HashOptions) (uint64, error) { - // Validate our format - if format <= formatInvalid || format >= formatMax { - return 0, &ErrFormat{} - } - - // Create default options - if opts == nil { - opts = &HashOptions{} - } - if opts.Hasher == nil { - opts.Hasher = fnv.New64() - } - if opts.TagName == "" { - opts.TagName = "hash" - } - - // Reset the hash - opts.Hasher.Reset() - - // Create our walker and walk the structure - w := &walker{ - format: format, - h: opts.Hasher, - tag: opts.TagName, - zeronil: opts.ZeroNil, - ignorezerovalue: opts.IgnoreZeroValue, - sets: opts.SlicesAsSets, - stringer: opts.UseStringer, - } - return w.visit(reflect.ValueOf(v), nil) -} - -type walker struct { - format Format - h hash.Hash64 - tag string - zeronil bool - ignorezerovalue bool - sets bool - stringer bool -} - -type visitOpts struct { - // Flags are a bitmask of flags to affect behavior of this visit - Flags visitFlag - - // Information about the struct containing this field - Struct interface{} - StructField string -} - -var timeType = reflect.TypeOf(time.Time{}) - -func (w *walker) visit(v reflect.Value, opts *visitOpts) (uint64, error) { - t := reflect.TypeOf(0) - - // Loop since these can be wrapped in multiple layers of pointers - // and interfaces. - for { - // If we have an interface, dereference it. We have to do this up - // here because it might be a nil in there and the check below must - // catch that. - if v.Kind() == reflect.Interface { - v = v.Elem() - continue - } - - if v.Kind() == reflect.Ptr { - if w.zeronil { - t = v.Type().Elem() - } - v = reflect.Indirect(v) - continue - } - - break - } - - // If it is nil, treat it like a zero. - if !v.IsValid() { - v = reflect.Zero(t) - } - - // Binary writing can use raw ints, we have to convert to - // a sized-int, we'll choose the largest... - switch v.Kind() { - case reflect.Int: - v = reflect.ValueOf(int64(v.Int())) - case reflect.Uint: - v = reflect.ValueOf(uint64(v.Uint())) - case reflect.Bool: - var tmp int8 - if v.Bool() { - tmp = 1 - } - v = reflect.ValueOf(tmp) - } - - k := v.Kind() - - // We can shortcut numeric values by directly binary writing them - if k >= reflect.Int && k <= reflect.Complex64 { - // A direct hash calculation - w.h.Reset() - err := binary.Write(w.h, binary.LittleEndian, v.Interface()) - return w.h.Sum64(), err - } - - switch v.Type() { - case timeType: - w.h.Reset() - b, err := v.Interface().(time.Time).MarshalBinary() - if err != nil { - return 0, err - } - - err = binary.Write(w.h, binary.LittleEndian, b) - return w.h.Sum64(), err - } - - switch k { - case reflect.Array: - var h uint64 - l := v.Len() - for i := 0; i < l; i++ { - current, err := w.visit(v.Index(i), nil) - if err != nil { - return 0, err - } - - h = hashUpdateOrdered(w.h, h, current) - } - - return h, nil - - case reflect.Map: - var includeMap IncludableMap - if opts != nil && opts.Struct != nil { - if v, ok := opts.Struct.(IncludableMap); ok { - includeMap = v - } - } - - // Build the hash for the map. We do this by XOR-ing all the key - // and value hashes. This makes it deterministic despite ordering. - var h uint64 - for _, k := range v.MapKeys() { - v := v.MapIndex(k) - if includeMap != nil { - incl, err := includeMap.HashIncludeMap( - opts.StructField, k.Interface(), v.Interface()) - if err != nil { - return 0, err - } - if !incl { - continue - } - } - - kh, err := w.visit(k, nil) - if err != nil { - return 0, err - } - vh, err := w.visit(v, nil) - if err != nil { - return 0, err - } - - fieldHash := hashUpdateOrdered(w.h, kh, vh) - h = hashUpdateUnordered(h, fieldHash) - } - - if w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - - return h, nil - - case reflect.Struct: - parent := v.Interface() - var include Includable - if impl, ok := parent.(Includable); ok { - include = impl - } - - if impl, ok := parent.(Hashable); ok { - return impl.Hash() - } - - // If we can address this value, check if the pointer value - // implements our interfaces and use that if so. - if v.CanAddr() { - vptr := v.Addr() - parentptr := vptr.Interface() - if impl, ok := parentptr.(Includable); ok { - include = impl - } - - if impl, ok := parentptr.(Hashable); ok { - return impl.Hash() - } - } - - t := v.Type() - h, err := w.visit(reflect.ValueOf(t.Name()), nil) - if err != nil { - return 0, err - } - - l := v.NumField() - for i := 0; i < l; i++ { - if innerV := v.Field(i); v.CanSet() || t.Field(i).Name != "_" { - var f visitFlag - fieldType := t.Field(i) - if fieldType.PkgPath != "" { - // Unexported - continue - } - - tag := fieldType.Tag.Get(w.tag) - if tag == "ignore" || tag == "-" { - // Ignore this field - continue - } - - if w.ignorezerovalue { - if innerV.IsZero() { - continue - } - } - - // if string is set, use the string value - if tag == "string" || w.stringer { - if impl, ok := innerV.Interface().(fmt.Stringer); ok { - innerV = reflect.ValueOf(impl.String()) - } else if tag == "string" { - // We only show this error if the tag explicitly - // requests a stringer. - return 0, &ErrNotStringer{ - Field: v.Type().Field(i).Name, - } - } - } - - // Check if we implement includable and check it - if include != nil { - incl, err := include.HashInclude(fieldType.Name, innerV) - if err != nil { - return 0, err - } - if !incl { - continue - } - } - - switch tag { - case "set": - f |= visitFlagSet - } - - kh, err := w.visit(reflect.ValueOf(fieldType.Name), nil) - if err != nil { - return 0, err - } - - vh, err := w.visit(innerV, &visitOpts{ - Flags: f, - Struct: parent, - StructField: fieldType.Name, - }) - if err != nil { - return 0, err - } - - fieldHash := hashUpdateOrdered(w.h, kh, vh) - h = hashUpdateUnordered(h, fieldHash) - } - - if w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - } - - return h, nil - - case reflect.Slice: - // We have two behaviors here. If it isn't a set, then we just - // visit all the elements. If it is a set, then we do a deterministic - // hash code. - var h uint64 - var set bool - if opts != nil { - set = (opts.Flags & visitFlagSet) != 0 - } - l := v.Len() - for i := 0; i < l; i++ { - current, err := w.visit(v.Index(i), nil) - if err != nil { - return 0, err - } - - if set || w.sets { - h = hashUpdateUnordered(h, current) - } else { - h = hashUpdateOrdered(w.h, h, current) - } - } - - if set && w.format != FormatV1 { - // Important: read the docs for hashFinishUnordered - h = hashFinishUnordered(w.h, h) - } - - return h, nil - - case reflect.String: - // Directly hash - w.h.Reset() - _, err := w.h.Write([]byte(v.String())) - return w.h.Sum64(), err - - default: - return 0, fmt.Errorf("unknown kind to hash: %s", k) - } - -} - -func hashUpdateOrdered(h hash.Hash64, a, b uint64) uint64 { - // For ordered updates, use a real hash function - h.Reset() - - // We just panic if the binary writes fail because we are writing - // an int64 which should never be fail-able. - e1 := binary.Write(h, binary.LittleEndian, a) - e2 := binary.Write(h, binary.LittleEndian, b) - if e1 != nil { - panic(e1) - } - if e2 != nil { - panic(e2) - } - - return h.Sum64() -} - -func hashUpdateUnordered(a, b uint64) uint64 { - return a ^ b -} - -// After mixing a group of unique hashes with hashUpdateUnordered, it's always -// necessary to call hashFinishUnordered. Why? Because hashUpdateUnordered -// is a simple XOR, and calling hashUpdateUnordered on hashes produced by -// hashUpdateUnordered can effectively cancel out a previous change to the hash -// result if the same hash value appears later on. For example, consider: -// -// hashUpdateUnordered(hashUpdateUnordered("A", "B"), hashUpdateUnordered("A", "C")) = -// H("A") ^ H("B")) ^ (H("A") ^ H("C")) = -// (H("A") ^ H("A")) ^ (H("B") ^ H(C)) = -// H(B) ^ H(C) = -// hashUpdateUnordered(hashUpdateUnordered("Z", "B"), hashUpdateUnordered("Z", "C")) -// -// hashFinishUnordered "hardens" the result, so that encountering partially -// overlapping input data later on in a different context won't cancel out. -func hashFinishUnordered(h hash.Hash64, a uint64) uint64 { - h.Reset() - - // We just panic if the writes fail - e1 := binary.Write(h, binary.LittleEndian, a) - if e1 != nil { - panic(e1) - } - - return h.Sum64() -} - -// visitFlag is used as a bitmask for affecting visit behavior -type visitFlag uint - -const ( - visitFlagInvalid visitFlag = iota - visitFlagSet = iota << 1 -) diff --git a/vendor/github.com/mitchellh/hashstructure/v2/include.go b/vendor/github.com/mitchellh/hashstructure/v2/include.go deleted file mode 100644 index 702d35415..000000000 --- a/vendor/github.com/mitchellh/hashstructure/v2/include.go +++ /dev/null @@ -1,22 +0,0 @@ -package hashstructure - -// Includable is an interface that can optionally be implemented by -// a struct. It will be called for each field in the struct to check whether -// it should be included in the hash. -type Includable interface { - HashInclude(field string, v interface{}) (bool, error) -} - -// IncludableMap is an interface that can optionally be implemented by -// a struct. It will be called when a map-type field is found to ask the -// struct if the map item should be included in the hash. -type IncludableMap interface { - HashIncludeMap(field string, k, v interface{}) (bool, error) -} - -// Hashable is an interface that can optionally be implemented by a struct -// to override the hash value. This value will override the hash value for -// the entire struct. Entries in the struct will not be hashed. -type Hashable interface { - Hash() (uint64, error) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index afaf34c34..52b5e9278 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -416,9 +416,6 @@ github.com/maxbrunsfeld/counterfeiter/v6/generator # github.com/mitchellh/go-wordwrap v1.0.0 ## explicit github.com/mitchellh/go-wordwrap -# github.com/mitchellh/hashstructure/v2 v2.0.2 -## explicit; go 1.14 -github.com/mitchellh/hashstructure/v2 # github.com/mitchellh/mapstructure v1.4.1 ## explicit; go 1.14 github.com/mitchellh/mapstructure