diff --git a/migration/analyzer.go b/migration/analyzer.go new file mode 100644 index 0000000..e7f0a88 --- /dev/null +++ b/migration/analyzer.go @@ -0,0 +1,30 @@ +package migration + +type Analyzer interface { + Analyze(plan *Plan) *StateMigration +} + +type defaultAnalyzer struct { + resolvers []Resolver +} + +var _ Analyzer = (*defaultAnalyzer)(nil) + +func NewDefaultAnalyzer() Analyzer { + return &defaultAnalyzer{ + resolvers: []Resolver{ + &StateImportResolver{}, + }, + } +} + +func (a *defaultAnalyzer) Analyze(plan *Plan) *StateMigration { + var migration StateMigration + + for _, r := range a.resolvers { + actions := r.Resolve(plan) + migration.AppendActions(actions...) + } + + return &migration +} diff --git a/migration/generate.go b/migration/generate.go new file mode 100644 index 0000000..8ed277c --- /dev/null +++ b/migration/generate.go @@ -0,0 +1,13 @@ +package migration + +func Generate(planJSON []byte) ([]byte, error) { + plan, err := NewPlan(planJSON) + if err != nil { + return nil, err + } + + analyzer := NewDefaultAnalyzer() + migration := analyzer.Analyze(plan) + + return migration.Render() +} diff --git a/migration/migration.go b/migration/migration.go index 66fe6ac..eb0a7c8 100644 --- a/migration/migration.go +++ b/migration/migration.go @@ -1,26 +1,51 @@ package migration import ( - "github.com/minamijoyo/tfedit/migration/schema" - _ "github.com/minamijoyo/tfedit/migration/schema/aws" // Register schema for aws + "bytes" + "fmt" + "text/template" ) -func Generate(planJSON []byte) ([]byte, error) { - plan, err := NewPlan(planJSON) - if err != nil { - return nil, err - } +// StateMigration is a type which is equivalent to tfmigrate.StateMigratorConfig of +// minamijoyo/tfmigrate. +// The current implementation doesn't encode migration actions to a file +// directly with gohcl, so we avoid to depend on tfmigrate's type and we define +// only what we need here. +type StateMigration struct { + // Dir is a working directory for executing terraform command. + Dir string + // Actions is a list of state action. + Actions []Action +} + +var migrationTemplate = `migration "state" "awsv4upgrade" { + actions = [ + {{- range .Actions }} + "{{ .MigrationAction }}", + {{- end }} + ] +} +` + +var compiledMigrationTemplate = template.Must(template.New("migration").Parse(migrationTemplate)) + +// AppendActions appends a list of actions to migration. +func (m *StateMigration) AppendActions(actions ...Action) { + m.Actions = append(m.Actions, actions...) +} - var migration StateMigration - for _, rc := range plan.ResourceChanges() { - if rc.Change.Actions.Create() { - address := rc.Address - after := rc.Change.After.(map[string]interface{}) - importID := schema.ImportID(rc.Type, after) - action := NewStateImportAction(address, importID) - migration.AppendAction(action) - } +// Render converts a state migration config to bytes +// Encoding StateMigratorConfig directly with gohcl has some problems. +// An array contains multiple elements is output as one line. It's not readable +// for multiple actions. In additon, the default value is set explicitly, it's +// not only redundant but also increases cognitive load for user who isn't +// familiar with tfmigrate. +// So we use text/template to render a migration file. +func (m *StateMigration) Render() ([]byte, error) { + var output bytes.Buffer + if err := compiledMigrationTemplate.Execute(&output, m); err != nil { + return nil, fmt.Errorf("failed to render migration file: %s", err) } - return migration.Render() + return output.Bytes(), nil } diff --git a/migration/resolver.go b/migration/resolver.go new file mode 100644 index 0000000..06853c5 --- /dev/null +++ b/migration/resolver.go @@ -0,0 +1,30 @@ +package migration + +import ( + "github.com/minamijoyo/tfedit/migration/schema" + _ "github.com/minamijoyo/tfedit/migration/schema/aws" // Register schema for aws +) + +type Resolver interface { + Resolve(plan *Plan) []Action +} + +type StateImportResolver struct { +} + +var _ Resolver = (*StateImportResolver)(nil) + +func (r *StateImportResolver) Resolve(plan *Plan) []Action { + actions := []Action{} + for _, rc := range plan.ResourceChanges() { + if rc.Change.Actions.Create() { + address := rc.Address + after := rc.Change.After.(map[string]interface{}) + importID := schema.ImportID(rc.Type, after) + action := NewStateImportAction(address, importID) + actions = append(actions, action) + } + } + + return actions +} diff --git a/migration/state_migration.go b/migration/state_migration.go deleted file mode 100644 index 1cb1658..0000000 --- a/migration/state_migration.go +++ /dev/null @@ -1,51 +0,0 @@ -package migration - -import ( - "bytes" - "fmt" - "text/template" -) - -// StateMigration is a type which is equivalent to tfmigrate.StateMigratorConfig of -// minamijoyo/tfmigrate. -// The current implementation doesn't encode migration actions to a file -// directly with gohcl, so we avoid to depend on tfmigrate's type and we define -// only what we need here. -type StateMigration struct { - // Dir is a working directory for executing terraform command. - Dir string - // Actions is a list of state action. - Actions []Action -} - -var migrationTemplate = `migration "state" "awsv4upgrade" { - actions = [ - {{- range .Actions }} - "{{ .MigrationAction }}", - {{- end }} - ] -} -` - -var compiledMigrationTemplate = template.Must(template.New("migration").Parse(migrationTemplate)) - -// AppendAction appends an action to migration. -func (m *StateMigration) AppendAction(action Action) { - m.Actions = append(m.Actions, action) -} - -// Render converts a state migration config to bytes -// Encoding StateMigratorConfig directly with gohcl has some problems. -// An array contains multiple elements is output as one line. It's not readable -// for multiple actions. In additon, the default value is set explicitly, it's -// not only redundant but also increases cognitive load for user who isn't -// familiar with tfmigrate. -// So we use text/template to render a migration file. -func (m *StateMigration) Render() ([]byte, error) { - var output bytes.Buffer - if err := compiledMigrationTemplate.Execute(&output, m); err != nil { - return nil, fmt.Errorf("failed to render migration file: %s", err) - } - - return output.Bytes(), nil -}