Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 111 additions & 22 deletions cmd/terraform/backend/backend_commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestExecuteDeleteCommandWithValues(t *testing.T) {
force: true,
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().
DeleteBackend(gomock.Any()).
Expand All @@ -98,7 +98,7 @@ func TestExecuteDeleteCommandWithValues(t *testing.T) {
force: false,
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().
DeleteBackend(gomock.Any()).
Expand All @@ -124,7 +124,7 @@ func TestExecuteDeleteCommandWithValues(t *testing.T) {
force: true,
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(nil, nil, errors.New("config init failed"))
},
expectError: true,
Expand All @@ -137,7 +137,7 @@ func TestExecuteDeleteCommandWithValues(t *testing.T) {
force: true,
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().
DeleteBackend(gomock.Any()).
Expand All @@ -152,7 +152,7 @@ func TestExecuteDeleteCommandWithValues(t *testing.T) {
mockConfigInit, mockProv := setupTestWithMocks(t)
tt.setupMocks(mockConfigInit, mockProv)

err := executeDeleteCommandWithValues(tt.component, tt.stack, tt.identity, tt.force)
err := executeDeleteCommandWithValues(tt.component, tt.stack, tt.identity, tt.force, promptedFlags{})

if tt.expectError {
assert.Error(t, err)
Expand Down Expand Up @@ -187,7 +187,7 @@ func TestExecuteDescribeCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
DescribeBackend(atmosConfig, "vpc", map[string]string{"format": "yaml"}).
Expand All @@ -204,7 +204,7 @@ func TestExecuteDescribeCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
DescribeBackend(atmosConfig, "vpc", map[string]string{"format": "json"}).
Expand All @@ -230,7 +230,7 @@ func TestExecuteDescribeCommandWithValues(t *testing.T) {
format: "yaml",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(nil, nil, errors.New("config init failed"))
},
expectError: true,
Expand All @@ -244,7 +244,7 @@ func TestExecuteDescribeCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
DescribeBackend(atmosConfig, "vpc", map[string]string{"format": "yaml"}).
Expand All @@ -259,7 +259,7 @@ func TestExecuteDescribeCommandWithValues(t *testing.T) {
mockConfigInit, mockProv := setupTestWithMocks(t)
tt.setupMocks(mockConfigInit, mockProv)

err := executeDescribeCommandWithValues(tt.component, tt.stack, tt.identity, tt.format)
err := executeDescribeCommandWithValues(tt.component, tt.stack, tt.identity, tt.format, promptedFlags{})

if tt.expectError {
assert.Error(t, err)
Expand Down Expand Up @@ -292,7 +292,7 @@ func TestExecuteListCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("", "dev", "").
InitConfigAndAuth("", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
ListBackends(atmosConfig, map[string]string{"format": "table"}).
Expand All @@ -308,7 +308,7 @@ func TestExecuteListCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("", "dev", "").
InitConfigAndAuth("", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
ListBackends(atmosConfig, map[string]string{"format": "json"}).
Expand All @@ -332,7 +332,7 @@ func TestExecuteListCommandWithValues(t *testing.T) {
format: "table",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("", "dev", "").
InitConfigAndAuth("", "dev", "", false, false).
Return(nil, nil, errors.New("config init failed"))
},
expectError: true,
Expand All @@ -345,7 +345,7 @@ func TestExecuteListCommandWithValues(t *testing.T) {
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("", "dev", "").
InitConfigAndAuth("", "dev", "", false, false).
Return(atmosConfig, nil, nil)
mp.EXPECT().
ListBackends(atmosConfig, map[string]string{"format": "table"}).
Expand All @@ -360,7 +360,7 @@ func TestExecuteListCommandWithValues(t *testing.T) {
mockConfigInit, mockProv := setupTestWithMocks(t)
tt.setupMocks(mockConfigInit, mockProv)

err := executeListCommandWithValues(tt.stack, tt.identity, tt.format)
err := executeListCommandWithValues(tt.stack, tt.identity, tt.format, false)

if tt.expectError {
assert.Error(t, err)
Expand Down Expand Up @@ -456,7 +456,7 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
identity: "",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().
CreateBackend(gomock.Any()).
Expand Down Expand Up @@ -485,7 +485,7 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
identity: "",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(nil, nil, errors.New("config init failed"))
},
expectError: true,
Expand All @@ -497,7 +497,7 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
identity: "",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "").
InitConfigAndAuth("vpc", "dev", "", false, false).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().
CreateBackend(gomock.Any()).
Expand All @@ -512,7 +512,7 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
identity: "aws-prod",
setupMocks: func(mci *MockConfigInitializer, mp *MockProvisioner) {
mci.EXPECT().
InitConfigAndAuth("vpc", "prod", "aws-prod").
InitConfigAndAuth("vpc", "prod", "aws-prod", false, false).
Return(&schema.AtmosConfiguration{}, &schema.AuthContext{AWS: &schema.AWSAuthContext{}}, nil)
mp.EXPECT().
CreateBackend(gomock.Any()).
Expand All @@ -532,7 +532,7 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
mockConfigInit, mockProv := setupTestWithMocks(t)
tt.setupMocks(mockConfigInit, mockProv)

err := executeProvisionCommandWithValues(tt.component, tt.stack, tt.identity)
err := executeProvisionCommandWithValues(tt.component, tt.stack, tt.identity, promptedFlags{})

if tt.expectError {
assert.Error(t, err)
Expand All @@ -546,6 +546,95 @@ func TestExecuteProvisionCommandWithValues(t *testing.T) {
}
}

// TestExecuteCommandWithValues_ThreadsPromptedFlagsToConfigInitializer is the regression test
// CodeRabbit's review requested: it proves that when a backend subcommand's component/stack
// were resolved via an interactive prompt (StandardOptions.ComponentPrompted /
// StackPrompted), the executeXCommandWithValues helpers pass those booleans through to
// ConfigInitializer.InitConfigAndAuth unchanged, rather than silently defaulting to false (which
// would make profile-fallback re-exec drop the prompted value and force a second prompt, or
// fail outright, in the re-exec'd child -- see auth.ReExecContext).
func TestExecuteCommandWithValues_ThreadsPromptedFlagsToConfigInitializer(t *testing.T) {
tests := []struct {
name string
componentPrompted bool
stackPrompted bool
invoke func(mci *MockConfigInitializer, mp *MockProvisioner) error
}{
{
name: "provision: both prompted",
componentPrompted: true,
stackPrompted: true,
invoke: func(mci *MockConfigInitializer, mp *MockProvisioner) error {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "", true, true).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().CreateBackend(gomock.Any()).Return(nil)
return executeProvisionCommandWithValues("vpc", "dev", "", promptedFlags{Component: true, Stack: true})
},
},
{
name: "provision: only stack prompted",
componentPrompted: false,
stackPrompted: true,
invoke: func(mci *MockConfigInitializer, mp *MockProvisioner) error {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "", false, true).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().CreateBackend(gomock.Any()).Return(nil)
return executeProvisionCommandWithValues("vpc", "dev", "", promptedFlags{Component: false, Stack: true})
},
},
{
name: "delete: both prompted",
componentPrompted: true,
stackPrompted: true,
invoke: func(mci *MockConfigInitializer, mp *MockProvisioner) error {
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "", true, true).
Return(&schema.AtmosConfiguration{}, nil, nil)
mp.EXPECT().DeleteBackend(gomock.Any()).Return(nil)
return executeDeleteCommandWithValues("vpc", "dev", "", true, promptedFlags{Component: true, Stack: true})
},
},
{
name: "describe: both prompted",
componentPrompted: true,
stackPrompted: true,
invoke: func(mci *MockConfigInitializer, mp *MockProvisioner) error {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("vpc", "dev", "", true, true).
Return(atmosConfig, nil, nil)
mp.EXPECT().DescribeBackend(atmosConfig, "vpc", map[string]string{"format": "yaml"}).Return(nil)
return executeDescribeCommandWithValues("vpc", "dev", "", "yaml", promptedFlags{Component: true, Stack: true})
},
},
{
name: "list: stack prompted, no component parameter",
componentPrompted: false,
stackPrompted: true,
invoke: func(mci *MockConfigInitializer, mp *MockProvisioner) error {
atmosConfig := &schema.AtmosConfiguration{}
mci.EXPECT().
InitConfigAndAuth("", "dev", "", false, true).
Return(atmosConfig, nil, nil)
mp.EXPECT().ListBackends(atmosConfig, map[string]string{"format": "table"}).Return(nil)
return executeListCommandWithValues("dev", "", "table", true)
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockConfigInit, mockProv := setupTestWithMocks(t)

err := tt.invoke(mockConfigInit, mockProv)

assert.NoError(t, err)
})
}
}

func TestBackendSubcommands_BindStackFlagFromCommand(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -596,7 +685,7 @@ func TestBackendSubcommands_BindStackFlagFromCommand(t *testing.T) {

expectedErr := errors.New("stop after stack parse")
mockConfigInit.EXPECT().
InitConfigAndAuth(tt.component, "dev", "").
InitConfigAndAuth(tt.component, "dev", "", false, false).
Return(nil, nil, expectedErr)

require.NoError(t, tt.cmd.Flags().Set("stack", "dev"))
Expand Down Expand Up @@ -675,7 +764,7 @@ func TestBackendSubcommands_StackFromViperWhenNotSetOnCLI(t *testing.T) {

expectedErr := errors.New("stop after stack parse")
mockConfigInit.EXPECT().
InitConfigAndAuth(tt.component, "dev", "").
InitConfigAndAuth(tt.component, "dev", "", false, false).
Return(nil, nil, expectedErr)

err := tt.cmd.RunE(tt.cmd, tt.args)
Expand Down
3 changes: 2 additions & 1 deletion cmd/terraform/backend/backend_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ var createCmd = &cobra.Command{
stack = v.GetString("stack")
}
identity := flags.ParseGlobalFlags(cmd, v).Identity.Value()
return executeProvisionCommandWithValues(result.Component, stack, identity)
prompted := promptedFlags{Component: result.ComponentPrompted, Stack: result.StackPrompted}
return executeProvisionCommandWithValues(result.Component, stack, identity, prompted)
},
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/terraform/backend/backend_delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ Requires the --force flag for safety. The backend must be empty
stack = v.GetString("stack")
}
identity := flags.ParseGlobalFlags(cmd, v).Identity.Value()
return executeDeleteCommandWithValues(result.Component, stack, identity, force)
prompted := promptedFlags{Component: result.ComponentPrompted, Stack: result.StackPrompted}
return executeDeleteCommandWithValues(result.Component, stack, identity, force, prompted)
},
}

Expand Down
3 changes: 2 additions & 1 deletion cmd/terraform/backend/backend_describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ This includes backend settings, variables, and metadata from the stack manifest.
stack = v.GetString("stack")
}
identity := flags.ParseGlobalFlags(cmd, v).Identity.Value()
return executeDescribeCommandWithValues(result.Component, stack, identity, result.Format)
prompted := promptedFlags{Component: result.ComponentPrompted, Stack: result.StackPrompted}
return executeDescribeCommandWithValues(result.Component, stack, identity, result.Format, prompted)
},
}

Expand Down
Loading
Loading