diff --git a/bt/terraform/apply.go b/bt/terraform/apply.go index b461d32..aafac4b 100644 --- a/bt/terraform/apply.go +++ b/bt/terraform/apply.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "path/filepath" "github.com/DavidGamba/dgtools/bt/config" "github.com/DavidGamba/dgtools/fsmodtime" @@ -23,6 +24,7 @@ func applyRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error profile := opt.Value("profile").(string) cfg := config.ConfigFromContext(ctx) + dir := DirFromContext(ctx) LogConfig(cfg, profile) ws, err := updateWSIfSelected(cfg.Config.DefaultTerraformProfile, cfg.Profile(profile), ws) @@ -42,12 +44,12 @@ func applyRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error planFile := "" if ws == "" { planFile = ".tf.plan" - applyFile = ".tf.apply" + applyFile = filepath.Join(dir, ".tf.apply") } else { planFile = fmt.Sprintf(".tf.plan-%s", ws) - applyFile = fmt.Sprintf(".tf.apply-%s", ws) + applyFile = filepath.Join(dir, fmt.Sprintf(".tf.apply-%s", ws)) } - files, modified, err := fsmodtime.Target(os.DirFS("."), []string{applyFile}, []string{planFile}) + files, modified, err := fsmodtime.Target(os.DirFS(dir), []string{applyFile}, []string{planFile}) if err != nil { Logger.Printf("failed to check changes for: '%s'\n", applyFile) } @@ -65,7 +67,7 @@ func applyRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error cmd = append(cmd, args...) dataDir := fmt.Sprintf("TF_DATA_DIR=%s", getDataDir(cfg.Config.DefaultTerraformProfile, cfg.Profile(profile))) Logger.Printf("export %s\n", dataDir) - ri := run.CMDCtx(ctx, cmd...).Stdin().Log().Env(dataDir) + ri := run.CMDCtx(ctx, cmd...).Stdin().Log().Env(dataDir).Dir(dir) if ws != "" { wsEnv := fmt.Sprintf("TF_WORKSPACE=%s", ws) Logger.Printf("export %s\n", wsEnv) @@ -82,5 +84,7 @@ func applyRun(ctx context.Context, opt *getoptions.GetOpt, args []string) error return fmt.Errorf("failed to create file: %w", err) } fh.Close() + Logger.Printf("Create %s\n", applyFile) + return nil } diff --git a/bt/terraform/apply_test.go b/bt/terraform/apply_test.go new file mode 100644 index 0000000..3b333e5 --- /dev/null +++ b/bt/terraform/apply_test.go @@ -0,0 +1,167 @@ +package terraform + +import ( + "context" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/DavidGamba/dgtools/bt/config" + "github.com/DavidGamba/dgtools/run" + "github.com/DavidGamba/go-getoptions" +) + +func TestApply(t *testing.T) { + t.Setenv("HOME", "/home/user") + + t.Run("TestApply without config", func(t *testing.T) { + buf := setupLogging() + ctx := context.Background() + cfg, _, _ := config.Get(ctx, "x") + ctx = config.NewConfigContext(ctx, cfg) + tDir := t.TempDir() + ctx = NewDirContext(ctx, tDir) + mock := run.CMDCtx(ctx).Mock(func(r *run.RunInfo) error { + if r.GetDir() != tDir { + return fmt.Errorf("unexpected dir: %s", r.GetDir()) + } + if !slices.Equal(r.Cmd, []string{"terraform", "apply", "-input", ".tf.plan-dev", "-no-color"}) { + return fmt.Errorf("unexpected cmd: %v", r.Cmd) + } + for _, e := range r.GetEnv() { + if strings.Contains(e, "TF_DATA_DIR") { + if e != "TF_DATA_DIR=.terraform" { + return fmt.Errorf("unexpected env: %v", e) + } + } + } + return nil + }) + ctx = run.ContextWithRunInfo(ctx, mock) + opt := getoptions.New() + opt.String("profile", "default") + opt.String("ws", "dev") + err := applyRun(ctx, opt, []string{}) + if err != nil { + t.Errorf("TestApply error: %s", err) + } + if _, err := os.Stat(filepath.Join(tDir, ".tf.apply-dev")); os.IsNotExist(err) { + t.Errorf("no .tf.apply file: %s", err) + } + t.Log(buf.String()) + }) + + t.Run("TestApply with default config but no valid profile selected", func(t *testing.T) { + _ = os.Remove(".tf.apply") + buf := setupLogging() + ctx := context.Background() + cfg := getDefaultConfig() + ctx = config.NewConfigContext(ctx, cfg) + mock := run.CMDCtx(ctx).Mock(func(r *run.RunInfo) error { + if r.GetDir() != "." { + return fmt.Errorf("unexpected dir: %s", r.GetDir()) + } + if !slices.Equal(r.Cmd, []string{"tofu", "apply", "-input", ".tf.plan-dev", "-no-color"}) { + return fmt.Errorf("unexpected cmd: %v", r.Cmd) + } + for _, e := range r.GetEnv() { + if strings.Contains(e, "TF_DATA_DIR") { + if e != "TF_DATA_DIR=.terraform" { + return fmt.Errorf("unexpected env: %v", e) + } + } + } + return nil + }) + ctx = run.ContextWithRunInfo(ctx, mock) + opt := getoptions.New() + opt.String("profile", "default") + opt.String("ws", "dev") + err := applyRun(ctx, opt, []string{}) + if err != nil { + t.Errorf("TestApply error: %s", err) + } + if _, err := os.Stat(".tf.apply-dev"); os.IsNotExist(err) { + t.Errorf("no .tf.apply file: %s", err) + } + _ = os.Remove(".tf.apply") + t.Log(buf.String()) + }) + + t.Run("TestApply with default config and dev profile selected", func(t *testing.T) { + buf := setupLogging() + ctx := context.Background() + cfg := getDefaultConfig() + ctx = config.NewConfigContext(ctx, cfg) + tDir := t.TempDir() + ctx = NewDirContext(ctx, tDir) + mock := run.CMDCtx(ctx).Mock(func(r *run.RunInfo) error { + if r.GetDir() != tDir { + return fmt.Errorf("unexpected dir: %s", r.GetDir()) + } + if !slices.Equal(r.Cmd, []string{"tofu", "apply", "-input", ".tf.plan-dev", "-no-color"}) { + return fmt.Errorf("unexpected cmd: %v", r.Cmd) + } + for _, e := range r.GetEnv() { + if strings.Contains(e, "TF_DATA_DIR") { + if e != "TF_DATA_DIR=.terraform" { + return fmt.Errorf("unexpected env: %v", e) + } + } + } + return nil + }) + ctx = run.ContextWithRunInfo(ctx, mock) + opt := getoptions.New() + opt.String("profile", "dev") + opt.String("ws", "dev") + err := applyRun(ctx, opt, []string{}) + if err != nil { + t.Errorf("TestApply error: %s", err) + } + if _, err := os.Stat(filepath.Join(tDir, ".tf.apply-dev")); os.IsNotExist(err) { + t.Errorf("no .tf.apply file: %s", err) + } + t.Log(buf.String()) + }) + + t.Run("TestApply with default config and prod profile selected", func(t *testing.T) { + buf := setupLogging() + ctx := context.Background() + cfg := getDefaultConfig() + ctx = config.NewConfigContext(ctx, cfg) + tDir := t.TempDir() + ctx = NewDirContext(ctx, tDir) + mock := run.CMDCtx(ctx).Mock(func(r *run.RunInfo) error { + if r.GetDir() != tDir { + return fmt.Errorf("unexpected dir: %s", r.GetDir()) + } + if !slices.Equal(r.Cmd, []string{"terraform", "apply", "-input", ".tf.plan-prod", "-no-color"}) { + return fmt.Errorf("unexpected cmd: %v", r.Cmd) + } + for _, e := range r.GetEnv() { + if strings.Contains(e, "TF_DATA_DIR") { + if e != "TF_DATA_DIR=.terraform-prod" { + return fmt.Errorf("unexpected env: %v", e) + } + } + } + return nil + }) + ctx = run.ContextWithRunInfo(ctx, mock) + opt := getoptions.New() + opt.String("profile", "prod") + opt.String("ws", "prod") + err := applyRun(ctx, opt, []string{}) + if err != nil { + t.Errorf("TestApply error: %s", err) + } + if _, err := os.Stat(filepath.Join(tDir, ".tf.apply-prod")); os.IsNotExist(err) { + t.Errorf("no .tf.apply file: %s", err) + } + t.Log(buf.String()) + }) +}