|
| 1 | +# Enhance Provision Command Output with SSH Connection Details |
| 2 | + |
| 3 | +**Issue**: #242 |
| 4 | +**Parent Epic**: N/A (standalone improvement) |
| 5 | +**Related**: User Experience, CLI Output |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +Improve the `provision` command output to display all essential information needed to connect to the newly provisioned instance. Currently, users must manually inspect the environment state file (`data/{env-name}/environment.json`) or remember configuration details to determine how to connect to the provisioned instance. |
| 10 | + |
| 11 | +This enhancement will display the instance IP address, SSH port, and SSH private key path directly in the command output, enabling users to immediately connect to their provisioned infrastructure without additional commands. |
| 12 | + |
| 13 | +## Goals |
| 14 | + |
| 15 | +- [ ] Display instance IP address in provision command output |
| 16 | +- [ ] Display SSH service port in provision command output |
| 17 | +- [ ] Display absolute path to SSH private key in provision command output |
| 18 | +- [ ] Format output to be clear and copy-paste friendly for SSH connection |
| 19 | +- [ ] Maintain consistency with existing output formatting patterns |
| 20 | + |
| 21 | +## 🏗️ Architecture Requirements |
| 22 | + |
| 23 | +**DDD Layer**: Presentation |
| 24 | +**Module Path**: `src/presentation/cli/commands/provision.rs` |
| 25 | +**Pattern**: CLI Command Output Enhancement |
| 26 | + |
| 27 | +### Module Structure Requirements |
| 28 | + |
| 29 | +- [ ] Follow DDD layer separation (see [docs/codebase-architecture.md](../docs/codebase-architecture.md)) |
| 30 | +- [ ] Use `UserOutput` for all user-facing output (see [docs/contributing/output-handling.md](../docs/contributing/output-handling.md)) |
| 31 | +- [ ] Extract connection details from environment runtime outputs and user inputs |
| 32 | +- [ ] Format output according to presentation layer conventions |
| 33 | + |
| 34 | +### Architectural Constraints |
| 35 | + |
| 36 | +- [ ] Use `UserOutput` methods exclusively (never `println!`, `eprintln!`) |
| 37 | +- [ ] Extract data from domain model (Environment state) without duplicating logic |
| 38 | +- [ ] Maintain separation between command orchestration and output formatting |
| 39 | +- [ ] Ensure output is testable through UserOutput inspection |
| 40 | + |
| 41 | +### Anti-Patterns to Avoid |
| 42 | + |
| 43 | +- ❌ Direct use of `println!`, `eprintln!`, or stdout/stderr |
| 44 | +- ❌ Embedding formatting logic in domain or application layers |
| 45 | +- ❌ Hardcoding SSH port instead of reading from configuration |
| 46 | +- ❌ Displaying relative paths instead of absolute paths for SSH keys |
| 47 | + |
| 48 | +## Specifications |
| 49 | + |
| 50 | +### Current Output |
| 51 | + |
| 52 | +When running `provision {environment}`, users currently see: |
| 53 | + |
| 54 | +```text |
| 55 | +⏳ [1/3] Validating environment... |
| 56 | + ✓ Environment name validated: provision-output-test (took 0ms) |
| 57 | +⏳ [2/3] Creating command handler... |
| 58 | + ✓ Done (took 0ms) |
| 59 | +⏳ [3/3] Provisioning infrastructure... |
| 60 | + ✓ Infrastructure provisioned (took 28.8s) |
| 61 | +✅ Environment 'provision-output-test' provisioned successfully |
| 62 | +``` |
| 63 | + |
| 64 | +### Proposed Enhanced Output |
| 65 | + |
| 66 | +After provisioning completes, display connection details: |
| 67 | + |
| 68 | +```text |
| 69 | +⏳ [1/3] Validating environment... |
| 70 | + ✓ Environment name validated: provision-output-test (took 0ms) |
| 71 | +⏳ [2/3] Creating command handler... |
| 72 | + ✓ Done (took 0ms) |
| 73 | +⏳ [3/3] Provisioning infrastructure... |
| 74 | + ✓ Infrastructure provisioned (took 28.8s) |
| 75 | +✅ Environment 'provision-output-test' provisioned successfully |
| 76 | +
|
| 77 | +Instance Connection Details: |
| 78 | + IP Address: 10.140.190.171 |
| 79 | + SSH Port: 22 |
| 80 | + SSH Private Key: /home/user/path/to/fixtures/testing_rsa |
| 81 | + SSH Username: torrust |
| 82 | +
|
| 83 | +Connect using: |
| 84 | + ssh -i /home/user/path/to/fixtures/testing_rsa torrust@10.140.190.171 -p 22 |
| 85 | +``` |
| 86 | + |
| 87 | +### Data Sources |
| 88 | + |
| 89 | +The information is available in the environment state after provisioning: |
| 90 | + |
| 91 | +```json |
| 92 | +{ |
| 93 | + "Provisioned": { |
| 94 | + "context": { |
| 95 | + "user_inputs": { |
| 96 | + "ssh_credentials": { |
| 97 | + "ssh_priv_key_path": "/path/to/private/key", |
| 98 | + "ssh_username": "torrust" |
| 99 | + }, |
| 100 | + "ssh_port": 22 |
| 101 | + }, |
| 102 | + "runtime_outputs": { |
| 103 | + "instance_ip": "10.140.190.171" |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | +} |
| 108 | +``` |
| 109 | + |
| 110 | +### Implementation Location |
| 111 | + |
| 112 | +The output enhancement should be added in the provision command handler: |
| 113 | + |
| 114 | +- File: `src/presentation/cli/commands/provision.rs` |
| 115 | +- Method: After successful provisioning in the command execution |
| 116 | +- Access data from: `Environment<Provisioned>` state |
| 117 | +- Output via: `UserOutput` methods |
| 118 | + |
| 119 | +### Formatting Guidelines |
| 120 | + |
| 121 | +1. **Section Header**: "Instance Connection Details:" |
| 122 | +2. **Field Alignment**: Use consistent spacing for readability |
| 123 | +3. **Copy-Paste Format**: Provide complete SSH command ready to copy |
| 124 | +4. **Absolute Paths**: Always display absolute paths for SSH keys |
| 125 | +5. **Port Display**: Always show port even if default (22) |
| 126 | + |
| 127 | +## Implementation Plan |
| 128 | + |
| 129 | +### Phase 1: Data Extraction (estimated: 30 minutes) |
| 130 | + |
| 131 | +- [ ] Task 1.1: Locate provision command completion handler in `src/presentation/cli/commands/provision.rs` |
| 132 | +- [ ] Task 1.2: Extract instance IP from `runtime_outputs.instance_ip` |
| 133 | +- [ ] Task 1.3: Extract SSH port from `user_inputs.ssh_port` |
| 134 | +- [ ] Task 1.4: Extract SSH private key path from `user_inputs.ssh_credentials.ssh_priv_key_path` |
| 135 | +- [ ] Task 1.5: Extract SSH username from `user_inputs.ssh_credentials.ssh_username` |
| 136 | + |
| 137 | +### Phase 2: Output Formatting (estimated: 30 minutes) |
| 138 | + |
| 139 | +- [ ] Task 2.1: Create formatted output message with connection details |
| 140 | +- [ ] Task 2.2: Ensure absolute path resolution for SSH private key |
| 141 | +- [ ] Task 2.3: Format SSH command with all required parameters |
| 142 | +- [ ] Task 2.4: Add output after successful provision confirmation using `UserOutput` |
| 143 | +- [ ] Task 2.5: Test output formatting with different path lengths and configurations |
| 144 | + |
| 145 | +### Phase 3: Edge Cases and Testing (estimated: 1 hour) |
| 146 | + |
| 147 | +- [ ] Task 3.1: Test with custom SSH port (non-22) |
| 148 | +- [ ] Task 3.2: Test with relative vs absolute SSH key paths |
| 149 | +- [ ] Task 3.3: Test with long absolute paths |
| 150 | +- [ ] Task 3.4: Verify output in E2E tests captures new information |
| 151 | +- [ ] Task 3.5: Update any relevant documentation |
| 152 | + |
| 153 | +## Acceptance Criteria |
| 154 | + |
| 155 | +> **Note for Contributors**: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations. |
| 156 | +
|
| 157 | +**Quality Checks**: |
| 158 | + |
| 159 | +- [ ] Pre-commit checks pass: `./scripts/pre-commit.sh` |
| 160 | + |
| 161 | +**Task-Specific Criteria**: |
| 162 | + |
| 163 | +- [ ] Provision command output includes instance IP address |
| 164 | +- [ ] Provision command output includes SSH port (even if default 22) |
| 165 | +- [ ] Provision command output includes absolute path to SSH private key |
| 166 | +- [ ] Provision command output includes SSH username |
| 167 | +- [ ] Output provides ready-to-copy SSH connection command |
| 168 | +- [ ] Output uses `UserOutput` methods (no direct `println!`/`eprintln!`) |
| 169 | +- [ ] Output formatting is consistent with existing patterns |
| 170 | +- [ ] Works correctly with custom SSH ports (non-22) |
| 171 | +- [ ] Works correctly with both relative and absolute SSH key paths |
| 172 | +- [ ] E2E tests continue to pass with new output format |
| 173 | + |
| 174 | +## Related Documentation |
| 175 | + |
| 176 | +- [docs/contributing/output-handling.md](../contributing/output-handling.md) - Output handling conventions |
| 177 | +- [docs/console-commands.md](../console-commands.md) - Command documentation |
| 178 | +- [docs/user-guide/commands/provision.md](../user-guide/commands/provision.md) - Provision command guide |
| 179 | +- [docs/codebase-architecture.md](../codebase-architecture.md) - Architecture overview |
| 180 | + |
| 181 | +## Notes |
| 182 | + |
| 183 | +### Design Decisions |
| 184 | + |
| 185 | +1. **Always Show Port**: Even though 22 is the default SSH port, we explicitly display it to: |
| 186 | + |
| 187 | + - Avoid user confusion about what port to use |
| 188 | + - Support custom port configurations transparently |
| 189 | + - Make the SSH command complete and unambiguous |
| 190 | + |
| 191 | +2. **Absolute Paths**: SSH key paths are converted to absolute paths to: |
| 192 | + |
| 193 | + - Ensure the command works regardless of user's current directory |
| 194 | + - Avoid path resolution ambiguity |
| 195 | + - Match how SSH tools expect paths |
| 196 | + |
| 197 | +3. **Complete SSH Command**: Providing the full SSH command: |
| 198 | + - Reduces friction for users to connect to their instance |
| 199 | + - Serves as documentation for less experienced users |
| 200 | + - Eliminates the need for a separate "how to connect" command |
| 201 | + |
| 202 | +### Future Enhancements |
| 203 | + |
| 204 | +- Consider adding output to other commands (configure, release, run) showing current connection status |
| 205 | +- Could add a dedicated `connection-info` or `ssh-info` command for retrieving connection details later |
| 206 | +- Might integrate with SSH config file generation for easier connection management |
| 207 | + |
| 208 | +### Alternative Approaches Considered |
| 209 | + |
| 210 | +1. **Create separate `show` command**: Rejected because it adds extra step for users |
| 211 | +2. **Output only IP**: Rejected because users still need port and key path |
| 212 | +3. **Minimal output**: Rejected because the goal is to provide complete, actionable information |
0 commit comments