Skip to content

Commit 6c18dc0

Browse files
authored
feat: Create advancedClusterToNew command skeleton (#58)
1 parent 12c7f10 commit 6c18dc0

File tree

20 files changed

+359
-182
lines changed

20 files changed

+359
-182
lines changed

cmd/plugin/main.go

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

7+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli/adv2v2"
78
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli/clu2adv"
89
"github.com/spf13/cobra"
910
)
@@ -15,6 +16,7 @@ func main() {
1516
Aliases: []string{"tf"},
1617
}
1718
terraformCmd.AddCommand(clu2adv.Builder())
19+
terraformCmd.AddCommand(adv2v2.Builder())
1820

1921
completionOption := &cobra.CompletionOptions{
2022
DisableDefaultCmd: true,

internal/cli/adv2v2/adv2v2.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package adv2v2
2+
3+
import (
4+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli"
5+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/convert"
6+
"github.com/spf13/afero"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func Builder() *cobra.Command {
11+
o := &cli.BaseOpts{
12+
Fs: afero.NewOsFs(),
13+
Convert: convert.AdvancedClusterToV2,
14+
}
15+
cmd := &cobra.Command{
16+
Use: "advancedClusterToV2",
17+
Short: "Convert advanced_cluster from provider version 1 to 2",
18+
Long: "Convert a Terraform configuration from mongodbatlas_advanced_cluster in provider version 1.X.X (SDKv2)" +
19+
" to version 2.X.X (TPF - Terraform Plugin Framework)",
20+
Aliases: []string{"adv2v2"},
21+
RunE: o.RunE,
22+
}
23+
cli.SetupCommonFlags(cmd, o)
24+
return cmd
25+
}

internal/cli/clu2adv/clu2adv.go

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

33
import (
4+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/cli"
5+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/convert"
46
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/flag"
57
"github.com/spf13/afero"
68
"github.com/spf13/cobra"
79
)
810

911
func Builder() *cobra.Command {
10-
o := &opts{fs: afero.NewOsFs()}
12+
o := &struct {
13+
*cli.BaseOpts
14+
includeMoved bool
15+
}{
16+
BaseOpts: &cli.BaseOpts{
17+
Fs: afero.NewOsFs(),
18+
},
19+
}
20+
o.Convert = func(config []byte) ([]byte, error) {
21+
return convert.ClusterToAdvancedCluster(config, o.includeMoved)
22+
}
1123
cmd := &cobra.Command{
1224
Use: "clusterToAdvancedCluster",
1325
Short: "Convert cluster to advanced_cluster preview provider 2.0.0",
1426
Long: "Convert a Terraform configuration from mongodbatlas_cluster to " +
1527
"mongodbatlas_advanced_cluster preview provider 2.0.0",
1628
Aliases: []string{"clu2adv"},
17-
RunE: func(_ *cobra.Command, _ []string) error {
18-
if err := o.PreRun(); err != nil {
19-
return err
20-
}
21-
return o.Run()
22-
},
29+
RunE: o.RunE,
2330
}
24-
cmd.Flags().StringVarP(&o.file, flag.File, flag.FileShort, "", "input file")
25-
_ = cmd.MarkFlagRequired(flag.File)
26-
cmd.Flags().StringVarP(&o.output, flag.Output, flag.OutputShort, "", "output file")
27-
_ = cmd.MarkFlagRequired(flag.Output)
28-
cmd.Flags().BoolVarP(&o.replaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
29-
"replace output file if exists")
30-
cmd.Flags().BoolVarP(&o.watch, flag.Watch, flag.WatchShort, false,
31-
"keeps the plugin running and watches the input file for changes")
31+
cli.SetupCommonFlags(cmd, o.BaseOpts)
3232
cmd.Flags().BoolVarP(&o.includeMoved, flag.IncludeMoved, flag.IncludeMovedShort, false,
3333
"include moved blocks in the output file")
3434
return cmd

internal/cli/clu2adv/opts.go

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

internal/cli/common.go

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package cli
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
"github.com/fsnotify/fsnotify"
8+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/file"
9+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/flag"
10+
"github.com/spf13/afero"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
type ConvertFn func(config []byte) ([]byte, error)
15+
16+
// BaseOpts contains common functionality for CLI commands that convert files.
17+
type BaseOpts struct {
18+
Fs afero.Fs
19+
Convert ConvertFn
20+
File string
21+
Output string
22+
ReplaceOutput bool
23+
Watch bool
24+
}
25+
26+
// RunE is the entry point for the command.
27+
func (o *BaseOpts) RunE(cmd *cobra.Command, args []string) error {
28+
if err := o.preRun(); err != nil {
29+
return err
30+
}
31+
return o.run()
32+
}
33+
34+
// preRun validates the input and output files before running the command.
35+
func (o *BaseOpts) preRun() error {
36+
if err := file.MustExist(o.Fs, o.File); err != nil {
37+
return err
38+
}
39+
if !o.ReplaceOutput {
40+
return file.MustNotExist(o.Fs, o.Output)
41+
}
42+
return nil
43+
}
44+
45+
// run executes the conversion and optionally watches for file changes.
46+
func (o *BaseOpts) run() error {
47+
if err := o.generateFile(false); err != nil {
48+
return err
49+
}
50+
if o.Watch {
51+
return o.watchFile()
52+
}
53+
return nil
54+
}
55+
56+
// generateFile reads the input file, converts it, and writes the output.
57+
func (o *BaseOpts) generateFile(allowParseErrors bool) error {
58+
inConfig, err := afero.ReadFile(o.Fs, o.File)
59+
if err != nil {
60+
return fmt.Errorf("failed to read file %s: %w", o.File, err)
61+
}
62+
63+
outConfig, err := o.Convert(inConfig)
64+
if err != nil {
65+
if allowParseErrors {
66+
outConfig = []byte("# CONVERT ERROR: " + err.Error() + "\n\n")
67+
outConfig = append(outConfig, inConfig...)
68+
} else {
69+
return err
70+
}
71+
}
72+
73+
if err := afero.WriteFile(o.Fs, o.Output, outConfig, 0o600); err != nil {
74+
return fmt.Errorf("failed to write file %s: %w", o.Output, err)
75+
}
76+
return nil
77+
}
78+
79+
// watchFile watches the input file for changes and regenerates the output.
80+
func (o *BaseOpts) watchFile() error {
81+
watcher, err := fsnotify.NewWatcher()
82+
if err != nil {
83+
return err
84+
}
85+
defer watcher.Close()
86+
87+
if err := watcher.Add(o.File); err != nil {
88+
return err
89+
}
90+
91+
for {
92+
if err := o.waitForFileEvent(watcher); err != nil {
93+
return err
94+
}
95+
}
96+
}
97+
98+
// waitForFileEvent waits for file system events and regenerates the output file.
99+
func (o *BaseOpts) waitForFileEvent(watcher *fsnotify.Watcher) error {
100+
watcherError := errors.New("watcher has been closed")
101+
select {
102+
case event, ok := <-watcher.Events:
103+
if !ok {
104+
return watcherError
105+
}
106+
if event.Has(fsnotify.Write) {
107+
if err := o.generateFile(true); err != nil {
108+
return err
109+
}
110+
}
111+
case err, ok := <-watcher.Errors:
112+
if !ok {
113+
return watcherError
114+
}
115+
return err
116+
}
117+
return nil
118+
}
119+
120+
// SetupCommonFlags sets up the common flags used by all commands.
121+
func SetupCommonFlags(cmd *cobra.Command, opts *BaseOpts) {
122+
cmd.Flags().StringVarP(&opts.File, flag.File, flag.FileShort, "", "input file")
123+
_ = cmd.MarkFlagRequired(flag.File)
124+
cmd.Flags().StringVarP(&opts.Output, flag.Output, flag.OutputShort, "", "output file")
125+
_ = cmd.MarkFlagRequired(flag.Output)
126+
cmd.Flags().BoolVarP(&opts.ReplaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
127+
"replace output file if exists")
128+
cmd.Flags().BoolVarP(&opts.Watch, flag.Watch, flag.WatchShort, false,
129+
"keeps the plugin running and watches the input file for changes")
130+
}

internal/convert/adv2v2.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package convert
2+
3+
import "github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/hcl"
4+
5+
// AdvancedClusterToV2 transforms all mongodbatlas_advanced_cluster resource definitions in a
6+
// Terraform configuration file from SDKv2 schema to TPF (Terraform Plugin Framework) schema.
7+
// All other resources and data sources are left untouched.
8+
// TODO: Not implemented yet.
9+
func AdvancedClusterToV2(config []byte) ([]byte, error) {
10+
parser, err := hcl.GetParser(config)
11+
if err != nil {
12+
return nil, err
13+
}
14+
return parser.Bytes(), nil
15+
}

internal/convert/adv2v2_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package convert_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/convert"
7+
)
8+
9+
func TestAdvancedClusterToV2(t *testing.T) {
10+
runConvertTests(t, "adv2v2", func(testName string, inConfig []byte) ([]byte, error) {
11+
return convert.AdvancedClusterToV2(inConfig)
12+
})
13+
}
File renamed without changes.

internal/convert/clu2adv_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package convert_test
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/mongodb-labs/atlas-cli-plugin-terraform/internal/convert"
8+
)
9+
10+
func TestClusterToAdvancedCluster(t *testing.T) {
11+
runConvertTests(t, "clu2adv", func(testName string, inConfig []byte) ([]byte, error) {
12+
includeMoved := strings.Contains(testName, "includeMoved")
13+
return convert.ClusterToAdvancedCluster(inConfig, includeMoved)
14+
})
15+
}

0 commit comments

Comments
 (0)