From b50b1a8fc0682e37a804f4718dd607dcfc4df4e9 Mon Sep 17 00:00:00 2001 From: Jordan Keister Date: Mon, 5 Aug 2024 13:58:52 -0500 Subject: [PATCH] use a type to protect migration interactions Signed-off-by: Jordan Keister --- alpha/action/migrate.go | 7 +- alpha/action/migrate_test.go | 294 +++++++----------- .../000_bundle_object_to_csv_metadata.go | 63 ++-- alpha/action/migrations/migrations.go | 63 ++-- alpha/action/migrations/migrations_test.go | 39 +++ alpha/action/render.go | 20 +- alpha/action/render_test.go | 51 +-- alpha/template/basic/basic.go | 7 +- alpha/template/semver/semver.go | 2 +- alpha/template/semver/types.go | 7 +- cmd/opm/alpha/template/basic.go | 13 +- cmd/opm/alpha/template/semver.go | 7 + cmd/opm/migrate/cmd.go | 15 +- cmd/opm/render/cmd.go | 19 +- 14 files changed, 315 insertions(+), 292 deletions(-) create mode 100644 alpha/action/migrations/migrations_test.go diff --git a/alpha/action/migrate.go b/alpha/action/migrate.go index 962cd6489..8122d1648 100644 --- a/alpha/action/migrate.go +++ b/alpha/action/migrate.go @@ -5,6 +5,7 @@ import ( "fmt" "os" + "github.com/operator-framework/operator-registry/alpha/action/migrations" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/pkg/image" ) @@ -12,7 +13,7 @@ import ( type Migrate struct { CatalogRef string OutputDir string - Level string + Migrations *migrations.Migrations WriteFunc declcfg.WriteFunc FileExt string @@ -29,8 +30,8 @@ func (m Migrate) Run(ctx context.Context) error { } r := Render{ - Refs: []string{m.CatalogRef}, - MigrationLevel: m.Level, + Refs: []string{m.CatalogRef}, + Migrations: m.Migrations, // Only allow catalogs to be migrated. AllowedRefMask: RefSqliteImage | RefSqliteFile | RefDCImage | RefDCDir, diff --git a/alpha/action/migrate_test.go b/alpha/action/migrate_test.go index 7fe006429..203540542 100644 --- a/alpha/action/migrate_test.go +++ b/alpha/action/migrate_test.go @@ -2,7 +2,6 @@ package action_test import ( "context" - "fmt" "io/fs" "os" "path/filepath" @@ -11,25 +10,36 @@ import ( "github.com/stretchr/testify/require" "github.com/operator-framework/operator-registry/alpha/action" + "github.com/operator-framework/operator-registry/alpha/action/migrations" "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" ) -type migrationLevel string +type fauxMigration struct { + token string + help string + migrate func(*declcfg.DeclarativeConfig) error +} -const ( - bundleObjectType migrationLevel = "bundle-object" - csvMetadataType migrationLevel = "bundle-object-to-csv-metadata" -) +func (m fauxMigration) Token() migrations.MigrationToken { + return migrations.MigrationToken(m.token) +} +func (m fauxMigration) Help() string { + return m.help +} +func (m fauxMigration) Migrate(config *declcfg.DeclarativeConfig) error { + return m.migrate(config) +} func TestMigrate(t *testing.T) { type spec struct { - name string - migrate action.Migrate - expectedFiles map[string]string - expectErr error + name string + migrate action.Migrate + expectedFiles map[string]string + expectErr error + migrationCount int } sqliteBundles := map[image.Reference]string{ @@ -47,6 +57,11 @@ func TestMigrate(t *testing.T) { reg, err := newMigrateRegistry(t, sqliteBundles) require.NoError(t, err) + migrationCounter := 0 + testMigrations := []migrations.Migration{ + fauxMigration{"faux-migration", "my help text", func(_ *declcfg.DeclarativeConfig) error { migrationCounter++; return nil }}, + } + specs := []spec{ { name: "SqliteImage/Success", @@ -58,8 +73,8 @@ func TestMigrate(t *testing.T) { Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogSqlite(bundleObjectType), - "bar/catalog.yaml": migrateBarCatalogSqlite(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), }, }, { @@ -72,8 +87,8 @@ func TestMigrate(t *testing.T) { Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogSqlite(bundleObjectType), - "bar/catalog.yaml": migrateBarCatalogSqlite(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), }, }, { @@ -86,7 +101,7 @@ func TestMigrate(t *testing.T) { Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogFBC(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogFBC(), }, }, { @@ -99,7 +114,7 @@ func TestMigrate(t *testing.T) { Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogFBC(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogFBC(), }, }, { @@ -113,9 +128,46 @@ func TestMigrate(t *testing.T) { }, expectErr: action.ErrNotAllowed, }, + { + name: "SqliteImage/Success/NoMigrations", + migrate: action.Migrate{ + CatalogRef: "test.registry/migrate/catalog:sqlite", + OutputDir: filepath.Join(tmpDir, "sqlite-image"), + WriteFunc: declcfg.WriteYAML, + FileExt: ".yaml", + Registry: reg, + Migrations: nil, + }, + expectedFiles: map[string]string{ + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), + }, + }, + { + name: "SqliteImage/Success/WithMigrations", + migrate: action.Migrate{ + CatalogRef: "test.registry/migrate/catalog:sqlite", + OutputDir: filepath.Join(tmpDir, "sqlite-image"), + WriteFunc: declcfg.WriteYAML, + FileExt: ".yaml", + Registry: reg, + Migrations: &migrations.Migrations{ + Migrations: testMigrations, + }, + }, + expectedFiles: map[string]string{ + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), + }, + migrationCount: 1, + }, } for _, s := range specs { t.Run(s.name, func(t *testing.T) { + var migrationPre int + if s.migrationCount != 0 { + migrationPre = migrationCounter + } err := s.migrate.Run(context.Background()) require.ErrorIs(t, err, s.expectErr) if s.expectErr != nil { @@ -134,6 +186,11 @@ func TestMigrate(t *testing.T) { require.Equal(t, expectedData, string(actualData)) return nil }) + err = os.RemoveAll(s.migrate.OutputDir) + require.NoError(t, err) + if s.migrationCount != 0 { + require.Equal(t, s.migrationCount, migrationPre+s.migrationCount) + } }) } } @@ -178,9 +235,8 @@ func newMigrateRegistry(t *testing.T, imageMap map[image.Reference]string) (imag return reg, nil } -func migrateFooCatalogSqlite(m migrationLevel) string { - - template := `--- +func migrateFooCatalogSqlite() string { + return `--- defaultChannel: beta name: foo schema: olm.package @@ -233,7 +289,12 @@ properties: value: packageName: bar versionRange: <0.1.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= relatedImages: - image: test.registry/foo-operator/foo-bundle:v0.1.0 name: "" @@ -263,7 +324,12 @@ properties: value: packageName: bar versionRange: <0.1.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwiaW5zdGFsbCI6eyJzcGVjIjp7ImRlcGxveW1lbnRzIjpbeyJuYW1lIjoiZm9vLW9wZXJhdG9yIiwic3BlYyI6eyJ0ZW1wbGF0ZSI6eyJzcGVjIjp7ImNvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQ6djAuMi4wIn1dfX19fSx7Im5hbWUiOiJmb28tb3BlcmF0b3ItMiIsInNwZWMiOnsidGVtcGxhdGUiOnsic3BlYyI6eyJjb250YWluZXJzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvby0yOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQtMjp2MC4yLjAifV19fX19XX0sInN0cmF0ZWd5IjoiZGVwbG95bWVudCJ9LCJyZWxhdGVkSW1hZ2VzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvbzp2MC4yLjAiLCJuYW1lIjoib3BlcmF0b3IifSx7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLW90aGVyOnYwLjIuMCIsIm5hbWUiOiJvdGhlciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19 relatedImages: - image: test.registry/foo-operator/foo-2:v0.2.0 name: "" @@ -279,61 +345,11 @@ relatedImages: name: operator schema: olm.bundle ` - bundle1CsvMetadata := `- type: olm.csv.metadata - value: - annotations: - olm.skipRange: <0.1.0 - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Foo - name: foos.test.foo - version: v1 - displayName: Foo Operator - provider: {}` - - bundle1BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0=` - - bundle2CsvMetadata := `- type: olm.csv.metadata - value: - annotations: - olm.skipRange: <0.2.0 - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Foo - name: foos.test.foo - version: v1 - displayName: Foo Operator - provider: {}` - - bundle2BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwiaW5zdGFsbCI6eyJzcGVjIjp7ImRlcGxveW1lbnRzIjpbeyJuYW1lIjoiZm9vLW9wZXJhdG9yIiwic3BlYyI6eyJ0ZW1wbGF0ZSI6eyJzcGVjIjp7ImNvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQ6djAuMi4wIn1dfX19fSx7Im5hbWUiOiJmb28tb3BlcmF0b3ItMiIsInNwZWMiOnsidGVtcGxhdGUiOnsic3BlYyI6eyJjb250YWluZXJzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvby0yOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQtMjp2MC4yLjAifV19fX19XX0sInN0cmF0ZWd5IjoiZGVwbG95bWVudCJ9LCJyZWxhdGVkSW1hZ2VzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvbzp2MC4yLjAiLCJuYW1lIjoib3BlcmF0b3IifSx7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLW90aGVyOnYwLjIuMCIsIm5hbWUiOiJvdGhlciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19` - var bundle1Metadata, bundle2Metadata string - switch m { - case csvMetadataType: - bundle1Metadata = bundle1CsvMetadata - bundle2Metadata = bundle2CsvMetadata - case bundleObjectType: - bundle1Metadata = bundle1BundleObject - bundle2Metadata = bundle2BundleObject - } - - return fmt.Sprintf(template, bundle1Metadata, bundle2Metadata) } -func migrateBarCatalogSqlite(m migrationLevel) string { - template := `--- +func migrateBarCatalogSqlite() string { + return `--- defaultChannel: alpha name: bar schema: olm.package @@ -361,7 +377,12 @@ properties: value: packageName: bar version: 0.1.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSJ9XX19 +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhci52MC4xLjAifSwic3BlYyI6eyJjdXN0b21yZXNvdXJjZWRlZmluaXRpb25zIjp7Im93bmVkIjpbeyJncm91cCI6InRlc3QuYmFyIiwia2luZCI6IkJhciIsIm5hbWUiOiJiYXJzLnRlc3QuYmFyIiwidmVyc2lvbiI6InYxYWxwaGExIn1dfSwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Jhci1vcGVyYXRvci9iYXI6djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= relatedImages: - image: test.registry/bar-operator/bar-bundle:v0.1.0 name: "" @@ -382,7 +403,12 @@ properties: value: packageName: bar version: 0.2.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSJ9XX19 +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJiYXIudjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmJhciIsImtpbmQiOiJCYXIiLCJuYW1lIjoiYmFycy50ZXN0LmJhciIsInZlcnNpb24iOiJ2MWFscGhhMSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9iYXItb3BlcmF0b3IvYmFyOnYwLjIuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwic2tpcHMiOlsiYmFyLnYwLjEuMCJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0= relatedImages: - image: test.registry/bar-operator/bar-bundle:v0.2.0 name: "" @@ -390,58 +416,10 @@ relatedImages: name: operator schema: olm.bundle ` - - bundle1CsvMetadata := `- type: olm.csv.metadata - value: - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Bar - name: bars.test.bar - version: v1alpha1 - provider: {}` - - bundle1BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSJ9XX19 -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhci52MC4xLjAifSwic3BlYyI6eyJjdXN0b21yZXNvdXJjZWRlZmluaXRpb25zIjp7Im93bmVkIjpbeyJncm91cCI6InRlc3QuYmFyIiwia2luZCI6IkJhciIsIm5hbWUiOiJiYXJzLnRlc3QuYmFyIiwidmVyc2lvbiI6InYxYWxwaGExIn1dfSwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Jhci1vcGVyYXRvci9iYXI6djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0=` - - bundle2CsvMetadata := `- type: olm.csv.metadata - value: - annotations: - olm.skipRange: <0.2.0 - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Bar - name: bars.test.bar - version: v1alpha1 - provider: {}` - - bundle2BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImJhcnMudGVzdC5iYXIifSwic3BlYyI6eyJncm91cCI6InRlc3QuYmFyIiwibmFtZXMiOnsia2luZCI6IkJhciIsInBsdXJhbCI6ImJhcnMifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MWFscGhhMSJ9XX19 -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJiYXIudjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmJhciIsImtpbmQiOiJCYXIiLCJuYW1lIjoiYmFycy50ZXN0LmJhciIsInZlcnNpb24iOiJ2MWFscGhhMSJ9XX0sInJlbGF0ZWRJbWFnZXMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9iYXItb3BlcmF0b3IvYmFyOnYwLjIuMCIsIm5hbWUiOiJvcGVyYXRvciJ9XSwic2tpcHMiOlsiYmFyLnYwLjEuMCJdLCJ2ZXJzaW9uIjoiMC4yLjAifX0=` - - var bundle1Metadata, bundle2Metadata string - switch m { - case csvMetadataType: - bundle1Metadata = bundle1CsvMetadata - bundle2Metadata = bundle2CsvMetadata - case bundleObjectType: - bundle1Metadata = bundle1BundleObject - bundle2Metadata = bundle2BundleObject - } - - return fmt.Sprintf(template, bundle1Metadata, bundle2Metadata) } -func migrateFooCatalogFBC(m migrationLevel) string { - template := `--- +func migrateFooCatalogFBC() string { + return `--- defaultChannel: beta name: foo properties: @@ -502,7 +480,12 @@ properties: value: packageName: bar versionRange: <0.1.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 relatedImages: - image: test.registry/foo-operator/foo-bundle:v0.1.0 name: "" @@ -532,7 +515,12 @@ properties: value: packageName: bar versionRange: <0.1.0 -%s +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwiaW5zdGFsbCI6eyJzcGVjIjp7ImRlcGxveW1lbnRzIjpbeyJuYW1lIjoiZm9vLW9wZXJhdG9yIiwic3BlYyI6eyJ0ZW1wbGF0ZSI6eyJzcGVjIjp7ImNvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQ6djAuMi4wIn1dfX19fSx7Im5hbWUiOiJmb28tb3BlcmF0b3ItMiIsInNwZWMiOnsidGVtcGxhdGUiOnsic3BlYyI6eyJjb250YWluZXJzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvby0yOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQtMjp2MC4yLjAifV19fX19XX0sInN0cmF0ZWd5IjoiZGVwbG95bWVudCJ9LCJyZWxhdGVkSW1hZ2VzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvbzp2MC4yLjAiLCJuYW1lIjoib3BlcmF0b3IifSx7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLW90aGVyOnYwLjIuMCIsIm5hbWUiOiJvdGhlciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19 +- type: olm.bundle.object + value: + data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19 relatedImages: - image: test.registry/foo-operator/foo-2:v0.2.0 name: "" @@ -548,56 +536,4 @@ relatedImages: name: operator schema: olm.bundle ` - - bundle1CsvMetadata := `- type: olm.csv.metadata - value: - annotations: - olm.skipRange: <0.1.0 - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Foo - name: foos.test.foo - version: v1 - displayName: Foo Operator - provider: {}` - - bundle1BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMS4wIn0sIm5hbWUiOiJmb28udjAuMS4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwicmVsYXRlZEltYWdlcyI6W3siaW1hZ2UiOiJ0ZXN0LnJlZ2lzdHJ5L2Zvby1vcGVyYXRvci9mb286djAuMS4wIiwibmFtZSI6Im9wZXJhdG9yIn1dLCJ2ZXJzaW9uIjoiMC4xLjAifX0= -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19` - - bundle2CsvMetadata := `- type: olm.csv.metadata - value: - annotations: - olm.skipRange: <0.2.0 - apiServiceDefinitions: {} - crdDescriptions: - owned: - - kind: Foo - name: foos.test.foo - version: v1 - displayName: Foo Operator - provider: {}` - - bundle2BundleObject := `- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoib3BlcmF0b3JzLmNvcmVvcy5jb20vdjFhbHBoYTEiLCJraW5kIjoiQ2x1c3RlclNlcnZpY2VWZXJzaW9uIiwibWV0YWRhdGEiOnsiYW5ub3RhdGlvbnMiOnsib2xtLnNraXBSYW5nZSI6Ilx1MDAzYzAuMi4wIn0sIm5hbWUiOiJmb28udjAuMi4wIn0sInNwZWMiOnsiY3VzdG9tcmVzb3VyY2VkZWZpbml0aW9ucyI6eyJvd25lZCI6W3siZ3JvdXAiOiJ0ZXN0LmZvbyIsImtpbmQiOiJGb28iLCJuYW1lIjoiZm9vcy50ZXN0LmZvbyIsInZlcnNpb24iOiJ2MSJ9XX0sImRpc3BsYXlOYW1lIjoiRm9vIE9wZXJhdG9yIiwiaW5zdGFsbCI6eyJzcGVjIjp7ImRlcGxveW1lbnRzIjpbeyJuYW1lIjoiZm9vLW9wZXJhdG9yIiwic3BlYyI6eyJ0ZW1wbGF0ZSI6eyJzcGVjIjp7ImNvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQ6djAuMi4wIn1dfX19fSx7Im5hbWUiOiJmb28tb3BlcmF0b3ItMiIsInNwZWMiOnsidGVtcGxhdGUiOnsic3BlYyI6eyJjb250YWluZXJzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvby0yOnYwLjIuMCJ9XSwiaW5pdENvbnRhaW5lcnMiOlt7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLWluaXQtMjp2MC4yLjAifV19fX19XX0sInN0cmF0ZWd5IjoiZGVwbG95bWVudCJ9LCJyZWxhdGVkSW1hZ2VzIjpbeyJpbWFnZSI6InRlc3QucmVnaXN0cnkvZm9vLW9wZXJhdG9yL2Zvbzp2MC4yLjAiLCJuYW1lIjoib3BlcmF0b3IifSx7ImltYWdlIjoidGVzdC5yZWdpc3RyeS9mb28tb3BlcmF0b3IvZm9vLW90aGVyOnYwLjIuMCIsIm5hbWUiOiJvdGhlciJ9XSwicmVwbGFjZXMiOiJmb28udjAuMS4wIiwic2tpcHMiOlsiZm9vLnYwLjEuMSIsImZvby52MC4xLjIiXSwidmVyc2lvbiI6IjAuMi4wIn19 -- type: olm.bundle.object - value: - data: eyJhcGlWZXJzaW9uIjoiYXBpZXh0ZW5zaW9ucy5rOHMuaW8vdjEiLCJraW5kIjoiQ3VzdG9tUmVzb3VyY2VEZWZpbml0aW9uIiwibWV0YWRhdGEiOnsibmFtZSI6ImZvb3MudGVzdC5mb28ifSwic3BlYyI6eyJncm91cCI6InRlc3QuZm9vIiwibmFtZXMiOnsia2luZCI6IkZvbyIsInBsdXJhbCI6ImZvb3MifSwidmVyc2lvbnMiOlt7Im5hbWUiOiJ2MSJ9XX19` - - var bundle1Metadata, bundle2Metadata string - switch m { - case csvMetadataType: - bundle1Metadata = bundle1CsvMetadata - bundle2Metadata = bundle2CsvMetadata - case bundleObjectType: - bundle1Metadata = bundle1BundleObject - bundle2Metadata = bundle2BundleObject - } - - return fmt.Sprintf(template, bundle1Metadata, bundle2Metadata) } diff --git a/alpha/action/migrations/000_bundle_object_to_csv_metadata.go b/alpha/action/migrations/000_bundle_object_to_csv_metadata.go index 5edd2ef5f..d5c6896ea 100644 --- a/alpha/action/migrations/000_bundle_object_to_csv_metadata.go +++ b/alpha/action/migrations/000_bundle_object_to_csv_metadata.go @@ -9,42 +9,39 @@ import ( "github.com/operator-framework/operator-registry/alpha/property" ) -var bundleObjectToCSVMetadata = newMigration( - "bundle-object-to-csv-metadata", - "migrates bundles' `olm.bundle.object` to `olm.csv.metadata`", - func(cfg *declcfg.DeclarativeConfig) error { - convertBundleObjectToCSVMetadata := func(b *declcfg.Bundle) error { - if b.Image == "" || b.CsvJSON == "" { - return nil - } - - var csv v1alpha1.ClusterServiceVersion - if err := json.Unmarshal([]byte(b.CsvJSON), &csv); err != nil { - return err - } - - props := b.Properties[:0] - for _, p := range b.Properties { - switch p.Type { - case property.TypeBundleObject: - // Get rid of the bundle objects - case property.TypeCSVMetadata: - // If this bundle already has a CSV metadata - // property, we won't mutate the bundle at all. - return nil - default: - // Keep all of the other properties - props = append(props, p) - } - } - b.Properties = append(props, property.MustBuildCSVMetadata(csv)) +func bundleObjectToCSVMetadata(cfg *declcfg.DeclarativeConfig) error { + convertBundleObjectToCSVMetadata := func(b *declcfg.Bundle) error { + if b.Image == "" || b.CsvJSON == "" { return nil } - for bi := range cfg.Bundles { - if err := convertBundleObjectToCSVMetadata(&cfg.Bundles[bi]); err != nil { - return err + var csv v1alpha1.ClusterServiceVersion + if err := json.Unmarshal([]byte(b.CsvJSON), &csv); err != nil { + return err + } + + props := b.Properties[:0] + for _, p := range b.Properties { + switch p.Type { + case property.TypeBundleObject: + // Get rid of the bundle objects + case property.TypeCSVMetadata: + // If this bundle already has a CSV metadata + // property, we won't mutate the bundle at all. + return nil + default: + // Keep all of the other properties + props = append(props, p) } } + b.Properties = append(props, property.MustBuildCSVMetadata(csv)) return nil - }) + } + + for bi := range cfg.Bundles { + if err := convertBundleObjectToCSVMetadata(&cfg.Bundles[bi]); err != nil { + return err + } + } + return nil +} diff --git a/alpha/action/migrations/migrations.go b/alpha/action/migrations/migrations.go index 6f4e3a3ed..1742e481b 100644 --- a/alpha/action/migrations/migrations.go +++ b/alpha/action/migrations/migrations.go @@ -9,24 +9,32 @@ import ( "github.com/operator-framework/operator-registry/alpha/declcfg" ) +type MigrationToken string + +const ( + invalidMigration string = "" + NoMigrations string = "none" + AllMigrations string = "all" +) + type Migration interface { - Name() string + Token() MigrationToken Help() string Migrate(*declcfg.DeclarativeConfig) error } -func newMigration(name string, help string, fn func(config *declcfg.DeclarativeConfig) error) Migration { - return &simpleMigration{name: name, help: help, fn: fn} +func newMigration(token string, help string, fn func(config *declcfg.DeclarativeConfig) error) Migration { + return &simpleMigration{token: MigrationToken(token), help: help, fn: fn} } type simpleMigration struct { - name string - help string - fn func(*declcfg.DeclarativeConfig) error + token MigrationToken + help string + fn func(*declcfg.DeclarativeConfig) error } -func (s simpleMigration) Name() string { - return s.name +func (s simpleMigration) Token() MigrationToken { + return s.token } func (s simpleMigration) Migrate(config *declcfg.DeclarativeConfig) error { @@ -41,22 +49,33 @@ type Migrations struct { Migrations []Migration } -func GetLastMigrationName() string { - if len(allMigrations) == 0 { - return "" +func validateName(name string) (MigrationToken, error) { + if name == AllMigrations { + return MigrationToken(AllMigrations), nil } - return allMigrations[len(allMigrations)-1].Name() + for _, migration := range allMigrations { + if migration.Token() == MigrationToken(name) { + return migration.Token(), nil + } + } + return MigrationToken(invalidMigration), fmt.Errorf("unknown migration level %q", name) } // allMigrations represents the migration catalog // the order of these migrations is important var allMigrations = []Migration{ - bundleObjectToCSVMetadata, + newMigration(NoMigrations, "do nothing", func(_ *declcfg.DeclarativeConfig) error { return nil }), + newMigration("bundle-object-to-csv-metadata", `migrates bundles' "olm.bundle.object" to "olm.csv.metadata"`, bundleObjectToCSVMetadata), } -func NewMigrations(level string) (*Migrations, error) { - if level == "" { - return &Migrations{}, nil +func NewMigrations(name string) (*Migrations, error) { + if name == AllMigrations { + return &Migrations{Migrations: slices.Clone(allMigrations)}, nil + } + + token, err := validateName(name) + if err != nil { + return nil, err } migrations := slices.Clone(allMigrations) @@ -65,28 +84,28 @@ func NewMigrations(level string) (*Migrations, error) { keep := migrations[:0] for _, migration := range migrations { keep = append(keep, migration) - if migration.Name() == level { + if migration.Token() == token { found = true break } } if !found { - return nil, fmt.Errorf("unknown migration level %q", level) + return nil, fmt.Errorf("unknown migration level %q", name) } return &Migrations{Migrations: keep}, nil } func HelpText() string { var help strings.Builder - help.WriteString(" The migrator will run all migrations up to and including the selected level.\n\n") - help.WriteString(" Available migrators:\n") + help.WriteString("\nThe migrator will run all migrations up to and including the selected level.\n\n") + help.WriteString("Available migrators:\n") if len(allMigrations) == 0 { help.WriteString(" (no migrations available in this version)\n") } - tabber := tabwriter.NewWriter(&help, 20, 30, 1, '\t', tabwriter.AlignRight) + tabber := tabwriter.NewWriter(&help, 0, 0, 1, ' ', 0) for _, migration := range allMigrations { - fmt.Fprintf(tabber, " - %s\t%s\n", migration.Name(), migration.Help()) + fmt.Fprintf(tabber, " - %s\t: %s\n", migration.Token(), migration.Help()) } tabber.Flush() return help.String() diff --git a/alpha/action/migrations/migrations_test.go b/alpha/action/migrations/migrations_test.go new file mode 100644 index 000000000..faf588c11 --- /dev/null +++ b/alpha/action/migrations/migrations_test.go @@ -0,0 +1,39 @@ +package migrations + +import ( + "testing" + + "github.com/operator-framework/operator-registry/alpha/declcfg" +) + +func TestMigrations(t *testing.T) { + tests := []struct { + name string + migrator Migration + expectedResult error + }{ + { + name: "NoMigrations", + migrator: newMigration(NoMigrations, "do nothing", func(_ *declcfg.DeclarativeConfig) error { return nil }), + expectedResult: nil, + }, + { + name: "BundleObjectToCSVMetadata", + migrator: newMigration("bundle-object-to-csv-metadata", `migrates bundles' "olm.bundle.object" to "olm.csv.metadata"`, bundleObjectToCSVMetadata), + expectedResult: nil, // Replace with the expected result for this migration + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + config := &declcfg.DeclarativeConfig{} // Replace with your actual config + + err := test.migrator.Migrate(config) + if err != test.expectedResult { + t.Errorf("Expected error: %v, but got: %v", test.expectedResult, err) + } + + // Add additional success criteria evaluation between each migration's execution if needed + }) + } +} diff --git a/alpha/action/render.go b/alpha/action/render.go index 75ea58a07..798ffb5e5 100644 --- a/alpha/action/render.go +++ b/alpha/action/render.go @@ -54,8 +54,8 @@ type Render struct { Refs []string Registry image.Registry AllowedRefMask RefType - MigrationLevel string ImageRefTemplate *template.Template + Migrations *migrations.Migrations skipSqliteDeprecationLog bool } @@ -88,7 +88,7 @@ func (r Render) Run(ctx context.Context) (*declcfg.DeclarativeConfig, error) { }) } - if err := migrate(cfg, r.MigrationLevel); err != nil { + if err := r.migrate(cfg); err != nil { return nil, fmt.Errorf("migrate: %v", err) } @@ -414,18 +414,12 @@ func moveBundleObjectsToEndOfPropertySlices(cfg *declcfg.DeclarativeConfig) { } } -func migrate(cfg *declcfg.DeclarativeConfig, migrateLevel string) error { - mobj, err := migrations.NewMigrations(migrateLevel) - if err != nil { - return err - } - - err = mobj.Migrate(cfg) - if err != nil { - return err +func (r Render) migrate(cfg *declcfg.DeclarativeConfig) error { + // If there are no migrations, do nothing. + if r.Migrations == nil { + return nil } - - return nil + return r.Migrations.Migrate(cfg) } func combineConfigs(cfgs []declcfg.DeclarativeConfig) *declcfg.DeclarativeConfig { diff --git a/alpha/action/render_test.go b/alpha/action/render_test.go index a00721812..9428be8ad 100644 --- a/alpha/action/render_test.go +++ b/alpha/action/render_test.go @@ -20,6 +20,7 @@ import ( "k8s.io/apimachinery/pkg/util/yaml" "github.com/operator-framework/operator-registry/alpha/action" + "github.com/operator-framework/operator-registry/alpha/action/migrations" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/alpha/property" "github.com/operator-framework/operator-registry/pkg/containertools" @@ -29,9 +30,6 @@ import ( "github.com/operator-framework/operator-registry/pkg/sqlite" ) -// aligns with supported migrations in alpha.action.migrations -const migrationLevelCSVMetadata = "bundle-object-to-csv-metadata" - func TestRender(t *testing.T) { type spec struct { name string @@ -75,13 +73,18 @@ func TestRender(t *testing.T) { image.SimpleReference("test.registry/foo-operator/foo-bundle:v0.2.0"): "testdata/foo-bundle-v0.2.0", } assert.NoError(t, generateSqliteFile(dbFile, imageMap)) + allMigrations, err := migrations.NewMigrations(migrations.AllMigrations) + require.NoError(t, err) + noMigrations, err := migrations.NewMigrations(migrations.NoMigrations) + require.NoError(t, err) specs := []spec{ { name: "Success/SqliteIndexImage", render: action.Render{ - Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, - Registry: reg, + Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, + Registry: reg, + Migrations: noMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Packages: []declcfg.Package{ @@ -172,9 +175,9 @@ func TestRender(t *testing.T) { { name: "Success/SqliteIndexImageCSVMigration", render: action.Render{ - Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, - Registry: reg, - MigrationLevel: migrationLevelCSVMetadata, + Refs: []string{"test.registry/foo-operator/foo-index-sqlite:v0.2.0"}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Packages: []declcfg.Package{ @@ -355,9 +358,9 @@ func TestRender(t *testing.T) { { name: "Success/SqliteFileMigration", render: action.Render{ - Refs: []string{dbFile}, - Registry: reg, - MigrationLevel: migrationLevelCSVMetadata, + Refs: []string{dbFile}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Packages: []declcfg.Package{ @@ -642,9 +645,9 @@ func TestRender(t *testing.T) { { name: "Success/DeclcfgImageMigrate", render: action.Render{ - Refs: []string{"test.registry/foo-operator/foo-index-declcfg:v0.2.0"}, - MigrationLevel: migrationLevelCSVMetadata, - Registry: reg, + Refs: []string{"test.registry/foo-operator/foo-index-declcfg:v0.2.0"}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Packages: []declcfg.Package{ @@ -739,9 +742,9 @@ func TestRender(t *testing.T) { { name: "Success/DeclcfgDirectoryMigrate", render: action.Render{ - Refs: []string{"testdata/foo-index-v0.2.0-declcfg"}, - MigrationLevel: migrationLevelCSVMetadata, - Registry: reg, + Refs: []string{"testdata/foo-index-v0.2.0-declcfg"}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Packages: []declcfg.Package{ @@ -886,9 +889,9 @@ func TestRender(t *testing.T) { { name: "Success/BundleImageMigration", render: action.Render{ - Refs: []string{"test.registry/foo-operator/foo-bundle:v0.2.0"}, - Registry: reg, - MigrationLevel: migrationLevelCSVMetadata, + Refs: []string{"test.registry/foo-operator/foo-bundle:v0.2.0"}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Bundles: []declcfg.Bundle{ @@ -981,9 +984,9 @@ func TestRender(t *testing.T) { { name: "Success/BundleImageWithNoCSVRelatedImagesMigration", render: action.Render{ - Refs: []string{"test.registry/foo-operator/foo-bundle-no-csv-related-images:v0.2.0"}, - Registry: reg, - MigrationLevel: migrationLevelCSVMetadata, + Refs: []string{"test.registry/foo-operator/foo-bundle-no-csv-related-images:v0.2.0"}, + Registry: reg, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Bundles: []declcfg.Bundle{ @@ -1077,7 +1080,7 @@ func TestRender(t *testing.T) { Refs: []string{"testdata/foo-bundle-v0.2.0"}, ImageRefTemplate: template.Must(template.New("imageRef").Parse("test.registry/{{.Package}}-operator/{{.Package}}:v{{.Version}}")), Registry: reg, - MigrationLevel: migrationLevelCSVMetadata, + Migrations: allMigrations, }, expectCfg: &declcfg.DeclarativeConfig{ Bundles: []declcfg.Bundle{ diff --git a/alpha/template/basic/basic.go b/alpha/template/basic/basic.go index fc467162e..0456dc151 100644 --- a/alpha/template/basic/basic.go +++ b/alpha/template/basic/basic.go @@ -9,6 +9,7 @@ import ( "k8s.io/apimachinery/pkg/util/yaml" "github.com/operator-framework/operator-registry/alpha/action" + "github.com/operator-framework/operator-registry/alpha/action/migrations" "github.com/operator-framework/operator-registry/alpha/declcfg" "github.com/operator-framework/operator-registry/pkg/image" ) @@ -16,8 +17,8 @@ import ( const schema string = "olm.template.basic" type Template struct { - Registry image.Registry - MigrationLevel string + Registry image.Registry + Migrations *migrations.Migrations } type BasicTemplate struct { @@ -60,7 +61,7 @@ func (t Template) Render(ctx context.Context, reader io.Reader) (*declcfg.Declar r := action.Render{ Registry: t.Registry, AllowedRefMask: action.RefBundleImage, - MigrationLevel: t.MigrationLevel, + Migrations: t.Migrations, } for _, b := range cfg.Bundles { diff --git a/alpha/template/semver/semver.go b/alpha/template/semver/semver.go index 67b71b9e1..dfd584a1c 100644 --- a/alpha/template/semver/semver.go +++ b/alpha/template/semver/semver.go @@ -35,7 +35,7 @@ func (t Template) Render(ctx context.Context) (*declcfg.DeclarativeConfig, error AllowedRefMask: action.RefBundleImage, Refs: []string{b}, Registry: t.Registry, - MigrationLevel: t.MigrationLevel, + Migrations: t.Migrations, } c, err := r.Run(ctx) if err != nil { diff --git a/alpha/template/semver/types.go b/alpha/template/semver/types.go index 458252321..f170074c3 100644 --- a/alpha/template/semver/types.go +++ b/alpha/template/semver/types.go @@ -5,14 +5,15 @@ import ( "github.com/blang/semver/v4" + "github.com/operator-framework/operator-registry/alpha/action/migrations" "github.com/operator-framework/operator-registry/pkg/image" ) // data passed into this module externally type Template struct { - Data io.Reader - Registry image.Registry - MigrationLevel string + Data io.Reader + Registry image.Registry + Migrations *migrations.Migrations } // IO structs -- BEGIN diff --git a/cmd/opm/alpha/template/basic.go b/cmd/opm/alpha/template/basic.go index 3729df960..b1b77a888 100644 --- a/cmd/opm/alpha/template/basic.go +++ b/cmd/opm/alpha/template/basic.go @@ -16,7 +16,8 @@ import ( func newBasicTemplateCmd() *cobra.Command { var ( - template basic.Template + template basic.Template + migrateLevel string ) cmd := &cobra.Command{ Use: "basic basic-template-file", @@ -63,6 +64,14 @@ When FILE is '-' or not provided, the template is read from standard input`, template.Registry = reg + if migrateLevel != "" { + m, err := migrations.NewMigrations(migrateLevel) + if err != nil { + log.Fatal(err) + } + template.Migrations = m + } + // only taking first file argument cfg, err := template.Render(cmd.Context(), data) if err != nil { @@ -75,7 +84,7 @@ When FILE is '-' or not provided, the template is read from standard input`, }, } - cmd.Flags().StringVar(&template.MigrationLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) + cmd.Flags().StringVar(&migrateLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) return cmd } diff --git a/cmd/opm/alpha/template/semver.go b/cmd/opm/alpha/template/semver.go index 7caf93036..e0547a67c 100644 --- a/cmd/opm/alpha/template/semver.go +++ b/cmd/opm/alpha/template/semver.go @@ -72,6 +72,13 @@ When FILE is '-' or not provided, the template is read from standard input`, Data: data, Registry: reg, } + if migrateLevel != "" { + m, err := migrations.NewMigrations(migrateLevel) + if err != nil { + log.Fatal(err) + } + template.Migrations = m + } out, err := template.Render(cmd.Context()) if err != nil { log.Fatalf("semver %q: %v", source, err) diff --git a/cmd/opm/migrate/cmd.go b/cmd/opm/migrate/cmd.go index 501763090..edf496a33 100644 --- a/cmd/opm/migrate/cmd.go +++ b/cmd/opm/migrate/cmd.go @@ -14,8 +14,9 @@ import ( func NewCmd() *cobra.Command { var ( - migrate action.Migrate - output string + migrate action.Migrate + migrateLevel string + output string ) cmd := &cobra.Command{ Use: "migrate ", @@ -43,6 +44,14 @@ parsers that assume that a file contains exactly one valid JSON object. log.Fatalf("invalid --output value %q, expected (json|yaml)", output) } + if migrateLevel != "" { + m, err := migrations.NewMigrations(migrateLevel) + if err != nil { + log.Fatal(err) + } + migrate.Migrations = m + } + logrus.Infof("rendering index %q as file-based catalog", migrate.CatalogRef) if err := migrate.Run(cmd.Context()); err != nil { logrus.New().Fatal(err) @@ -52,7 +61,7 @@ parsers that assume that a file contains exactly one valid JSON object. }, } cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format (json|yaml)") - cmd.Flags().StringVar(&migrate.Level, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) + cmd.Flags().StringVar(&migrateLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) return cmd } diff --git a/cmd/opm/render/cmd.go b/cmd/opm/render/cmd.go index ebc8bb69d..683d11a1c 100644 --- a/cmd/opm/render/cmd.go +++ b/cmd/opm/render/cmd.go @@ -22,7 +22,8 @@ func NewCmd(showAlphaHelp bool) *cobra.Command { output string imageRefTemplate string - deprecatedMigrateFlag bool + oldMigrateAllFlag bool + migrateLevel string ) cmd := &cobra.Command{ Use: "render [catalog-image | catalog-directory | bundle-image | bundle-directory | sqlite-file]...", @@ -67,9 +68,16 @@ database files. } // if the deprecated flag was used, set the level explicitly to the last migration to perform all migrations - if deprecatedMigrateFlag { - render.MigrationLevel = migrations.GetLastMigrationName() + var m *migrations.Migrations + if oldMigrateAllFlag { + m, err = migrations.NewMigrations(migrations.AllMigrations) + } else if migrateLevel != "" { + m, err = migrations.NewMigrations(migrateLevel) } + if err != nil { + log.Fatal(err) + } + render.Migrations = m cfg, err := render.Run(cmd.Context()) if err != nil { @@ -83,9 +91,8 @@ database files. } cmd.Flags().StringVarP(&output, "output", "o", "json", "Output format of the streamed file-based catalog objects (json|yaml)") - cmd.Flags().StringVar(&render.MigrationLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) - cmd.Flags().BoolVar(&deprecatedMigrateFlag, "migrate", false, "Perform migrations on the rendered FBC") - cmd.Flags().MarkDeprecated("migrate", "use --migrate-level instead") + cmd.Flags().StringVar(&migrateLevel, "migrate-level", "", "Name of the last migration to run (default: none)\n"+migrations.HelpText()) + cmd.Flags().BoolVar(&oldMigrateAllFlag, "migrate", false, "Perform all available schema migrations on the rendered FBC") cmd.MarkFlagsMutuallyExclusive("migrate", "migrate-level") // Alpha flags