Skip to content

Commit

Permalink
always use buildx build and set output for custom dockerfile builds
Browse files Browse the repository at this point in the history
bumps minimal versions
  • Loading branch information
Emilgardis committed Jul 15, 2022
1 parent e729e88 commit edf1e17
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 67 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ One of these container engines is required. If both are installed, `cross` will
default to `docker`.

- [Docker]. Note that on Linux non-sudo users need to be in the `docker` group.
Read the official [post-installation steps][post]. Requires version 1.24 or later.
Read the official [post-installation steps][post]. Requires version 20.10 (API 1.40) or later.

[post]: https://docs.docker.com/install/linux/linux-postinstall/

- [Podman]. Requires version 1.6.3 or later.
- [Podman]. Requires version 3.4.0 or later.

## Installation

Expand Down
16 changes: 12 additions & 4 deletions src/docker/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;

use crate::docker::{DockerOptions, DockerPaths};
use crate::docker::{self, DockerOptions, DockerPaths};
use crate::shell::MessageInfo;
use crate::{docker, CargoMetadata, TargetTriple};
use crate::{errors::*, file, CommandExt, ToUtf8};
use crate::{CargoMetadata, TargetTriple};

use super::{get_image_name, parse_docker_opts, path_hash, ImagePlatform};

Expand Down Expand Up @@ -70,7 +70,8 @@ impl<'a> Dockerfile<'a> {
build_args: impl IntoIterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
msg_info: &mut MessageInfo,
) -> Result<String> {
let mut docker_build = docker::subcommand(&options.engine, "build");
let mut docker_build = docker::subcommand(&options.engine, "buildx");
docker_build.arg("build");
docker_build.env("DOCKER_SCAN_SUGGEST", "false");
self.runs_with()
.specify_platform(&options.engine, &mut docker_build);
Expand Down Expand Up @@ -140,9 +141,16 @@ impl<'a> Dockerfile<'a> {
docker_build.args(["--file".into(), path]);

if let Some(build_opts) = options.config.build_opts() {
// FIXME: Use shellwords
docker_build.args(parse_docker_opts(&build_opts)?);
}

let has_output = options.config.build_opts().map_or(false, |opts| {
opts.contains("--load") || opts.contains("--output")
});
if options.engine.kind.is_docker() && !has_output {
docker_build.args(&["--output", "type=docker"]);
};

if let Some(context) = self.context() {
docker_build.arg(&context);
} else {
Expand Down
147 changes: 88 additions & 59 deletions src/docker/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ pub enum EngineType {
Other,
}

impl EngineType {
/// Returns `true` if the engine type is [`Podman`](Self::Podman) or [`PodmanRemote`](Self::PodmanRemote).
#[must_use]
pub fn is_podman(&self) -> bool {
matches!(self, Self::Podman | Self::PodmanRemote)
}

/// Returns `true` if the engine type is [`Docker`](EngineType::Docker).
#[must_use]
pub fn is_docker(&self) -> bool {
matches!(self, Self::Docker)
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Engine {
pub kind: EngineType,
Expand Down Expand Up @@ -117,82 +131,97 @@ fn get_engine_info(
EngineType::Other
};

let mut cmd = Command::new(ce);
cmd.args(&["version", "-f", "{{ .Server.Os }},,,{{ .Server.Arch }}"]);

let out = cmd.run_and_get_output(msg_info)?;

let stdout = out.stdout()?.to_lowercase();

let osarch = stdout
.trim()
.split_once(",,,")
.map(|(os, arch)| -> Result<_> { Ok((ContainerOs::new(os)?, Architecture::new(arch)?)) })
.transpose();

let osarch = match (kind, osarch) {
(_, Ok(Some(osarch))) => Some(osarch),
(EngineType::PodmanRemote | EngineType::Podman, Ok(None)) => get_podman_info(ce, msg_info)?,
(_, Err(e)) => {
return Err(e.wrap_err(format!(
"command `{}` returned unexpected data",
cmd.command_pretty(msg_info, |_| false)
)));
// this can fail: podman can give partial output
// linux,,,Error: template: version:1:15: executing "version" at <.Arch>:
// can't evaluate field Arch in type *define.Version
let os_arch_server = engine_info(
ce,
&["version", "-f", "{{ .Server.Os }},,,{{ .Server.Arch }}"],
",,,",
msg_info,
);

let (os_arch_other, os_arch_server_result) = match os_arch_server {
Ok(Some(os_arch)) => (Ok(Some(os_arch)), None),
result => {
if kind.is_podman() {
(get_podman_info(ce, msg_info), result.err())
} else {
(get_custom_info(ce, msg_info), result.err())
}
}
(EngineType::Docker | EngineType::Other, Ok(None)) => None,
};

let osarch = if osarch.is_some() {
osarch
} else if !out.status.success() {
get_custom_info(ce, msg_info).with_error(|| {
cmd.status_result(msg_info, out.status, Some(&out))
.expect_err("status_result should error")
})?
} else {
get_custom_info(ce, msg_info)?
let os_arch = match (os_arch_other, os_arch_server_result) {
(Ok(os_arch), _) => os_arch,
(Err(e), Some(server_err)) => return Err(server_err.to_section_report().with_error(|| e)),
(Err(e), None) => return Err(e.to_section_report()),
};

let (os, arch) = osarch.map_or(<_>::default(), |(os, arch)| (Some(os), Some(arch)));
let (os, arch) = os_arch.map_or(<_>::default(), |(os, arch)| (Some(os), Some(arch)));
Ok((kind, arch, os))
}

fn get_podman_info(
#[derive(Debug, thiserror::Error)]
pub enum EngineInfoError {
#[error(transparent)]
Eyre(eyre::Report),
#[error("could not get os and arch")]
CommandError(#[from] CommandError),
}

impl EngineInfoError {
pub fn to_section_report(self) -> eyre::Report {
match self {
EngineInfoError::Eyre(e) => e,
EngineInfoError::CommandError(e) => {
e.to_section_report().wrap_err("could not get os and arch")
}
}
}
}

/// Get engine info
fn engine_info(
ce: &Path,
args: &[&str],
sep: &str,
msg_info: &mut MessageInfo,
) -> Result<Option<(ContainerOs, Architecture)>> {
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
let mut cmd = Command::new(ce);
cmd.args(&["info", "-f", "{{ .Version.OsArch }}"]);
cmd.run_and_get_stdout(msg_info)
.map(|s| {
s.to_lowercase()
.trim()
.split_once('/')
.map(|(os, arch)| -> Result<_> {
Ok((ContainerOs::new(os)?, Architecture::new(arch)?))
})
})
.wrap_err("could not determine os and architecture of vm")?
cmd.args(args);
let out = cmd
.run_and_get_output(msg_info)
.map_err(EngineInfoError::Eyre)?;

cmd.status_result(msg_info, out.status, Some(&out))?;

out.stdout()?
.to_lowercase()
.trim()
.split_once(sep)
.map(|(os, arch)| -> Result<_> { Ok((ContainerOs::new(os)?, Architecture::new(arch)?)) })
.transpose()
.map_err(EngineInfoError::Eyre)
}

fn get_podman_info(
ce: &Path,
msg_info: &mut MessageInfo,
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
engine_info(ce, &["info", "-f", "{{ .Version.OsArch }}"], "/", msg_info)
}

fn get_custom_info(
ce: &Path,
msg_info: &mut MessageInfo,
) -> Result<Option<(ContainerOs, Architecture)>> {
let mut cmd = Command::new(ce);
cmd.args(&["info", "-f", "{{ .Client.Os }},,,{{ .Client.Arch }}"]);
cmd.run_and_get_stdout(msg_info)
.map(|s| {
s.to_lowercase()
.trim()
.split_once(",,,")
.map(|(os, arch)| -> Result<_> {
Ok((ContainerOs::new(os)?, Architecture::new(arch)?))
})
})
.unwrap_or_default()
.transpose()
) -> Result<Option<(ContainerOs, Architecture)>, EngineInfoError> {
engine_info(
ce,
&["version", "-f", "{{ .Client.Os }},,,{{ .Client.Arch }}"],
",,,",
msg_info,
)
}

pub fn get_container_engine() -> Result<PathBuf, which::Error> {
Expand Down
2 changes: 1 addition & 1 deletion src/docker/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ pub(crate) fn docker_seccomp(
) -> Result<()> {
// docker uses seccomp now on all installations
if target.needs_docker_seccomp() {
let seccomp = if engine_type == EngineType::Docker && cfg!(target_os = "windows") {
let seccomp = if engine_type.is_docker() && cfg!(target_os = "windows") {
// docker on windows fails due to a bug in reading the profile
// /~https://github.com/docker/for-win/issues/12760
"unconfined".to_owned()
Expand Down
2 changes: 1 addition & 1 deletion xtask/src/build_docker_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ pub fn build_docker_image(

if push {
docker_build.arg("--push");
} else if no_output {
} else if engine.kind.is_docker() && no_output {
docker_build.args(&["--output", "type=tar,dest=/dev/null"]);
} else {
docker_build.arg("--load");
Expand Down

0 comments on commit edf1e17

Please sign in to comment.