Skip to content

Commit dc8d2a2

Browse files
committed
fix terraform flags, add tests
1 parent b067c15 commit dc8d2a2

14 files changed

+808
-349
lines changed

cmd/terraform/flags.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,41 @@ func registerExecutionFlags(registry *flags.FlagRegistry) {
6262
Description: "Customize User-Agent string in Terraform provider requests (sets TF_APPEND_USER_AGENT)",
6363
EnvVars: []string{"ATMOS_APPEND_USER_AGENT"},
6464
})
65+
registry.Register(&flags.StringFlag{
66+
Name: "auto-generate-backend-file",
67+
Shorthand: "",
68+
Default: "",
69+
Description: "Override auto_generate_backend_file setting from atmos.yaml (true/false)",
70+
EnvVars: []string{"ATMOS_AUTO_GENERATE_BACKEND_FILE"},
71+
})
72+
registry.Register(&flags.StringFlag{
73+
Name: "deploy-run-init",
74+
Shorthand: "",
75+
Default: "",
76+
Description: "Override deploy_run_init setting from atmos.yaml (true/false)",
77+
EnvVars: []string{"ATMOS_DEPLOY_RUN_INIT"},
78+
})
79+
registry.Register(&flags.StringFlag{
80+
Name: "init-run-reconfigure",
81+
Shorthand: "",
82+
Default: "",
83+
Description: "Override init_run_reconfigure setting from atmos.yaml (true/false)",
84+
EnvVars: []string{"ATMOS_INIT_RUN_RECONFIGURE"},
85+
})
86+
registry.Register(&flags.StringFlag{
87+
Name: "planfile",
88+
Shorthand: "",
89+
Default: "",
90+
Description: "Path to a terraform plan file to use instead of generating a new plan",
91+
EnvVars: []string{"ATMOS_PLANFILE"},
92+
})
93+
registry.Register(&flags.StringFlag{
94+
Name: "skip-planfile",
95+
Shorthand: "",
96+
Default: "",
97+
Description: "Skip writing the plan to a planfile (true/false)",
98+
EnvVars: []string{"ATMOS_SKIP_PLANFILE"},
99+
})
65100
}
66101

67102
// registerProcessingFlags adds flags for template and function processing.

cmd/terraform/flags_test.go

