Radish alpha
r
Radicle CI broker
Radicle
Git (anonymous pull)
Log in to clone via SSH
feat: add a "cibtool timeout" subcommand
Lars Wirzenius committed 1 year ago
commit 0e78c6a8d86b7fc93a4557fb725b12b0124693af
parent 68f392dd7806bbf2297635792160f6ac18b540a9
3 files changed +115 -0
modified src/bin/cibtool.rs
@@ -120,6 +120,8 @@ enum Cmd {
    Run(RunCmd),
    Report(cibtoolcmd::ReportCmd),
    Trigger(cibtoolcmd::TriggerCmd),
+
    #[clap(hide = true)]
+
    Timeout(cibtoolcmd::TimeoutCmd),
}

impl Subcommand for Cmd {
@@ -130,6 +132,7 @@ impl Subcommand for Cmd {
            Self::Run(x) => x.run(args),
            Self::Report(x) => x.run(args),
            Self::Trigger(x) => x.run(args),
+
            Self::Timeout(x) => x.run(args),
        }
    }
}
@@ -351,4 +354,7 @@ enum CibToolError {

    #[error("programming error: failed to set up inter-thread notification channel")]
    Notification(#[source] NotificationError),
+

+
    #[error(transparent)]
+
    Timeout(#[from] radicle_ci_broker::timeoutcmd::TimeoutError),
}
modified src/bin/cibtoolcmd/mod.rs
@@ -20,3 +20,6 @@ pub use run::*;

mod trigger;
pub use trigger::*;
+

+
mod timeout;
+
pub use timeout::*;
added src/bin/cibtoolcmd/timeout.rs
@@ -0,0 +1,106 @@
+
use std::{
+
    process::Command,
+
    thread::sleep,
+
    time::{Duration, Instant},
+
};
+

+
use radicle_ci_broker::timeoutcmd::TimeoutCommand;
+

+
use super::*;
+

+
/// Trigger a CI run.
+
///
+
/// This is meant for developer experimentation.
+
#[derive(Parser)]
+
pub struct TimeoutCmd {
+
    /// A Bash script to run. Should start with "exec", or time out won't work.
+
    #[clap(long)]
+
    script: String,
+

+
    /// Text to be fed to script via stdin.
+
    #[clap(long, default_value = "")]
+
    stdin: String,
+

+
    /// Generate at least this much data to feed to script via stdin.
+
    #[clap(long)]
+
    generate: Option<usize>,
+

+
    /// Terminate script after this many seconds.
+
    #[clap(long)]
+
    timeout: u64,
+

+
    /// Verbose output: show stdout and stderr output lines.
+
    #[clap(short, long)]
+
    verbose: bool,
+

+
    /// Don't empty stdout and stderr buffers, let them fill up.
+
    #[clap(long)]
+
    fill_buffers: bool,
+

+
    /// Kill script after this many seconds, unconditionally.
+
    #[clap(long)]
+
    kill_after: Option<u64>,
+
}
+

+
impl Leaf for TimeoutCmd {
+
    fn run(&self, _args: &Args) -> Result<(), CibToolError> {
+
        let mut cmd = Command::new("bash");
+
        cmd.arg("-c").arg(&self.script);
+

+
        let mut to = TimeoutCommand::new(Duration::from_secs(self.timeout));
+

+
        if let Some(bytes) = self.generate {
+
            let mut stdin: Vec<u8> = vec![];
+
            while stdin.len() < bytes {
+
                for byte in b"hello, world\n" {
+
                    stdin.push(*byte);
+
                }
+
            }
+
            to.feed_stdin(stdin.as_slice());
+
            println!("generated stdin has {} bytes", stdin.len());
+
        } else {
+
            to.feed_stdin(self.stdin.as_bytes());
+
        }
+

+
        let started = Instant::now();
+
        println!("spawn child");
+
        let running = to.spawn(cmd)?;
+

+
        if let Some(secs) = self.kill_after {
+
            sleep(Duration::from_secs(secs));
+
            running.kill().unwrap();
+
        }
+

+
        let mut stdout_bytes = 0;
+
        if !self.fill_buffers {
+
            let stdout = running.stdout();
+
            while let Some(line) = stdout.line() {
+
                stdout_bytes += line.as_bytes().len();
+
                if self.verbose {
+
                    println!("stdout: {line:?}");
+
                }
+
            }
+
            println!("finished reading stdout");
+

+
            let stderr = running.stderr();
+
            while let Some(line) = stderr.line() {
+
                if self.verbose {
+
                    println!("stderr: {line:?}");
+
                }
+
            }
+
            println!("finished reading stderr");
+
        }
+

+
        let tor = running.wait()?;
+
        let elapsed = started.elapsed();
+
        let speed = (stdout_bytes as f64) / elapsed.as_secs_f64();
+

+
        println!("stdout bytes: {stdout_bytes}");
+
        println!("duration: {} ms", elapsed.as_millis());
+
        println!("speed: {:.0} B/s", speed);
+
        println!("exit: {}", tor.status());
+
        println!("timed out? {}", tor.timed_out());
+

+
        Ok(())
+
    }
+
}