Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require Docker 23, Statically link Krane #423

Open
wants to merge 5 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ tabled = "0.10"
tar = "0.4"
tempfile = "3"
term_size = "0.3"
test-case = "3"
tinytemplate = "1"
tokio = "1"
tokio-stream = "0.1"
Expand Down
5 changes: 5 additions & 0 deletions twoliter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ filetime.workspace = true
flate2.workspace = true
futures.workspace = true
krane-static.workspace = true
lazy_static.workspace = true
log.workspace = true
oci-cli-wrapper.workspace = true
olpc-cjson.workspace = true
Expand All @@ -36,6 +37,7 @@ tokio = { workspace = true, features = ["fs", "macros", "process", "rt-multi-thr
toml.workspace = true
tracing = { workspace = true, features = ["log"] }
uuid = { workspace = true, features = ["v4"] }
which.workspace = true

# Binary dependencies. These are binaries that we want to embed in the Twoliter binary
buildsys = { workspace = true }
Expand All @@ -51,6 +53,9 @@ bytes.workspace = true
flate2.workspace = true
tar.workspace = true

[dev-dependencies]
test-case.workspace = true

[features]
default = ["integ-tests"]
integ-tests = []
6 changes: 5 additions & 1 deletion twoliter/embedded/build.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# syntax=docker/dockerfile:1.4.3
# Twoliter requires minimum Docker version 23.0.0, which ships with a bundled syntax image 1.4.3
# We refrain from an explicit `syntax` directive as this can lead to unwanted network requests at
# build time.
# See https://hub.docker.com/r/docker/dockerfile for more information

# This Dockerfile has three sections which are used to build rpm.spec packages, to create
# kits, and to create Bottlerocket images, respectively. They are marked as Sections 1-3.
# buildsys uses Section 1 during build-package calls, Section 2 during build-kit calls,
Expand Down
23 changes: 23 additions & 0 deletions twoliter/src/docker/commands.rs
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
use crate::common::exec;
use anyhow::{Context, Result};
use semver::Version;
use tokio::process::Command;

pub(crate) struct Docker;

impl Docker {
/// Fetches the version of the docker daemon
pub(crate) async fn server_version() -> Result<Version> {
let version_str = exec(
Command::new("docker").args(["version", "--format", "{{.Server.Version}}"]),
true,
)
.await
// Convert Result<Option<String>> to Option<String>
.ok()
.flatten()
.map(|s| s.trim().to_string())
.context("Failed to fetch docker version")?;

Version::parse(&version_str).context("Failed to parse docker version as semver")
}
}
1 change: 1 addition & 0 deletions twoliter/src/docker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ mod commands;
mod image;

pub(crate) use self::image::ImageUri;
pub(crate) use commands::Docker;
3 changes: 3 additions & 0 deletions twoliter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use anyhow::Result;
use clap::Parser;

mod cargo_make;
pub(crate) mod cleanup;
mod cmd;
mod common;
mod compatibility;
mod docker;
mod preflight;
mod project;
mod schema_version;
/// Test code that should only be compiled when running tests.
Expand All @@ -20,5 +22,6 @@ mod tools;
async fn main() -> Result<()> {
let args = Args::parse();
init_logger(args.log_level);
preflight::preflight().await?;
cmd::run(args).await
}
80 changes: 80 additions & 0 deletions twoliter/src/preflight.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! This module performs checks that the current environment is compatible with twoliter, as well
//! as any other "global" setup that must occur before the build process begins.
use anyhow::{ensure, Result};
use lazy_static::lazy_static;
use semver::{Comparator, Op, Prerelease, VersionReq};
use which::which_global;

use crate::docker::Docker;

const REQUIRED_TOOLS: &[&str] = &["docker", "gzip", "lz4"];

lazy_static! {
// Twoliter relies on minimum Dockerfile syntax 1.4.3, which is shipped in Docker 23.0.0 by default
// We do not use explicit `syntax=` directives to avoid network connections during the build.
static ref MINIMUM_DOCKER_VERSION: VersionReq = VersionReq {
comparators: [
Comparator {
op: Op::GreaterEq,
major: 23,
minor: None,
patch: None,
pre: Prerelease::default(),
}
].into()
};
}

/// Runs all common setup required for twoliter.
///
/// * Ensures that any required system tools are installed an accessible.
/// * Sets up interrupt handler to cleanup on SIGINT
pub(crate) async fn preflight() -> Result<()> {
check_environment().await?;

Ok(())
}

pub(crate) async fn check_environment() -> Result<()> {
check_for_required_tools()?;
check_docker_version().await?;

Ok(())
}

fn check_for_required_tools() -> Result<()> {
for tool in REQUIRED_TOOLS {
ensure!(
which_global(tool).is_ok(),
"Failed to find required tool `{tool}` in PATH"
);
}
Ok(())
}

async fn check_docker_version() -> Result<()> {
let docker_version = Docker::server_version().await?;

ensure!(
MINIMUM_DOCKER_VERSION.matches(&docker_version),
"docker found in PATH does not meet the minimum version requirements for twoliter: {}",
MINIMUM_DOCKER_VERSION.to_string(),
);

Ok(())
}

#[cfg(test)]
mod test {
use super::*;
use semver::Version;
use test_case::test_case;

#[test_case(Version::parse("25.0.5").unwrap(), true; "25.0.5 passes")]
#[test_case(Version::parse("27.1.4").unwrap(), true; "27.1.4 passes")]
#[test_case(Version::parse("18.0.9").unwrap(), false; "18.0.9 fails")]
#[test_case(Version::parse("20.10.27").unwrap(), false)]
fn test_docker_version_req(version: Version, is_ok: bool) {
assert_eq!(MINIMUM_DOCKER_VERSION.matches(&version), is_ok)
}
}