Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 16 additions & 9 deletions src/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use log::info;
use miette::{Diagnostic, Result};
use std::{
collections::HashMap,
path::{Path, PathBuf},
process::{Command, Output},
};

Expand Down Expand Up @@ -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<Self> {
pub fn new(environment: Environment, config_path: &Path) -> Result<Self> {
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,
})
}

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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<()> {
Expand All @@ -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(())
}
Expand All @@ -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(())
}
Expand All @@ -295,19 +301,20 @@ impl DockerHandler {

pub async fn is_anyone_connected(&self) -> Result<bool> {
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<Output> {
fn run_docker_command_with_output(&self, args: Vec<&str>) -> Result<Output> {
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()))?;

Expand All @@ -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(|_| ())
}
}
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
44 changes: 44 additions & 0 deletions tests/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand Down