Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions refactor/extract_module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package refactor

import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/raymyers/hcl/v2/hclwrite"
"github.com/zclconf/go-cty/cty"
)

func Extract(addresses []string, toFolder, configPath string) (*UpdatePlan, error) {
filenames, err := filepath.Glob(configPath + "/*.tf")
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
plan := newUpdatePlan()
var parsedOutFile *hclwrite.File
toFile := filepath.Join(configPath, toFolder, "main.tf")
if _, err := os.Stat(toFile); errors.Is(err, os.ErrNotExist) {
parsedOutFile, err = ParseHclBytes([]byte{}, toFile)
if err != nil {
return nil, err
}
} else {
parsedOutFile, err = ParseHclFile(toFile)
if err != nil {
return nil, err
}
}

beforeOutText := string(parsedOutFile.Bytes())
for _, filename := range filenames {
fromPath, _ := filepath.Abs(filename)
toPath, _ := filepath.Abs(toFile)
if fromPath != "" && fromPath != toPath {
parsedInFile, err := ParseHclFile(filename)
beforeText := string(parsedInFile.Bytes())
for _, fromAddressText := range addresses {
fromAddress := ParseAddress(fromAddressText)

if err != nil {
return nil, err
}

if err != nil {
return nil, err
}
if moveAddrToFile(fromAddress, parsedInFile, parsedOutFile) {
if fromAddress.elementType == TypeResource {
moduleName := filepath.Base(toFolder)
toAddress := ParseAddress("module." + moduleName + "." + strings.Join(fromAddress.labels, "."))
AddModuleBlock(parsedInFile, moduleName, toFolder)
AddMovedBlock(parsedInFile, fromAddress, toAddress)
}
}
}
afterText := string(parsedInFile.Bytes())
if err != nil {
return nil, err
}

diffText, err := diffText(beforeText, afterText, 3)
if len(diffText) > 0 {
fmt.Printf("Diff for %v\n%v\n", filename, diffText)
plan.addFileUpdate(&FileUpdate{filename, beforeText, afterText})
}
}
}
afterOutText := string(parsedOutFile.Bytes())
diffText, err := diffText(beforeOutText, afterOutText, 3)
if len(diffText) > 0 {
fmt.Printf("Diff for %v\n%v\n", toFile, diffText)
plan.addFileUpdate(&FileUpdate{toFile, beforeOutText, afterOutText})
}
return &plan, nil
}

func AddModuleBlock(file *hclwrite.File, moduleName, toFolder string) {
file.Body().AppendNewline()
movedBlock := file.Body().AppendNewBlock("module", []string{moduleName})

movedBlock.Body().SetAttributeValue("source", cty.StringVal(toFolder))
}
33 changes: 33 additions & 0 deletions refactor/extract_module_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package refactor

import (
"testing"
)

