-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy patherrors.rs
More file actions
167 lines (144 loc) · 5.63 KB
/
Copy patherrors.rs
File metadata and controls
167 lines (144 loc) · 5.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Error types for the Configure command handler
use crate::shared::command::CommandError;
/// Comprehensive error type for the `ConfigureCommandHandler`
#[derive(Debug, thiserror::Error)]
pub enum ConfigureCommandHandlerError {
#[error("Command execution failed: {0}")]
Command(#[from] CommandError),
#[error("Failed to persist environment state: {0}")]
StatePersistence(#[from] crate::domain::environment::repository::RepositoryError),
}
impl crate::shared::Traceable for ConfigureCommandHandlerError {
fn trace_format(&self) -> String {
match self {
Self::Command(e) => {
format!("ConfigureCommandHandlerError: Command execution failed - {e}")
}
Self::StatePersistence(e) => {
format!("ConfigureCommandHandlerError: Failed to persist environment state - {e}")
}
}
}
fn trace_source(&self) -> Option<&dyn crate::shared::Traceable> {
match self {
Self::Command(e) => Some(e),
Self::StatePersistence(_) => None,
}
}
fn error_kind(&self) -> crate::shared::ErrorKind {
match self {
Self::Command(_) => crate::shared::ErrorKind::CommandExecution,
Self::StatePersistence(_) => crate::shared::ErrorKind::StatePersistence,
}
}
}
impl ConfigureCommandHandlerError {
/// Provides detailed troubleshooting guidance for this error
///
/// Returns context-specific help text that guides users toward resolving
/// the issue. This implements the project's tiered help system pattern
/// for actionable error messages.
///
/// # Examples
///
/// ```rust
/// use torrust_tracker_deployer_lib::application::command_handlers::configure::ConfigureCommandHandlerError;
/// use torrust_tracker_deployer_lib::shared::command::CommandError;
///
/// let error = ConfigureCommandHandlerError::Command(
/// CommandError::ExecutionFailed {
/// command: "ansible-playbook".to_string(),
/// exit_code: "2".to_string(),
/// stdout: String::new(),
/// stderr: "connection failed".to_string(),
/// }
/// );
///
/// let help = error.help();
/// assert!(help.contains("Command Execution"));
/// assert!(help.contains("Troubleshooting"));
/// ```
#[must_use]
pub fn help(&self) -> &'static str {
match self {
Self::Command(_) => {
"Command Execution Failed - Troubleshooting:
1. Verify Ansible is installed: ansible --version
2. Check instance connectivity:
- Verify instance is running: lxc list
- Test SSH access: ssh -i <key> <user>@<ip>
- Check Ansible inventory file exists and is correct
3. Common Ansible issues:
- Python not installed on target: Install python3
- Wrong SSH user or key: Check inventory file
- Permission denied: Verify SSH key permissions (chmod 600)
- Connection refused: Check SSH service on instance
4. Check Ansible playbook syntax:
ansible-playbook --syntax-check <playbook>.yml
5. Run with verbose output for more details:
ansible-playbook -vvv <playbook>.yml
For Ansible troubleshooting, see the Ansible documentation."
}
Self::StatePersistence(_) => {
"State Persistence Failed - Troubleshooting:
1. Check file system permissions for the data directory
2. Verify available disk space: df -h
3. Ensure no other process is accessing the environment files
4. Check for file system errors: dmesg | tail
5. Verify the data directory is writable
State files are stored in: data/<env-name>/
The repository handles directory creation atomically during save.
If partially created files exist, remove them and retry.
If the problem persists, report it with full system details."
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::domain::environment::repository::RepositoryError;
use crate::shared::command::CommandError;
#[test]
fn it_should_provide_help_for_command_execution() {
let error = ConfigureCommandHandlerError::Command(CommandError::ExecutionFailed {
command: "ansible-playbook".to_string(),
exit_code: "2".to_string(),
stdout: String::new(),
stderr: "error".to_string(),
});
let help = error.help();
assert!(help.contains("Command Execution"));
assert!(help.contains("Troubleshooting"));
assert!(help.contains("Ansible"));
}
#[test]
fn it_should_provide_help_for_state_persistence() {
let error = ConfigureCommandHandlerError::StatePersistence(RepositoryError::NotFound);
let help = error.help();
assert!(help.contains("State Persistence"));
assert!(help.contains("Troubleshooting"));
assert!(help.contains("data/<env-name>/"));
}
#[test]
fn it_should_have_help_for_all_error_variants() {
let errors = vec![
ConfigureCommandHandlerError::Command(CommandError::ExecutionFailed {
command: "test".to_string(),
exit_code: "1".to_string(),
stdout: String::new(),
stderr: "error".to_string(),
}),
ConfigureCommandHandlerError::StatePersistence(RepositoryError::NotFound),
];
for error in errors {
let help = error.help();
assert!(!help.is_empty(), "Help text should not be empty");
assert!(
help.contains("Troubleshooting"),
"Help should contain troubleshooting guidance"
);
assert!(help.len() > 50, "Help should be detailed");
}
}
}