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

Merged
merged 5 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 0 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,4 @@ jobs:
- uses: actions/setup-go@v5
with:
go-version: "^1.18"
# Install `patch`, needed to build `krane-bundle`
- run: sudo apt-get install -y patch
- run: make build
60 changes: 52 additions & 8 deletions Cargo.lock

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

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ targets = ["x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl"]

[workspace.metadata.cross.build]
pre-build = [
# install golang and patch for krane-bundle
"apt update && apt --assume-yes install golang-1.22 patch",
# install golang and patch for krane-static
"apt update && apt --assume-yes install golang-1.22",
"update-alternatives --install /usr/bin/go go /usr/lib/go-1.22/bin/go 10",
# give the builder access to the go build and module caches
"mkdir /.cache && chmod a+rw /.cache",
Expand All @@ -51,7 +51,7 @@ bottlerocket-types = { version = "0.0.14", git = "https://github.com/bottlerocke
bottlerocket-variant = { version = "0.1", path = "tools/bottlerocket-variant" }
buildsys = { version = "0.1", path = "tools/buildsys", lib = true, artifact = [ "bin:buildsys" ] }
buildsys-config = { version = "0.1", path = "tools/buildsys-config" }
krane-bundle = { version = "0.1", path = "tools/krane" }
krane-static = { version = "0.1", path = "tools/krane" }
oci-cli-wrapper = { version = "0.1", path = "tools/oci-cli-wrapper" }
parse-datetime = { version = "0.1", path = "tools/parse-datetime" }
pipesys = { version = "0.1", path = "tools/pipesys", lib = true, artifact = [ "bin:pipesys" ] }
Expand Down Expand Up @@ -84,6 +84,7 @@ bytes = "1"
chrono = { version = "0.4", default-features = false }
clap = "4"
coldsnap = { version = "0.6", default-features = false }
ctrlc = "3"
cbgbt marked this conversation as resolved.
Show resolved Hide resolved
daemonize = "0.5"
duct = "0.13"
env_logger = "0.11"
Expand Down Expand Up @@ -124,6 +125,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
19 changes: 8 additions & 11 deletions tools/attribution/attribution.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,20 @@ echo "Clarifying crate dependency licenses..."
cargo --locked Cargo.toml

# =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^=
# go-containerregistry
pushd /src/tools/krane
../build-cache-fetch hashes/crane
TARBALL=$(grep -oP '\(\K[^\)]*' hashes/crane)
GO_CONTAINERREGISTRY_UNPACK_DIR=$(mktemp -d)
tar --strip-components=1 -xvf "${TARBALL}" -C "${GO_CONTAINERREGISTRY_UNPACK_DIR}"

pushd "${GO_CONTAINERREGISTRY_UNPACK_DIR}/cmd/krane"
# krane-static
echo "Clarifying golang dependencies of krane-static"
KRANE_STATIC_VENDOR_DIR=$(mktemp -d)
cp -r /src/tools/krane/go-src/* "${KRANE_STATIC_VENDOR_DIR}"

pushd "${KRANE_STATIC_VENDOR_DIR}"
go mod vendor
popd

/usr/libexec/tools/bottlerocket-license-scan \
--clarify /src/clarify.toml \
--spdx-data /usr/libexec/tools/spdx-data \
--out-dir ${LICENSEDIR}/krane \
go-vendor "${GO_CONTAINERREGISTRY_UNPACK_DIR}/cmd/krane/vendor"
popd
--out-dir ${LICENSEDIR}/krane-static \
go-vendor "${KRANE_STATIC_VENDOR_DIR}/vendor"

# =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^= =^.^=
# cargo-make (we currently use cargo-make from the SDK, but will ship it in Twoliter in the future)
Expand Down
8 changes: 2 additions & 6 deletions tools/krane/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "krane-bundle"
name = "krane-static"
version = "0.1.0"
authors = ["Sean P. Kelly <[email protected]>"]
license = "Apache-2.0 OR MIT"
Expand All @@ -8,11 +8,7 @@ publish = false

[dependencies]
anyhow.workspace = true
flate2.workspace = true
lazy_static.workspace = true
tempfile.workspace = true
libc.workspace = true

[build-dependencies]
flate2.workspace = true
tar.workspace = true
which.workspace = true
8 changes: 3 additions & 5 deletions tools/krane/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
## krane-bundle
## krane-static

This crate packages the `krane` utility from [google/go-containerregistry].

The utility is compiled by a build script, the output of which is compressed and stored in the Rust
crate as via `include_bytes!`.
At runtime, `krane-bundle` writes the decompressed binary to a temp file, passing the
filepath of that file to any caller.
The program is replicated as static library exposed via C FFI.
Rust bindings are provided which imitate `std::process::Command::output`.

[google/go-containerregistry]: https://github.com/google/go-containerregistry
109 changes: 16 additions & 93 deletions tools/krane/build.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,38 @@
use flate2::{read::GzDecoder, write::GzEncoder};
use std::fs::File;
use std::io::{self, prelude::*};
use std::path::{Path, PathBuf};
use std::env;
use std::path::PathBuf;
use std::process::Command;
use std::{env, fs};
use tar::Archive;

const CRANE_VERSION: &str = "0.20.1";

const REQUIRED_TOOLS: &[&str] = &["patch", "go"];
const REQUIRED_TOOLS: &[&str] = &["go"];

fn main() {
let script_dir = env::current_dir().unwrap();
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());

println!("cargo::rerun-if-changed=../build-cache-fetch");
println!("cargo::rerun-if-changed=hashes/crane");
println!("cargo::rerun-if-changed=patches");
println!("cargo::rerun-if-changed=go-src");

ensure_required_tools_installed();

// Download and checksum-verify crane
env::set_current_dir(&out_dir).expect("Failed to set current directory");
Command::new(script_dir.join("../build-cache-fetch"))
.arg(script_dir.join("hashes/crane"))
.status()
.expect("Failed to execute build-cache-fetch");

// extract crane sources
let crane_archive = out_dir.join(format!("go-containerregistry-v{CRANE_VERSION}.tar.gz"));
let crane_tgz = File::open(&crane_archive).expect("Failed to open crane archive");
let mut tar_archive = Archive::new(GzDecoder::new(crane_tgz));

let crane_output_dir = out_dir.join(format!("go-containerregistry-v{CRANE_VERSION}"));
tar_archive
.unpack(&crane_output_dir)
.expect("Failed to extract crane sources");

// Perform any local modifications
let crane_source_dir = crane_output_dir.join(format!("go-containerregistry-{CRANE_VERSION}"));
apply_source_patches(&crane_source_dir, script_dir.join("patches"));

// build krane
let build_output_loc = out_dir.join("krane");
Command::new("go")
.arg("build")
// build krane FFI wrapper
let build_output_loc = out_dir.join("libkrane.a");
let exit_status = Command::new("go")
.env("GOOS", get_goos())
.env("GOARCH", get_goarch())
.arg("build")
.arg("-buildmode=c-archive")
.arg("-o")
.arg(&build_output_loc)
.current_dir(crane_source_dir.join("cmd/krane"))
.arg("main.go")
.current_dir(script_dir.join("go-src"))
.status()
.expect("Failed to build crane");

// compress krane
let krane_gz_path = out_dir.join("krane.gz");
let compressed_output_file =
File::create(&krane_gz_path).expect("Failed to crate krane.gz file");

let krane_binary = File::open(&build_output_loc).expect("Failed to open krane binary");
let mut reader = io::BufReader::new(&krane_binary);
let mut encoder = GzEncoder::new(&compressed_output_file, flate2::Compression::best());

let mut buffer = Vec::with_capacity(
krane_binary
.metadata()
.expect("Failed to get krane binary metadata")
.len() as usize,
assert!(
exit_status.success(),
"Failed to build krane -- go compiler exited nonzero"
);
reader
.read_to_end(&mut buffer)
.expect("Failed to read krane binary");
encoder
.write_all(&buffer)
.expect("Failed to write compressed krane binary");
encoder
.finish()
.expect("Failed to finish writing compressed krane binary");

println!("cargo::rustc-env=KRANE_GZ_PATH={}", krane_gz_path.display());
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=krane");
}

fn ensure_required_tools_installed() {
Expand All @@ -88,42 +42,12 @@ fn ensure_required_tools_installed() {
}
}

fn apply_source_patches(source_path: impl AsRef<Path>, patch_dir: impl AsRef<Path>) {
let source_path = source_path.as_ref();
let patch_dir = patch_dir.as_ref();

let mut patches = fs::read_dir(patch_dir)
.expect("Failed to read patch directory")
.filter_map(|entry| entry.ok())
.map(|entry| entry.path())
.filter(|path| path.extension().map(|ext| ext == "patch").unwrap_or(false))
.collect::<Vec<_>>();
patches.sort();

for patch in patches {
println!("Executing `patch -p1 -i '{}'`", patch.display());

let patch_status = Command::new("patch")
.current_dir(source_path)
.arg("-p1")
.arg("-i")
.arg(patch.as_os_str())
.status()
.expect("Failed to execute patch command");

if !patch_status.success() {
panic!("Failed to apply patch '{}'", patch.display());
}
}
}

fn get_goos() -> &'static str {
let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Failed to read CARGO_CFG_TARGET_OS");
match target_os.as_str() {
"linux" => "linux",
"windows" => "windows",
"macos" => "darwin",
// Add more mappings as needed
other => panic!("Unsupported target OS: {}", other),
}
}
Expand All @@ -137,7 +61,6 @@ fn get_goarch() -> &'static str {
"aarch64" => "arm64",
"arm" => "arm",
"wasm32" => "wasm",
// Add more mappings as needed
other => panic!("Unsupported target architecture: {}", other),
}
}
Loading
Loading