Skip to content

Commit

Permalink
Merge pull request #317 from sam-berning/crane-push
Browse files Browse the repository at this point in the history
pubsys: enable the use of crane to publish kits
  • Loading branch information
sam-berning committed Jul 11, 2024
2 parents 8a29af7 + f02c9c6 commit bc62668
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 191 deletions.
37 changes: 2 additions & 35 deletions Cargo.lock

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

2 changes: 0 additions & 2 deletions tools/buildsys-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,3 @@ publish = false
exclude = ["README.md"]

[dependencies]
anyhow = "1"
serde = { version = "1", features = ["derive"]}
32 changes: 0 additions & 32 deletions tools/buildsys-config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,2 @@
use anyhow::anyhow;
use serde::Deserialize;
use std::fmt::{Display, Formatter};

pub const EXTERNAL_KIT_DIRECTORY: &str = "build/external-kits";
pub const EXTERNAL_KIT_METADATA: &str = "build/external-kits/external-kit-metadata.json";

#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum DockerArchitecture {
Amd64,
Arm64,
}

impl TryFrom<&str> for DockerArchitecture {
type Error = anyhow::Error;

fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
match value {
"x86_64" | "amd64" => Ok(DockerArchitecture::Amd64),
"aarch64" | "arm64" => Ok(DockerArchitecture::Arm64),
_ => Err(anyhow!("invalid architecture '{}'", value)),
}
}
}

impl Display for DockerArchitecture {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Self::Amd64 => "amd64",
Self::Arm64 => "arm64",
})
}
}
1 change: 0 additions & 1 deletion tools/buildsys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ pipesys = { version = "0.1", path = "../pipesys" }
rand = { version = "0.8", default-features = false, features = ["std", "std_rng"] }
regex = "1"
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "blocking"] }
semver = { version = "1", features = ["serde"]}
serde = { version = "1", features = ["derive"] }
serde_plain = "1"
serde_json = "1"
Expand Down
1 change: 1 addition & 0 deletions tools/oci-cli-wrapper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ publish = false

[dependencies]
async-trait = "0.1"
regex = "1"
serde = { version = "1", features = ["derive"]}
serde_json = "1"
snafu = "0.8"
Expand Down
49 changes: 48 additions & 1 deletion tools/oci-cli-wrapper/src/crane.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::fs::File;
use std::path::Path;

use async_trait::async_trait;
use snafu::ResultExt;
use tar::Archive as TarArchive;
use tempfile::TempDir;

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

pub struct CraneCLI {
pub(crate) cli: CommandLine,
Expand Down Expand Up @@ -43,4 +48,46 @@ impl ImageTool for CraneCLI {
serde_json::from_slice(bytes.as_slice()).context(error::ConfigDeserializeSnafu)?;
Ok(image_view.config)
}

async fn push_oci_archive(&self, path: &Path, uri: &str) -> Result<()> {
let temp_dir = TempDir::new_in(path.parent().unwrap()).context(error::CraneTempSnafu)?;

let mut oci_file = File::open(path).context(error::ArchiveReadSnafu)?;

let mut oci_archive = TarArchive::new(&mut oci_file);
oci_archive
.unpack(temp_dir.path())
.context(error::ArchiveExtractSnafu)?;
self.cli
.spawn(
&["push", &temp_dir.path().to_string_lossy(), uri],
format!("failed to push image {}", uri),
)
.await
}

async fn push_multi_platform_manifest(
&self,
platform_images: Vec<(DockerArchitecture, String)>,
uri: &str,
) -> Result<()> {
let images: Vec<&str> = platform_images
.iter()
.map(|(_, image)| image.as_str())
.collect();

let mut manifest_create_args = vec!["index", "append"];
for image in images {
manifest_create_args.extend_from_slice(&["-m", image])
}
manifest_create_args.extend_from_slice(&["-t", uri]);
self.cli
.output(
&manifest_create_args,
format!("could not push multi-platform manifest to {}", uri),
)
.await?;

Ok(())
}
}
86 changes: 84 additions & 2 deletions tools/oci-cli-wrapper/src/docker.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::path::Path;

use async_trait::async_trait;
use snafu::ResultExt;
use regex::Regex;
use snafu::{OptionExt, ResultExt};
use std::fs::File;
use tar::Archive;
use tempfile::NamedTempFile;

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

pub struct DockerCLI {
pub(crate) cli: CommandLine,
Expand Down Expand Up @@ -68,4 +69,85 @@ impl ImageTool for DockerCLI {
.await?;
serde_json::from_slice(bytes.as_slice()).context(error::ConfigDeserializeSnafu)
}

async fn push_oci_archive(&self, path: &Path, uri: &str) -> Result<()> {
let out = self
.cli
.output(
&["load", format!("--input={}", path.display()).as_str()],
format!("could not load archive from {}", path.display()),
)
.await?;
let out = String::from_utf8_lossy(&out);
let digest_expression =
Regex::new("(?<digest>sha256:[0-9a-f]{64})").context(error::RegexSnafu)?;
let caps = digest_expression
.captures(&out)
.context(error::NoDigestSnafu)?;
let digest = &caps["digest"];

self.cli
.output(
&["tag", digest, uri],
format!("could not tag image as {uri}"),
)
.await?;

self.cli
.spawn(&["push", uri], format!("failed to push image '{uri}'"))
.await?;

Ok(())
}

async fn push_multi_platform_manifest(
&self,
platform_images: Vec<(DockerArchitecture, String)>,
uri: &str,
) -> Result<()> {
let images: Vec<&str> = platform_images
.iter()
.map(|(_, image)| image.as_str())
.collect();

let mut manifest_create_args = vec!["manifest", "create", uri];
manifest_create_args.extend_from_slice(&images);
self.cli
.output(
&manifest_create_args,
format!("could not create manifest list {uri}"),
)
.await?;

for (arch, image) in platform_images.iter() {
self.cli
.output(
&[
"manifest",
"annotate",
format!("--arch={}", arch).as_str(),
uri,
image,
],
format!("could not annotate manifest {uri} for arch {arch}"),
)
.await?;
}

self.cli
.output(
&["manifest", "push", uri],
format!("could not push manifest to {uri}"),
)
.await?;

self.cli
.output(
&["manifest", "rm", uri],
format!("could not delete manifest {uri}"),
)
.await?;

Ok(())
}
}
Loading

0 comments on commit bc62668

Please sign in to comment.