Skip to content

Commit

Permalink
feat: run pipeform when running terraform plan
Browse files Browse the repository at this point in the history
  • Loading branch information
suzuki-shunsuke committed Jan 29, 2025
1 parent 255a05a commit 1e9936a
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 11 deletions.
72 changes: 72 additions & 0 deletions internal/exec/shell_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,78 @@ func getNextShellLevel() (int, error) {
return shellVal, nil
}

// ExecuteShellCommandWithPipe prints and executes the provided command with args and flags
func ExecuteShellCommandWithPipe(
atmosConfig schema.AtmosConfiguration,
command string,
args []string,
dir string,
env []string,
dryRun bool,
redirectStdError string,
stdin io.Reader,
stdout io.Writer,
) (func() error, error) {
newShellLevel, err := getNextShellLevel()
if err != nil {
return nil, err
}
updatedEnv := append(env, fmt.Sprintf("ATMOS_SHLVL=%d", newShellLevel))

cmd := exec.Command(command, args...)
cmd.Env = append(os.Environ(), updatedEnv...)
cmd.Dir = dir
if stdin != nil {
cmd.Stdin = stdin
} else {
cmd.Stdin = os.Stdin
}
if stdout != nil {
cmd.Stdout = stdout
} else {
cmd.Stdout = os.Stdout
}

if runtime.GOOS == "windows" && redirectStdError == "/dev/null" {
redirectStdError = "NUL"
}

if redirectStdError == "/dev/stderr" {
cmd.Stderr = os.Stderr
} else if redirectStdError == "/dev/stdout" {
cmd.Stderr = os.Stdout
} else if redirectStdError == "" {
cmd.Stderr = os.Stderr
} else {
f, err := os.OpenFile(redirectStdError, os.O_WRONLY|os.O_CREATE, 0o644)
if err != nil {
u.LogWarning(atmosConfig, err.Error())
return nil, err
}

defer func(f *os.File) {
err = f.Close()
if err != nil {
u.LogWarning(atmosConfig, err.Error())
}
}(f)

cmd.Stderr = f
}

u.LogDebug(atmosConfig, "\nExecuting command:")
u.LogDebug(atmosConfig, cmd.String())

if dryRun {
return nil, nil
}

if err := cmd.Start(); err != nil {
return nil, err
}
return cmd.Wait, nil
}

// ExecuteShellCommand prints and executes the provided command with args and flags
func ExecuteShellCommand(
atmosConfig schema.AtmosConfiguration,
Expand Down
94 changes: 83 additions & 11 deletions internal/exec/terraform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package exec

import (
"fmt"
"io"
"os"
osexec "os/exec"
"path/filepath"
"strings"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"

cfg "github.com/cloudposse/atmos/pkg/config"
"github.com/cloudposse/atmos/pkg/schema"
Expand Down Expand Up @@ -524,17 +526,87 @@ func ExecuteTerraform(info schema.ConfigAndStacksInfo) error {

// Execute the provided command (except for `terraform workspace` which was executed above)
if !(info.SubCommand == "workspace" && info.SubCommand2 == "") {
err = ExecuteShellCommand(
atmosConfig,
info.Command,
allArgsAndFlags,
componentPath,
info.ComponentEnvList,
info.DryRun,
info.RedirectStdErr,
)
if err != nil {
return err
if info.SubCommand == "plan" {
allArgsAndFlags = append(allArgsAndFlags, "-json")
pipeformStdin, planStdout := io.Pipe()
// Get a plan file
p := ""
for i, a := range allArgsAndFlags {
if a == "-out" || a == "--out" {
p = allArgsAndFlags[i+1]
break
}
}
if p == "" {
p = planFile
allArgsAndFlags = append(allArgsAndFlags, "-out", p)
}

// terraform plan
waitPlan, err := ExecuteShellCommandWithPipe(
atmosConfig,
info.Command,
allArgsAndFlags,
componentPath,
info.ComponentEnvList,
info.DryRun,
info.RedirectStdErr,
nil,
planStdout,
)
if err != nil {
return err
}
// pipeform
waitPipeform, err := ExecuteShellCommandWithPipe(
atmosConfig,
"pipeform",
nil,
componentPath,
info.ComponentEnvList,
info.DryRun,
info.RedirectStdErr,
pipeformStdin,
nil,
)
if err != nil {
return err
}
g := &errgroup.Group{}
g.Go(func() error {
err := waitPlan()
planStdout.Close()
return err
})
g.Go(waitPipeform)
if err := g.Wait(); err != nil {
return err
}
// terraform show
if err := ExecuteShellCommand(
atmosConfig,
info.Command,
[]string{"show", p},
componentPath,
info.ComponentEnvList,
info.DryRun,
info.RedirectStdErr,
); err != nil {
return err
}
} else {
err = ExecuteShellCommand(
atmosConfig,
info.Command,
allArgsAndFlags,
componentPath,
info.ComponentEnvList,
info.DryRun,
info.RedirectStdErr,
)
if err != nil {
return err
}
}
}

Expand Down

0 comments on commit 1e9936a

Please sign in to comment.