0
0
mirror of https://github.com/thegeeklab/wp-opentofu.git synced 2024-06-02 18:39:41 +02:00

refactor: switch to plugin Cmd and add tests

This commit is contained in:
Robert Kaussow 2024-05-12 11:52:28 +02:00
parent 2eda2b50fc
commit 034ffa719a
Signed by: xoxys
GPG Key ID: 4E692A2EAECC03C0
8 changed files with 782 additions and 231 deletions

4
go.mod
View File

@ -5,6 +5,7 @@ go 1.22
require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/rs/zerolog v1.32.0
github.com/stretchr/testify v1.9.0
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1
github.com/urfave/cli/v2 v2.27.2
golang.org/x/sys v0.20.0
@ -14,6 +15,7 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.1.1 // indirect
github.com/huandu/xstrings v1.3.3 // indirect
github.com/imdario/mergo v0.3.11 // indirect
@ -22,10 +24,12 @@ require (
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

1
go.sum
View File

@ -90,6 +90,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=

View File

@ -8,7 +8,9 @@ import (
"io"
"os"
"github.com/thegeeklab/wp-opentofu/tofu"
"github.com/thegeeklab/wp-plugin-go/v2/trace"
"github.com/thegeeklab/wp-plugin-go/v2/types"
)
var (
@ -43,21 +45,21 @@ func (p *Plugin) run(ctx context.Context) error {
func (p *Plugin) FlagsFromContext() error {
if p.Context.String("init-option") != "" {
initOptions := InitOptions{}
initOptions := tofu.InitOptions{}
if err := json.Unmarshal([]byte(p.Context.String("init-option")), &initOptions); err != nil {
return fmt.Errorf("cannot unmarshal init_option: %w", err)
}
p.Settings.InitOptions = initOptions
p.Settings.Tofu.InitOptions = initOptions
}
if p.Context.String("fmt-option") != "" {
fmtOptions := FmtOptions{}
fmtOptions := tofu.FmtOptions{}
if err := json.Unmarshal([]byte(p.Context.String("fmt-option")), &fmtOptions); err != nil {
return fmt.Errorf("cannot unmarshal fmt_option: %w", err)
}
p.Settings.FmtOptions = fmtOptions
p.Settings.Tofu.FmtOptions = fmtOptions
}
return nil
@ -70,9 +72,9 @@ func (p *Plugin) Validate() error {
p.Settings.DataDir = value
}
p.Settings.OutFile = "plan.tfout"
p.Settings.Tofu.OutFile = "plan.tfout"
if p.Settings.DataDir == ".terraform" {
p.Settings.OutFile = fmt.Sprintf("%s.plan.tfout", p.Settings.DataDir)
p.Settings.Tofu.OutFile = fmt.Sprintf("%s.plan.tfout", p.Settings.DataDir)
}
return nil
@ -80,8 +82,10 @@ func (p *Plugin) Validate() error {
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
batchCmd := make([]*Cmd, 0)
batchCmd = append(batchCmd, p.versionCommand())
tofu := p.Settings.Tofu
batchCmd := make([]*types.Cmd, 0)
batchCmd = append(batchCmd, tofu.Version())
if p.Settings.TofuVersion != "" {
err := installPackage(p.Plugin.Network.Context, p.Plugin.Network.Client, p.Settings.TofuVersion, maxDecompressionSize)
@ -90,29 +94,29 @@ func (p *Plugin) Execute() error {
}
}
batchCmd = append(batchCmd, p.initCommand())
batchCmd = append(batchCmd, p.getModulesCommand())
batchCmd = append(batchCmd, tofu.Init())
batchCmd = append(batchCmd, tofu.GetModules())
for _, action := range p.Settings.Action.Value() {
switch action {
case "fmt":
batchCmd = append(batchCmd, p.fmtCommand())
batchCmd = append(batchCmd, tofu.Fmt())
case "validate":
batchCmd = append(batchCmd, p.validateCommand())
batchCmd = append(batchCmd, tofu.Validate())
case "plan":
batchCmd = append(batchCmd, p.planCommand(false))
batchCmd = append(batchCmd, tofu.Plan(false))
case "plan-destroy":
batchCmd = append(batchCmd, p.planCommand(true))
batchCmd = append(batchCmd, tofu.Plan(true))
case "apply":
batchCmd = append(batchCmd, p.applyCommand())
batchCmd = append(batchCmd, tofu.Apply())
case "destroy":
batchCmd = append(batchCmd, p.destroyCommand())
batchCmd = append(batchCmd, tofu.Destroy())
default:
return fmt.Errorf("%w: %s", ErrActionUnknown, action)
}
}
if err := deleteDir(p.Settings.DataDir); err != nil {
if err := os.RemoveAll(p.Settings.DataDir); err != nil {
return err
}
@ -136,5 +140,5 @@ func (p *Plugin) Execute() error {
}
}
return deleteDir(p.Settings.DataDir)
return os.RemoveAll(p.Settings.DataDir)
}

View File

@ -3,9 +3,9 @@ package plugin
import (
"fmt"
"github.com/thegeeklab/wp-opentofu/tofu"
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
//go:generate go run ../internal/docs/main.go -output=../docs/data/data-raw.yaml
@ -18,39 +18,11 @@ type Plugin struct {
// Settings for the Plugin.
type Settings struct {
Action cli.StringSlice
TofuVersion string
InitOptions InitOptions
FmtOptions FmtOptions
Action cli.StringSlice
RootDir string
DataDir string
OutFile string
Parallelism int
Targets cli.StringSlice
Refresh bool
NoLog bool
}
// InitOptions include options for the OpenTofu init command.
type InitOptions struct {
BackendConfig []string `json:"backend-config"`
Lock *bool `json:"lock"`
LockTimeout string `json:"lock-timeout"`
}
// FmtOptions fmt options for the OpenTofu fmt command.
type FmtOptions struct {
List *bool `json:"list"`
Write *bool `json:"write"`
Diff *bool `json:"diff"`
Check *bool `json:"check"`
}
type Cmd struct {
*execabs.Cmd
Private bool
TofuVersion string
Tofu tofu.Tofu
}
func New(e wp.ExecuteFunc, build ...string) *Plugin {
@ -123,14 +95,14 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "no-log",
Usage: "suppress tofu command output for `plan`, `apply` and `destroy` action",
EnvVars: []string{"PLUGIN_NO_LOG"},
Destination: &settings.NoLog,
Destination: &settings.Tofu.NoLog,
Category: category,
},
&cli.StringSliceFlag{
Name: "targets",
Usage: "targets to run `plan` or `apply` action on",
EnvVars: []string{"PLUGIN_TARGETS"},
Destination: &settings.Targets,
Destination: &settings.Tofu.Targets,
Category: category,
},
&cli.StringFlag{
@ -144,7 +116,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "refresh",
Usage: "enables refreshing of the state before `plan` and `apply` commands",
EnvVars: []string{"PLUGIN_REFRESH"},
Destination: &settings.Refresh,
Destination: &settings.Tofu.Refresh,
Value: true,
Category: category,
},

View File

@ -1,172 +0,0 @@
package plugin
import (
"fmt"
"golang.org/x/sys/execabs"
)
const (
tofuBin = "/usr/local/bin/tofu"
)
func (p *Plugin) versionCommand() *Cmd {
return &Cmd{
Cmd: execabs.Command(tofuBin, "version"),
Private: p.Settings.NoLog,
}
}
func (p *Plugin) initCommand() *Cmd {
args := []string{
"init",
}
for _, v := range p.Settings.InitOptions.BackendConfig {
args = append(args, fmt.Sprintf("-backend-config=%s", v))
}
// Fail tofu execution on prompt
args = append(args, "-input=false")
return &Cmd{
Cmd: execabs.Command(tofuBin, args...),
}
}
func (p *Plugin) getModulesCommand() *Cmd {
return &Cmd{
Cmd: execabs.Command(tofuBin, "get"),
}
}
func (p *Plugin) validateCommand() *Cmd {
return &Cmd{
Cmd: execabs.Command(tofuBin, "validate"),
}
}
func (p *Plugin) fmtCommand() *Cmd {
args := []string{
"fmt",
}
if p.Settings.FmtOptions.List != nil {
args = append(args, fmt.Sprintf("-list=%t", *p.Settings.FmtOptions.List))
}
if p.Settings.FmtOptions.Write != nil {
args = append(args, fmt.Sprintf("-write=%t", *p.Settings.FmtOptions.Write))
}
if p.Settings.FmtOptions.Diff != nil {
args = append(args, fmt.Sprintf("-diff=%t", *p.Settings.FmtOptions.Diff))
}
if p.Settings.FmtOptions.Check != nil {
args = append(args, fmt.Sprintf("-check=%t", *p.Settings.FmtOptions.Check))
}
return &Cmd{
Cmd: execabs.Command(tofuBin, args...),
}
}
func (p *Plugin) planCommand(destroy bool) *Cmd {
args := []string{
"plan",
}
if destroy {
args = append(args, "-destroy")
} else {
args = append(args, fmt.Sprintf("-out=%s", p.Settings.OutFile))
}
for _, value := range p.Settings.Targets.Value() {
args = append(args, "--target", value)
}
if p.Settings.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", p.Settings.Parallelism))
}
if p.Settings.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *p.Settings.InitOptions.Lock))
}
if p.Settings.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", p.Settings.InitOptions.LockTimeout))
}
if !p.Settings.Refresh {
args = append(args, "-refresh=false")
}
return &Cmd{
Cmd: execabs.Command(tofuBin, args...),
Private: p.Settings.NoLog,
}
}
func (p *Plugin) applyCommand() *Cmd {
args := []string{
"apply",
}
for _, v := range p.Settings.Targets.Value() {
args = append(args, "--target", v)
}
if p.Settings.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", p.Settings.Parallelism))
}
if p.Settings.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *p.Settings.InitOptions.Lock))
}
if p.Settings.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", p.Settings.InitOptions.LockTimeout))
}
if !p.Settings.Refresh {
args = append(args, "-refresh=false")
}
args = append(args, p.Settings.OutFile)
return &Cmd{
Cmd: execabs.Command(tofuBin, args...),
Private: p.Settings.NoLog,
}
}
func (p *Plugin) destroyCommand() *Cmd {
args := []string{
"destroy",
}
for _, v := range p.Settings.Targets.Value() {
args = append(args, fmt.Sprintf("-target=%s", v))
}
if p.Settings.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", p.Settings.Parallelism))
}
if p.Settings.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *p.Settings.InitOptions.Lock))
}
if p.Settings.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", p.Settings.InitOptions.LockTimeout))
}
args = append(args, "-auto-approve")
return &Cmd{
Cmd: execabs.Command(tofuBin, args...),
Private: p.Settings.NoLog,
}
}

