Skip to content

Commit

Permalink
Adding support for Podman
Browse files Browse the repository at this point in the history
- Via the new container_engine parameter in Cross.toml, the user can now
  specify whether they want to use Podman or Docker.
- Several changes to src/docker.rs to pass in different options based on whether
  docker or podman is being used. This also mandates the use of `--userns host`
  which does require bumping the minimum Docker version.
  • Loading branch information
ostrosco committed Nov 9, 2019
1 parent 4fb4fc1 commit e141449
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 61 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ $ cross rustc --target powerpc-unknown-linux-gnu --release -- -C lto
You can place a `Cross.toml` file in the root of your Cargo project to tweak
`cross`'s behavior:

### Using an Alternative Container Engine

`cross` allows the user to specify the container engine they would like to use.
Currently, only Docker and Podman are supported. `cross` will default to using
Docker. For other container engines, you can use the
`target.{{TARGET}}.container_engine` field in `Cross.toml`:

``` toml
[target.aarch64-unknown-linux-gnu]
container_engine = "podman"
```

### Custom Docker images

`cross` provides default Docker images for the targets listed below. However, it
Expand Down
120 changes: 60 additions & 60 deletions src/docker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use std::{env, fs};

use atty::Stream;
use error_chain::bail;
use lazy_static::lazy_static;
use semver::{Version, VersionReq};

use crate::{Target, Toml};
use crate::cargo::Root;
Expand All @@ -14,45 +12,19 @@ use crate::extensions::CommandExt;
use crate::id;

const DOCKER_IMAGES: &[&str] = &include!(concat!(env!("OUT_DIR"), "/docker-images.rs"));
const DOCKER: &str = "docker";

lazy_static! {
/// Retrieve the Docker Daemon version.
///
/// # Panics
/// Panics if the version cannot be retrieved or parsed
static ref DOCKER_VERSION: Version = {
let version_string = Command::new("docker")
.arg("version")
.arg("--format={{.Server.APIVersion}}")
.run_and_get_stdout(false)
.expect("Unable to obtain Docker version");
// API versions don't have "patch" version
Version::parse(&format!("{}.0", version_string.trim()))
.expect("Cannot parse Docker engine version")
};

/// Version requirements for user namespace.
///
/// # Panics
/// Panics if the parsing fails
static ref USERNS_REQUIREMENT: VersionReq = {
VersionReq::parse(">= 1.24")
.expect("Unable to parse version requirements")
};
}

