docker_test/
lib.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5 */
6
7pub mod build;
8pub mod util;
9
10use anyhow::Error;
11use camino::Utf8PathBuf as PathBuf;
12use cfg_if::cfg_if;
13use std::process::{Command, Output};
14use std::result;
15
16pub type Result<T> = result::Result<T, Error>;
17
18pub const DOCKER_IMAGE: &str = "docker.io/debian:bullseye-slim";
19
20cfg_if! {
21    if #[cfg(feature = "docker")] {
22        pub const DOCKER_CMD: &str = "docker";
23    } else {
24        pub const DOCKER_CMD: &str = "podman";
25    }
26
27}
28
29// FIXME: Should probably used the Podman API
30pub fn cmd(args: Vec<&str>) -> Result<Output> {
31    println!("CMD: {:?}", args);
32    let out = Command::new(DOCKER_CMD).args(args).output()?;
33    let stdout = String::from_utf8(out.clone().stdout).unwrap();
34    let stderr = String::from_utf8(out.clone().stderr).unwrap();
35    println!("STDOUT: {stdout}");
36    println!("STDERR: {stderr}");
37    assert!(out.status.success());
38    Ok(out)
39}
40
41pub struct Container {
42    id: String,
43}
44
45impl Container {
46    pub fn new() -> Result<Self> {
47        let running = cmd(vec!["run", "--detach", DOCKER_IMAGE, "sleep", "15m"])?;
48        let container = Container {
49            id: String::from_utf8(running.stdout)?.trim().to_string(),
50        };
51        Ok(container)
52    }
53
54    pub fn binary_path(&self, src_bin: &PathBuf) -> Result<PathBuf> {
55        let bin = src_bin.components().last().unwrap();
56        let dest_base = PathBuf::from("/usr/local/bin");
57        let dest_bin = dest_base.join(bin);
58        Ok(dest_bin)
59    }
60
61    pub fn copy_binary(&self, src_bin: &PathBuf) -> Result<PathBuf> {
62        let dest_bin = self.binary_path(src_bin)?;
63
64        let _out = self.cp(src_bin.as_str(), dest_bin.as_str())?;
65        self.exec(vec!["chmod", "755", dest_bin.as_str()])?;
66
67        Ok(dest_bin)
68    }
69
70    pub fn kill(&self) -> Result<()> {
71        let _out = cmd(vec!["rm", "--force", self.id.as_str()])?;
72        Ok(())
73    }
74
75    pub fn exec(self: &Self, cmd: Vec<&str>) -> Result<Output> {
76        self.exec_as("root", cmd)
77    }
78
79    pub fn exec_as(self: &Self, user: &str, cmd: Vec<&str>) -> Result<Output> {
80        let out = Command::new(DOCKER_CMD)
81            .arg("exec")
82            .arg("--user")
83            .arg(user)
84            .arg("-i")
85            .arg(&self.id)
86            .args(cmd)
87            .output()?;
88        Ok(out)
89    }
90
91    pub fn exec_w_pass<'a>(
92        self: &Self,
93        user: &'a str,
94        pass: &'a str,
95        mut cmd: Vec<&'a str>,
96    ) -> Result<Output> {
97        let mut ncmd = vec!["echo", pass, "|"];
98        ncmd.append(&mut cmd);
99        let out = self.exec_as(user, ncmd)?;
100        Ok(out)
101    }
102
103    pub fn cp(self: &Self, from: &str, to: &str) -> Result<Output> {
104        let remote = format!("{}:{}", self.id, to);
105        let out = cmd(vec!["cp", from, remote.as_str()])?;
106        if !out.status.success() {
107            anyhow::bail!("Copy of {} to {} failed", from, remote);
108        }
109        Ok(out)
110    }
111}
112
113impl Drop for Container {
114    fn drop(self: &mut Self) {
115        self.kill().unwrap();
116    }
117}