diff --git a/CODEOWNERS b/CODEOWNERS index ab4dcf5bb..6c3ed2313 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,7 +1,6 @@ # See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#example-of-a-codeowners-file # * @equinix/governor-devrel-engineering -/cmd/migration-tool @equinix/governor-metal-client-interfaces *metal* @equinix/governor-metal-client-interfaces *fabric* @equinix/governor-digin-fabric *connection_e2e* @equinix/governor-digin-fabric diff --git a/cmd/migration-tool/Makefile b/cmd/migration-tool/Makefile deleted file mode 100644 index bb0e5eaab..000000000 --- a/cmd/migration-tool/Makefile +++ /dev/null @@ -1,21 +0,0 @@ - -BINARY =equinix-migration-tool -GOCMD =go -TEST ?=$$(go list ./... |grep -v 'vendor') - -default: clean build test - -all: default - -test: - echo $(TEST) | \ - xargs -t ${GOCMD} test -v -timeout=10m - -clean: - ${GOCMD} clean - rm -f ${BINARY} - -build: - ${GOCMD} build -o ${BINARY} - -.PHONY: build clean release diff --git a/cmd/migration-tool/README.md b/cmd/migration-tool/README.md deleted file mode 100644 index dd0662458..000000000 --- a/cmd/migration-tool/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Equinix Terraform Provider Migration Tool - -[Equinix Metal](https://metal.equinix.com/) (formerly Packet), has been fully integrated into Platform Equinix and therefore the teraform provider changes too. Together with Equinix Fabric and Equinix Network Edge, from `v1.5.0` the Equinix Terraform Provider will be used to interact with the resources provided by Equinix Metal. - -This tool will target a terraform working directory and transform all\* `metal` or `packet` names found in *.tf* and *.tfstate* files to the `equinix` provider name. It creates a backup of the target directory *\.backup* as a sibling folder. - -\**This tool will not transform variable names or comments even if they contain the words `metal` or `packet`.* - -## Provider Setup and Config Verfification - -The migration will transform the `metal` or `packet` provider block as well as the required_providers in the terraform block and, if included, it comments the attribute `version` to take the latest available of the `equinix` provider: - -From: - -```hcl -terraform { - required_providers { - packet = { - source = "packethost/packet" - version = "3.2.1" - } - } -} - -provider "packet" { - auth_token = var.auth_token -} -``` - -To: - -```hcl -terraform { - required_providers { - equinix = { - source = "equinix/equinix" - #version = "3.2.1" - } - } -} - -provider "equinix" { - auth_token = var.auth_token -} -``` - -__NOTE__ - -If your code already includes both `equinix` provider and `metal` | `packet`, the resulting code will have two `equinix` provider blocks and they will also be duplicated in the required_providers definition. If this is your case, after migrate you must manually combine them in a single one with all the parameters required: - -From: - -```hcl -provider "equinix" { - auth_token = var.auth_token -} -provider "equinix" { - client_id = var.client_id - client_secret = var.client_secret -} -``` - -To: - -```hcl -provider "equinix" { - auth_token = var.auth_token - client_id = var.client_id - client_secret = var.client_secret -} -``` - -If you have any other requirements in the provider definition that this tool does not address, you will need to manually modify them after running a migration. - -## Remote State - -The **equinix-terraform-tool** does not support [remote state](https://www.terraform.io/docs/state/remote.html). If you are using remote state, then the recommended approach is to copy the state file locally, run the **equinix-terraform-tool**, and then push the state file back to the remote location. See the documentation [here](https://www.terraform.io/docs/backends/config.html) for details about how to unconfigure and reconfigure your backend. - -## Using the tool - -To migrate your terraform project, follow these steps: - -From the project directory, run `terraform plan`, make sure there are no pending changes in your plan. - -Execute the **equinix-terraform-tool** binary, passing the path to your project directory, example: - -`equinix-terraform-tool migrate -dir=` - -After migrating, run `terraform plan` again and verify there are no new pending modifications. - -For Terraform v.10+, you will need to initialize terraform for the directory using `terraform init` - -If the migration was not successful, manually restore the project files -from the .backup directory or run: - -`equinix-terraform-tool backup -dir= -restore` - -After you have verified the migration was successful, delete the -backup directory or run: - -`equinix-terraform-tool backup -dir= -purge` - -## Credits - -Based on [OCI Provider migration tool](https://registry.terraform.io/providers/hashicorp/oci/latest/docs/guides/version-2-upgrade#migration-tool) - *Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.* diff --git a/cmd/migration-tool/actions.go b/cmd/migration-tool/actions.go deleted file mode 100644 index 94bc07b8b..000000000 --- a/cmd/migration-tool/actions.go +++ /dev/null @@ -1,191 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "io" - "io/ioutil" - "os" - "path" - "path/filepath" -) - -// Individual file io strategies for different operations -type FileAction func(string, string) error - -// Traverse a directory, executing the supplied FileAction on each file -func ProcessDirectory(targetDir string, backupDir string, fileActionFn FileAction, targetExtns ...string) (err error) { - _, err = os.Stat(targetDir) - - if err != nil { - return fmt.Errorf("error reading directory\n %s", err) - } - - files, err := ioutil.ReadDir(targetDir) - if err != nil { - return fmt.Errorf("error reading directory contents \n %s", err) - } - - for _, res := range files { - targetRes := path.Join(targetDir, res.Name()) - backupRes := path.Join(backupDir, res.Name()) - - if res.IsDir() { - err = ProcessDirectory(targetRes, backupRes, fileActionFn, targetExtns...) - - if err != nil { - return err - } - } else { - if len(targetExtns) == 0 { - err = fileActionFn(targetRes, backupRes) - - if err != nil { - return err - } - } else { - if contains(targetExtns, filepath.Ext(res.Name())) { - err = fileActionFn(targetRes, backupRes) - - if err != nil { - return err - } - } else { - fmt.Println("Skipping: ", targetDir) - } - } - } - } - - return -} - -// Copy file from targetFile path to backupFile path -func CopyFile(targetFile string, backupFile string) (err error) { - // make sure directory structure exists - bkDir := path.Dir(backupFile) - _, err = os.Stat(bkDir) - - if err != nil { - if os.IsNotExist(err) { - oDir := path.Dir(targetFile) - fi, err := os.Stat(oDir) - if err != nil { - return fmt.Errorf("error reading original directory %s", err) - } - - err = os.MkdirAll(bkDir, fi.Mode()) - - if err != nil { - return fmt.Errorf("error creating directory for file %s", err) - } - } else { - return fmt.Errorf("unexpected error reading original directory %s", err) - } - } - - src, err := os.Open(targetFile) - if err != nil { - return fmt.Errorf("error reading original file\n %s", err) - } - - defer src.Close() - - dst, err := os.Create(backupFile) - if err != nil { - return fmt.Errorf("error creating backup file\n %s", err) - } - - defer dst.Close() - - fmt.Printf("Copying %s --> %s", targetFile, backupFile) - size, err := io.Copy(dst, src) - if err != nil { - return fmt.Errorf("error writing file\n %s", err) - } - - fmt.Printf(", %d bytes\n", size) - return -} - -// Read file from backup location, apply transforms and overwrite original file -func MigratePlanFile(targetFile string, backupFile string) (err error) { - src, err := os.Open(backupFile) - if err != nil { - return fmt.Errorf("error reading file\n %s", err) - } - - defer src.Close() - - dst, err := os.Create(targetFile) - if err != nil { - return fmt.Errorf("error creating write location\n %s", err) - } - - defer dst.Close() - - wrtr := bufio.NewWriter(dst) - - var replaceStrategy func(string) string - if filepath.Ext(backupFile) == ".tf" { - replaceStrategy = replaceTemplateTokens - } else { - replaceStrategy = replaceStatefileTokens - } - - scanner := bufio.NewScanner(src) - for scanner.Scan() { - str := scanner.Text() - str = replaceStrategy(str) - fmt.Fprintln(wrtr, str) - } - wrtr.Flush() - - return -} - -// Scan TF files for terraform:required_providers and provider blocks and define or update Equinix provider -func TransformProvider(targetFile string, backupFile string) error { - fmt.Printf("Scanning %s\n", targetFile) - - fileInfo, err := os.Stat(targetFile) - if err != nil { - return fmt.Errorf("error while updating provider\n %s", err) - } - - const maxSize = 1024 * 1024 - if fileInfo.Size() > maxSize { - return fmt.Errorf("file too large to process") - } - - fileBytes, err := ioutil.ReadFile(targetFile) - if err != nil { - return fmt.Errorf("error updating terraform:required_providers block\n %s", err) - } - - content := string(fileBytes) - - content, err = scanAndUpdateRequiredProvider(content) - if err != nil { - return fmt.Errorf("error updating terraform:required_providers block\n %s", err) - } - - content, err = scanAndUpdateProvider(content) - if err != nil { - return fmt.Errorf("error updating provider block\n %s", err) - } - - ioutil.WriteFile(targetFile, []byte(content), fileInfo.Mode()) - - return err -} - -// find a string in a slice of strings -func contains(items []string, target string) bool { - for _, item := range items { - if item == target { - return true - } - } - return false -} diff --git a/cmd/migration-tool/commands.go b/cmd/migration-tool/commands.go deleted file mode 100644 index 4d819d30a..000000000 --- a/cmd/migration-tool/commands.go +++ /dev/null @@ -1,117 +0,0 @@ -package main - -import ( - "fmt" - "os" -) - -// Copy target directory and append .backup -func CreateBackup(targetDir string, backupDir string) (err error) { - fmt.Println("creating backup...", targetDir, "-->", backupDir) - - fi, err := os.Stat(targetDir) - if err != nil { - return fmt.Errorf("error reading directory\n %s", err) - } - - if !fi.IsDir() { - return fmt.Errorf("file targeted for migration") - } - - _, err = os.Stat(backupDir) - - if err == nil { - return fmt.Errorf("attempting to overwrite backups") - } - - fmt.Println("copying", targetDir, "-->", backupDir) - - err = ProcessDirectory(targetDir, backupDir, CopyFile) - - if err != nil { - return err - } - - bfi, err := os.Stat(backupDir) - - if fi.Size() != bfi.Size() { - return fmt.Errorf("backup corrupt") - } - - fmt.Println("complete") - return -} - -// Overwrite target directory with contents of .backup directory -func RestoreBackup(backupDir string, targetDir string) (err error) { - fmt.Println("restoring from backup...") - - fi, err := os.Stat(backupDir) - if err != nil { - return fmt.Errorf("error reading backup\n %s", err) - } - - err = os.RemoveAll(targetDir) - - if err != nil { - return fmt.Errorf("error removing original directory\n %s", err) - } - - os.MkdirAll(targetDir, fi.Mode()) - - err = ProcessDirectory(backupDir, targetDir, CopyFile) - - if err != nil { - return fmt.Errorf("error restoring from backup directory\n %s", err) - } - - fmt.Println("complete") - return -} - -// Remove .backup directory -func DeleteBackup(backupDir string) (err error) { - fmt.Println("Purging backup...") - - err = os.RemoveAll(backupDir) - - if err != nil { - return fmt.Errorf("error removing backup directory\n %s", err) - } - - fmt.Println("complete") - return -} - -// Traverse all .tf files and apply transforms -func Migrate(targetDir string, backupDir string) (err error) { - fmt.Println("migrating plan directory...") - err = CreateBackup(targetDir, backupDir) - - if err != nil { - return fmt.Errorf("error backing up directory before migration\n %s", err) - } - - err = ProcessDirectory(targetDir, backupDir, MigratePlanFile, ".tf", ".tfstate") - - if err != nil { - return fmt.Errorf("error removing backup directory\n %s", err) - } - - fmt.Println("complete") - return -} - -// Traverse all .tf files and migrate or update Equinix provider -func MigrateProvider(targetDir string, backupDir string) (err error) { - fmt.Println("scanning tf files for provider...") - - err = ProcessDirectory(targetDir, backupDir, TransformProvider, ".tf") - - if err != nil { - return fmt.Errorf("error scanning providers for missing region value\n %s", err) - } - - fmt.Println("complete") - return -} diff --git a/cmd/migration-tool/main.go b/cmd/migration-tool/main.go deleted file mode 100644 index b1b210395..000000000 --- a/cmd/migration-tool/main.go +++ /dev/null @@ -1,114 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "path" -) - -func main() { - if len(os.Args) < 2 { - fmt.Println("Missing required command. One of [migrate, backup, version]") - os.Exit(1) - } - - if os.Args[1] == "-v" || os.Args[1] == "-version" || os.Args[1] == "--version" || os.Args[1] == "version" { - fmt.Println(Version) - os.Exit(0) - } - - if os.Args[1] == "backup" { - backup := flag.NewFlagSet("backup", flag.PanicOnError) - backup.Usage = func() { - backup.PrintDefaults() - os.Exit(0) - } - dir := backup.String("dir", "", "Required, specify the plan directory to operate on") - purge := backup.Bool("purge", false, "Optional, whether to purge the backup directory") - restore := backup.Bool("restore", false, "Optional, whether to restore from the backup directory") - - err := backup.Parse(os.Args[2:]) - - if *dir == "" { - fmt.Println("Missing required directory flag\nCommand flags:") - backup.PrintDefaults() - os.Exit(1) - } - - if err != nil { - panic(err) - } - - targetDir := path.Clean(*dir) - backupDir := targetDir + ".backup" - - fmt.Println(targetDir) - - if *purge { - err := DeleteBackup(backupDir) - if err != nil { - panic(err) - } - - return - } - - if *restore { - err := RestoreBackup(backupDir, targetDir) - if err != nil { - panic(err) - } - - return - } - - err = CreateBackup(targetDir, backupDir) - - if err != nil { - panic(err) - } - os.Exit(0) - } - - if os.Args[1] == "migrate" { - migrate := flag.NewFlagSet("migrate", flag.PanicOnError) - migrate.Usage = func() { - migrate.PrintDefaults() - os.Exit(0) - } - dir := migrate.String("dir", "", "Required, specify the plan directory to operate on") - err := migrate.Parse(os.Args[2:]) - - if *dir == "" { - fmt.Println("Missing required directory flag\nCommand flags:") - migrate.PrintDefaults() - os.Exit(1) - } - - if err != nil { - panic(err) - } - - targetDir := path.Clean(*dir) - backupDir := targetDir + ".backup" - - err = Migrate(targetDir, backupDir) - - if err != nil { - panic(err) - } - - err = MigrateProvider(targetDir, backupDir) - - if err != nil { - panic(err) - } - - fmt.Println(`Migration Successful!`) - os.Exit(0) - } - - fmt.Println("Unknown command") - os.Exit(1) -} diff --git a/cmd/migration-tool/transforms.go b/cmd/migration-tool/transforms.go deleted file mode 100644 index 849c92360..000000000 --- a/cmd/migration-tool/transforms.go +++ /dev/null @@ -1,317 +0,0 @@ -package main - -import ( - "fmt" - "regexp" - "strings" - "unicode/utf8" -) - -// matches block headers, ex: -// resource "metal_project" "fooproject" { -// data "packet_vlan" "foovlan" { -var matchBlockHeader = regexp.MustCompile(`(resource|data)(\s+")(metal|packet)(.*?)`) - -// matches resource interpolation strings (Terraform v0.11 and earlier), ex: -// device_id = "${metal_device.foodevice.id}" -var matchResourceInterpolation = regexp.MustCompile(`(.*?)(\${\s*)(metal|packet)(_.*?)`) - -// matches resource reference (Terraform v0.12+), ex: -// device_id = metal_device.foodevice.id -var matchResourceReference = regexp.MustCompile(`(.*?)(=\s*)(metal|packet)(_.*?)`) - -// matches resource reference in function, ex: -// cidr_notation = join("/", [cidrhost(metal_reserved_ip_block.fooblock.cidr_notation, 0), "32"]) -var matchResourceFunction = regexp.MustCompile(`(.*?)(\(\s*)(metal|packet)(_.*?)`) - -// matches resource reference in conditional, ex: -// ip_address = "${var.network_type == "public" ? metal_device.foodevice.access_public_ipv4 : metal_device.foodevice.access_private_ipv4}" -// ip_address = var.network_type == "public" ? metal_device.foodevice.access_public_ipv4 : metal_device.foodevice.access_private_ipv4 -var matchResourceConditional = regexp.MustCompile(`(.*?[:|\?])(\s*)(metal|packet)(_.*?)`) - -// matches resource reference in for loop,ex: -// toset([for network in metal_device.foodevice.network : network.family]) -var matchResourceForLoop = regexp.MustCompile(`(.*?)(in\s*)(metal|packet)(_.*?)`) - -// matches resource in expression,ex: -// tolist([metal_device.foodevice[*].access_public_ipv4]) -// !metal_ip_attachment.fooattach.public -// totalSpeed = metal_connection.fooconnA.speed + metal_connection.fooconnB.speed -var matchResourceExpression = regexp.MustCompile(`(.*?[\+|-|\*|\/|>|<|&|\|\||%|!|\[]\s*)(metal|packet)(_.*?)`) - -// matches datasource references, ex: -// address_family = "${lookup(data.packet_device_bgp_neighbors.test.bgp_neighbors[0], "address_family")}" -var matchDatasourceReference = regexp.MustCompile(`(.*?data)(\.)(metal|packet)(_.*?)`) - -// replace specific string patterns in template files -func replaceTemplateTokens(str string) string { - // resources - str = matchBlockHeader.ReplaceAllString(str, `$1 "equinix_metal$4`) - str = matchResourceInterpolation.ReplaceAllString(str, `$1${equinix_metal$4`) - str = matchResourceReference.ReplaceAllString(str, `${1}= equinix_metal$4`) - str = matchResourceFunction.ReplaceAllString(str, `$1(equinix_metal$4`) - str = matchResourceConditional.ReplaceAllString(str, `$1 equinix_metal$4`) - str = matchResourceForLoop.ReplaceAllString(str, `${1}in equinix_metal$4`) - str = matchResourceExpression.ReplaceAllString(str, `${1}equinix_metal$3`) - // datasources - return matchDatasourceReference.ReplaceAllString(str, `$1.equinix_metal$4`) -} - -// matches '"metal_' or '"packet_' prefixes in statefile -var matchStatePrefixes = regexp.MustCompile(`(.*")(metal|packet)(_.*)`) - -// matches provider url in statefile -var matchStateProvider = regexp.MustCompile(`(.*?)(equinix\/metal|packethost\/packet)(\\".*?)`) - -// replace metal|packet in statefile -func replaceStatefileTokens(str string) string { - // provider - str = matchStateProvider.ReplaceAllString(str, `${1}equinix/equinix$3`) - // datasources - str = matchDatasourceReference.ReplaceAllString(str, `$1.equinix_metal$4`) - // metal and prefixes - return matchStatePrefixes.ReplaceAllString(str, `${1}equinix_metal$3`) -} - -// rewrite matching required provider to have equinix provider with no version -func updateRequiredProvider(content string) (string, error) { - idx, _ := findToken("metal", content) - if idx == -1 { - idx, _ = findToken("packet", content) - } - if idx == -1 { - return content, nil - } - - subStr := content[idx:] // ignore everything before metal/packet provider - - // replace provider name - subStr = strings.Replace(subStr, "metal", "equinix", 1) - subStr = strings.Replace(subStr, "packet", "equinix", 1) - - blockStart, blockEnd := indexOpenCloseTokens('{', '}', subStr) // limit search to logical provider block - if blockStart == -1 || blockEnd == -1 { - return content, fmt.Errorf("required Provider metal/packet block start or end not detected") - } - - blkContents := subStr[:blockEnd] // get just from provider name to the end of logical block - // replace source - blkContents = strings.Replace(blkContents, "equinix/metal", "equinix/equinix", 1) - blkContents = strings.Replace(blkContents, "packethost/packet", "equinix/equinix", 1) - - // comment version - blkContents = strings.Replace(blkContents, "version", "#version", 1) - - return content[:idx] + blkContents + subStr[blockEnd:], nil -} - -// find all required_providers definitions and make required transforms -func scanAndUpdateRequiredProvider(content string) (string, error) { - for start, i := 0, -1; ; { - i, _ = findTokenAfter("required_providers", content, start) - - // "required_providers" block not present in file - if i == -1 { - return content, nil - } - - start += i - - blockStart, blockEnd := indexOpenCloseTokens('{', '}', content[start:]) - - if blockStart == -1 { - return content, fmt.Errorf("required provider detected, block start not found") - } - - if blockEnd == -1 { - return content, fmt.Errorf("required provider detected, block end not found") - } - - end := start + blockEnd + 1 - - res, err := updateRequiredProvider(content[start:end]) - if err != nil { - return content, fmt.Errorf("problem parsing terraform:required_providers block\n %s", err) - } - - content = content[:start] + res + content[end:] - - start = end - } -} - -// rewrite matching provider block to have equinix provider -func updateProviderBlock(content string) (string, error) { - idx, _ := findToken("metal", content) - if idx == -1 { - idx, _ = findToken("packet", content) - } - if idx == -1 { - return content, nil - } - - subStr := content[idx:] // ignore everything before metal/packet provider - - // replace provider name - subStr = strings.Replace(subStr, "metal", "equinix", 1) - subStr = strings.Replace(subStr, "packet", "equinix", 1) - - return content[:idx] + subStr, nil -} - -// find all providers blocks and make required transforms -func scanAndUpdateProvider(content string) (string, error) { - for start, i := 0, -1; ; { - i, _ = findTokenAfter("provider", content, start) - - // "providers" block not present in file - if i == -1 { - return content, nil - } - - start += i - - blockStart, blockEnd := indexOpenCloseTokens('{', '}', content[start:]) - - if blockStart == -1 { - return content, fmt.Errorf("provider detected, block start not found") - } - - if blockEnd == -1 { - return content, fmt.Errorf("provider detected, block end not found") - } - - end := start + blockEnd + 1 - - res, err := updateProviderBlock(content[start:end]) - if err != nil { - return content, fmt.Errorf("problem parsing provider block\n %s", err) - } - - content = content[:start] + res + content[end:] - - start = end - } -} - -// return the text extent of a token match in a string -func findToken(token string, content string) (start int, end int) { - idx := strings.Index(content, token) - return idx, idx + len(token) -} - -// return the text extent of a token match in a string after a specified index -func findTokenAfter(token string, content string, begin int) (start int, end int) { - newStr := content[begin:] - idx := strings.Index(newStr, token) - - if idx == -1 { - return -1, -1 - } - - return idx, idx + len(token) -} - -// parse logical terraform blocks to find open and closing braces -func indexOpenCloseTokens(open rune, close rune, content string) (start int, end int) { - ct := 0 - start = -1 - - for idx := 0; idx < len(content); { - rn, rnWidth := utf8.DecodeRuneInString(content[idx:]) - - // keep track of opening brackets to account for nesting - if rn == open { - ct++ - if start < 0 { // start index still -1, record the first opening bracket - start = idx - } - } - - // closing brackets decrement nest level - if rn == close { - ct-- - if ct == 0 { // bracket count back to 0, record the final closing bracket - return start, idx - } - } - - idx += rnWidth - nextRn, nextRnWidth := utf8.DecodeRuneInString(content[idx:]) - - // match " and advance idx to closing " - if rn == '"' { - for idx < len(content)-1 { - rn1, w1 := utf8.DecodeRuneInString(content[idx:]) - rn2, w2 := utf8.DecodeRuneInString(content[idx+w1:]) - - if rn1 == '\\' && rn2 == '"' { - idx += w1 + w2 - continue - } - - idx += w1 - if rn1 == '"' { - break - } - } - continue - } - - // match '#' and advance idx to line end - if rn == '#' { - for idx < len(content) { - rn1, w1 := utf8.DecodeRuneInString(content[idx:]) - idx += w1 - - if rn1 == '\n' { - break - } - } - continue - } - - // match '//' and advance idx to line end - if rn == '/' && nextRn == '/' { - idx += nextRnWidth - for idx < len(content) { - rn1, w1 := utf8.DecodeRuneInString(content[idx:]) - if rn1 == '\n' { - break - } - idx += w1 - } - continue - } - - // match '/*' and advance idx to closing '*/' - if rn == '/' && nextRn == '*' { - idx += nextRnWidth - for idx < len(content)-1 { - rn1, w1 := utf8.DecodeRuneInString(content[idx:]) - rn2, w2 := utf8.DecodeRuneInString(content[idx+w1:]) - idx += w1 - if rn1 == '*' && rn2 == '/' { - idx += w2 - break - } - } - continue - } - - // match '${' and advance idx to closing '}' - if rn == '$' && nextRn == '{' { - idx += rnWidth + nextRnWidth - for idx < len(content)-1 { - rn1, w1 := utf8.DecodeRuneInString(content[idx:]) - idx += w1 - if rn1 == '}' { - break - } - } - continue - } - } - - return start, -1 -} diff --git a/cmd/migration-tool/transforms_test.go b/cmd/migration-tool/transforms_test.go deleted file mode 100644 index b4179a731..000000000 --- a/cmd/migration-tool/transforms_test.go +++ /dev/null @@ -1,309 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMigrationReplaceTemplateTokens_basic(t *testing.T) { - const original = ` -resource "metal_port" "bond0" { - port_id = local.bond0_id - layer2 = false - bonded = true - vlan_ids = [metal_vlan.test.id] -} - -resource "metal_vlan" "test" { - description = "test" - metro = "sv" - project = metal_project.test.id -} -` - - const expected = ` -resource "equinix_metal_port" "bond0" { - port_id = local.bond0_id - layer2 = false - bonded = true - vlan_ids = [equinix_metal_vlan.test.id] -} - -resource "equinix_metal_vlan" "test" { - description = "test" - metro = "sv" - project = equinix_metal_project.test.id -} -` - - var actual strings.Builder - scanner := bufio.NewScanner(strings.NewReader(original)) - for scanner.Scan() { - str := scanner.Text() - str = replaceTemplateTokens(str) - actual.WriteString(fmt.Sprintf("%s\n", str)) - } - - assert.Equal(t, expected, actual.String(), "Result matches expected result") -} - -func TestMigrationReplaceTemplateTokens_multiMatchPerLine(t *testing.T) { - const original = ` -terraform { - required_providers { - packet = { - source = "packethost/packet" - version = "1.0.0" - } - } -} - -# Configure Packet Provider. -provider "packet" { - auth_token = var.auth_token -} - -data "packet_project" "test" { - name = var.packet_project_name -} - -resource "packet_connection" "test" { - name = var.packet_connection_name - organization_id = data.packet_project.test.organization_id - project_id = data.packet_project.test.project_id - metro = var.packet_connection_metro - redundancy = var.packet_connection_redundancy - type = "shared" - description = var.packet_connection_description - tags = var.packet_connection_tags -} - -resource "packet_device" "test" { - count = 3 - - hostname = "tf.coreos2" - plan = "c3.small.x86" - metro = "sv" - operating_system = "ubuntu_20_04" - billing_cycle = "hourly" - project_id = local.project_id -} - -data "packet_device_bgp_neighbors" "test" { - device_id = packet_device.test[1].id -} - -locals { - address_family = "${lookup(data.packet_device_bgp_neighbors.test.bgp_neighbors[0], "address_family")}" - ips = tolist([packet_device.test[*].access_public_ipv4]) - ip_address = var.packet_network_type == "public" ? metal_device.foodevice.access_public_ipv4 : metal_device.foodevice.access_private_ipv4 -} -` - - const expected = ` -terraform { - required_providers { - packet = { - source = "packethost/packet" - version = "1.0.0" - } - } -} - -# Configure Packet Provider. -provider "packet" { - auth_token = var.auth_token -} - -data "equinix_metal_project" "test" { - name = var.packet_project_name -} - -resource "equinix_metal_connection" "test" { - name = var.packet_connection_name - organization_id = data.equinix_metal_project.test.organization_id - project_id = data.equinix_metal_project.test.project_id - metro = var.packet_connection_metro - redundancy = var.packet_connection_redundancy - type = "shared" - description = var.packet_connection_description - tags = var.packet_connection_tags -} - -resource "equinix_metal_device" "test" { - count = 3 - - hostname = "tf.coreos2" - plan = "c3.small.x86" - metro = "sv" - operating_system = "ubuntu_20_04" - billing_cycle = "hourly" - project_id = local.project_id -} - -data "equinix_metal_device_bgp_neighbors" "test" { - device_id = equinix_metal_device.test[1].id -} - -locals { - address_family = "${lookup(data.equinix_metal_device_bgp_neighbors.test.bgp_neighbors[0], "address_family")}" - ips = tolist([equinix_metal_device.test[*].access_public_ipv4]) - ip_address = var.packet_network_type == "public" ? equinix_metal_device.foodevice.access_public_ipv4 : equinix_metal_device.foodevice.access_private_ipv4 -} -` - - var actual strings.Builder - scanner := bufio.NewScanner(strings.NewReader(original)) - for scanner.Scan() { - str := scanner.Text() - str = replaceTemplateTokens(str) - actual.WriteString(fmt.Sprintf("%s\n", str)) - } - - assert.Equal(t, expected, actual.String(), "Result matches expected result") -} - -func TestMigrationBraceIndexing_basic(t *testing.T) { - const str = `{{}}` - expectStart := 0 - expectEnd := 3 - start, end := indexOpenCloseTokens('{', '}', str) - - if start != expectStart { - t.Errorf("expected %d, got %d\n", expectStart, start) - } - - if end != expectEnd { - t.Errorf("expected %d, got %d\n", expectEnd, end) - } -} - -func TestMigrationBraceIndexingWith_strings(t *testing.T) { - const str = ` " " { { } } ` - expectStart := 5 - expectEnd := 11 - start, end := indexOpenCloseTokens('{', '}', str) - - if start != expectStart { - t.Errorf("expected %d, got %d\n", expectStart, start) - } - - if end != expectEnd { - t.Errorf("expected %d, got %d\n", expectEnd, end) - } -} - -func TestMigrationBraceIndexing_Provider(t *testing.T) { - const str = `provider "metal" { - auth_token = "${var.auth_token}" -}` - - expectStart := 17 - expectEnd := len(str) - 1 - start, end := indexOpenCloseTokens('{', '}', str) - - if start != expectStart { - t.Errorf("expected %d, got %d\n", expectStart, start) - } - - if end != expectEnd { - t.Errorf("expected %d, got %d\n", expectEnd, end) - } -} - -func TestMigrationFindOpeningBrace(t *testing.T) { - const str = `}{}` - expect := 1 - start, _ := indexOpenCloseTokens('{', '}', str) - - if start != expect { - t.Errorf("expected %d, got %d\n", expect, start) - } -} - -func TestMigrationFindClosingBrace(t *testing.T) { - const str = `{}}` - expect := 1 - _, end := indexOpenCloseTokens('{', '}', str) - if end != expect { - t.Errorf("expected %d, got %d\n", expect, end) - } -} - -func TestMigrationMissingOpeningBrace(t *testing.T) { - const str = `}}` - expect := -1 - start, _ := indexOpenCloseTokens('{', '}', str) - - if start != expect { - t.Errorf("expected %d, got %d\n", expect, start) - } -} - -func TestMigrationMissingClosingBrace(t *testing.T) { - const str = `{{}` - expect := -1 - _, end := indexOpenCloseTokens('{', '}', str) - - if end != expect { - t.Errorf("expected %d, got %d\n", expect, end) - } -} - -func TestMigrationReplaceProvider(t *testing.T) { - // given - context := ` -provider "metal" { - auth_token = var.auth_token -}` - expected := ` -provider "equinix" { - auth_token = var.auth_token -}` - // when - result, _ := scanAndUpdateProvider(context) - - // then - assert.Equal(t, expected, result, "Result matches expected result") -} - -func TestMigrationReplaceRequiredProvider(t *testing.T) { - // given - context := ` -terraform { - required_providers { - metal = { - source = "equinix/metal" - #commment - version = "3.2.1" - } - foo = { - source = "foo/fooprovider" - version = "1.0.0" - } - } -}` - expected := ` -terraform { - required_providers { - equinix = { - source = "equinix/equinix" - #commment - #version = "3.2.1" - } - foo = { - source = "foo/fooprovider" - version = "1.0.0" - } - } -}` - // when - result, _ := scanAndUpdateRequiredProvider(context) - - // then - assert.Equal(t, expected, result, "Result matches expected result") -} diff --git a/cmd/migration-tool/version.go b/cmd/migration-tool/version.go deleted file mode 100644 index 858312436..000000000 --- a/cmd/migration-tool/version.go +++ /dev/null @@ -1,4 +0,0 @@ -package main - -// Version is set at build-time in the release process -var Version = "dev"