Skip to content

Commit

Permalink
Merge pull request #823 from openSUSE/logs_store_dest
Browse files Browse the repository at this point in the history
Destination option for agama logs store
  • Loading branch information
mchf committed Nov 15, 2023
2 parents 769e8ff + d7aa245 commit 57a31cb
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 17 deletions.
82 changes: 65 additions & 17 deletions rust/agama-cli/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,28 @@ pub enum LogsCommands {
#[clap(long, short = 'v')]
/// Verbose output
verbose: bool,
#[clap(long, short = 'd')]
/// Path to destination directory. Optionally with the archive file name at the end.
/// An extension will be appended automatically depending on used compression.
destination: Option<PathBuf>,
},
/// List logs which will be collected
List,
}

// main entry point called from agama CLI main loop
/// Main entry point called from agama CLI main loop
pub async fn run(subcommand: LogsCommands) -> anyhow::Result<()> {
match subcommand {
LogsCommands::Store { verbose } => {
LogsCommands::Store {
verbose,
destination,
} => {
// feed internal options structure by what was received from user
// for now we always use / add defaults if any
let destination = parse_destination(destination)?;
let options = LogOptions {
verbose,
destination,
..Default::default()
};

Expand All @@ -45,6 +54,38 @@ pub async fn run(subcommand: LogsCommands) -> anyhow::Result<()> {
}
}

/// Whatewer passed in destination formed into an absolute path with archive name
///
/// # Arguments:
/// * destination
/// - if None then a default is returned
/// - if a path to a directory then a default file name for the archive will be appended to the
/// path
/// - if path with a file name then it is used as is for resulting archive, just extension will
/// be appended later on (depends on used compression)
fn parse_destination(destination: Option<PathBuf>) -> Result<PathBuf, io::Error> {
let err = io::Error::new(io::ErrorKind::InvalidInput, "Invalid destination path");
let mut buffer = destination.unwrap_or(PathBuf::from(DEFAULT_RESULT));
let path = buffer.as_path();

// existing directory -> append an archive name
if path.is_dir() {
buffer.push("agama-logs");
// a path with file name
// sadly, is_some_and is unstable
} else if path.parent().is_some() {
// validate if parent directory realy exists
if !path.parent().unwrap().is_dir() {
return Err(err);
}
}

// buffer is <directory> or <directory>/<file_name> here
// and we know that directory tree which leads to the <file_name> is valid.
// <file_name> creation can still fail later on.
Ok(buffer)
}

const DEFAULT_COMMANDS: [(&str, &str); 3] = [
// (<command to be executed>, <file name used for storing result of the command>)
("journalctl -u agama", "agama"),
Expand Down Expand Up @@ -77,7 +118,7 @@ const DEFAULT_RESULT: &str = "/tmp/agama_logs";
const DEFAULT_COMPRESSION: (&str, &str) = ("bzip2", "tar.bz2");
const DEFAULT_TMP_DIR: &str = "agama-logs";

// A wrapper around println which shows (or not) the text depending on the boolean variable
/// A wrapper around println which shows (or not) the text depending on the boolean variable
fn showln(show: bool, text: &str) {
if !show {
return;
Expand All @@ -86,7 +127,7 @@ fn showln(show: bool, text: &str) {
println!("{}", text);
}

// A wrapper around println which shows (or not) the text depending on the boolean variable
/// A wrapper around println which shows (or not) the text depending on the boolean variable
fn show(show: bool, text: &str) {
if !show {
return;
Expand All @@ -95,12 +136,13 @@ fn show(show: bool, text: &str) {
print!("{}", text);
}

// Configurable parameters of the "agama logs" which can be
// set by user when calling a (sub)command
/// Configurable parameters of the "agama logs" which can be
/// set by user when calling a (sub)command
struct LogOptions {
paths: Vec<String>,
commands: Vec<(String, String)>,
verbose: bool,
destination: PathBuf,
}

impl Default for LogOptions {
Expand All @@ -112,11 +154,12 @@ impl Default for LogOptions {
.map(|(cmd, name)| (cmd.to_string(), name.to_string()))
.collect(),
verbose: false,
destination: PathBuf::from(DEFAULT_RESULT),
}
}
}

// Struct for log represented by a file
/// Struct for log represented by a file
struct LogPath {
// log source
src_path: String,
Expand All @@ -134,7 +177,7 @@ impl LogPath {
}
}

// Struct for log created on demand by a command
/// Struct for log created on demand by a command
struct LogCmd {
// command which stdout / stderr is logged
cmd: String,
Expand Down Expand Up @@ -234,8 +277,8 @@ impl LogItem for LogCmd {
}
}

// Collect existing / requested paths which should already exist in the system.
// Turns them into list of log sources
/// Collect existing / requested paths which should already exist in the system.
/// Turns them into list of log sources
fn paths_to_log_sources(paths: &Vec<String>, tmp_dir: &TempDir) -> Vec<Box<dyn LogItem>> {
let mut log_sources: Vec<Box<dyn LogItem>> = Vec::new();

Expand All @@ -249,7 +292,7 @@ fn paths_to_log_sources(paths: &Vec<String>, tmp_dir: &TempDir) -> Vec<Box<dyn L
log_sources
}

// Some info can be collected via particular commands only, turn it into log sources
/// Some info can be collected via particular commands only, turn it into log sources
fn cmds_to_log_sources(
commands: &Vec<(String, String)>,
tmp_dir: &TempDir,
Expand All @@ -267,7 +310,7 @@ fn cmds_to_log_sources(
log_sources
}

// Compress given directory into a tar archive
/// Compress given directory into a tar archive
fn compress_logs(tmp_dir: &TempDir, result: &String) -> io::Result<()> {
let compression = DEFAULT_COMPRESSION.0;
let tmp_path = tmp_dir
Expand Down Expand Up @@ -305,8 +348,8 @@ fn compress_logs(tmp_dir: &TempDir, result: &String) -> io::Result<()> {
}
}

// Sets the archive owner to root:root. Also sets the file permissions to read/write for the
// owner only.
/// Sets the archive owner to root:root. Also sets the file permissions to read/write for the
/// owner only.
fn set_archive_permissions(archive: &String) -> io::Result<()> {
let attr = fs::metadata(archive)?;
let mut permissions = attr.permissions();
Expand All @@ -324,7 +367,7 @@ fn set_archive_permissions(archive: &String) -> io::Result<()> {
Ok(())
}

// Handler for the "agama logs store" subcommand
/// Handler for the "agama logs store" subcommand
fn store(options: LogOptions) -> Result<(), io::Error> {
if !Uid::effective().is_root() {
panic!("No Root, no logs. Sorry.");
Expand All @@ -334,7 +377,12 @@ fn store(options: LogOptions) -> Result<(), io::Error> {
let commands = options.commands;
let paths = options.paths;
let verbose = options.verbose;
let result = format!("{}.{}", DEFAULT_RESULT, DEFAULT_COMPRESSION.1);
let opt_dest = options.destination.into_os_string();
let destination = opt_dest.to_str().ok_or(io::Error::new(
io::ErrorKind::InvalidInput,
"Malformed destination path",
))?;
let result = format!("{}.{}", destination, DEFAULT_COMPRESSION.1);

showln(verbose, "Collecting Agama logs:");

Expand Down Expand Up @@ -374,7 +422,7 @@ fn store(options: LogOptions) -> Result<(), io::Error> {
compress_logs(&tmp_dir, &result)
}

// Handler for the "agama logs list" subcommand
/// Handler for the "agama logs list" subcommand
fn list(options: LogOptions) {
for list in [
("Log paths: ", options.paths),
Expand Down
6 changes: 6 additions & 0 deletions rust/package/agama-cli.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Wed Nov 15 11:27:10 UTC 2023 - Michal Filka <mfilka@suse.com>

- Improved "agama logs store" (gh#openSUSE/agama#823)
- added an option which allows to define the archive destination

-------------------------------------------------------------------
Tue Nov 14 15:44:15 UTC 2023 - Jorik Cronenberg <jorik.cronenberg@suse.com>

Expand Down

0 comments on commit 57a31cb

Please sign in to comment.