Skip to content

Commit f976ca3

Browse files
authored
Merge pull request #3687 from rina23q/improve/3646/add-summary-output-to-diag-tarball
feat: tedge diag adds summary output to the archive
2 parents 1609634 + 20c1062 commit f976ca3

File tree

4 files changed

+116
-30
lines changed

4 files changed

+116
-30
lines changed

crates/core/tedge/src/cli/diag/collect.rs

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1+
use crate::cli::diag::logger::DualLogger;
12
use crate::command::Command;
2-
use crate::error;
3-
use crate::info;
43
use crate::log::MaybeFancy;
54
use crate::log::Spinner;
6-
use crate::warning;
75
use anyhow::Context;
86
use camino::Utf8Path;
97
use camino::Utf8PathBuf;
@@ -39,44 +37,57 @@ impl Command for DiagCollectCommand {
3937
}
4038

4139
async fn execute(&self, _: TEdgeConfig) -> Result<(), MaybeFancy<anyhow::Error>> {
42-
let plugins = self.read_diag_plugins().await?;
40+
file::create_directory_with_defaults(&self.diag_dir)
41+
.await
42+
.with_context(|| format!("failed to create directory at {}", self.diag_dir))?;
43+
let mut logger = DualLogger::new(self.diag_dir.join("summary.log"))
44+
.context("Failed to initialize logging")?;
45+
46+
let plugins = self.read_diag_plugins(&mut logger).await?;
4347
let plugin_count = plugins.len();
4448
if plugin_count == 0 {
45-
error!("No diagnostic plugins were found");
49+
logger.error("No diagnostic plugins were found");
4650
std::process::exit(2)
4751
}
4852

49-
file::create_directory_with_defaults(&self.diag_dir)
50-
.await
51-
.with_context(|| format!("failed to create directory at {}", self.diag_dir))?;
52-
5353
let mut skipped_count = 0;
5454
let mut error_count = 0;
5555

5656
for plugin in plugins {
5757
let banner = format!("Executing {plugin}");
58-
let spinner = Spinner::start(banner);
58+
let spinner = Spinner::start(banner.clone());
5959
let res = self.execute_diag_plugin(&plugin).await;
6060

6161
match spinner.finish(res) {
62-
Ok(exit_status) if exit_status.success() => {}
63-
Ok(exit_status) if exit_status.code() == Some(2) => {
64-
skipped_count += 1;
65-
info!("{plugin} is marked skipped");
66-
}
6762
Ok(exit_status) => {
68-
error_count += 1;
69-
error!("{plugin} failed with exit status: {exit_status}");
63+
logger.only_to_file(&format!("{banner}... ✓"));
64+
65+
match exit_status.code() {
66+
Some(0) => {}
67+
Some(2) => {
68+
skipped_count += 1;
69+
logger.info(&format!("{plugin} is marked skipped"));
70+
}
71+
Some(code) => {
72+
error_count += 1;
73+
logger.error(&format!("{plugin} failed with exit status: {code}"));
74+
}
75+
None => {
76+
error_count += 1;
77+
logger.error(&format!("{plugin} terminated by signal"));
78+
}
79+
}
7080
}
7181
Err(err) => {
7282
error_count += 1;
73-
error!("{plugin} failed with error: {err}");
83+
logger.only_to_file(&format!("{banner}... ✗"));
84+
logger.error(&format!("{plugin} failed with error: {err}"));
7485
}
7586
}
7687
}
7788

7889
let success_count = plugin_count - skipped_count - error_count;
79-
eprintln!("\nTotal {plugin_count} executed: {success_count} completed, {error_count} failed, {skipped_count} skipped");
90+
logger.log(&format!("\nTotal {plugin_count} executed: {success_count} completed, {error_count} failed, {skipped_count} skipped"));
8091

8192
self.compress_into_a_tarball()
8293
.with_context(|| "Failed to compress diagnostic information")?;
@@ -96,15 +107,21 @@ impl Command for DiagCollectCommand {
96107
}
97108

98109
impl DiagCollectCommand {
99-
async fn read_diag_plugins(&self) -> Result<BTreeSet<Utf8PathBuf>, anyhow::Error> {
110+
async fn read_diag_plugins(
111+
&self,
112+
logger: &mut DualLogger,
113+
) -> Result<BTreeSet<Utf8PathBuf>, anyhow::Error> {
100114
let mut plugins = BTreeSet::new();
101115
for dir_path in &self.plugin_dir {
102-
match Self::read_diag_plugins_from_dir(dir_path.as_ref()).await {
116+
match self
117+
.read_diag_plugins_from_dir(logger, dir_path.as_ref())
118+
.await
119+
{
103120
Ok(plugin_files) => {
104121
plugins.extend(plugin_files);
105122
}
106123
Err(err) => {
107-
warning!("Failed to read plugins from {dir_path}: {err}");
124+
logger.warning(&format!("Failed to read plugins from {dir_path}: {err}"));
108125
continue;
109126
}
110127
}
@@ -113,6 +130,8 @@ impl DiagCollectCommand {
113130
}
114131

115132
async fn read_diag_plugins_from_dir(
133+
&self,
134+
logger: &mut DualLogger,
116135
dir_path: &Utf8Path,
117136
) -> Result<BTreeSet<Utf8PathBuf>, anyhow::Error> {
118137
let mut plugins = BTreeSet::new();
@@ -126,10 +145,10 @@ impl DiagCollectCommand {
126145
plugins.insert(path);
127146
continue;
128147
} else {
129-
warning!("Skipping non-executable file: {:?}", entry.path());
148+
logger.warning(&format!("Skipping non-executable file: {:?}", entry.path()));
130149
}
131150
} else {
132-
warning!("Ignoring invalid path: {:?}", entry.path());
151+
logger.warning(&format!("Ignoring invalid path: {:?}", entry.path()));
133152
}
134153
}
135154
Ok(plugins)
@@ -180,6 +199,7 @@ impl DiagCollectCommand {
180199
tar.append_dir_all(&self.tarball_name, &self.diag_dir)?;
181200
tar.finish()?;
182201

202+
// Cannot write this message to summary.log since the tarball has already been created
183203
eprintln!("Diagnostic information saved to {tarball_path}");
184204
Ok(tarball_path)
185205
}
@@ -206,7 +226,8 @@ mod tests {
206226
with_exec_permission(command.first_plugin_dir().join("plugin_b"), "pwd");
207227
with_exec_permission(command.first_plugin_dir().join("plugin_c"), "pwd");
208228

209-
let plugins = command.read_diag_plugins().await.unwrap();
229+
let mut logger = DualLogger::new(command.diag_dir.join("summary.log")).unwrap();
230+
let plugins = command.read_diag_plugins(&mut logger).await.unwrap();
210231
assert_eq!(plugins.len(), 3);
211232
}
212233

@@ -225,7 +246,8 @@ mod tests {
225246
with_exec_permission(third_plugin_dir.utf8_path().join("plugin_3a"), "pwd");
226247
with_exec_permission(third_plugin_dir.utf8_path().join("plugin_3b"), "pwd");
227248

228-
let plugins = command.read_diag_plugins().await.unwrap();
249+
let mut logger = DualLogger::new(command.diag_dir.join("summary.log")).unwrap();
250+
let plugins = command.read_diag_plugins(&mut logger).await.unwrap();
229251
assert_eq!(plugins.len(), 6);
230252
}
231253

@@ -238,7 +260,8 @@ mod tests {
238260
let mut command = DiagCollectCommand::new(&ttd);
239261
command.add_plugin_dir(&second_plugin_dir);
240262

241-
let plugins = command.read_diag_plugins().await.unwrap();
263+
let mut logger = DualLogger::new(command.diag_dir.join("summary.log")).unwrap();
264+
let plugins = command.read_diag_plugins(&mut logger).await.unwrap();
242265
assert_eq!(plugins.len(), 0);
243266
}
244267

@@ -257,7 +280,8 @@ mod tests {
257280
"pwd",
258281
);
259282

260-
let plugins = command.read_diag_plugins().await.unwrap();
283+
let mut logger = DualLogger::new(command.diag_dir.join("summary.log")).unwrap();
284+
let plugins = command.read_diag_plugins(&mut logger).await.unwrap();
261285
assert_eq!(plugins.len(), 0);
262286
}
263287

@@ -268,7 +292,8 @@ mod tests {
268292
ttd.dir("plugins").file("plugin_a.ignore");
269293
ttd.dir("plugins").file("plugin_b.ignore");
270294

271-
let plugins = command.read_diag_plugins().await.unwrap();
295+
let mut logger = DualLogger::new(command.diag_dir.join("summary.log")).unwrap();
296+
let plugins = command.read_diag_plugins(&mut logger).await.unwrap();
272297
assert_eq!(plugins.len(), 0);
273298
}
274299

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use crate::error;
2+
use crate::info;
3+
use crate::warning;
4+
use std::fs::File;
5+
use std::fs::OpenOptions;
6+
use std::io::Write;
7+
use std::path::Path;
8+
9+
/// Log to both stderr and a file
10+
#[derive(Debug)]
11+
pub struct DualLogger {
12+
file: File,
13+
}
14+
15+
impl DualLogger {
16+
pub fn new(path: impl AsRef<Path>) -> std::io::Result<Self> {
17+
let file = OpenOptions::new().create(true).append(true).open(path)?;
18+
Ok(DualLogger { file })
19+
}
20+
21+
pub fn log(&mut self, message: &str) {
22+
eprintln!("{message}");
23+
let _ = writeln!(self.file, "{message}");
24+
}
25+
26+
pub fn info(&mut self, message: &str) {
27+
info!("{message}");
28+
let _ = writeln!(self.file, "info: {message}");
29+
}
30+
31+
pub fn warning(&mut self, message: &str) {
32+
warning!("{message}");
33+
let _ = writeln!(self.file, "warning: {message}");
34+
}
35+
36+
pub fn error(&mut self, message: &str) {
37+
error!("{message}");
38+
let _ = writeln!(self.file, "error: {message}");
39+
}
40+
41+
pub fn only_to_file(&mut self, message: &str) {
42+
let _ = writeln!(self.file, "{message}");
43+
}
44+
}

crates/core/tedge/src/cli/diag/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod cli;
22
mod collect;
3+
mod logger;
34

45
pub use cli::TEdgeDiagCli;

tests/RobotFramework/tests/tedge/diag/tedge_diag_collect.robot

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,17 @@ Test Tags theme:troubleshooting theme:cli theme:plugins
1111

1212
*** Test Cases ***
1313
Run tedge diag collect
14-
Execute Command tedge diag collect --name default stdout=${False}
14+
Execute Command tedge diag collect --name default
1515
File Should Exist /tmp/default.tar.gz
16+
Directory Should Not Exist /tmp/default
17+
1618
Execute Command tar -xvzf /tmp/default.tar.gz -C /tmp
1719
Validate preset plugins default
1820

21+
File Should Exist /tmp/default/summary.log
22+
${summary}= Execute Command cat /tmp/default/summary.log
23+
Should Contain ${summary} Executing
24+
1925
Run tedge diag collect with multiple plugin directories
2026
Transfer To Device ${CURDIR}/00_template.sh /setup/diag-plugins/00_template.sh
2127
Execute Command
@@ -54,6 +60,16 @@ No tarball is created when there is no plugin
5460
... exp_exit_code=2
5561
File Should Not Exist /tmp/tedge-diag-no-plugin.tar.gz
5662

63+
Invalid plugin is skipped
64+
Execute Command touch /setup/diag-plugins/not_plugin
65+
${stderr}= Execute Command
66+
... tedge diag collect --name tedge-diag-invalid --plugin-dir /setup/diag-plugins --plugin-dir /usr/share/tedge/diag-plugins --keep-dir
67+
... stdout=${False}
68+
... stderr=${True}
69+
${summary}= Execute Command cat /tmp/tedge-diag-invalid/summary.log
70+
Should Contain ${stderr} warning: Skipping
71+
Should Contain ${summary} warning: Skipping
72+
5773

5874
*** Keywords ***
5975
Validate preset plugins

0 commit comments

Comments
 (0)