-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.go
129 lines (116 loc) · 2.84 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Terraform-state-mover helps refactoring terraform code by offering an interactive prompt for the `terraform state mv` command.
package main
import (
"fmt"
"github.com/manifoldco/promptui"
"github.com/urfave/cli/v2"
"log"
"os"
"reflect"
"time"
)
var (
version = "dev"
)
type config struct {
delay time.Duration
verbose bool
dryrun bool
}
func main() {
// do not use "-v" to print the version
cli.VersionFlag = &cli.BoolFlag{
Name: "version", Aliases: []string{},
Usage: "print the version only",
}
app := &cli.App{
Name: "terraform-state-mover",
Usage: "refactoring Terraform code has never been easier",
Authors: []*cli.Author{{Name: "Maximilian Bode", Email: "[email protected]"}},
Action: action,
Flags: []cli.Flag{
&cli.DurationFlag{
Name: "delay", Aliases: []string{"d"},
Usage: "Delay between terraform state mv calls. Helps to avoid rate-limits.",
Value: time.Second * 0,
},
&cli.BoolFlag{
Name: "verbose", Aliases: []string{"v"},
Usage: "Be more verbose - prints e.g. terraform mv calls",
Value: false,
},
&cli.BoolFlag{
Name: "dry-run", Aliases: []string{"n"},
Usage: "Do not actually move state, enables -v",
Value: false,
},
},
UsageText: "terraform-state-mover [-v] [-d delay] [-n] [-- <terraform args>]",
Version: version,
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
func action(ctx *cli.Context) error {
var planArgs []string
for i, elem := range os.Args {
if "--" == elem {
planArgs = os.Args[i+1:]
}
}
cfg := readConfig(ctx)
changes, err := changes(cfg, planArgs)
if err != nil {
return err
}
dests := filterByAction(changes, create)
srcs := filterByDestinationResourceTypes(filterByAction(changes, del), dests)
moves := make(map[Resource]Resource)
for len(srcs) > 0 && len(dests) > 0 {
src, dest, err := prompt(srcs, dests)
if err != nil {
if err == promptui.ErrInterrupt && len(moves) > 0 {
fmt.Println("Interrupted. These moves would have been executed based on your selections:")
for src, dest := range moves {
fmt.Printf(" terraform state mv '%s' '%s'\n", src.Address, dest.Address)
}
}
return err
}
if reflect.DeepEqual(src, Resource{}) {
break
}
moves[src] = dest
delete(srcs, src)
delete(dests, dest)
}
if len(moves) == 0 {
fmt.Println("Nothing to do.")
}
var firstEntry = true
for src, dest := range moves {
if firstEntry {
firstEntry = false
} else {
wait(cfg)
}
if err := move(cfg, src, dest); err != nil {
return err
}
}
return nil
}
func readConfig(ctx *cli.Context) config {
return config{
delay: ctx.Duration("delay"),
verbose: ctx.Bool("verbose") || ctx.Bool("dry-run"),
dryrun: ctx.Bool("dry-run"),
}
}
func wait(cfg config) {
if cfg.verbose && cfg.delay > 0 {
fmt.Println("Waiting", cfg.delay, "...")
}
time.Sleep(cfg.delay)
}