View File

@ -13,6 +13,7 @@ import (
"github.com/Masterminds/semver/v3"
"github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-opentofu/tofu"
)
func installPackage(ctx context.Context, client *http.Client, version string, maxSize int64) error {
@ -34,7 +35,7 @@ func installPackage(ctx context.Context, client *http.Client, version string, ma
}
defer func() {
_ = deleteDir(tmpdir)
_ = os.RemoveAll(tmpdir)
}()
log.Debug().
@ -51,7 +52,7 @@ func installPackage(ctx context.Context, client *http.Client, version string, ma
return fmt.Errorf("failed to unzip: %w", err)
}
if err := os.Rename(filepath.Join(tmpdir, "tofu"), tofuBin); err != nil {
if err := os.Rename(filepath.Join(tmpdir, "tofu"), tofu.TofuBin); err != nil {
return fmt.Errorf("failed to rename: %w", err)
}
@ -169,7 +170,3 @@ func sanitizeArchivePath(d, t string) (string, error) {
return "", fmt.Errorf("%w: %v", ErrTaintedPath, t)
}
func deleteDir(path string) error {
return os.RemoveAll(path)
}

200
tofu/tofu.go Normal file
View File

@ -0,0 +1,200 @@
package tofu
import (
"fmt"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
const TofuBin = "/usr/local/bin/tofu"
type Tofu struct {
InitOptions InitOptions
FmtOptions FmtOptions
OutFile string
Parallelism int
Targets cli.StringSlice
Refresh bool
NoLog bool
}
// InitOptions include options for the OpenTofu init command.
type InitOptions struct {
BackendConfig []string `json:"backend-config"`
Lock *bool `json:"lock"`
LockTimeout string `json:"lock-timeout"`
}
// FmtOptions fmt options for the OpenTofu fmt command.
type FmtOptions struct {
List *bool `json:"list"`
Write *bool `json:"write"`
Diff *bool `json:"diff"`
Check *bool `json:"check"`
}
func (t *Tofu) Version() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(TofuBin, "version"),
Private: t.NoLog,
}
}
func (t *Tofu) Init() *types.Cmd {
args := []string{
"init",
}
for _, v := range t.InitOptions.BackendConfig {
args = append(args, fmt.Sprintf("-backend-config=%s", v))
}
// Fail tofu execution on prompt
args = append(args, "-input=false")
return &types.Cmd{
Cmd: execabs.Command(TofuBin, args...),
}
}
func (t *Tofu) GetModules() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(TofuBin, "get"),
}
}
func (t *Tofu) Validate() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(TofuBin, "validate"),
}
}
func (t *Tofu) Fmt() *types.Cmd {
args := []string{
"fmt",
}
if t.FmtOptions.List != nil {
args = append(args, fmt.Sprintf("-list=%t", *t.FmtOptions.List))
}
if t.FmtOptions.Write != nil {
args = append(args, fmt.Sprintf("-write=%t", *t.FmtOptions.Write))
}
if t.FmtOptions.Diff != nil {
args = append(args, fmt.Sprintf("-diff=%t", *t.FmtOptions.Diff))
}
if t.FmtOptions.Check != nil {
args = append(args, fmt.Sprintf("-check=%t", *t.FmtOptions.Check))
}
return &types.Cmd{
Cmd: execabs.Command(TofuBin, args...),
}
}
func (t *Tofu) Plan(destroy bool) *types.Cmd {
args := []string{
"plan",
}
if destroy {
args = append(args, "-destroy")
} else if t.OutFile != "" {
args = append(args, fmt.Sprintf("-out=%s", t.OutFile))
}
for _, value := range t.Targets.Value() {
args = append(args, "--target", value)
}
if t.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", t.Parallelism))
}
if t.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *t.InitOptions.Lock))
}
if t.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", t.InitOptions.LockTimeout))
}
if !t.Refresh {
args = append(args, "-refresh=false")
}
return &types.Cmd{
Cmd: execabs.Command(TofuBin, args...),
Private: t.NoLog,
}
}
func (t *Tofu) Apply() *types.Cmd {
args := []string{
"apply",
}
for _, v := range t.Targets.Value() {
args = append(args, "--target", v)
}
if t.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", t.Parallelism))
}
if t.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *t.InitOptions.Lock))
}
if t.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", t.InitOptions.LockTimeout))
}
if !t.Refresh {
args = append(args, "-refresh=false")
}
if t.OutFile != "" {
args = append(args, t.OutFile)
}
return &types.Cmd{
Cmd: execabs.Command(TofuBin, args...),
Private: t.NoLog,
}
}
func (t *Tofu) Destroy() *types.Cmd {
args := []string{
"destroy",
}
for _, v := range t.Targets.Value() {
args = append(args, fmt.Sprintf("-target=%s", v))
}
if t.Parallelism > 0 {
args = append(args, fmt.Sprintf("-parallelism=%d", t.Parallelism))
}
if t.InitOptions.Lock != nil {
args = append(args, fmt.Sprintf("-lock=%t", *t.InitOptions.Lock))
}
if t.InitOptions.LockTimeout != "" {
args = append(args, fmt.Sprintf("-lock-timeout=%s", t.InitOptions.LockTimeout))
}
args = append(args, "-auto-approve")
return &types.Cmd{
Cmd: execabs.Command(TofuBin, args...),
Private: t.NoLog,
}
}

