Skip to content

Commit 97c71ba

Browse files
committed
reduce duplication in commands
1 parent 1fd6eb3 commit 97c71ba

File tree

5 files changed

+142
-203
lines changed

5 files changed

+142
-203
lines changed

internal/cli/adv2new/adv2new.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package adv2new
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 := &cli.BaseOpts{
13+
Fs: afero.NewOsFs(),
14+
Converter: cli.ConvertFunc(convert.AdvancedClusterToNew),
15+
}
1116
cmd := &cobra.Command{
1217
Use: "advancedClusterToNew",
1318
Short: "Convert advanced_cluster from provider version 1 to 2",
@@ -21,13 +26,13 @@ func Builder() *cobra.Command {
2126
return o.Run()
2227
},
2328
}
24-
cmd.Flags().StringVarP(&o.file, flag.File, flag.FileShort, "", "input file")
29+
cmd.Flags().StringVarP(&o.File, flag.File, flag.FileShort, "", "input file")
2530
_ = cmd.MarkFlagRequired(flag.File)
26-
cmd.Flags().StringVarP(&o.output, flag.Output, flag.OutputShort, "", "output file")
31+
cmd.Flags().StringVarP(&o.Output, flag.Output, flag.OutputShort, "", "output file")
2732
_ = cmd.MarkFlagRequired(flag.Output)
28-
cmd.Flags().BoolVarP(&o.replaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
33+
cmd.Flags().BoolVarP(&o.ReplaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
2934
"replace output file if exists")
30-
cmd.Flags().BoolVarP(&o.watch, flag.Watch, flag.WatchShort, false,
35+
cmd.Flags().BoolVarP(&o.Watch, flag.Watch, flag.WatchShort, false,
3136
"keeps the plugin running and watches the input file for changes")
3237
return cmd
3338
}

internal/cli/adv2new/opts.go

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

internal/cli/clu2adv/clu2adv.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
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+
o.Fs = afero.NewOsFs()
17+
o.Converter = cli.ConvertFunc(func(config []byte) ([]byte, error) {
18+
return convert.ClusterToAdvancedCluster(config, o.includeMoved)
19+
})
1120
cmd := &cobra.Command{
1221
Use: "clusterToAdvancedCluster",
1322
Short: "Convert cluster to advanced_cluster preview provider 2.0.0",
@@ -21,13 +30,13 @@ func Builder() *cobra.Command {
2130
return o.Run()
2231
},
2332
}
24-
cmd.Flags().StringVarP(&o.file, flag.File, flag.FileShort, "", "input file")
33+
cmd.Flags().StringVarP(&o.File, flag.File, flag.FileShort, "", "input file")
2534
_ = cmd.MarkFlagRequired(flag.File)
26-
cmd.Flags().StringVarP(&o.output, flag.Output, flag.OutputShort, "", "output file")
35+
cmd.Flags().StringVarP(&o.Output, flag.Output, flag.OutputShort, "", "output file")
2736
_ = cmd.MarkFlagRequired(flag.Output)
28-
cmd.Flags().BoolVarP(&o.replaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
37+
cmd.Flags().BoolVarP(&o.ReplaceOutput, flag.ReplaceOutput, flag.ReplaceOutputShort, false,
2938
"replace output file if exists")
30-
cmd.Flags().BoolVarP(&o.watch, flag.Watch, flag.WatchShort, false,
39+
cmd.Flags().BoolVarP(&o.Watch, flag.Watch, flag.WatchShort, false,
3140
"keeps the plugin running and watches the input file for changes")
3241
cmd.Flags().BoolVarP(&o.includeMoved, flag.IncludeMoved, flag.IncludeMovedShort, false,
3342
"include moved blocks in the output file")

internal/cli/clu2adv/opts.go

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

internal/cli/common.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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/spf13/afero"
10+
)
11+
12+
// Converter defines the interface for different conversion functions.
13+
type Converter interface {
14+
Convert(config []byte) ([]byte, error)
15+
}
16+
17+
// ConvertFunc is a function type that implements the Converter interface.
18+
type ConvertFunc func(config []byte) ([]byte, error)
19+
20+
func (f ConvertFunc) Convert(config []byte) ([]byte, error) {
21+
return f(config)
22+
}
23+
24+
// BaseOpts contains common functionality for CLI commands that convert files.
25+
type BaseOpts struct {
26+
Fs afero.Fs
27+
Converter Converter
28+
File string
29+
Output string
30+
ReplaceOutput bool
31+
Watch bool
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.Converter.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+
}

0 commit comments

Comments
 (0)