Skip to content

Commit

Permalink
Refactor so that Dind now uses DockerCommandBuilder
Browse files Browse the repository at this point in the history
Added further function to `DocekrCommandBuilder` so that `Dind` can be implemented
in terms of `Command`. This allows the functionality of `Command` to be used to
program `Dind`.

Launching `dind` was also moved to the high-level interpreting functions, rather
than being buried deep in one of the command functions. This makes
it clearer when dind is launched.

`DockerCommandBuilder`s can now be instantiated as daemons, which produce a handle to
the daemon docker container. When this handle is dropped, the container is cleaned up.

Signed-off-by: Richard Lupton <[email protected]>
  • Loading branch information
rlupton20 committed Oct 17, 2019
1 parent 2215601 commit a935a18
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 64 deletions.
68 changes: 64 additions & 4 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,42 @@ use crate::errors::{FlokiError, FlokiSubprocessExitStatus};
use failure::Error;
use std::path;
use std::process::{Command, Stdio};
use uuid;

#[derive(Debug, Clone)]
pub struct DockerCommandBuilder {
name: String,
volumes: Vec<(String, String)>,
environment: Vec<(String, String)>,
switches: Vec<String>,
image: String,
}

#[derive(Debug)]
pub struct DaemonHandle {
name: String,
}

impl DaemonHandle {
fn from_builder(builder: DockerCommandBuilder) -> Self {
DaemonHandle { name: builder.name }
}
}
impl Drop for DaemonHandle {
fn drop(&mut self) {
info!("Stopping daemon docker container '{}'", self.name);
Command::new("docker")
.args(&["kill", &self.name])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("Unable to kill docker container")
.wait()
.expect("Unable to wait for docker container to die");
}
}

impl DockerCommandBuilder {
pub fn run(&self, subshell_command: &[&str]) -> Result<(), Error> {
debug!(
Expand Down Expand Up @@ -46,15 +73,51 @@ impl DockerCommandBuilder {
}
}

pub fn start_as_daemon(self, command: &[&str]) -> Result<DaemonHandle, Error> {
debug!("Starting daemon container '{}'", self.name);
let exit_status = Command::new("docker")
.args(&["run", "--rm"])
.args(&["--name", &self.name])
.args(&self.build_volume_switches())
.args(&self.build_environment_switches())
.args(&self.build_docker_switches())
.arg("-d")
.arg(&self.image)
.args(command)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|e| FlokiError::FailedToLaunchDocker { error: e })?
.wait()
.map_err(|e| FlokiError::FailedToCompleteDockerCommand { error: e })?;

if exit_status.success() {
Ok(DaemonHandle::from_builder(self))
} else {
Err(FlokiError::RunContainerFailed {
exit_status: FlokiSubprocessExitStatus {
process_description: "docker run".into(),
exit_status: exit_status,
},
})?
}
}

pub fn new(image: &str) -> Self {
DockerCommandBuilder {
name: uuid::Uuid::new_v4().to_string(),
volumes: Vec::new(),
environment: Vec::new(),
switches: Vec::new(),
image: image.into(),
}
}

pub fn name(&self) -> &str {
&self.name
}

pub fn add_volume(mut self, spec: (&str, &str)) -> Self {
let (src, dst) = spec;
self.volumes.push((src.to_string(), dst.to_string()));
Expand Down Expand Up @@ -126,10 +189,7 @@ pub fn enable_docker_in_docker(
command: DockerCommandBuilder,
dind: &mut crate::dind::Dind,
) -> Result<DockerCommandBuilder, Error> {
debug!("docker-in-docker: {:?}", &dind);
crate::dind::dind_preflight()?;
dind.launch()?;
Ok(command
.add_docker_switch(&format!("--link {}:floki-docker", dind.name))
.add_docker_switch(&format!("--link {}:floki-docker", dind.name()))
.add_environment("DOCKER_HOST", "tcp://floki-docker:2375"))
}
77 changes: 17 additions & 60 deletions src/dind.rs
Original file line number Diff line number Diff line change
@@ -1,80 +1,37 @@
/// Docker-in-docker structures
use failure::Error;
use std::process::{Command, Stdio};
use uuid;

use crate::errors::FlokiError;
use crate::command::{DaemonHandle, DockerCommandBuilder};
use crate::image::{image_exists_locally, pull_image};

#[derive(Debug)]
pub struct Dind {
pub name: String,
// The location of the mount directory to share with the dind container.
mount_source: String,
mount_target: String,
// Have we started a dind container?
started: bool,
command: DockerCommandBuilder,
}

impl Dind {
pub fn new(mount: (&str, &str)) -> Self {
let (src, dst) = mount;
Dind {
name: uuid::Uuid::new_v4().to_string(),
mount_source: src.to_string(),
mount_target: dst.to_string(),
started: false,
command: DockerCommandBuilder::new("docker:stable-dind")
.add_docker_switch("--privileged")
.add_volume(mount),
}
}

pub fn launch(&mut self) -> Result<(), Error> {
info!("Starting docker:dind container with name {}", &self.name);
Command::new("docker")
.args(&[
"run",
"--rm",
"--privileged",
"--name",
&self.name,
"-v",
&format!("{}:{}", self.mount_source, self.mount_target),
"-d",
"docker:stable-dind",
// Newer dind images enable TLS by default, which we want to
// disable, so we specify the dind command by hand.
"dockerd",
"--host=tcp://0.0.0.0:2375",
])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.map_err(|e| FlokiError::FailedToLaunchDocker { error: e })?
.wait()
.map_err(|e| FlokiError::FailedToCompleteDockerCommand { error: e })?;

self.started = true;

info!("docker:dind launched");

Ok(())
pub fn name(&self) -> &str {
self.command.name()
}
}

impl Drop for Dind {
fn drop(&mut self) {
if self.started {
info!("Stopping docker:dind container {}", &self.name);
Command::new("docker")
.args(&["kill", &self.name])
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.expect("Unable to kill docker container")
.wait()
.expect("Unable to wait for docker container to die");
}
pub fn launch(self) -> Result<DaemonHandle, Error> {
info!(
"Starting docker:dind container with name {}",
self.command.name()
);
let handle = self
.command
.start_as_daemon(&["dockerd", "--host=tcp://0.0.0.0:2375"])?;
info!("docker:dind launched");
Ok(handle)
}
}

Expand Down
15 changes: 15 additions & 0 deletions src/interpret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub(crate) fn run_container(

let subshell_command = subshell_command(&config.init, inner_command);

let _handle = launch_dind_if_needed(&config, dind)?;

cmd.run(&[config.shell.outer_shell(), "-c", &subshell_command])
}

Expand Down Expand Up @@ -194,6 +196,19 @@ fn subshell_command(init: &Vec<String>, command: &str) -> String {
args.join(" && ")
}

/// Launching dind if it's needed
fn launch_dind_if_needed(
config: &FlokiConfig,
dind: Dind,
) -> Result<Option<command::DaemonHandle>, Error> {
if config.dind {
crate::dind::dind_preflight()?;
Ok(Some(dind.launch()?))
} else {
Ok(None)
}
}

#[cfg(test)]
mod test {
use super::*;
Expand Down

0 comments on commit a935a18

Please sign in to comment.