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..de5f37a84 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{ @@ -39,83 +49,121 @@ func TestMigrate(t *testing.T) { image.SimpleReference("test.registry/bar-operator/bar-bundle:v0.2.0"): "testdata/bar-bundle-v0.2.0", } - tmpDir := t.TempDir() - dbFile := filepath.Join(tmpDir, "index.db") + sqliteDBDir := t.TempDir() + dbFile := filepath.Join(sqliteDBDir, "index.db") err := generateSqliteFile(dbFile, sqliteBundles) require.NoError(t, err) 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", migrate: action.Migrate{ CatalogRef: "test.registry/migrate/catalog:sqlite", - OutputDir: filepath.Join(tmpDir, "sqlite-image"), WriteFunc: declcfg.WriteYAML, FileExt: ".yaml", Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogSqlite(bundleObjectType), - "bar/catalog.yaml": migrateBarCatalogSqlite(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), }, }, { name: "SqliteFile/Success", migrate: action.Migrate{ CatalogRef: dbFile, - OutputDir: filepath.Join(tmpDir, "sqlite-file"), WriteFunc: declcfg.WriteYAML, FileExt: ".yaml", Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogSqlite(bundleObjectType), - "bar/catalog.yaml": migrateBarCatalogSqlite(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogSqlite(), + "bar/catalog.yaml": migrateBarCatalogSqlite(), }, }, { name: "DeclcfgImage/Success", migrate: action.Migrate{ CatalogRef: "test.registry/foo-operator/foo-index-declcfg:v0.2.0", - OutputDir: filepath.Join(tmpDir, "declcfg-image"), WriteFunc: declcfg.WriteYAML, FileExt: ".yaml", Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogFBC(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogFBC(), }, }, { name: "DeclcfgDir/Success", migrate: action.Migrate{ CatalogRef: "testdata/foo-index-v0.2.0-declcfg", - OutputDir: filepath.Join(tmpDir, "declcfg-dir"), WriteFunc: declcfg.WriteYAML, FileExt: ".yaml", Registry: reg, }, expectedFiles: map[string]string{ - "foo/catalog.yaml": migrateFooCatalogFBC(bundleObjectType), + "foo/catalog.yaml": migrateFooCatalogFBC(), }, }, { name: "BundleImage/Failure", migrate: action.Migrate{ CatalogRef: "test.registry/foo-operator/foo-bundle:v0.1.0", - OutputDir: filepath.Join(tmpDir, "bundle-image"), WriteFunc: declcfg.WriteYAML, FileExt: ".yaml", Registry: reg, }, expectErr: action.ErrNotAllowed, }, + { + name: "SqliteImage/Success/NoMigrations", + migrate: action.Migrate{ + CatalogRef: "test.registry/migrate/catalog:sqlite", + 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", + 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 + } + + s.migrate.OutputDir = t.TempDir() + err := s.migrate.Run(context.Background()) require.ErrorIs(t, err, s.expectErr) if s.expectErr != nil { @@ -134,6 +182,10 @@ func TestMigrate(t *testing.T) { require.Equal(t, expectedData, string(actualData)) return nil }) + + if s.migrationCount != 0 { + require.Equal(t, migrationCounter, migrationPre+s.migrationCount) + } }) } } @@ -178,9 +230,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 +284,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 +319,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 +340,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 +372,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 +398,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 +411,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 +475,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 +510,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 +531,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..22ff86b74 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,16 @@ type Migrations struct { Migrations []Migration } -func GetLastMigrationName() string { - if len(allMigrations) == 0 { - return "" - } - return allMigrations[len(allMigrations)-1].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 } migrations := slices.Clone(allMigrations) @@ -65,28 +67,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() == MigrationToken(name) { 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..4b5afa3ad --- /dev/null +++ b/alpha/action/migrations/migrations_test.go @@ -0,0 +1,139 @@ +package migrations + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-registry/alpha/declcfg" + "github.com/operator-framework/operator-registry/alpha/property" + "github.com/stretchr/testify/require" +) + +func TestMigrations(t *testing.T) { + noneMigration, err := NewMigrations(NoMigrations) + require.NoError(t, err) + csvMigration, err := NewMigrations("bundle-object-to-csv-metadata") + require.NoError(t, err) + allMigrations, err := NewMigrations(AllMigrations) + require.NoError(t, err) + + evaluators := map[MigrationToken]func(*declcfg.DeclarativeConfig) error{ + MigrationToken(NoMigrations): func(d *declcfg.DeclarativeConfig) error { + if diff := cmp.Diff(*d, unmigratedCatalogFBC()); diff != "" { + return fmt.Errorf("'none' migrator is not expected to change the config\n%s", diff) + } + return nil + }, + MigrationToken("bundle-object-to-csv-metadata"): func(d *declcfg.DeclarativeConfig) error { + if diff := cmp.Diff(*d, csvMetadataCatalogFBC()); diff != "" { + return fmt.Errorf("unexpected result of migration\n%s", diff) + } + return nil + }, + } + + tests := []struct { + name string + migrators *Migrations + }{ + { + name: "NoMigrations", + migrators: noneMigration, + }, + { + name: "BundleObjectToCSVMetadata", + migrators: csvMigration, + }, + { + name: "MigrationSequence", + migrators: allMigrations, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + var config declcfg.DeclarativeConfig = unmigratedCatalogFBC() + + for _, m := range test.migrators.Migrations { + err := m.Migrate(&config) + require.NoError(t, err) + err = evaluators[m.Token()](&config) + require.NoError(t, err) + } + }) + } +} + +func mustBuildCSVMetadata(r io.Reader) property.Property { + var csv v1alpha1.ClusterServiceVersion + if err := json.NewDecoder(r).Decode(&csv); err != nil { + panic(err) + } + return property.MustBuildCSVMetadata(csv) +} + +var fooRawCsv = []byte(`{"apiVersion": "operators.coreos.com/v1alpha1", "kind": "ClusterServiceVersion", "metadata": {"name": "foo.v0.1.0"}, "spec": {"displayName": "Foo Operator", "customresourcedefinitions": {"owned": [{"group": "test.foo", "version": "v1", "kind": "Foo", "name": "foos.test.foo"}]}, "version": "0.1.0", "relatedImages": [{"name": "operator", "image": "test.registry/foo-operator/foo:v0.1.0"}]}}`) + +var fooRawCrd = []byte(`--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: foos.test.foo +spec: + group: test.foo + names: + kind: Foo + plural: foos + versions: + - name: v1`, +) + +func unmigratedCatalogFBC() declcfg.DeclarativeConfig { + return declcfg.DeclarativeConfig{ + Bundles: []declcfg.Bundle{ + { + Schema: "olm.bundle", + Name: "foo.v0.1.0", + Package: "foo", + Image: "quay.io/openshift-community-operators/foo:v0.1.0", + Properties: []property.Property{ + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.1.0"), + property.MustBuildPackageRequired("bar", "<0.1.0"), + property.MustBuildBundleObject(fooRawCrd), + property.MustBuildBundleObject(fooRawCsv), + }, + Objects: []string{string(fooRawCsv), string(fooRawCrd)}, + CsvJSON: string(fooRawCsv), + }, + }, + } +} + +func csvMetadataCatalogFBC() declcfg.DeclarativeConfig { + return declcfg.DeclarativeConfig{ + Bundles: []declcfg.Bundle{ + { + Schema: "olm.bundle", + Name: "foo.v0.1.0", + Package: "foo", + Image: "quay.io/openshift-community-operators/foo:v0.1.0", + Properties: []property.Property{ + property.MustBuildGVK("test.foo", "v1", "Foo"), + property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"), + property.MustBuildPackage("foo", "0.1.0"), + property.MustBuildPackageRequired("bar", "<0.1.0"), + mustBuildCSVMetadata(bytes.NewReader(fooRawCsv)), + }, + Objects: []string{string(fooRawCsv), string(fooRawCrd)}, + CsvJSON: string(fooRawCsv), + }, + }, + } +} 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