Skip to content

Commit 5e3fa99

Browse files
authored
migrate: support migration of FBC to latest preferred FBC (#1144)
* remove defunct ref-style olm.bundle.object This commit removes a feature of FBC that has never actually been used in practice: the ability to reference a file in an olm.bundle.object property, where the path is relative to the directory in which the file containing the olm.bundle.object property exists. This was originally intended to be a way to avoid bloating the FBC, but it's presence has caused two problems: 1. It hinted that it would be okay for third-party properties and schemas to reference files in a similar way. 2. Because of (1), we have never really been able to make assumptions that would enable us to migrate and re-write FBC in a different hierarchy, which has been limiting. In short, it imposes a burden on catalog maintainers to keep a catalog in a filesystem structure that is imposed by the author of the catalog contribution. In practice, ref-style olm.bundle.object properties have never been used (as far as I'm aware), because no tooling has ever produced that style, and no one I have heard of is using other methods to render bundles into an FBC. Lastly, with the recent addition of the `olm.csv.metadata` property, the useful life of the `olm.bundle.object` property (which has always been alpha) is nearing an end. Signed-off-by: Joe Lanford <[email protected]> * migrate: support migration of FBC to latest preferred FBC This commit adds support for migrating FBC to the latest preferred FBC contents. Note that sqlite and bundle inputs are always rendered using the latest preferred FBC contents. The migrate command is updated to now support FBC images and directories as input (only sqlite was supported prior), such that the written output will always be migrated. The render command is updated with a `--migrate` flag that allows a caller to opt into migration during rendering. Under the hood, both of these subcommands use the action.Render struct, which has a new `Migrate` boolean field that callers can use to enable/disable the migration behavior. Signed-off-by: Joe Lanford <[email protected]> --------- Signed-off-by: Joe Lanford <[email protected]>
1 parent eaebcf6 commit 5e3fa99

File tree

15 files changed

+464
-300
lines changed

15 files changed

+464
-300
lines changed

alpha/action/migrate.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,11 @@ func (m Migrate) Run(ctx context.Context) error {
2929
}
3030

3131
r := Render{
32-
Refs: []string{m.CatalogRef},
32+
Refs: []string{m.CatalogRef},
33+
Migrate: true,
3334

34-
// Only allow sqlite images and files to be migrated. Other types cannot
35-
// always be migrated cleanly because they may contain file references.
36-
// Rendered sqlite databases never contain file references.
37-
AllowedRefMask: RefSqliteImage | RefSqliteFile,
38-
39-
skipSqliteDeprecationLog: true,
35+
// Only allow catalogs to be migrated.
36+
AllowedRefMask: RefSqliteImage | RefSqliteFile | RefDCImage | RefDCDir,
4037
}
4138
if m.Registry != nil {
4239
r.Registry = m.Registry

alpha/action/migrate_test.go

Lines changed: 160 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ func TestMigrate(t *testing.T) {
5050
Registry: reg,
5151
},
5252
expectedFiles: map[string]string{
53-
"foo/catalog.yaml": migrateFooCatalog(),
54-
"bar/catalog.yaml": migrateBarCatalog(),
53+
"foo/catalog.yaml": migrateFooCatalogSqlite(),
54+
"bar/catalog.yaml": migrateBarCatalogSqlite(),
5555
},
5656
},
5757
{
@@ -64,31 +64,35 @@ func TestMigrate(t *testing.T) {
6464
Registry: reg,
6565
},
6666
expectedFiles: map[string]string{
67-
"foo/catalog.yaml": migrateFooCatalog(),
68-
"bar/catalog.yaml": migrateBarCatalog(),
67+
"foo/catalog.yaml": migrateFooCatalogSqlite(),
68+
"bar/catalog.yaml": migrateBarCatalogSqlite(),
6969
},
7070
},
7171
{
72-
name: "DeclcfgImage/Failure",
72+
name: "DeclcfgImage/Success",
7373
migrate: action.Migrate{
7474
CatalogRef: "test.registry/foo-operator/foo-index-declcfg:v0.2.0",
7575
OutputDir: filepath.Join(tmpDir, "declcfg-image"),
7676
WriteFunc: declcfg.WriteYAML,
7777
FileExt: ".yaml",
7878
Registry: reg,
7979
},
80-
expectErr: action.ErrNotAllowed,
80+
expectedFiles: map[string]string{
81+
"foo/catalog.yaml": migrateFooCatalogFBC(),
82+
},
8183
},
8284
{
83-
name: "DeclcfgDir/Failure",
85+
name: "DeclcfgDir/Success",
8486
migrate: action.Migrate{
8587
CatalogRef: "testdata/foo-index-v0.2.0-declcfg",
8688
OutputDir: filepath.Join(tmpDir, "declcfg-dir"),
8789
WriteFunc: declcfg.WriteYAML,
8890
FileExt: ".yaml",
8991
Registry: reg,
9092
},
91-
expectErr: action.ErrNotAllowed,
93+
expectedFiles: map[string]string{
94+
"foo/catalog.yaml": migrateFooCatalogFBC(),
95+
},
9296
},
9397
{
9498
name: "BundleImage/Failure",
@@ -106,12 +110,22 @@ func TestMigrate(t *testing.T) {
106110
t.Run(s.name, func(t *testing.T) {
107111
err := s.migrate.Run(context.Background())
108112
require.ErrorIs(t, err, s.expectErr)
109-
for file, expectedData := range s.expectedFiles {
110-
path := filepath.Join(s.migrate.OutputDir, file)
111-
actualData, err := os.ReadFile(path)
113+
if s.expectErr != nil {
114+
return
115+
}
116+
actualFS := os.DirFS(s.migrate.OutputDir)
117+
fs.WalkDir(actualFS, ".", func(path string, d fs.DirEntry, err error) error {
112118
require.NoError(t, err)
119+
if d.IsDir() {
120+
return nil
121+
}
122+
actualData, err := fs.ReadFile(actualFS, path)
123+
require.NoError(t, err)
124+
expectedData, ok := s.expectedFiles[path]
125+
require.True(t, ok, "output directory contained unexpected file %q", path)
113126
require.Equal(t, expectedData, string(actualData))
114-
}
127+
return nil
128+
})
115129
})
116130
}
117131
}
@@ -156,7 +170,7 @@ func newMigrateRegistry(t *testing.T, imageMap map[image.Reference]string) (imag
156170
return reg, nil
157171
}
158172