Lines changed: 297 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package terraform
33
import (
44
"testing"
55

6+
"github.com/spf13/cobra"
7+
"github.com/spf13/viper"
68
"github.com/stretchr/testify/assert"
79
"github.com/stretchr/testify/require"
810

@@ -14,7 +16,7 @@ func TestTerraformFlags(t *testing.T) {
1416

1517
// Should have common flags (stack, dry-run) + Terraform-specific flags including identity.
1618
// Note: from-plan is defined in apply.go and deploy.go with NoOptDefVal, not here.
17-
assert.GreaterOrEqual(t, registry.Count(), 12)
19+
assert.GreaterOrEqual(t, registry.Count(), 17)
1820

1921
// Should include common flags.
2022
assert.True(t, registry.Has("stack"))
@@ -35,6 +37,14 @@ func TestTerraformFlags(t *testing.T) {
3537
assert.True(t, registry.Has("query"))
3638
assert.True(t, registry.Has("components"))
3739

40+
// Should include execution flags for controlling terraform behavior.
41+
// These flags override atmos.yaml settings and are used by various terraform subcommands.
42+
assert.True(t, registry.Has("auto-generate-backend-file"), "auto-generate-backend-file should be in TerraformFlags")
43+
assert.True(t, registry.Has("deploy-run-init"), "deploy-run-init should be in TerraformFlags")
44+
assert.True(t, registry.Has("init-run-reconfigure"), "init-run-reconfigure should be in TerraformFlags")
45+
assert.True(t, registry.Has("planfile"), "planfile should be in TerraformFlags")
46+
assert.True(t, registry.Has("skip-planfile"), "skip-planfile should be in TerraformFlags")
47+
3848
// Check upload-status flag.
3949
uploadFlag := registry.Get("upload-status")
4050
require.NotNil(t, uploadFlag)
@@ -76,10 +86,15 @@ func TestWithTerraformFlags(t *testing.T) {
7686
registry := parser.Registry()
7787

7888
// Should have all terraform flags.
79-
assert.GreaterOrEqual(t, registry.Count(), 12)
89+
assert.GreaterOrEqual(t, registry.Count(), 17)
8090
assert.True(t, registry.Has("stack"))
8191
assert.True(t, registry.Has("upload-status"))
8292
assert.True(t, registry.Has("identity"))
93+
94+
// Verify execution flags are present.
95+
assert.True(t, registry.Has("auto-generate-backend-file"))
96+
assert.True(t, registry.Has("deploy-run-init"))
97+
assert.True(t, registry.Has("init-run-reconfigure"))
8398
}
8499

85100
func TestWithTerraformAffectedFlags(t *testing.T) {
@@ -107,7 +122,7 @@ func TestCombinedTerraformFlags(t *testing.T) {
107122
registry := parser.Registry()
108123

109124
// Should have all flags from both registries.
110-
assert.GreaterOrEqual(t, registry.Count(), 19)
125+
assert.GreaterOrEqual(t, registry.Count(), 24)
111126

112127
// Should include terraform flags.
113128
assert.True(t, registry.Has("stack"))
@@ -118,4 +133,283 @@ func TestCombinedTerraformFlags(t *testing.T) {
118133
assert.True(t, registry.Has("repo-path"))
119134
assert.True(t, registry.Has("ref"))
120135
assert.True(t, registry.Has("include-dependents"))
136+
137+
// Should include execution flags for controlling terraform behavior.
138+
assert.True(t, registry.Has("auto-generate-backend-file"))
139+
assert.True(t, registry.Has("deploy-run-init"))
140+
assert.True(t, registry.Has("init-run-reconfigure"))
141+
assert.True(t, registry.Has("planfile"))
142+
assert.True(t, registry.Has("skip-planfile"))
143+
}
144+
145+
// TestExecutionFlagsProperties verifies that all execution flags have correct properties.
146+
// These flags control terraform execution behavior and override atmos.yaml settings.
147+
func TestExecutionFlagsProperties(t *testing.T) {
148+
registry := TerraformFlags()
149+
150+
// Test skip-init flag (bool flag).
151+
t.Run("skip-init", func(t *testing.T) {
152+
flag := registry.Get("skip-init")
153+
require.NotNil(t, flag, "skip-init flag should be registered")
154+
boolFlag, ok := flag.(*flags.BoolFlag)
155+
require.True(t, ok, "skip-init should be a BoolFlag")
156+
assert.Equal(t, false, boolFlag.Default, "skip-init default should be false")
157+
assert.Equal(t, []string{"ATMOS_SKIP_INIT"}, boolFlag.EnvVars, "skip-init should have ATMOS_SKIP_INIT env var")
158+
})
159+
160+
// Test auto-generate-backend-file flag (string flag for true/false override).
161+
t.Run("auto-generate-backend-file", func(t *testing.T) {
162+
flag := registry.Get("auto-generate-backend-file")
163+
require.NotNil(t, flag, "auto-generate-backend-file flag should be registered")
164+
strFlag, ok := flag.(*flags.StringFlag)
165+
require.True(t, ok, "auto-generate-backend-file should be a StringFlag")
166+
assert.Equal(t, "", strFlag.Default, "auto-generate-backend-file default should be empty")
167+
assert.Equal(t, []string{"ATMOS_AUTO_GENERATE_BACKEND_FILE"}, strFlag.EnvVars)
168+
})
169+
170+
// Test deploy-run-init flag (string flag for true/false override).
171+
t.Run("deploy-run-init", func(t *testing.T) {
172+
flag := registry.Get("deploy-run-init")
173+
require.NotNil(t, flag, "deploy-run-init flag should be registered")
174+
strFlag, ok := flag.(*flags.StringFlag)
175+
require.True(t, ok, "deploy-run-init should be a StringFlag")
176+
assert.Equal(t, "", strFlag.Default, "deploy-run-init default should be empty")
177+
assert.Equal(t, []string{"ATMOS_DEPLOY_RUN_INIT"}, strFlag.EnvVars)
178+
})
179+
180+
// Test init-run-reconfigure flag (string flag for true/false override).
181+
t.Run("init-run-reconfigure", func(t *testing.T) {
182+
flag := registry.Get("init-run-reconfigure")
183+
require.NotNil(t, flag, "init-run-reconfigure flag should be registered")
184+
strFlag, ok := flag.(*flags.StringFlag)
185+
require.True(t, ok, "init-run-reconfigure should be a StringFlag")
186+
assert.Equal(t, "", strFlag.Default, "init-run-reconfigure default should be empty")
187+
assert.Equal(t, []string{"ATMOS_INIT_RUN_RECONFIGURE"}, strFlag.EnvVars)
188+
})
189+
190+
// Test init-pass-vars flag (bool flag).
191+
t.Run("init-pass-vars", func(t *testing.T) {
192+
flag := registry.Get("init-pass-vars")
193+
require.NotNil(t, flag, "init-pass-vars flag should be registered")
194+
boolFlag, ok := flag.(*flags.BoolFlag)
195+
require.True(t, ok, "init-pass-vars should be a BoolFlag")
196+
assert.Equal(t, false, boolFlag.Default, "init-pass-vars default should be false")
197+
assert.Equal(t, []string{"ATMOS_INIT_PASS_VARS"}, boolFlag.EnvVars)
198+
})
199+
200+
// Test planfile flag (string flag for path).
201+
t.Run("planfile", func(t *testing.T) {
202+
flag := registry.Get("planfile")
203+
require.NotNil(t, flag, "planfile flag should be registered")
204+
strFlag, ok := flag.(*flags.StringFlag)
205+
require.True(t, ok, "planfile should be a StringFlag")
206+
assert.Equal(t, "", strFlag.Default, "planfile default should be empty")
207+
assert.Equal(t, []string{"ATMOS_PLANFILE"}, strFlag.EnvVars)
208+
})
209+
210+
// Test skip-planfile flag (string flag for true/false override).
211+
t.Run("skip-planfile", func(t *testing.T) {
212+
flag := registry.Get("skip-planfile")
213+
require.NotNil(t, flag, "skip-planfile flag should be registered")
214+
strFlag, ok := flag.(*flags.StringFlag)
215+
require.True(t, ok, "skip-planfile should be a StringFlag")
216+
assert.Equal(t, "", strFlag.Default, "skip-planfile default should be empty")
217+
assert.Equal(t, []string{"ATMOS_SKIP_PLANFILE"}, strFlag.EnvVars)
218+
})
219+
220+
// Test append-user-agent flag (string flag).
221+
t.Run("append-user-agent", func(t *testing.T) {
222+
flag := registry.Get("append-user-agent")
223+
require.NotNil(t, flag, "append-user-agent flag should be registered")
224+
strFlag, ok := flag.(*flags.StringFlag)
225+
require.True(t, ok, "append-user-agent should be a StringFlag")
226+
assert.Equal(t, "", strFlag.Default, "append-user-agent default should be empty")
227+
assert.Equal(t, []string{"ATMOS_APPEND_USER_AGENT"}, strFlag.EnvVars)
228+
})
229+
230+
// Test upload-status flag (bool flag).
231+
t.Run("upload-status", func(t *testing.T) {
232+
flag := registry.Get("upload-status")
233+
require.NotNil(t, flag, "upload-status flag should be registered")
234+
boolFlag, ok := flag.(*flags.BoolFlag)
235+
require.True(t, ok, "upload-status should be a BoolFlag")
236+
assert.Equal(t, false, boolFlag.Default, "upload-status default should be false")
237+
assert.Equal(t, []string{"ATMOS_UPLOAD_STATUS"}, boolFlag.EnvVars)
238+
})
239+
}
240+
241+
// TestFlagsCobraRegistration verifies that flags are properly registered on Cobra commands.
242+
// This test ensures the full pipeline from flag definition to CLI availability works.
243+
func TestFlagsCobraRegistration(t *testing.T) {
244+
t.Run("execution flags are visible on cobra command", func(t *testing.T) {
245+
cmd := &cobra.Command{Use: "test"}
246+
parser := flags.NewStandardParser(WithTerraformFlags())
247+
parser.RegisterFlags(cmd)
248+
249+
// Verify all execution flags are registered and visible.
250+
executionFlags := []string{
251+
"skip-init",
252+
"auto-generate-backend-file",
253+
"deploy-run-init",
254+
"init-run-reconfigure",
255+
"planfile",
256+
"skip-planfile",
257+
"upload-status",
258+
"init-pass-vars",
259+
"append-user-agent",
260+
}
261+
262+
for _, flagName := range executionFlags {
263+
flag := cmd.Flags().Lookup(flagName)
264+
assert.NotNil(t, flag, "%s flag should be registered on cobra command", flagName)
265+
}
266+
})
267+
268+
t.Run("affected flags are visible on cobra command", func(t *testing.T) {
269+
cmd := &cobra.Command{Use: "test"}
270+
parser := flags.NewStandardParser(WithTerraformAffectedFlags())
271+
parser.RegisterFlags(cmd)
272+
273+
// Verify all affected flags are registered.
274+
affectedFlags := []string{
275+
"repo-path",
276+
"ref",
277+
"sha",
278+
"ssh-key",
279+
"ssh-key-password",
280+
"include-dependents",
281+
"clone-target-ref",
282+
}
283+
284+
for _, flagName := range affectedFlags {
285+
flag := cmd.Flags().Lookup(flagName)
286+
assert.NotNil(t, flag, "%s flag should be registered on cobra command", flagName)
287+
}
288+
})
289+
}
290+
291+
// TestFlagsViperBinding verifies that flags are properly bound to Viper for value retrieval.
292+
func TestFlagsViperBinding(t *testing.T) {
293+
t.Run("execution flags bind to viper", func(t *testing.T) {
294+
cmd := &cobra.Command{Use: "test"}
295+
v := viper.New()
296+
parser := flags.NewStandardParser(WithTerraformFlags())
297+
parser.RegisterFlags(cmd)
298+
err := parser.BindToViper(v)
299+
require.NoError(t, err)
300+
301+
// Set values via viper and verify they can be retrieved.
302+
v.Set("skip-init", true)
303+
v.Set("auto-generate-backend-file", "false")
304+
v.Set("planfile", "/path/to/plan.tfplan")
305+
306+
assert.True(t, v.GetBool("skip-init"))
307+
assert.Equal(t, "false", v.GetString("auto-generate-backend-file"))
308+
assert.Equal(t, "/path/to/plan.tfplan", v.GetString("planfile"))
309+
})
310+
311+
t.Run("affected flags bind to viper", func(t *testing.T) {
312+
cmd := &cobra.Command{Use: "test"}
313+
v := viper.New()
314+
parser := flags.NewStandardParser(WithTerraformAffectedFlags())
315+
parser.RegisterFlags(cmd)
316+
err := parser.BindToViper(v)
317+
require.NoError(t, err)
318+
319+
// Set values via viper and verify they can be retrieved.
320+
v.Set("repo-path", "/path/to/repo")
321+
v.Set("ref", "main")
322+
v.Set("include-dependents", true)
323+
324+
assert.Equal(t, "/path/to/repo", v.GetString("repo-path"))
325+
assert.Equal(t, "main", v.GetString("ref"))
326+
assert.True(t, v.GetBool("include-dependents"))
327+
})
328+
}
329+
330+
// TestFlagsEnvironmentVariables verifies that environment variables are properly configured.
331+
func TestFlagsEnvironmentVariables(t *testing.T) {
332+
t.Run("execution flags have correct env var bindings", func(t *testing.T) {
333+
registry := TerraformFlags()
334+
335+
envVarTests := []struct {
336+
flagName string
337+
envVar string
338+
}{
339+
{"skip-init", "ATMOS_SKIP_INIT"},
340+
{"auto-generate-backend-file", "ATMOS_AUTO_GENERATE_BACKEND_FILE"},
341+
{"deploy-run-init", "ATMOS_DEPLOY_RUN_INIT"},
342+
{"init-run-reconfigure", "ATMOS_INIT_RUN_RECONFIGURE"},
343+
{"planfile", "ATMOS_PLANFILE"},
344+
{"skip-planfile", "ATMOS_SKIP_PLANFILE"},
345+
{"upload-status", "ATMOS_UPLOAD_STATUS"},
346+
{"init-pass-vars", "ATMOS_INIT_PASS_VARS"},
347+
{"append-user-agent", "ATMOS_APPEND_USER_AGENT"},
348+
}
349+
350+
for _, tc := range envVarTests {
351+
flag := registry.Get(tc.flagName)
352+
require.NotNil(t, flag, "%s flag should exist", tc.flagName)
353+
354+
var envVars []string
355+
switch f := flag.(type) {
356+
case *flags.BoolFlag:
357+
envVars = f.EnvVars
358+
case *flags.StringFlag:
359+
envVars = f.EnvVars
360+
}
361+
362+
assert.Contains(t, envVars, tc.envVar,
363+
"%s flag should have %s env var", tc.flagName, tc.envVar)
364+
}
365+
})
366+
367+
t.Run("affected flags have correct env var bindings", func(t *testing.T) {
368+
registry := TerraformAffectedFlags()
369+
370+
envVarTests := []struct {
371+
flagName string
372+
envVar string
373+
}{
374+
{"repo-path", "ATMOS_REPO_PATH"},
375+
{"ref", "ATMOS_REF"},
376+
{"sha", "ATMOS_SHA"},
377+
{"ssh-key", "ATMOS_SSH_KEY"},
378+
{"ssh-key-password", "ATMOS_SSH_KEY_PASSWORD"},
379+
{"include-dependents", "ATMOS_INCLUDE_DEPENDENTS"},
380+
{"clone-target-ref", "ATMOS_CLONE_TARGET_REF"},
381+
}
382+
383+
for _, tc := range envVarTests {
384+
flag := registry.Get(tc.flagName)
385+
require.NotNil(t, flag, "%s flag should exist", tc.flagName)
386+
387+
var envVars []string
388+
switch f := flag.(type) {
389+
case *flags.BoolFlag:
390+
envVars = f.EnvVars
391+
case *flags.StringFlag:
392+
envVars = f.EnvVars
393+
}
394+
395+
assert.Contains(t, envVars, tc.envVar,
396+
"%s flag should have %s env var", tc.flagName, tc.envVar)
397+
}
398+
})
399+
}
400+
401+
// TestIdentityFlagConfiguration verifies the identity flag has correct NoOptDefVal for interactive selection.
402+
func TestIdentityFlagConfiguration(t *testing.T) {
403+
registry := TerraformFlags()
404+
flag := registry.Get("identity")
405+
require.NotNil(t, flag)
406+
407+
strFlag, ok := flag.(*flags.StringFlag)
408+
require.True(t, ok)
409+
410+
assert.Equal(t, "", strFlag.Default, "identity default should be empty")
411+
assert.Equal(t, "__SELECT__", strFlag.NoOptDefVal, "identity NoOptDefVal should be __SELECT__ for interactive selection")
412+
assert.Equal(t, "i", strFlag.Shorthand, "identity should have -i shorthand")
413+
assert.Contains(t, strFlag.EnvVars, "ATMOS_IDENTITY")
414+
assert.Contains(t, strFlag.EnvVars, "IDENTITY")
121415
}

0 commit comments

Comments
 (0)