Skip to content

Commit

Permalink
Move floki_root resolution into Environment structure
Browse files Browse the repository at this point in the history
This helps reduce the amount of parameter passing that is needed in functions
which figure out how to launch the container. The location of the configuration
file, and the directory it is located in (or which needs to be mounted into
the container) is part of how `floki` is run, so belongs in the `Environment`
structure.

This commit doesn't fundamentally change the function in any way, it just moves
it.

Signed-off-by: Richard Lupton <[email protected]>
  • Loading branch information
rlupton20 committed Oct 17, 2019
1 parent 47a421a commit 7317e8c
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 86 deletions.
87 changes: 86 additions & 1 deletion src/environment.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::errors;
/// Query the current user environment
use failure::Error;
use std::env;
Expand All @@ -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<String>,
}

impl Environment {
/// Gather information on the environment floki is running in
pub fn gather() -> Result<Self, Error> {
pub fn gather(config_file: &Option<path::PathBuf>) -> Result<Self, Error> {
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(),
})
}
Expand Down Expand Up @@ -50,3 +56,82 @@ fn get_ssh_agent_socket_path() -> Option<String> {
Err(_) => None,
}
}

/// Search all ancestors of the current directory for a floki.yaml file name.
fn find_floki_yaml(current_directory: &path::Path) -> Result<path::PathBuf, Error> {
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<path::PathBuf>,
) -> 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(())
}
}
10 changes: 4 additions & 6 deletions src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down Expand Up @@ -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()
Expand Down
85 changes: 6 additions & 79 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use verify::verify_command;

use failure::Error;
use quicli::prelude::*;
use std::path;
use structopt::StructOpt;

fn main() -> CliResult {
Expand All @@ -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
Expand All @@ -65,93 +60,25 @@ 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)
}
}
}

/// 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<path::PathBuf, Error> {
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)
}

0 comments on commit 7317e8c

Please sign in to comment.