545
tofu/tofu_test.go Normal file
View File

@ -0,0 +1,545 @@
package tofu
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)
func boolPtr(b bool) *bool {
return &b
}
func TestTofu_Version(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "test version command",
tofu: &Tofu{},
want: []string{TofuBin, "version"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Version()
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_Init(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "init with no backend config",
tofu: &Tofu{},
want: []string{
TofuBin,
"init",
"-input=false",
},
},
{
name: "init with single backend config",
tofu: &Tofu{
InitOptions: InitOptions{
BackendConfig: []string{"key=value"},
},
},
want: []string{
TofuBin,
"init",
"-backend-config=key=value",
"-input=false",
},
},
{
name: "init with multiple backend configs",
tofu: &Tofu{
InitOptions: InitOptions{
BackendConfig: []string{"key1=value1", "key2=value2"},
},
},
want: []string{
TofuBin,
"init",
"-backend-config=key1=value1",
"-backend-config=key2=value2",
"-input=false",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Init()
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_GetModules(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "get modules command",
tofu: &Tofu{},
want: []string{TofuBin, "get"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.GetModules()
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_Validate(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "validate command",
tofu: &Tofu{},
want: []string{TofuBin, "validate"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Validate()
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_Fmt(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "fmt with no options",
tofu: &Tofu{},
want: []string{
TofuBin,
"fmt",
},
},
{
name: "fmt with list option",
tofu: &Tofu{
FmtOptions: FmtOptions{
List: boolPtr(true),
},
},
want: []string{
TofuBin,
"fmt",
"-list=true",
},
},
{
name: "fmt with write option",
tofu: &Tofu{
FmtOptions: FmtOptions{
Write: boolPtr(true),
},
},
want: []string{
TofuBin,
"fmt",
"-write=true",
},
},
{
name: "fmt with diff option",
tofu: &Tofu{
FmtOptions: FmtOptions{
Diff: boolPtr(true),
},
},
want: []string{
TofuBin,
"fmt",
"-diff=true",
},
},
{
name: "fmt with check option",
tofu: &Tofu{
FmtOptions: FmtOptions{
Check: boolPtr(true),
},
},
want: []string{
TofuBin,
"fmt",
"-check=true",
},
},
{
name: "fmt with multiple options",
tofu: &Tofu{
FmtOptions: FmtOptions{
List: boolPtr(true),
Write: boolPtr(true),
Diff: boolPtr(true),
Check: boolPtr(true),
},
},
want: []string{
TofuBin,
"fmt",
"-list=true",
"-write=true",
"-diff=true",
"-check=true",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Fmt()
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_Plan(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
destroy bool
want []string
}{
{
name: "plan with no options",
tofu: &Tofu{},
destroy: false,
want: []string{
TofuBin,
"plan",
"-refresh=false",
},
},
{
name: "plan with output options",
tofu: &Tofu{
OutFile: "plan.tfout",
},
destroy: false,
want: []string{
TofuBin,
"plan",
"-out=plan.tfout",
"-refresh=false",
},
},
{
name: "plan with destroy option",
tofu: &Tofu{},
destroy: true,
want: []string{
TofuBin,
"plan",
"-destroy",
"-refresh=false",
},
},
{
name: "plan with targets",
tofu: &Tofu{
Targets: *cli.NewStringSlice("target1", "target2"),
},
destroy: false,
want: []string{
TofuBin,
"plan",
"--target", "target1",
"--target", "target2",
"-refresh=false",
},
},
{
name: "plan with parallelism",
tofu: &Tofu{
Parallelism: 10,
},
destroy: false,
want: []string{
TofuBin,
"plan",
"-parallelism=10",
"-refresh=false",
},
},
{
name: "plan with lock option",
tofu: &Tofu{
InitOptions: InitOptions{
Lock: boolPtr(true),
},
},
destroy: false,
want: []string{
TofuBin,
"plan",
"-lock=true",
"-refresh=false",
},
},
{
name: "plan with lock timeout",
tofu: &Tofu{
InitOptions: InitOptions{
LockTimeout: "10s",
},
},
destroy: false,
want: []string{
TofuBin,
"plan",
"-lock-timeout=10s",
"-refresh=false",
},
},
{
name: "plan with refresh option",
tofu: &Tofu{
Refresh: true,
},
destroy: false,
want: []string{
TofuBin,
"plan",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Plan(tt.destroy)
assert.Equal(t, tt.want, cmd.Cmd.Args)
})
}
}
func TestTofu_Apply(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "apply with no options",
tofu: &Tofu{},
want: []string{
TofuBin,
"apply",
"-refresh=false",
},
},
{
name: "apply with targets",
tofu: &Tofu{
Targets: *cli.NewStringSlice("target1", "target2"),
},
want: []string{
TofuBin,
"apply",
"--target", "target1",
"--target", "target2",
"-refresh=false",
},
},
{
name: "apply with parallelism",
tofu: &Tofu{
Parallelism: 10,
},
want: []string{
TofuBin,
"apply",
"-parallelism=10",
"-refresh=false",
},
},
{
name: "apply with lock option",
tofu: &Tofu{
InitOptions: InitOptions{
Lock: boolPtr(true),
},
},
want: []string{
TofuBin,
"apply",
"-lock=true",
"-refresh=false",
},
},
{
name: "apply with lock timeout",
tofu: &Tofu{
InitOptions: InitOptions{
LockTimeout: "10s",
},
},
want: []string{
TofuBin,
"apply",
"-lock-timeout=10s",
"-refresh=false",
},
},
{
name: "apply with refresh option",
tofu: &Tofu{
Refresh: true,
},
want: []string{
TofuBin,
"apply",
},
},
{
name: "apply with output file",
tofu: &Tofu{
OutFile: "out.tfout",
},
want: []string{
TofuBin,
"apply",
"-refresh=false",
"out.tfout",
},
},
{
name: "apply with no log",
tofu: &Tofu{
NoLog: true,
},
want: []string{
TofuBin,
"apply",
"-refresh=false",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Apply()
assert.Equal(t, tt.want, cmd.Cmd.Args)
assert.Equal(t, tt.tofu.NoLog, cmd.Private)
})
}
}
func TestTofu_Destroy(t *testing.T) {
tests := []struct {
name string
tofu *Tofu
want []string
}{
{
name: "destroy with no options",
tofu: &Tofu{},
want: []string{
TofuBin,
"destroy",
"-auto-approve",
},
},
{
name: "destroy with targets",
tofu: &Tofu{
Targets: *cli.NewStringSlice("target1", "target2"),
},
want: []string{
TofuBin,
"destroy",
"-target=target1",
"-target=target2",
"-auto-approve",
},
},
{
name: "destroy with parallelism",
tofu: &Tofu{
Parallelism: 10,
},
want: []string{
TofuBin,
"destroy",
"-parallelism=10",
"-auto-approve",
},
},
{
name: "destroy with lock option",
tofu: &Tofu{
InitOptions: InitOptions{
Lock: boolPtr(true),
},
},
want: []string{
TofuBin,
"destroy",
"-lock=true",
"-auto-approve",
},
},
{
name: "destroy with lock timeout",
tofu: &Tofu{
InitOptions: InitOptions{
LockTimeout: "10s",
},
},
want: []string{
TofuBin,
"destroy",
"-lock-timeout=10s",
"-auto-approve",
},
},
{
name: "destroy with no log",
tofu: &Tofu{
NoLog: true,
},
want: []string{
TofuBin,
"destroy",
"-auto-approve",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cmd := tt.tofu.Destroy()
assert.Equal(t, tt.want, cmd.Cmd.Args)
assert.Equal(t, tt.tofu.NoLog, cmd.Private)
})
}
}