159-
func migrateFooCatalog() string {
173+
func migrateFooCatalogSqlite() string {
160174
return `---
161175
defaultChannel: beta
162176
name: foo
@@ -280,7 +294,7 @@ schema: olm.bundle
280294
`
281295
}
282296

283-
func migrateBarCatalog() string {
297+
func migrateBarCatalogSqlite() string {
284298
return `---
285299
defaultChannel: alpha
286300
name: bar
@@ -357,3 +371,135 @@ relatedImages:
357371
schema: olm.bundle
358372
`
359373
}
374+
375+
func migrateFooCatalogFBC() string {
376+
return `---
377+
defaultChannel: beta
378+
name: foo
379+
properties:
380+
- type: owner
381+
value:
382+
group: abc.com
383+
name: admin
384+
schema: olm.package
385+
---
386+
entries:
387+
- name: foo.v0.1.0
388+
skipRange: <0.1.0
389+
- name: foo.v0.2.0
390+
replaces: foo.v0.1.0
391+
skipRange: <0.2.0
392+
skips:
393+
- foo.v0.1.1
394+
- foo.v0.1.2
395+
name: beta
396+
package: foo
397+
properties:
398+
- type: user
399+
value:
400+
group: xyz.com
401+
name: account
402+
schema: olm.channel
403+
---
404+
entries:
405+
- name: foo.v0.2.0
406+
replaces: foo.v0.1.0
407+
skipRange: <0.2.0
408+
skips:
409+
- foo.v0.1.1
410+
- foo.v0.1.2
411+
name: stable
412+
package: foo
413+
schema: olm.channel
414+
---
415+
image: test.registry/foo-operator/foo-bundle:v0.1.0
416+
name: foo.v0.1.0
417+
package: foo
418+
properties:
419+
- type: olm.gvk
420+
value:
421+
group: test.foo
422+
kind: Foo
423+
version: v1
424+
- type: olm.gvk.required
425+
value:
426+
group: test.bar
427+
kind: Bar
428+
version: v1alpha1
429+
- type: olm.package
430+
value:
431+
packageName: foo
432+
version: 0.1.0
433+
- type: olm.package.required
434+
value:
435+
packageName: bar
436+
versionRange: <0.1.0
437+
- type: olm.csv.metadata
438+
value:
439+
annotations:
440+
olm.skipRange: <0.1.0
441+
apiServiceDefinitions: {}
442+
crdDescriptions:
443+
owned:
444+
- kind: Foo
445+
name: foos.test.foo
446+
version: v1
447+
displayName: Foo Operator
448+
provider: {}
449+
relatedImages:
450+
- image: test.registry/foo-operator/foo-bundle:v0.1.0
451+
name: ""
452+
- image: test.registry/foo-operator/foo:v0.1.0
453+
name: operator
454+
schema: olm.bundle
455+
---
456+
image: test.registry/foo-operator/foo-bundle:v0.2.0
457+
name: foo.v0.2.0
458+
package: foo
459+
properties:
460+
- type: olm.gvk
461+
value:
462+
group: test.foo
463+
kind: Foo
464+
version: v1
465+
- type: olm.gvk.required
466+
value:
467+
group: test.bar
468+
kind: Bar
469+
version: v1alpha1
470+
- type: olm.package
471+
value:
472+
packageName: foo
473+
version: 0.2.0
474+
- type: olm.package.required
475+
value:
476+
packageName: bar
477+
versionRange: <0.1.0
478+
- type: olm.csv.metadata
479+
value:
480+
annotations:
481+
olm.skipRange: <0.2.0
482+
apiServiceDefinitions: {}
483+
crdDescriptions:
484+
owned:
485+
- kind: Foo
486+
name: foos.test.foo
487+
version: v1
488+
displayName: Foo Operator
489+
provider: {}
490+
relatedImages:
491+
- image: test.registry/foo-operator/foo-2:v0.2.0
492+
name: ""
493+
- image: test.registry/foo-operator/foo-bundle:v0.2.0
494+
name: ""
495+
- image: test.registry/foo-operator/foo-init-2:v0.2.0
496+
name: ""
497+
- image: test.registry/foo-operator/foo-init:v0.2.0
498+
name: ""
499+
- image: test.registry/foo-operator/foo-other:v0.2.0
500+
name: other
501+
- image: test.registry/foo-operator/foo:v0.2.0
502+
name: operator
503+
schema: olm.bundle
504+
`
505+
}

alpha/action/render.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515

1616
"github.com/h2non/filetype"
1717
"github.com/h2non/filetype/matchers"
18+
"github.com/operator-framework/api/pkg/operators/v1alpha1"
1819
"github.com/sirupsen/logrus"
1920
"k8s.io/apimachinery/pkg/util/sets"
2021

@@ -52,6 +53,7 @@ type Render struct {
5253
Refs []string
5354
Registry image.Registry
5455
AllowedRefMask RefType
56+
Migrate bool
5557

5658
skipSqliteDeprecationLog bool
5759
}
@@ -90,6 +92,12 @@ func (r Render) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) {
9092
})
9193
}
9294

