Skip to content

Commit

Permalink
use a type to protect migration interactions
Browse files Browse the repository at this point in the history
Signed-off-by: Jordan Keister <jordan@nimblewidget.com>
  • Loading branch information
grokspawn committed Aug 8, 2024
1 parent 9e68d1c commit b50b1a8
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 292 deletions.
7 changes: 4 additions & 3 deletions alpha/action/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ 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"
)

type Migrate struct {
CatalogRef string
OutputDir string
Level string
Migrations *migrations.Migrations

WriteFunc declcfg.WriteFunc
FileExt string
Expand All @@ -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,
Expand Down
294 changes: 115 additions & 179 deletions alpha/action/migrate_test.go

Large diffs are not rendered by default.

63 changes: 30 additions & 33 deletions alpha/action/migrations/000_bundle_object_to_csv_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
63 changes: 41 additions & 22 deletions alpha/action/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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()
Expand Down
39 changes: 39 additions & 0 deletions alpha/action/migrations/migrations_test.go
Original file line number Diff line number Diff line change
@@ -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
})
}
}
20 changes: 7 additions & 13 deletions alpha/action/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ type Render struct {
Refs []string
Registry image.Registry
AllowedRefMask RefType
MigrationLevel string
ImageRefTemplate *template.Template
Migrations *migrations.Migrations

skipSqliteDeprecationLog bool
}
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit b50b1a8

Please sign in to comment.