diff --git a/README.md b/README.md index b934bf4..9d5cb5f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,9 @@ Each environment is defined in a `environment` sub-table, with the name used to | `exec_cmds`| String Array | A list of additional commands that will be run in the container when it is created, useful for adding additional packages. Passed to `docker exec` | `exec_cmds = ["apt update -y", "apt install -y cowsay"]`| | `exec_options` | String Array | Docker CLI options passed to the `docker exec` for all `exec_cmds` | `exec_options = ["-u", "user"]`| | `create_options` | String Array | Docker CLI options passed to `docker create` command. Note that `--name` is not allowed as that is provided by `berth`| `create_options = ["--privileged"]`| -| `presets` | String Array | The name(s) of preset(s) to merge into the environment, see below for more information | `presets = ["interactive", "working_dir_mount"]` | +| `presets` | String Array | The name(s) of preset(s) to merge into the environment, see below for more information | `presets = ["interactive", "working_dir_mount"]` | + +Note all commands are run with the current working directory as the provided configuration file's directory. The minimum configuration is: ```toml diff --git a/src/docker.rs b/src/docker.rs index 39ed8bd..1b27bc8 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -11,6 +11,7 @@ use log::info; use miette::{Diagnostic, Result}; use std::{ collections::HashMap, + path::{Path, PathBuf}, process::{Command, Output}, }; @@ -69,16 +70,21 @@ const CONTAINER_ENGINE: &str = "docker"; pub struct DockerHandler { env: Environment, docker: Docker, + config_dir: PathBuf, } impl DockerHandler { - pub fn new(environment: Environment) -> Result { + pub fn new(environment: Environment, config_path: &Path) -> Result { let docker = Docker::connect_with_local_defaults().map_err(docker_err!(ConnectingToDaemon))?; + let mut config_dir = config_path.to_path_buf(); + config_dir.pop(); + Ok(DockerHandler { env: environment, docker, + config_dir, }) } @@ -115,7 +121,7 @@ impl DockerHandler { .to_string_lossy() .to_string(); let args = vec!["build", "-t", &self.env.image, "-f", &dockerfile_path, "."]; - Self::run_docker_command(args)?; + self.run_docker_command(args)?; spinner.finish_and_clear(); @@ -249,7 +255,7 @@ impl DockerHandler { args.push(&self.env.image); args.extend_from_slice(&["tail", "-f", "/dev/null"]); - Self::run_docker_command(args) + self.run_docker_command(args) } fn exec_setup_commands(&self) -> Result<()> { @@ -264,7 +270,7 @@ impl DockerHandler { let split_cmd = shell_words::split(cmd).unwrap(); args.extend(split_cmd.iter().map(|s| s.as_str())); - Self::run_docker_command(args)?; + self.run_docker_command(args)?; } Ok(()) } @@ -278,7 +284,7 @@ impl DockerHandler { let split_cmd = shell_words::split(&fixed_string).unwrap(); args.extend(split_cmd.iter().map(|s| s.as_str())); - Self::run_docker_command(args)?; + self.run_docker_command(args)?; } Ok(()) } @@ -295,19 +301,20 @@ impl DockerHandler { pub async fn is_anyone_connected(&self) -> Result { let args = vec!["exec", &self.env.name, "ls", "/dev/pts"]; - let output = Self::run_docker_command_with_output(args)?; + let output = self.run_docker_command_with_output(args)?; let ps_count = String::from_utf8(output.stdout).unwrap().lines().count(); let no_connections_ps_count = 2; Ok(ps_count > no_connections_ps_count) } - fn run_docker_command_with_output(args: Vec<&str>) -> Result { + fn run_docker_command_with_output(&self, args: Vec<&str>) -> Result { let command = format!("{} {}", CONTAINER_ENGINE, shell_words::join(&args)); info!("{command}"); let output = Command::new(CONTAINER_ENGINE) .args(&args) + .current_dir(&self.config_dir) .output() .map_err(|_| DockerError::CommandFailed(command.clone()))?; @@ -323,7 +330,7 @@ impl DockerHandler { } } - fn run_docker_command(args: Vec<&str>) -> Result<()> { - Self::run_docker_command_with_output(args).map(|_| ()) + fn run_docker_command(&self, args: Vec<&str>) -> Result<()> { + self.run_docker_command_with_output(args).map(|_| ()) } } diff --git a/src/main.rs b/src/main.rs index dff688f..778bd50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,7 +60,7 @@ async fn main() -> Result<()> { let environment = Configuration::new(&app_config)?.find_environment_from_configuration()?; - let docker = DockerHandler::new(environment.clone())?; + let docker = DockerHandler::new(environment.clone(), &app_config.config_path)?; let result = { match &app_config.action { diff --git a/tests/docker.rs b/tests/docker.rs index 80f5582..67b9eae 100644 --- a/tests/docker.rs +++ b/tests/docker.rs @@ -87,6 +87,50 @@ fn copy_cmds() -> Result<()> { Ok(()) } +#[test] +#[serial] +fn relative_to_config_file() -> Result<()> { + let mut copy_file = NamedTempFile::new().unwrap(); + let file_text = "Hello World"; + writeln!(copy_file, "{}", file_text).unwrap(); + let copy_file_path = copy_file.path(); + + let config_file = NamedTempFile::new().unwrap(); + let config_file_path = config_file.path(); + + let harness = TestHarness::new().config_with_path( + &formatdoc!( + r#" + image = "alpine:edge" + entry_cmd = "/bin/ash" + cp_cmds = ["{} CONTAINER:{}"] + create_options = ["-it"] + entry_options = ["-it"] + "#, + copy_file_path.file_name().unwrap().to_str().unwrap(), + copy_file_path.to_str().unwrap() + ), + &config_file_path, + )?; + + harness + .args(vec![ + "--config-path", + config_file_path.to_str().unwrap(), + "[name]", + ])? + .run(DEFAULT_TIMEOUT)? + .send_line(&format!("cat {}", copy_file_path.to_str().unwrap()))? + .expect_string(file_text)? + .send_line("exit")? + .expect_terminate()? + .success()?; + + copy_file.close().unwrap(); + config_file.close().unwrap(); + Ok(()) +} + #[test] #[serial] fn exec_cmds() -> Result<()> {