You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix Destroy Command: Handle Created State Gracefully
Epic Subissue: 8 of 9 Issue: #X (to be assigned) Parent Epic: #34 - Create Environment Command Related: Destroy Command Handler, Destroy Presentation Layer, Subissue 9 (Fix Working Directory Support - to be created)
Overview
Fix a critical bug in the destroy command where it fails when attempting to destroy environments in the Created state. The command unconditionally tries to destroy OpenTofu infrastructure that was never provisioned, causing the operation to fail with "No such file or directory" errors.
This bug prevents users from cleaning up newly created environments that were never provisioned, forcing manual cleanup or confusing error messages.
Note: This issue focuses only on the Created state handling bug. A separate issue (Subissue 9) will address the working directory parameter support. For manual testing in this issue, use the default working directory (environments in ./data/) to avoid the working directory bug.
Goals
Fix destroy command to handle Created state environments gracefully
Skip infrastructure destruction for environments that were never provisioned
Maintain backward compatibility with provisioned environment destruction
Add comprehensive tests for both Created and Provisioned state scenarios
❌ Assuming infrastructure always exists for all states
❌ Breaking changes to existing destroy behavior for provisioned environments
❌ Silent failures (always log when skipping infrastructure destruction)
Specifications
Current Behavior (Incorrect)
When destroying an environment in the Created state (never provisioned), the destroy command unconditionally attempts to run OpenTofu destroy operations:
// src/application/command_handlers/destroy.rs (execute_destruction_with_tracking)fn execute_destruction_with_tracking(...) -> Result<...> {// Step 1: Destroy infrastructure via OpenTofuSelf::destroy_infrastructure(opentofu_client)// ❌ Fails for Created state.map_err(|e| (e,DestroyStep::DestroyInfrastructure))?;// Step 2: Clean up state filesSelf::cleanup_state_files(environment).map_err(|e| (e,DestroyStep::CleanupStateFiles))?;Ok(())}
This causes errors like:
Failed to start command 'tofu destroy -var-file=variables.tfvars -auto-approve':
No such file or directory (os error 2)
The environment transitions to DestroyFailed state with this error context:
The destroy command should be state-aware and skip infrastructure destruction for environments that never had infrastructure provisioned:
fnexecute_destruction_with_tracking(environment:&Environment<Destroying>,opentofu_client:&Arc<OpenTofuClient>,) -> Result<(),(DestroyCommandHandlerError,DestroyStep)>{// Only attempt infrastructure destruction if infrastructure was provisioned// States that had infrastructure: Provisioned, Configured, Released, Running// States that never had infrastructure: Createdifshould_destroy_infrastructure(environment){info!(
environment = %environment.name(),"Destroying provisioned infrastructure");Self::destroy_infrastructure(opentofu_client).map_err(|e| (e,DestroyStep::DestroyInfrastructure))?;}else{info!(
environment = %environment.name(),"Skipping infrastructure destruction (environment was never provisioned)");}// Always clean up state filesSelf::cleanup_state_files(environment).map_err(|e| (e,DestroyStep::CleanupStateFiles))?;Ok(())}fnshould_destroy_infrastructure(environment:&Environment<Destroying>) -> bool{// Check if the environment ever reached a provisioned state by checking// if the OpenTofu build directory exists with state fileslet tofu_build_dir = environment.tofu_build_dir();
tofu_build_dir.exists()}
State Transition Context
The environment state machine shows these possible states before Destroying:
Created → Destroying (never provisioned - skip infrastructure destroy)
Provisioning → Destroying (provisioning in progress - attempt infrastructure destroy)
Subissue 9: Fix Destroy Command Working Directory Support (to be created)
Notes
Discovery Context
This bug was discovered during manual testing of the create environment command (Issue #34):
Created a new environment using the create command (environment in Created state)
Attempted to destroy the environment immediately (without provisioning)
Destroy command failed with error: Failed to start command 'tofu destroy -var-file=variables.tfvars -auto-approve': No such file or directory (os error 2)
The command tried to run OpenTofu destroy even though no infrastructure was ever provisioned
Environment transitioned to DestroyFailed state instead of being properly destroyed
Relationship to Working Directory Issue
Important: This issue focuses only on the Created state handling. There is a separate bug where the destroy command doesn't accept the --working-dir parameter (Subissue 9, to be created).
For manual testing in this issue: Always use the default working directory (environments created in ./data/) to avoid the working directory bug. The working directory issue will be addressed separately.
Why Directory Existence Check Works
The implementation uses directory existence as a reliable indicator:
Fix Destroy Command: Handle Created State Gracefully
Epic Subissue: 8 of 9
Issue: #X (to be assigned)
Parent Epic: #34 - Create Environment Command
Related: Destroy Command Handler, Destroy Presentation Layer, Subissue 9 (Fix Working Directory Support - to be created)
Overview
Fix a critical bug in the destroy command where it fails when attempting to destroy environments in the
Createdstate. The command unconditionally tries to destroy OpenTofu infrastructure that was never provisioned, causing the operation to fail with "No such file or directory" errors.This bug prevents users from cleaning up newly created environments that were never provisioned, forcing manual cleanup or confusing error messages.
Note: This issue focuses only on the Created state handling bug. A separate issue (Subissue 9) will address the working directory parameter support. For manual testing in this issue, use the default working directory (environments in
./data/) to avoid the working directory bug.Goals
Createdstate environments gracefully🏗️ Architecture Requirements
DDD Layer: Application (command logic)
Module Path:
src/application/command_handlers/destroy.rsPattern: Command Handler Pattern
Module Structure Requirements
.help()methodsArchitectural Constraints
Anti-Patterns to Avoid
Specifications
Current Behavior (Incorrect)
When destroying an environment in the
Createdstate (never provisioned), the destroy command unconditionally attempts to run OpenTofu destroy operations:This causes errors like:
The environment transitions to
DestroyFailedstate with this error context:{ "DestroyFailed": { "context": { ... }, "state": { "context": { "failed_step": "DestroyInfrastructure", "error_kind": "CommandExecution", "error_summary": "Command execution failed: Failed to start command 'tofu destroy ...", ... } } } }Expected Behavior
The destroy command should be state-aware and skip infrastructure destruction for environments that never had infrastructure provisioned:
State Transition Context
The environment state machine shows these possible states before
Destroying:Created→Destroying(never provisioned - skip infrastructure destroy)Provisioning→Destroying(provisioning in progress - attempt infrastructure destroy)Provisioned→Destroying(provisioned - attempt infrastructure destroy)Configuring→Destroying(configuring - attempt infrastructure destroy)Configured→Destroying(configured - attempt infrastructure destroy)Key Insight: Only the
Createdstate means no infrastructure was ever created. All other states should attempt infrastructure destruction.Implementation Strategy
The fix uses a simple and reliable heuristic: check if the OpenTofu build directory exists before attempting infrastructure destruction.
Why This Works:
Alternative Approaches Considered and Rejected:
State Tracking Flag: Adding a
was_provisionedboolean to environment contextChecking Specific State Names: Only attempt destroy for known provisioned states
The directory existence check is simpler, more reliable, and handles edge cases better.
Implementation Plan
Subtask 1: Add Infrastructure Existence Check (1 hour)
should_destroy_infrastructure()helper function toDestroyCommandHandlerenvironment.tofu_build_dir().exists()Subtask 2: Update Destruction Logic (1 hour)
execute_destruction_with_tracking()to conditionally destroy infrastructureSubtask 3: Testing (2-3 hours)
Acceptance Criteria
Quality Checks:
./scripts/pre-commit.shBug Fix - Created State Handling:
Destroy command succeeds for environments in
CreatedstateInfrastructure destruction is skipped for
Createdstate environmentsState files are cleaned up regardless of infrastructure state
Appropriate log messages indicate why infrastructure destruction was skipped
Manual Test 1 - Destroy Created state environment (default working directory):
Manual Test 2 - Destroy Provisioned state environment (regression test):
Integration:
Related Documentation
Notes
Discovery Context
This bug was discovered during manual testing of the create environment command (Issue #34):
Createdstate)Failed to start command 'tofu destroy -var-file=variables.tfvars -auto-approve': No such file or directory (os error 2)DestroyFailedstate instead of being properly destroyedRelationship to Working Directory Issue
Important: This issue focuses only on the Created state handling. There is a separate bug where the destroy command doesn't accept the
--working-dirparameter (Subissue 9, to be created).For manual testing in this issue: Always use the default working directory (environments created in
./data/) to avoid the working directory bug. The working directory issue will be addressed separately.Why Directory Existence Check Works
The implementation uses directory existence as a reliable indicator:
This approach:
Backward Compatibility
This fix maintains backward compatibility:
No existing workflows are affected by this change. The fix only changes behavior for environments that currently fail to destroy.
State File Cleanup
Regardless of whether infrastructure destruction is attempted or skipped, the state file cleanup always happens:
This ensures:
./data/{ENV_NAME}/)./build/{ENV_NAME}/)