Skip to content

Commit 4dfbb63

Browse files
committed
Add confirmation prompt for new stack creation
- Add user confirmation prompt to deployNewStack for consistency with existing stack updates - Show stack details (name, parameter count, tag count) before prompting - Allow users to cancel stack creation before AWS resources are created - Update deployment tests to mock confirmation prompt and prevent hanging - Fix variable redeclaration error in deployment callback - Maintain consistent and safer user experience across all deployment scenarios
1 parent e6318ae commit 4dfbb63

File tree

2 files changed

+71
-3
lines changed

2 files changed

+71
-3
lines changed

internal/deploy/deployer.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,27 @@ func (d *AWSDeployer) DeployStack(ctx context.Context, stack *model.Stack) error
6767
func (d *AWSDeployer) deployNewStack(ctx context.Context, stack *model.Stack) error {
6868
fmt.Printf("=== Creating new stack %s ===\n", stack.Name)
6969

70+
// Show what will be created
71+
fmt.Printf("This will create a new CloudFormation stack with:\n")
72+
fmt.Printf("- Name: %s\n", stack.Name)
73+
if len(stack.Parameters) > 0 {
74+
fmt.Printf("- Parameters: %d\n", len(stack.Parameters))
75+
}
76+
if len(stack.Tags) > 0 {
77+
fmt.Printf("- Tags: %d\n", len(stack.Tags))
78+
}
79+
fmt.Println()
80+
81+
// Prompt for confirmation before creating new resources
82+
confirmed, err := prompt.ConfirmDeployment(stack.Name)
83+
if err != nil {
84+
return fmt.Errorf("failed to get user confirmation: %w", err)
85+
}
86+
if !confirmed {
87+
fmt.Printf("Stack creation cancelled for %s\n", stack.Name)
88+
return nil
89+
}
90+
7091
// Convert parameters to AWS format
7192
awsParams := make([]aws.Parameter, 0, len(stack.Parameters))
7293
for key, value := range stack.Parameters {
@@ -106,7 +127,7 @@ func (d *AWSDeployer) deployNewStack(ctx context.Context, stack *model.Stack) er
106127
cfnOps := d.awsClient.NewCloudFormationOperations()
107128

108129
// Deploy the stack with event streaming
109-
err := cfnOps.DeployStackWithCallback(ctx, deployInput, eventCallback)
130+
err = cfnOps.DeployStackWithCallback(ctx, deployInput, eventCallback)
110131
if err != nil {
111132
return fmt.Errorf("failed to create stack: %w", err)
112133
}

internal/deploy/deployer_test.go

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,15 @@ func TestAWSDeployer_DeployStack_Success(t *testing.T) {
158158
// Test successful stack deployment
159159
ctx := context.Background()
160160

161+
// Set up mock prompter for confirmation
162+
mockPrompter := &MockPrompter{}
163+
originalPrompter := prompt.GetDefaultPrompter()
164+
prompt.SetPrompter(mockPrompter)
165+
defer prompt.SetPrompter(originalPrompter)
166+
167+
// Mock user confirmation (new stack creation requires confirmation)
168+
mockPrompter.On("ConfirmDeployment", "test-stack").Return(true, nil)
169+
161170
// Create temporary template file
162171
tmpDir := t.TempDir()
163172
templateFile := filepath.Join(tmpDir, "test-template.json")
@@ -215,9 +224,18 @@ func TestAWSDeployer_DeployStack_Success(t *testing.T) {
215224
assert.NoError(t, err)
216225
mockClient.AssertExpectations(t)
217226
mockCfnOps.AssertExpectations(t)
227+
mockPrompter.AssertExpectations(t)
218228
}
219229

220230
func TestAWSDeployer_DeployStack_WithEmptyTemplate(t *testing.T) {
231+
// Set up mock prompter for confirmation
232+
mockPrompter := &MockPrompter{}
233+
originalPrompter := prompt.GetDefaultPrompter()
234+
prompt.SetPrompter(mockPrompter)
235+
defer prompt.SetPrompter(originalPrompter)
236+
237+
// Mock user confirmation (new stack creation requires confirmation)
238+
mockPrompter.On("ConfirmDeployment", "test-stack").Return(true, nil)
221239
// Test deploy stack with empty template body
222240
ctx := context.Background()
223241

@@ -253,9 +271,18 @@ func TestAWSDeployer_DeployStack_WithEmptyTemplate(t *testing.T) {
253271
assert.NoError(t, err)
254272
mockClient.AssertExpectations(t)
255273
mockCfnOps.AssertExpectations(t)
274+
mockPrompter.AssertExpectations(t)
256275
}
257276

258277
func TestAWSDeployer_DeployStack_AWSError(t *testing.T) {
278+
// Set up mock prompter for confirmation
279+
mockPrompter := &MockPrompter{}
280+
originalPrompter := prompt.GetDefaultPrompter()
281+
prompt.SetPrompter(mockPrompter)
282+
defer prompt.SetPrompter(originalPrompter)
283+
284+
// Mock user confirmation (new stack creation requires confirmation)
285+
mockPrompter.On("ConfirmDeployment", "test-stack").Return(true, nil)
259286
// Test deploy stack when AWS returns an error
260287
ctx := context.Background()
261288

@@ -303,6 +330,7 @@ func TestAWSDeployer_DeployStack_AWSError(t *testing.T) {
303330

304331
mockClient.AssertExpectations(t)
305332
mockCfnOps.AssertExpectations(t)
333+
mockPrompter.AssertExpectations(t)
306334
}
307335

308336
func TestAWSDeployer_DeployStack_NoChanges(t *testing.T) {
@@ -349,7 +377,7 @@ func TestAWSDeployer_DeployStack_NoChanges(t *testing.T) {
349377
// Execute
350378
err := deployer.DeployStack(ctx, stack)
351379

352-
// Verify - should succeed with no error despite NoChangesError
380+
// Verify - should succeed with no error when no changes detected
353381
assert.NoError(t, err)
354382
mockClient.AssertExpectations(t)
355383
mockCfnOps.AssertExpectations(t)
@@ -528,6 +556,14 @@ func TestAWSDeployer_ValidateTemplate_ValidationError(t *testing.T) {
528556
}
529557

530558
func TestAWSDeployer_DeployStack_WithYAMLTemplate(t *testing.T) {
559+
// Set up mock prompter for confirmation
560+
mockPrompter := &MockPrompter{}
561+
originalPrompter := prompt.GetDefaultPrompter()
562+
prompt.SetPrompter(mockPrompter)
563+
defer prompt.SetPrompter(originalPrompter)
564+
565+
// Mock user confirmation (new stack creation requires confirmation)
566+
mockPrompter.On("ConfirmDeployment", "test-stack").Return(true, nil)
531567
// Test deploying stack with YAML template content
532568
ctx := context.Background()
533569

@@ -573,12 +609,22 @@ Resources:
573609
assert.NoError(t, err)
574610
mockClient.AssertExpectations(t)
575611
mockCfnOps.AssertExpectations(t)
612+
mockPrompter.AssertExpectations(t)
576613
}
577614

578615
func TestAWSDeployer_DeployStack_WithMultipleParametersAndTags(t *testing.T) {
579-
// Test deploying stack with multiple parameters and tags
616+
// Test deployment with multiple parameters and tags
580617
ctx := context.Background()
581618

619+
// Set up mock prompter for confirmation
620+
mockPrompter := &MockPrompter{}
621+
originalPrompter := prompt.GetDefaultPrompter()
622+
prompt.SetPrompter(mockPrompter)
623+
defer prompt.SetPrompter(originalPrompter)
624+
625+
// Mock user confirmation (new stack creation requires confirmation)
626+
mockPrompter.On("ConfirmDeployment", "test-stack").Return(true, nil)
627+
582628
// Set up mocks
583629
mockCfnOps := &MockCloudFormationOperations{}
584630
mockClient := &MockAWSClient{}
@@ -616,4 +662,5 @@ func TestAWSDeployer_DeployStack_WithMultipleParametersAndTags(t *testing.T) {
616662
assert.NoError(t, err)
617663
mockClient.AssertExpectations(t)
618664
mockCfnOps.AssertExpectations(t)
665+
mockPrompter.AssertExpectations(t)
619666
}

0 commit comments

Comments
 (0)