diff --git a/src/environment.rs b/src/environment.rs index b9dbfbbe..5c6c1847 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,3 +1,4 @@ +use crate::errors; /// Query the current user environment use failure::Error; use std::env; @@ -8,15 +9,20 @@ use std::process::Command; pub struct Environment { pub user_details: (String, String), pub current_directory: path::PathBuf, + pub floki_root: path::PathBuf, + pub config_file: path::PathBuf, pub ssh_agent_socket: Option, } impl Environment { /// Gather information on the environment floki is running in - pub fn gather() -> Result { + pub fn gather(config_file: &Option) -> Result { + let (floki_root, config_path) = resolve_floki_root_and_config(config_file)?; Ok(Environment { user_details: get_user_details()?, current_directory: get_current_working_directory()?, + floki_root: floki_root, + config_file: config_path, ssh_agent_socket: get_ssh_agent_socket_path(), }) } @@ -50,3 +56,82 @@ fn get_ssh_agent_socket_path() -> Option { Err(_) => None, } } + +/// Search all ancestors of the current directory for a floki.yaml file name. +fn find_floki_yaml(current_directory: &path::Path) -> Result { + current_directory + .ancestors() + .map(|a| a.join("floki.yaml")) + .find(|f| f.is_file()) + .ok_or(errors::FlokiError::ProblemFindingConfigYaml {}.into()) +} + +/// Take a file path, and return a tuple consisting of it's parent directory and the file path +fn locate_file_in_parents(path: path::PathBuf) -> Result<(path::PathBuf, path::PathBuf), Error> { + let dir = path + .parent() + .ok_or_else(|| errors::FlokiInternalError::InternalAssertionFailed { + description: format!("config_file '{:?}' does not have a parent", &path), + })? + .to_path_buf(); + Ok((dir, path)) +} + +/// Resolve floki root directory and path to configuration file +fn resolve_floki_root_and_config( + config_file: &Option, +) -> Result<(path::PathBuf, path::PathBuf), Error> { + match config_file { + Some(path) => Ok((get_current_working_directory()?, path.clone())), + None => Ok(locate_file_in_parents(find_floki_yaml( + &get_current_working_directory()?, + )?)?), + } +} + +#[cfg(test)] +mod test { + use super::*; + use failure::format_err; + use std::fs; + use tempdir; + + fn touch_file(path: &path::Path) -> Result<(), Error> { + fs::create_dir_all( + path.parent() + .ok_or(format_err!("Unable to take parent of path"))?, + )?; + fs::OpenOptions::new().create(true).write(true).open(path)?; + Ok(()) + } + + #[test] + fn test_find_floki_yaml_current_dir() -> Result<(), Error> { + let tmp_dir = tempdir::TempDir::new("")?; + let floki_yaml_path = tmp_dir.path().join("floki.yaml"); + touch_file(&floki_yaml_path)?; + assert_eq!(find_floki_yaml(&tmp_dir.path())?, floki_yaml_path); + Ok(()) + } + + #[test] + fn test_find_floki_yaml_ancestor() -> Result<(), Error> { + let tmp_dir = tempdir::TempDir::new("")?; + let floki_yaml_path = tmp_dir.path().join("floki.yaml"); + touch_file(&floki_yaml_path)?; + assert_eq!( + find_floki_yaml(&tmp_dir.path().join("dir/subdir"))?, + floki_yaml_path + ); + Ok(()) + } + + #[test] + fn test_find_floki_yaml_sibling() -> Result<(), Error> { + let tmp_dir = tempdir::TempDir::new("")?; + let floki_yaml_path = tmp_dir.path().join("src/floki.yaml"); + touch_file(&floki_yaml_path)?; + assert!(find_floki_yaml(&tmp_dir.path().join("include")).is_err()); + Ok(()) + } +} diff --git a/src/interpret.rs b/src/interpret.rs index e3f96973..2fbb76dc 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -11,19 +11,18 @@ use std::path; /// Build a spec for the docker container, and then run it pub(crate) fn run_container( environ: &Environment, - floki_root: &path::Path, config: &FlokiConfig, command: &str, ) -> Result<(), Error> { - let (mut cmd, mut dind) = build_basic_command(&floki_root, &config)?; + let (mut cmd, mut dind) = build_basic_command(&environ.floki_root, &config)?; cmd = configure_dind(cmd, &config, &mut dind)?; cmd = configure_floki_user_env(cmd, &environ); - cmd = configure_floki_host_mountdir_env(cmd, &floki_root); + cmd = configure_floki_host_mountdir_env(cmd, &environ.floki_root); cmd = configure_forward_user(cmd, &config, &environ); cmd = configure_forward_ssh_agent(cmd, &config, &environ)?; cmd = configure_docker_switches(cmd, &config); - cmd = configure_working_directory(cmd, &environ, &floki_root, &config); + cmd = configure_working_directory(cmd, &environ, &config); cmd.run(command) } @@ -109,13 +108,12 @@ fn configure_docker_switches( fn configure_working_directory( cmd: DockerCommandBuilder, env: &Environment, - floki_root: &path::Path, config: &FlokiConfig, ) -> DockerCommandBuilder { cmd.set_working_directory( get_working_directory( &env.current_directory, - &floki_root, + &env.floki_root, &path::PathBuf::from(&config.mount), ) .to_str() diff --git a/src/main.rs b/src/main.rs index 3c3f5eb2..59e9213f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,6 @@ use verify::verify_command; use failure::Error; use quicli::prelude::*; -use std::path; use structopt::StructOpt; fn main() -> CliResult { @@ -41,16 +40,12 @@ fn main() -> CliResult { fn run_floki_from_args(args: &Cli) -> Result<(), Error> { debug!("Got command line arguments: {:?}", &args); - let environ = environment::Environment::gather()?; + let environ = environment::Environment::gather(&args.config_file)?; debug!("Got environment {:?}", &environ); - let (floki_root, config_file) = match &args.config_file { - Some(config_file) => (environ.current_directory.clone(), config_file.clone()), - None => locate_file_in_system(find_floki_yaml(&environ.current_directory)?)?, - }; - debug!("Selected configuration file: {:?}", &config_file); + debug!("Selected configuration file: {:?}", &environ.config_file); - let config = FlokiConfig::from_file(&config_file)?; + let config = FlokiConfig::from_file(&environ.config_file)?; verify_command(args.local, &config)?; // Dispatch appropriate subcommand @@ -65,13 +60,13 @@ fn run_floki_from_args(args: &Cli) -> Result<(), Error> { // Run a command in the floki container Some(Subcommand::Run { command }) => { let inner_command = interpret::command_in_shell(config.shell.inner_shell(), &command); - run_floki_container(&environ, &floki_root, &config, inner_command) + run_floki_container(&environ, &config, inner_command) } // Launch an interactive floki shell (the default) None => { let inner_command = config.shell.inner_shell().to_string(); - run_floki_container(&environ, &floki_root, &config, inner_command) + run_floki_container(&environ, &config, inner_command) } } } @@ -79,79 +74,11 @@ fn run_floki_from_args(args: &Cli) -> Result<(), Error> { /// Launch a floki container running the inner command fn run_floki_container( environ: &environment::Environment, - floki_root: &path::Path, config: &FlokiConfig, inner_command: String, ) -> Result<(), Error> { config.image.obtain_image()?; - let subshell_command = command::subshell_command(&config.init, inner_command); debug!("Running container with command '{}'", &subshell_command); - interpret::run_container(&environ, &floki_root, &config, &subshell_command) -} - -/// Search all ancestors of the current directory for a floki.yaml file name. -fn find_floki_yaml(current_directory: &path::Path) -> Result { - current_directory - .ancestors() - .map(|a| a.join("floki.yaml")) - .find(|f| f.is_file()) - .ok_or(errors::FlokiError::ProblemFindingConfigYaml {}.into()) -} - -/// Take a file path, and return a tuple consisting of it's directory and the file path -fn locate_file_in_system(path: path::PathBuf) -> Result<(path::PathBuf, path::PathBuf), Error> { - let dir = path - .parent() - .ok_or_else(|| errors::FlokiInternalError::InternalAssertionFailed { - description: format!("config_file '{:?}' does not have a parent", &path), - })? - .to_path_buf(); - Ok((dir, path)) -} - -#[cfg(test)] -mod test { - use super::*; - use std::fs; - use tempdir; - - fn touch_file(path: &path::Path) -> Result<(), Error> { - fs::create_dir_all( - path.parent() - .ok_or(format_err!("Unable to take parent of path"))?, - )?; - fs::OpenOptions::new().create(true).write(true).open(path)?; - Ok(()) - } - - #[test] - fn test_find_floki_yaml_current_dir() -> Result<(), Error> { - let tmp_dir = tempdir::TempDir::new("")?; - let floki_yaml_path = tmp_dir.path().join("floki.yaml"); - touch_file(&floki_yaml_path)?; - assert_eq!(find_floki_yaml(&tmp_dir.path())?, floki_yaml_path); - Ok(()) - } - - #[test] - fn test_find_floki_yaml_ancestor() -> Result<(), Error> { - let tmp_dir = tempdir::TempDir::new("")?; - let floki_yaml_path = tmp_dir.path().join("floki.yaml"); - touch_file(&floki_yaml_path)?; - assert_eq!( - find_floki_yaml(&tmp_dir.path().join("dir/subdir"))?, - floki_yaml_path - ); - Ok(()) - } - - #[test] - fn test_find_floki_yaml_sibling() -> Result<(), Error> { - let tmp_dir = tempdir::TempDir::new("")?; - let floki_yaml_path = tmp_dir.path().join("src/floki.yaml"); - touch_file(&floki_yaml_path)?; - assert!(find_floki_yaml(&tmp_dir.path().join("include")).is_err()); - Ok(()) - } + interpret::run_container(&environ, &config, &subshell_command) }