/// Add the `userns` flag, if needed
pub fn docker_command(subcommand: &str) -> Command {
let mut docker = Command::new("docker");
pub fn docker_command(container_engine: &str, subcommand: &str) -> Command {
let mut docker = Command::new(container_engine);
docker.arg(subcommand);
if USERNS_REQUIREMENT.matches(&DOCKER_VERSION) {
docker.args(&["--userns", "host"]);
}

// We always add the `---userns host` flag for compatibility with Podman.
docker.args(&["--userns", "host"]);
docker
}

/// Register binfmt interpreters
pub fn register(target: &Target, verbose: bool) -> Result<()> {
pub fn register(target: &Target, toml: Option<&Toml>, verbose: bool) -> Result<()> {
let cmd = if target.is_windows() {
// https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html
"mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc && \
Expand All @@ -61,7 +33,15 @@ pub fn register(target: &Target, verbose: bool) -> Result<()> {
"apt-get update && apt-get install --no-install-recommends -y \
binfmt-support qemu-user-static"
};
docker_command("run")

let mut container_engine = DOCKER.to_string();
if let Some(toml) = toml {
if let Some(engine) = toml.container_engine(target)? {
container_engine = engine;
}
}

docker_command(&container_engine, "run")
.arg("--privileged")
.arg("--rm")
.arg("ubuntu:16.04")
Expand Down Expand Up @@ -101,17 +81,50 @@ pub fn run(target: &Target,
// We create/regenerate the lockfile on the host system because the Docker
// container doesn't have write access to the root of the Cargo project
let cargo_toml = root.join("Cargo.toml");

let mut runner = None;
let mut container_engine = DOCKER.to_string();

Command::new("cargo").args(&["fetch",
"--manifest-path",
&cargo_toml.display().to_string()])
.run(verbose)
.chain_err(|| "couldn't generate Cargo.lock")?;

let mut docker = docker_command("run");
if let Some(toml) = toml {
runner = toml.runner(target)?;
if let Some(engine) = toml.container_engine(target)? {
container_engine = engine;
}
}
let mut docker = docker_command(&container_engine, "run");

if let Some(toml) = toml {
for var in toml.env_passthrough(target)? {
if var.contains('=') {
bail!("environment variable names must not contain the '=' character");
}

if var == "CROSS_RUNNER" {
bail!(
"CROSS_RUNNER environment variable name is reserved and cannot be pass through"
);
}

// Only specifying the environment variable name in the "-e"
// flag forwards the value from the parent shell
docker.args(&["-e", var]);
}
}

docker.arg("--rm");

// We need to specify the user for Docker, but not for Podman.
if container_engine == DOCKER {
docker.args(&["--user", &format!("{}:{}", id::user(), id::group())]);
}

docker
.arg("--rm")
.args(&["--user", &format!("{}:{}", id::user(), id::group())])
.args(&["-e", "XARGO_HOME=/xargo"])
.args(&["-e", "CARGO_HOME=/cargo"])
.args(&["-e", "CARGO_TARGET_DIR=/target"])
Expand All @@ -130,31 +143,18 @@ pub fn run(target: &Target,
docker.args(&opts);
}

let mut runner = None;

if let Some(toml) = toml {
for var in toml.env_passthrough(target)? {
if var.contains("=") {
bail!("environment variable names must not contain the '=' character");
}

if var == "CROSS_RUNNER" {
bail!("CROSS_RUNNER environment variable name is reserved and cannot be pass through");
}

// Only specifying the environment variable name in the "-e"
// flag forwards the value from the parent shell
docker.args(&["-e", var]);
}
docker
.args(&["-e", &format!("CROSS_RUNNER={}", runner.unwrap_or_else(|| String::new()))])
.args(&["-v", &format!("{}:/xargo:Z", xargo_dir.display())])
.args(&["-v", &format!("{}:/cargo:Z", cargo_dir.display())]);

runner = toml.runner(target)?;
// Prevent `bin` from being mounted inside the Docker container. This is
// not required for Podman.
if container_engine == DOCKER {
docker.args(&["-v", "/cargo/bin"]);
}

docker
.args(&["-e", &format!("CROSS_RUNNER={}", runner.unwrap_or_else(|| String::new()))])
.args(&["-v", &format!("{}:/xargo:Z", xargo_dir.display())])
.args(&["-v", &format!("{}:/cargo:Z", cargo_dir.display())])
.args(&["-v", "/cargo/bin"]) // Prevent `bin` from being mounted inside the Docker container.
.args(&["-v", &format!("{}:/project:Z,ro", root.display())])
.args(&["-v", &format!("{}:/rust:Z,ro", sysroot.display())])
.args(&["-v", &format!("{}:/target:Z", target_dir.display())])
Expand Down
23 changes: 22 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ fn run() -> Result<ExitStatus> {
needs_interpreter &&
target.needs_interpreter() &&
!interpreter::is_registered(&target)? {
docker::register(&target, verbose)?
docker::register(&target, toml.as_ref(), verbose)?
}

return docker::run(&target,
Expand Down Expand Up @@ -329,6 +329,27 @@ impl Toml {
}
}

/// Returns the `target.{}.container_engine` part of `Cross.toml`
pub fn container_engine(&self, target: &Target) -> Result<Option<String>> {
let triple = target.triple();

if let Some(value) = self
.table
.get("target")
.and_then(|t| t.get(triple))
.and_then(|t| t.get("container_engine"))
{
let value = value
.as_str()
.ok_or_else(|| format!("target.{}.container_engine must be a string", triple))?
.to_string();
Ok(Some(value))
} else {
Ok(None)
}
}


/// Returns the `build.image` or the `target.{}.xargo` part of `Cross.toml`
pub fn xargo(&self, target: &Target) -> Result<Option<bool>> {
let triple = target.triple();
Expand Down

0 comments on commit e141449

Please sign in to comment.