Skip to content

Commit f7830c6

Browse files
authored
feat: Adds optionally moved blocks (#26)
* remove adv2 command * e2e tests * flags file * includeMoved * atlas cli setup * add e2e tests * fix linter * add moved e2e test * extract comments to const * note to remind delete old clusters * typo * func comments for getResourceName and getResourceLabel
1 parent 153a387 commit f7830c6

22 files changed

+558
-47
lines changed

.github/workflows/code-health.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,20 @@ jobs:
2020
run: make build
2121
- name: Unit Test
2222
run: make test
23+
24+
e2e:
25+
runs-on: ubuntu-latest
26+
permissions: {}
27+
steps:
28+
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
29+
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34
30+
with:
31+
go-version-file: 'go.mod'
32+
- name: setup Atlas CLI
33+
uses: mongodb/atlas-github-action@15663d068c40a8582d881560961fce9d45e0df9a
34+
- name: E2E Test
35+
run: make test-e2e
36+
2337
lint:
2438
runs-on: ubuntu-latest
2539
permissions: {}

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,15 @@ clean: ## Clean binary folders
2525

2626
.PHONY: test
2727
test: ## Run unit tests
28-
go test ./... -timeout=30s -parallel=4 -race
28+
go test ./internal/... -timeout=30s -parallel=4 -race
2929

3030
.PHONY: test-update
3131
test-update: ## Run unit tests and update the golden files
32-
go test ./... -timeout=30s -parallel=4 -race -update
32+
go test ./internal/... -timeout=30s -parallel=4 -race -update
33+
34+
.PHONY: test-e2e
35+
test-e2e: local ## Run E2E tests (running the plugin binary)
36+
ATLAS_CLI_EXTRA_PLUGIN_DIRECTORY="${PWD}/bin-plugin" go test ./test/... -timeout=30s -parallel=4 -race
3337

3438
.PHONY: local
3539
local: clean build ## Allow to run the plugin locally

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ you can also use shorter aliases, e.g.:
3232
atlas tf clu2adv -f in.tf -o out.tf
3333
```
3434

35-
If you want to overwrite the output file if it exists, or even use the same output file as the input file, use the `--replaceOutput true` or the `-r` flag.
35+
If you want to include the `moved blocks` in the output file, use the `--includeMoved` or the `-m` flag.
3636

37-
You can use the `--watch true` or the `-w` flag to keep the plugin running and watching for changes in the input file. You can have input and output files open in an editor and see easily how changes to the input file affect the output file.
37+
If you want to overwrite the output file if it exists, or even use the same output file as the input file, use the `--replaceOutput` or the `-r` flag.
38+
39+
You can use the `--watch` or the `-w` flag to keep the plugin running and watching for changes in the input file. You can have input and output files open in an editor and see easily how changes to the input file affect the output file.
3840

3941
### Limitations
4042

cmd/plugin/main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"fmt"
55
"os"
66

7-
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli/adv2"
87
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli/clu2adv"
98
"github.com/spf13/cobra"
109
)
@@ -15,7 +14,7 @@ func main() {
1514
Short: "Utilities for Terraform's MongoDB Atlas Provider",
1615
Aliases: []string{"tf"},
1716
}
18-
terraformCmd.AddCommand(clu2adv.Builder(), adv2.Builder())
17+
terraformCmd.AddCommand(clu2adv.Builder())
1918

2019
completionOption := &cobra.CompletionOptions{
2120
DisableDefaultCmd: true,

internal/cli/adv2/adv2.go

Lines changed: 0 additions & 20 deletions
This file was deleted.

internal/cli/clu2adv/clu2adv.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package clu2adv
22

33
import (
4+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/flag"
45
"github.com/spf13/afero"
56
"github.com/spf13/cobra"
67
)
@@ -19,11 +20,12 @@ func Builder() *cobra.Command {
1920
return o.Run()
2021
},
2122
}
22-
cmd.Flags().StringVarP(&o.file, "file", "f", "", "input file")
23-
_ = cmd.MarkFlagRequired("file")
24-
cmd.Flags().StringVarP(&o.output, "output", "o", "", "output file")
25-
_ = cmd.MarkFlagRequired("output")
26-
cmd.Flags().BoolVarP(&o.replaceOutput, "replaceOutput", "r", false, "replace output file if exists")
27-
cmd.Flags().BoolVarP(&o.watch, "watch", "w", false, "keeps the plugin running and watches the input file for changes")
23+
cmd.Flags().StringVarP(&o.file, flag.File, flag.FileShort, "", "input file")
24+
_ = cmd.MarkFlagRequired(flag.File)
25+
cmd.Flags().StringVarP(&o.output, flag.Output, flag.OutputShort, "", "output file")
26+
_ = cmd.MarkFlagRequired(flag.Output)
27+
cmd.Flags().BoolVarP(&o.replaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false, "replace output file if exists")
28+
cmd.Flags().BoolVarP(&o.watch, flag.Watch, flag.WatchShort, false, "keeps the plugin running and watches the input file for changes")
29+
cmd.Flags().BoolVarP(&o.includeMoved, flag.IncludeMoved, flag.IncludeMovedShort, false, "include moved blocks in the output file")
2830
return cmd
2931
}

internal/cli/clu2adv/opts.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ type opts struct {
1616
output string
1717
replaceOutput bool
1818
watch bool
19+
includeMoved bool
1920
}
2021

2122
func (o *opts) PreRun() error {
@@ -43,7 +44,7 @@ func (o *opts) generateFile(allowParseErrors bool) error {
4344
if err != nil {
4445
return fmt.Errorf("failed to read file %s: %w", o.file, err)
4546
}
46-
outConfig, err := convert.ClusterToAdvancedCluster(inConfig)
47+
outConfig, err := convert.ClusterToAdvancedCluster(inConfig, o.includeMoved)
4748
if err != nil {
4849
if allowParseErrors {
4950
outConfig = []byte("# CONVERT ERROR: " + err.Error() + "\n\n")

internal/convert/const_names.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,7 @@ const (
4848
nKey = "key"
4949
nValue = "value"
5050
nUseRepSpecsPerShard = "use_replication_spec_per_shard"
51+
nMoved = "moved"
52+
nFrom = "from"
53+
nTo = "to"
5154
)

internal/convert/convert.go

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@ const (
2323
valClusterType = "REPLICASET"
2424
valMaxPriority = 7
2525
valMinPriority = 1
26-
errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
27-
errRepSpecs = "setting " + nRepSpecs
28-
errConfigs = "setting " + nConfig
29-
errPriority = "setting " + nPriority
30-
errNumShards = "setting " + nNumShards
26+
27+
errFreeCluster = "free cluster (because no " + nRepSpecs + ")"
28+
errRepSpecs = "setting " + nRepSpecs
29+
errConfigs = "setting " + nConfig
30+
errPriority = "setting " + nPriority
31+
errNumShards = "setting " + nNumShards
32+
33+
commentGeneratedBy = "Generated by atlas-cli-plugin-terraform."
34+
commentConfirmReferences = "Please confirm that all references to this resource are updated."
35+
commentMovedBlock = "Moved blocks"
36+
commentRemovedOld = "Note: Remember to remove or comment out the old cluster definitions."
3137
)
3238

3339
type attrVals struct {
@@ -40,24 +46,33 @@ type attrVals struct {
4046
// All other resources and data sources are left untouched.
4147
// Note: hclwrite.Tokens are used instead of cty.Value so expressions with interpolations like var.region can be preserved.
4248
// cty.Value only supports literal expressions.
43-
func ClusterToAdvancedCluster(config []byte) ([]byte, error) {
49+
func ClusterToAdvancedCluster(config []byte, includeMoved bool) ([]byte, error) {
50+
var moveLabels []string
4451
parser, err := hcl.GetParser(config)
4552
if err != nil {
4653
return nil, err
4754
}
48-
for _, block := range parser.Body().Blocks() {
49-
converted, err := convertResource(block)
55+
parserb := parser.Body()
56+
for _, block := range parserb.Blocks() {
57+
convertedResource, err := convertResource(block)
5058
if err != nil {
51-
return nil, err
59+
return nil,
60+
err
5261
}
53-
converted = converted || convertDataSource(block)
54-
if converted {
62+
if includeMoved && convertedResource {
63+
if moveLabel := getResourceLabel(block); moveLabel != "" {
64+
moveLabels = append(moveLabels, moveLabel)
65+
}
66+
}
67+
convertedDataSource := convertDataSource(block)
68+
if convertedResource || convertedDataSource {
5569
blockb := block.Body()
5670
blockb.AppendNewline()
57-
hcl.AppendComment(blockb, "Generated by atlas-cli-plugin-terraform.")
58-
hcl.AppendComment(blockb, "Please confirm that all references to this resource are updated.")
71+
hcl.AppendComment(blockb, commentGeneratedBy)
72+
hcl.AppendComment(blockb, commentConfirmReferences)
5973
}
6074
}
75+
fillMovedBlocks(parserb, moveLabels)
6176
return parser.Bytes(), nil
6277
}
6378

@@ -99,6 +114,25 @@ func convertDataSource(block *hclwrite.Block) bool {
99114
return false
100115
}
101116

117+
func fillMovedBlocks(body *hclwrite.Body, moveLabels []string) {
118+
if len(moveLabels) == 0 {
119+
return
120+
}
121+
body.AppendNewline()
122+
hcl.AppendComment(body, commentMovedBlock)
123+
hcl.AppendComment(body, commentRemovedOld)
124+
body.AppendNewline()
125+
for i, moveLabel := range moveLabels {
126+
block := body.AppendNewBlock(nMoved, nil)
127+
blockb := block.Body()
128+
hcl.SetAttrExpr(blockb, nFrom, fmt.Sprintf("%s.%s", cluster, moveLabel))
129+
hcl.SetAttrExpr(blockb, nTo, fmt.Sprintf("%s.%s", advCluster, moveLabel))
130+
if i < len(moveLabels)-1 {
131+
body.AppendNewline()
132+
}
133+
}
134+
}
135+
102136
// fillFreeTierCluster is the entry point to convert clusters in free tier
103137
func fillFreeTierCluster(resourceb *hclwrite.Body) error {
104138
resourceb.SetAttributeValue(nClusterType, cty.StringVal(valClusterType))
@@ -338,6 +372,8 @@ func setResourceName(resource *hclwrite.Block, name string) {
338372
resource.SetLabels(labels)
339373
}
340374

375+
// getResourceName returns the first label of a block, if it exists.
376+
// e.g. in resource "mongodbatlas_cluster" "mycluster", the first label is "mongodbatlas_cluster".
341377
func getResourceName(resource *hclwrite.Block) string {
342378
labels := resource.Labels()
343379
if len(labels) == 0 {
@@ -346,6 +382,16 @@ func getResourceName(resource *hclwrite.Block) string {
346382
return labels[0]
347383
}
348384

385+
// getResourceLabel returns the second label of a block, if it exists.
386+
// e.g. in resource "mongodbatlas_cluster" "mycluster", the second label is "mycluster".
387+
func getResourceLabel(resource *hclwrite.Block) string {
388+
labels := resource.Labels()
389+
if len(labels) <= 1 {
390+
return ""
391+
}
392+
return labels[1]
393+
}
394+
349395
func checkDynamicBlock(body *hclwrite.Body) error {
350396
for _, block := range body.Blocks() {
351397
if block.Type() == "dynamic" {

internal/convert/convert_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ func TestClusterToAdvancedCluster(t *testing.T) {
3838
t.Run(testName, func(t *testing.T) {
3939
inConfig, err := afero.ReadFile(fs, inputFile)
4040
require.NoError(t, err)
41-
outConfig, err := convert.ClusterToAdvancedCluster(inConfig)
41+
includeMoved := strings.Contains(testName, "includeMoved")
42+
outConfig, err := convert.ClusterToAdvancedCluster(inConfig, includeMoved)
4243
if err == nil {
4344
g.Assert(t, testName, outConfig)
4445
} else {

0 commit comments

Comments
 (0)