From 3a960e45737bd85071e67d3eca61fe50608f6016 Mon Sep 17 00:00:00 2001 From: Alex Huszagh Date: Fri, 30 Sep 2022 07:27:38 -0500 Subject: [PATCH] Fix image build errors with Podman and NerdCTL. Ensures that Docker and NerdCTL, which support the custom `--output` and `--cache-from` flags use them, while Podman and unknown container engines use the strict, minimal subset Podman supports. This is because Podman only supports a registry/repository, without a tag, for `--cache-from`, which means it synchronizes poorly with our target subs, like `centos`. Therefore, unless unsupported, we should always use the features available for our container engine. This also fixes the `--pull` flag on NerdCTL, which is unsupported. Engines without BuildKit extensions by default, such as NerdCTL, now do not use `buildx` unless explicitly enabled with `CROSS_CONTAINER_ENGINE_NO_BUILDKIT=0`. --- .changes/1033.json | 5 ++++ .github/workflows/ci.yml | 41 ++++++++++++++++++++++++++++++++- ci/test-podman.sh | 41 +++++++++++++++++++++++++++++++++ src/docker/custom.rs | 2 +- src/docker/engine.rs | 30 ++++++++++++++++++++++-- xtask/src/build_docker_image.rs | 12 ++++++---- 6 files changed, 123 insertions(+), 8 deletions(-) create mode 100644 .changes/1033.json create mode 100755 ci/test-podman.sh diff --git a/.changes/1033.json b/.changes/1033.json new file mode 100644 index 000000000..435d43df6 --- /dev/null +++ b/.changes/1033.json @@ -0,0 +1,5 @@ +{ + "description": "fix --cache-from using podman.", + "type": "fixed", + "issues": [1031] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1e88e30a0..792e11923 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -319,6 +319,45 @@ jobs: run: ./ci/test-docker-in-docker.sh shell: bash + podman: + name: podman + runs-on: ubuntu-latest + needs: [shellcheck, test, check] + if: github.event_name == 'push' + strategy: + fail-fast: false + outputs: + has-image: ${{ steps.prepare-meta.outputs.has-image }} + images: ${{ steps.build-docker-image.outputs.images && fromJSON(steps.build-docker-image.outputs.images) }} + coverage-artifact: ${{ steps.cov.outputs.artifact-name }} + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/actions/setup-rust + + - name: Install Podman + env: + DEBIAN_FRONTEND: noninteractive + run: | + sudo apt-get update + sudo apt-get install podman --no-install-recommends --assume-yes + + - name: LLVM instrument coverage + id: cov + uses: ./.github/actions/cargo-llvm-cov + with: + name: cross-podman-aarch64-unknown-linux-gnu + + - name: Install cross + run: cargo install --path . --force --debug + + - name: Run Podman Test + run: ./ci/test-podman.sh + env: + CROSS_CONTAINER_ENGINE: podman + TARGET: aarch64-unknown-linux-gnu + shell: bash + publish: needs: [build, check, fmt, clippy, cargo-deny] runs-on: ubuntu-latest @@ -331,7 +370,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} conclusion: - needs: [shellcheck, fmt, clippy, test, generate-matrix, build, publish, check, remote, bisect, docker-in-docker, foreign] + needs: [shellcheck, fmt, clippy, test, generate-matrix, build, publish, check, remote, bisect, docker-in-docker, foreign, podman] if: always() runs-on: ubuntu-latest steps: diff --git a/ci/test-podman.sh b/ci/test-podman.sh new file mode 100755 index 000000000..774977c79 --- /dev/null +++ b/ci/test-podman.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091,SC1090 + +# test to see that running and building images with podman works. + +set -x +set -eo pipefail + +export CROSS_CONTAINER_ENGINE=podman +if [[ -z "${TARGET}" ]]; then + export TARGET="aarch64-unknown-linux-gnu" +fi + +ci_dir=$(dirname "${BASH_SOURCE[0]}") +ci_dir=$(realpath "${ci_dir}") +. "${ci_dir}"/shared.sh + +main() { + local td= + local parent= + local target="${TARGET}" + + retry cargo fetch + cargo build + export CROSS="${PROJECT_HOME}/target/debug/cross" + + td="$(mkcargotemp -d)" + parent=$(dirname "${td}") + pushd "${td}" + cargo init --bin --name "hello" . + + echo '[build] +pre-build = ["apt-get update"]' > "${parent}/Cross.toml" + + CROSS_CONTAINER_ENGINE="${CROSS_ENGINE}" "${CROSS}" build --target "${target}" --verbose + + popd + rm -rf "${td}" +} + +main diff --git a/src/docker/custom.rs b/src/docker/custom.rs index b6c916e20..b8ef31055 100644 --- a/src/docker/custom.rs +++ b/src/docker/custom.rs @@ -72,7 +72,7 @@ impl<'a> Dockerfile<'a> { ) -> Result { let uses_zig = options.cargo_variant.uses_zig(); let mut docker_build = docker::command(&options.engine); - match docker::Engine::has_buildkit() { + match options.engine.has_buildkit() { true => docker_build.args(["buildx", "build"]), false => docker_build.arg("build"), }; diff --git a/src/docker/engine.rs b/src/docker/engine.rs index f36b91ddd..cc881f70b 100644 --- a/src/docker/engine.rs +++ b/src/docker/engine.rs @@ -17,6 +17,7 @@ pub enum EngineType { Docker, Podman, PodmanRemote, + Nerdctl, Other, } @@ -32,6 +33,29 @@ impl EngineType { pub fn is_docker(&self) -> bool { matches!(self, Self::Docker) } + + /// Returns `true` if the engine build supports the `--output` flag. + #[must_use] + pub fn supports_output_flag(&self) -> bool { + !matches!(self, Self::Other) + } + + /// Returns `true` if the engine build supports the `--pull` flag. + #[must_use] + pub fn supports_pull_flag(&self) -> bool { + !matches!(self, Self::Nerdctl | Self::Other) + } + + /// Returns `true` if the engine build supports the `--cache-from type=` key. + /// + /// Some container engines, especially podman, do not support the `type` + /// key of `--cache-from` during the image build steps. They also do + /// not support any tags for the `--cache-from` steps either. See: + /// https://docs.podman.io/en/latest/markdown/podman-build.1.html#cache-from + #[must_use] + pub fn supports_cache_from_type(&self) -> bool { + matches!(self, Self::Docker | Self::Nerdctl) + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -110,10 +134,10 @@ impl Engine { } #[must_use] - pub fn has_buildkit() -> bool { + pub fn has_buildkit(&self) -> bool { !env::var("CROSS_CONTAINER_ENGINE_NO_BUILDKIT") .map(|x| bool_from_envvar(&x)) - .unwrap_or_default() + .unwrap_or(self.kind == EngineType::Nerdctl) } } @@ -132,6 +156,8 @@ fn get_engine_info( EngineType::PodmanRemote } else if stdout_help.contains("podman") { EngineType::Podman + } else if stdout_help.contains("nerdctl") { + EngineType::Nerdctl } else if stdout_help.contains("docker") && !stdout_help.contains("emulate") { EngineType::Docker } else { diff --git a/xtask/src/build_docker_image.rs b/xtask/src/build_docker_image.rs index 07df5bf95..53b4aaae4 100644 --- a/xtask/src/build_docker_image.rs +++ b/xtask/src/build_docker_image.rs @@ -176,7 +176,7 @@ pub fn build_docker_image( msg_info.note(format_args!("Build {target} for {}", platform.target))?; } let mut docker_build = docker::command(engine); - let has_buildkit = docker::Engine::has_buildkit(); + let has_buildkit = engine.has_buildkit(); match has_buildkit { true => docker_build.args(["buildx", "build"]), false => docker_build.arg("build"), @@ -187,7 +187,7 @@ pub fn build_docker_image( if push { docker_build.arg("--push"); - } else if engine.kind.is_docker() && no_output { + } else if engine.kind.supports_output_flag() && no_output { docker_build.args(["--output", "type=tar,dest=/dev/null"]); } else if has_buildkit { docker_build.arg("--load"); @@ -216,10 +216,12 @@ pub fn build_docker_image( tags = vec![target.image_name(&repository, tag)]; } - docker_build.arg("--pull"); + if engine.kind.supports_pull_flag() { + docker_build.arg("--pull"); + } if no_cache { docker_build.arg("--no-cache"); - } else { + } else if engine.kind.supports_cache_from_type() { docker_build.args([ "--cache-from", &format!( @@ -227,6 +229,8 @@ pub fn build_docker_image( target.image_name(&repository, "main") ), ]); + } else { + docker_build.args(["--cache-from", &format!("{repository}/{}", target.name)]); } if push {