Skip to content

Commit

Permalink
Merge pull request #305 from jmt-lab/crane-support
Browse files Browse the repository at this point in the history
twoliter: add support for crane
  • Loading branch information
jmt-lab committed Jun 25, 2024
2 parents a765c3f + 3e5c3c6 commit 13dc97b
Show file tree
Hide file tree
Showing 9 changed files with 430 additions and 121 deletions.
35 changes: 35 additions & 0 deletions 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 @@ -5,6 +5,7 @@ members = [
"tools/bottlerocket-variant",
"tools/buildsys",
"tools/buildsys-config",
"tools/oci-cli-wrapper",
"tools/parse-datetime",
"tools/pipesys",
"tools/pubsys",
Expand Down
17 changes: 17 additions & 0 deletions tools/oci-cli-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "oci-cli-wrapper"
version = "0.1.0"
authors = ["Jarrett Tierney <jmt@amazon.com>"]
license = "Apache-2.0 OR MIT"
edition = "2021"
publish = false

[dependencies]
async-trait = "0.1"
serde = { version = "1", features = ["derive"]}
serde_json = "1"
snafu = "0.8"
tar = "0.4"
tempfile = "3"
tokio = { version = "1.32", features = ["process"] }
which = "6"
51 changes: 51 additions & 0 deletions tools/oci-cli-wrapper/src/cli.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use snafu::{ensure, ResultExt};
use std::path::PathBuf;
use tokio::process::Command;

use crate::{error, Result};

pub(crate) struct CommandLine {
pub(crate) path: PathBuf,
}

impl CommandLine {
pub(crate) async fn output(&self, args: &[&str], error_msg: String) -> Result<Vec<u8>> {
let output = Command::new(&self.path)
.args(args)
.output()
.await
.context(error::CommandFailedSnafu { message: error_msg })?;
ensure!(
output.status.success(),
error::OperationFailedSnafu {
message: String::from_utf8_lossy(&output.stderr),
program: self.path.clone(),
args: args.iter().map(|x| x.to_string()).collect::<Vec<_>>()
}
);
Ok(output.stdout)
}

pub(crate) async fn spawn(&self, args: &[&str], error_msg: String) -> Result<()> {
let status = Command::new(&self.path)
.args(args)
.spawn()
.context(error::CommandFailedSnafu {
message: error_msg.clone(),
})?
.wait()
.await
.context(error::CommandFailedSnafu {
message: error_msg.clone(),
})?;
ensure!(
status.success(),
error::OperationFailedSnafu {
message: error_msg.clone(),
program: self.path.clone(),
args: args.iter().map(|x| x.to_string()).collect::<Vec<_>>()
}
);
Ok(())
}
}
46 changes: 46 additions & 0 deletions tools/oci-cli-wrapper/src/crane.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::path::Path;

use async_trait::async_trait;
use snafu::ResultExt;

use crate::{cli::CommandLine, error, ConfigView, ImageTool, ImageView, Result};

pub struct CraneCLI {
pub(crate) cli: CommandLine,
}

#[async_trait]
impl ImageTool for CraneCLI {
async fn pull_oci_image(&self, path: &Path, uri: &str) -> Result<()> {
let archive_path = path.to_string_lossy();
self.cli
.spawn(
&["pull", "--format", "oci", uri, archive_path.as_ref()],
format!("failed to pull image archive from {}", uri),
)
.await?;
Ok(())
}

async fn get_manifest(&self, uri: &str) -> Result<Vec<u8>> {
self.cli
.output(
&["manifest", uri],
format!("failed to fetch manifest for resource at {}", uri),
)
.await
}

async fn get_config(&self, uri: &str) -> Result<ConfigView> {
let bytes = self
.cli
.output(
&["config", uri],
format!("failed to fetch image config from {}", uri),
)
.await?;
let image_view: ImageView =
serde_json::from_slice(bytes.as_slice()).context(error::ConfigDeserializeSnafu)?;
Ok(image_view.config)
}
}
71 changes: 71 additions & 0 deletions tools/oci-cli-wrapper/src/docker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use std::path::Path;

use async_trait::async_trait;
use snafu::ResultExt;
use std::fs::File;
use tar::Archive;
use tempfile::NamedTempFile;

use crate::cli::CommandLine;
use crate::{error, ConfigView, ImageTool, Result};

pub struct DockerCLI {
pub(crate) cli: CommandLine,
}

#[async_trait]
impl ImageTool for DockerCLI {
async fn pull_oci_image(&self, path: &Path, uri: &str) -> Result<()> {
// First we pull the image to local daemon
self.cli
.spawn(
&["pull", uri],
format!("failed to pull image to local docker from {}", uri),
)
.await?;
// Now we can use docker save to save the archive to a temppath
let temp_file = NamedTempFile::new_in(path).context(crate::error::DockerTempSnafu)?;
let tmp_path = temp_file.path().to_string_lossy();
self.cli
.spawn(
&["save", uri, "-o", tmp_path.as_ref()],
format!("failed to save image archive from {} to {}", uri, tmp_path),
)
.await?;
let archive_file = File::open(temp_file.path()).context(crate::error::ArchiveReadSnafu)?;
let mut archive = Archive::new(archive_file);
archive
.unpack(path)
.context(crate::error::ArchiveExtractSnafu)?;
Ok(())
}

async fn get_manifest(&self, uri: &str) -> Result<Vec<u8>> {
self.cli
.output(
&["manifest", "inspect", uri],
format!("failed to inspect manifest of resource at {}", uri),
)
.await
}

async fn get_config(&self, uri: &str) -> Result<ConfigView> {
self.cli
.spawn(&["pull", uri], format!("failed to pull image from {}", uri))
.await?;
let bytes = self
.cli
.output(
&[
"image",
"inspect",
uri,
"--format",
"\"{{ json .Config }}\"",
],
format!("failed to fetch image config from {}", uri),
)
.await?;
serde_json::from_slice(bytes.as_slice()).context(error::ConfigDeserializeSnafu)
}
}
Loading

0 comments on commit 13dc97b

Please sign in to comment.