func TestExtract(t *testing.T) {
cases := []struct {
name string
args []string
ok bool
from string
to string
}{
{
name: "case_data_single_block",
args: []string{"a.a", "mymodule"},
ok: true,
from: "test_data/extract_module/case_one_resource/from",
to: "test_data/extract_module/case_one_resource/to",
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
plan, err := Extract(tc.args[0:], tc.args[len(tc.args)-1], tc.from)
if (err == nil) != tc.ok {

}
assertMockCmdFileOutput(t, tc.name, tc.from, tc.to, plan)
})
}
}
57 changes: 28 additions & 29 deletions refactor/mv.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package refactor
import (
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"

Expand All @@ -15,18 +14,18 @@ func Mv(fromAddressString, toFile, configPath string) (*UpdatePlan, error) {
if err != nil {
return nil, err
}
if err != nil {
return nil, err
}
plan := newUpdatePlan()
var parsedOutFile *hclwrite.File
if _, err := os.Stat(toFile); errors.Is(err, os.ErrNotExist) {
parsedOutFile, err = ParseHclBytes([]byte{}, toFile)

// Assume toFile is relative to config path. Will this always be true?
toFilePath := filepath.Join(configPath, toFile)
if _, err := os.Stat(toFilePath); errors.Is(err, os.ErrNotExist) {
parsedOutFile, err = ParseHclBytes([]byte{}, toFilePath)
if err != nil {
return nil, err
}
} else {
parsedOutFile, err = ParseHclFile(toFile)
parsedOutFile, err = ParseHclFile(toFilePath)
if err != nil {
return nil, err
}
Expand All @@ -35,8 +34,7 @@ func Mv(fromAddressString, toFile, configPath string) (*UpdatePlan, error) {
beforeOutText := string(parsedOutFile.Bytes())
for _, filename := range filenames {
fromPath, _ := filepath.Abs(filename)
toPath, _ := filepath.Abs(toFile)
if fromPath != "" && fromPath != toPath {
if fromPath != "" && fromPath != toFilePath {
parsedInFile, err := ParseHclFile(filename)
if err != nil {
return nil, err
Expand All @@ -61,8 +59,8 @@ func Mv(fromAddressString, toFile, configPath string) (*UpdatePlan, error) {
afterOutText := string(parsedOutFile.Bytes())
diffText, err := diffText(beforeOutText, afterOutText, 3)
if len(diffText) > 0 {
fmt.Printf("Diff for %v\n%v\n", toFile, diffText)
plan.addFileUpdate(&FileUpdate{toFile, beforeOutText, afterOutText})
fmt.Printf("Diff for %v\n%v\n", toFilePath, diffText)
plan.addFileUpdate(&FileUpdate{toFilePath, beforeOutText, afterOutText})
}
return &plan, nil
}
Expand All @@ -75,11 +73,7 @@ func findOrCreateLocalsBlock(parsedFile *hclwrite.File) *hclwrite.Block {
return parsedFile.Body().AppendNewBlock("locals", []string{})
}

func writeParsedFile(parsedFile *hclwrite.File, toFile string) error {
return ioutil.WriteFile(toFile, parsedFile.Bytes(), 0644)
}

func moveLocals(parsedInFile, parsedOutFile *hclwrite.File) {
func moveLocals(parsedInFile, parsedOutFile *hclwrite.File) bool {
for _, block := range parsedInFile.Body().Blocks() {

if block.Type() == "locals" {
Expand All @@ -93,24 +87,29 @@ func moveLocals(parsedInFile, parsedOutFile *hclwrite.File) {
if !parsedInFile.Body().RemoveBlock(block) {
fmt.Printf("WARN locals block could not be removed\n")
}
return true
}
}
return false
}

func moveLocal(localName string, parsedInFile, parsedOutFile *hclwrite.File) {
fmt.Printf("moveLocal %v\n", localName)
func moveLocal(localName string, parsedInFile, parsedOutFile *hclwrite.File) bool {
for _, block := range parsedInFile.Body().Blocks() {

if block.Type() == "locals" {
attr := block.Body().GetAttribute(localName)
if attr != nil {
toLocalsBlock := findOrCreateLocalsBlock(parsedOutFile)
toLocalsBlock.Body().AppendUnstructuredTokens(attr.BuildTokens(nil))

block.Body().RemoveAttribute(localName)
// This can leave an empty block. Maybe check for that.
return true
}
block.Body().RemoveAttribute(localName)
// This can leave an empty block. Maybe check for that.

}
}
return false
}

func labelsEqual(a, b []string) bool {
Expand All @@ -132,30 +131,30 @@ func min(a, b int) int {
return b
}

func moveBlock(addr *Address, parsedInFile, parsedOutFile *hclwrite.File) {
func moveBlock(addr *Address, parsedInFile, parsedOutFile *hclwrite.File) bool {
found := false
addrLabels := addr.labels
for _, block := range parsedInFile.Body().Blocks() {
blockLabelsLimited := block.Labels()[0:min(len(addrLabels), len(block.Labels()))]
if string(addr.BlockType()) == block.Type() && matchLabels(addr.labels, blockLabelsLimited) {
fmt.Printf("## Block matched %v %v\n", block.Type(), block.Labels())
// fmt.Printf("## Block matched %v %v\n", block.Type(), block.Labels())
parsedOutFile.Body().AppendNewline()
parsedOutFile.Body().AppendBlock(block)
if !parsedInFile.Body().RemoveBlock(block) {
fmt.Printf("WARN locals block could not be removed\n")
}
found = true
}
}
return found
}

func moveAddrToFile(addr *Address, parsedInFile, parsedOutFile *hclwrite.File) error {
func moveAddrToFile(addr *Address, parsedInFile, parsedOutFile *hclwrite.File) bool {
if addr.elementType == TypeLocal && len(addr.labels) == 0 {
moveLocals(parsedInFile, parsedOutFile)
return moveLocals(parsedInFile, parsedOutFile)
} else if addr.elementType == TypeLocal {
localName := addr.labels[0]
moveLocal(localName, parsedInFile, parsedOutFile)
} else {
moveBlock(addr, parsedInFile, parsedOutFile)
return moveLocal(localName, parsedInFile, parsedOutFile)
}

return nil
return moveBlock(addr, parsedInFile, parsedOutFile)
}
Loading