95+
if r.Migrate {
96+
if err := migrate(cfg); err != nil {
97+
return nil, fmt.Errorf("migrate: %v", err)
98+
}
99+
}
100+
93101
cfgs = append(cfgs, *cfg)
94102
}
95103

@@ -395,6 +403,50 @@ func moveBundleObjectsToEndOfPropertySlices(cfg *declcfg.DeclarativeConfig) {
395403
}
396404
}
397405

406+
func migrate(cfg *declcfg.DeclarativeConfig) error {
407+
migrations := []func(*declcfg.DeclarativeConfig) error{
408+
convertObjectsToCSVMetadata,
409+
}
410+
411+
for _, m := range migrations {
412+
if err := m(cfg); err != nil {
413+
return err
414+
}
415+
}
416+
return nil
417+
}
418+
419+
func convertObjectsToCSVMetadata(cfg *declcfg.DeclarativeConfig) error {
420+
BundleLoop:
421+
for bi, b := range cfg.Bundles {
422+
if b.Image == "" || b.CsvJSON == "" {
423+
continue
424+
}
425+
426+
var csv v1alpha1.ClusterServiceVersion
427+
if err := json.Unmarshal([]byte(b.CsvJSON), &csv); err != nil {
428+
return err
429+
}
430+
431+
props := b.Properties[:0]
432+
for _, p := range b.Properties {
433+
switch p.Type {
434+
case property.TypeBundleObject:
435+
// Get rid of the bundle objects
436+
case property.TypeCSVMetadata:
437+
// If this bundle already has a CSV metadata
438+
// property, we won't mutate the bundle at all.
439+
continue BundleLoop
440+
default:
441+
// Keep all of the other properties
442+
props = append(props, p)
443+
}
444+
}
445+
cfg.Bundles[bi].Properties = append(props, property.MustBuildCSVMetadata(csv))
446+
}
447+
return nil
448+
}
449+
398450
func combineConfigs(cfgs []declcfg.DeclarativeConfig) *declcfg.DeclarativeConfig {
399451
out := &declcfg.DeclarativeConfig{}
400452
for _, in := range cfgs {

0 commit comments

Comments
 (0)