diff --git a/src/docker.rs b/src/docker.rs index ff58946bf..9e09c4704 100644 --- a/src/docker.rs +++ b/src/docker.rs @@ -1,4 +1,4 @@ -use std::io::Read; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; use std::{env, fs}; @@ -259,14 +259,20 @@ pub fn run( .run_and_get_status(verbose) } -/// (Assemble) and return the image to execute inside -pub fn image_assemble( - verbose: bool, - config: &Config, - target: &Target, - host_root: &Path, -) -> Result { - if let Some(dockerfile) = config.dockerfile(target)? { +#[derive(Debug, PartialEq)] +pub enum Dockerfile<'a> { + File { + path: &'a str, + context: Option<&'a str>, + name: Option<&'a str>, + }, + Stdin { + content: &'a str, + }, +} + +impl<'a> Dockerfile<'a> { + pub fn build(&self, verbose: bool, host_root: &Path) -> Result { let mut docker_build = docker_command("build")?; docker_build.current_dir(host_root); docker_build.env("DOCKER_SCAN_SUGGEST", "false"); @@ -277,34 +283,87 @@ pub fn image_assemble( docker_build.args(["--iidfile".as_ref(), iidfile_path.as_os_str()]); - docker_build.args(["--file", &dockerfile]); - - if let Some(image) = config.image(target)? { + if let Some(image) = self.image_name() { docker_build.args(["--tag", &image]); }; - if let Some(context) = config.dockerfile_context(target)? { + if let Some(context) = self.context() { docker_build.arg(&context); } else { // This `.` is techically the host_root, not cwd + // When using Dockerfile::Stdin, this will be ignored by docker. docker_build.arg("."); } + + match self { + Dockerfile::File { path, .. } => { + docker_build.args(["--file", &path]); + } + Dockerfile::Stdin { content } => { + // XXX: Ugly hack to circumvent platform issues. we could use echo + let stdin_path = tempfile::NamedTempFile::new() + .wrap_err("could not create a temporary file")? + .into_temp_path(); + let mut file = std::fs::File::options().write(true).open(&stdin_path)?; + file.write_all(content.as_bytes())?; + file.sync_data()?; + docker_build.arg("-").stdin(file); + } + } + docker_build .run(verbose) - .wrap_err_with(|| format!("could not build dockerfile `{dockerfile}`"))?; + .wrap_err_with(|| format!("build failed"))?; - let image_name = if let Some(image) = config.image(target)? { - image + let image_name = if let Some(image) = self.image_name() { + image.to_owned() } else { let mut image_name = String::new(); std::fs::File::open(&iidfile_path)?.read_to_string(&mut image_name)?; image_name }; + if image_name.is_empty() { eyre::bail!("no image returned from docker build") } - std::fs::remove_file(iidfile_path)?; Ok(image_name) + } + + fn image_name(&self) -> Option<&'a str> { + match self { + Dockerfile::File { + name: Some(name), .. + } => Some(name), + _ => None, + } + } + + fn context(&self) -> Option<&'a str> { + match self { + Dockerfile::File { + context: Some(context), + .. + } => Some(context), + _ => None, + } + } +} + +/// (Assemble) and return the image to execute inside +pub fn image_assemble( + verbose: bool, + config: &Config, + target: &Target, + host_root: &Path, +) -> Result { + if let Some(dockerfile) = config.dockerfile(target)? { + Dockerfile::File { + path: &dockerfile, + context: config.dockerfile_context(&target)?.as_deref(), + name: config.image(&target)?.as_deref(), + } + .build(verbose, host_root) + .wrap_err("when building dockerfile") } else { Ok(image(config, target)?) }