mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
feature: pack s9pk (#2642)
* TODO: images * wip * pack s9pk images * include path in packsource error * debug info * add cmd as context to invoke * filehelper bugfix * fix file helper * fix exposeForDependents * misc fixes * force image removal * fix filtering * fix deadlock * fix api * chore: Up the version of the package.json * always allow concurrency within same call stack * Update core/startos/src/s9pk/merkle_archive/expected.rs Co-authored-by: Jade <2364004+Blu-J@users.noreply.github.com> --------- Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Jade <2364004+Blu-J@users.noreply.github.com>
This commit is contained in:
@@ -1,32 +1,26 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use itertools::Itertools;
|
||||
use models::ImageId;
|
||||
use rpc_toolkit::{from_fn_async, Empty, HandlerExt, ParentHandler};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::fs::File;
|
||||
use tokio::process::Command;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::context::CliContext;
|
||||
use crate::prelude::*;
|
||||
use crate::s9pk::manifest::Manifest;
|
||||
use crate::s9pk::merkle_archive::source::DynFileSource;
|
||||
use crate::s9pk::merkle_archive::Entry;
|
||||
use crate::s9pk::v2::compat::CONTAINER_TOOL;
|
||||
use crate::s9pk::v2::pack::ImageConfig;
|
||||
use crate::s9pk::v2::SIG_CONTEXT;
|
||||
use crate::s9pk::S9pk;
|
||||
use crate::util::io::TmpDir;
|
||||
use crate::util::serde::{apply_expr, HandlerExtSerde};
|
||||
use crate::util::Invoke;
|
||||
|
||||
pub const SKIP_ENV: &[&str] = &["TERM", "container", "HOME", "HOSTNAME"];
|
||||
|
||||
pub fn s9pk() -> ParentHandler<CliContext> {
|
||||
ParentHandler::new()
|
||||
.subcommand("pack", from_fn_async(super::v2::pack::pack).no_display())
|
||||
.subcommand("edit", edit())
|
||||
.subcommand("inspect", inspect())
|
||||
}
|
||||
@@ -77,117 +71,21 @@ fn inspect() -> ParentHandler<CliContext, S9pkPath> {
|
||||
#[derive(Deserialize, Serialize, Parser, TS)]
|
||||
struct AddImageParams {
|
||||
id: ImageId,
|
||||
image: String,
|
||||
arches: Option<Vec<String>>,
|
||||
#[command(flatten)]
|
||||
config: ImageConfig,
|
||||
}
|
||||
async fn add_image(
|
||||
ctx: CliContext,
|
||||
AddImageParams { id, image, arches }: AddImageParams,
|
||||
AddImageParams { id, config }: AddImageParams,
|
||||
S9pkPath { s9pk: s9pk_path }: S9pkPath,
|
||||
) -> Result<(), Error> {
|
||||
let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?, false)
|
||||
let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?)
|
||||
.await?
|
||||
.into_dyn();
|
||||
let arches: BTreeSet<_> = arches
|
||||
.unwrap_or_else(|| vec!["x86_64".to_owned(), "aarch64".to_owned()])
|
||||
.into_iter()
|
||||
.collect();
|
||||
s9pk.as_manifest_mut().images.insert(id, config);
|
||||
let tmpdir = TmpDir::new().await?;
|
||||
for arch in arches {
|
||||
let sqfs_path = tmpdir.join(format!("image.{arch}.squashfs"));
|
||||
let docker_platform = if arch == "x86_64" {
|
||||
"--platform=linux/amd64".to_owned()
|
||||
} else if arch == "aarch64" {
|
||||
"--platform=linux/arm64".to_owned()
|
||||
} else {
|
||||
format!("--platform=linux/{arch}")
|
||||
};
|
||||
let env = String::from_utf8(
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("run")
|
||||
.arg("--rm")
|
||||
.arg(&docker_platform)
|
||||
.arg("--entrypoint")
|
||||
.arg("env")
|
||||
.arg(&image)
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await?,
|
||||
)?
|
||||
.lines()
|
||||
.filter(|l| {
|
||||
l.trim()
|
||||
.split_once("=")
|
||||
.map_or(false, |(v, _)| !SKIP_ENV.contains(&v))
|
||||
})
|
||||
.join("\n")
|
||||
+ "\n";
|
||||
let workdir = Path::new(
|
||||
String::from_utf8(
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("run")
|
||||
.arg(&docker_platform)
|
||||
.arg("--rm")
|
||||
.arg("--entrypoint")
|
||||
.arg("pwd")
|
||||
.arg(&image)
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await?,
|
||||
)?
|
||||
.trim(),
|
||||
)
|
||||
.to_owned();
|
||||
let container_id = String::from_utf8(
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("create")
|
||||
.arg(&docker_platform)
|
||||
.arg(&image)
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await?,
|
||||
)?;
|
||||
Command::new("bash")
|
||||
.arg("-c")
|
||||
.arg(format!(
|
||||
"{CONTAINER_TOOL} export {container_id} | mksquashfs - {sqfs} -tar",
|
||||
container_id = container_id.trim(),
|
||||
sqfs = sqfs_path.display()
|
||||
))
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await?;
|
||||
Command::new(CONTAINER_TOOL)
|
||||
.arg("rm")
|
||||
.arg(container_id.trim())
|
||||
.invoke(ErrorKind::Docker)
|
||||
.await?;
|
||||
let archive = s9pk.as_archive_mut();
|
||||
archive.set_signer(ctx.developer_key()?.clone(), SIG_CONTEXT);
|
||||
archive.contents_mut().insert_path(
|
||||
Path::new("images")
|
||||
.join(&arch)
|
||||
.join(&id)
|
||||
.with_extension("squashfs"),
|
||||
Entry::file(DynFileSource::new(sqfs_path)),
|
||||
)?;
|
||||
archive.contents_mut().insert_path(
|
||||
Path::new("images")
|
||||
.join(&arch)
|
||||
.join(&id)
|
||||
.with_extension("env"),
|
||||
Entry::file(DynFileSource::new(Arc::<[u8]>::from(Vec::from(env)))),
|
||||
)?;
|
||||
archive.contents_mut().insert_path(
|
||||
Path::new("images")
|
||||
.join(&arch)
|
||||
.join(&id)
|
||||
.with_extension("json"),
|
||||
Entry::file(DynFileSource::new(Arc::<[u8]>::from(
|
||||
serde_json::to_vec(&serde_json::json!({
|
||||
"workdir": workdir
|
||||
}))
|
||||
.with_kind(ErrorKind::Serialization)?,
|
||||
))),
|
||||
)?;
|
||||
}
|
||||
s9pk.as_manifest_mut().images.insert(id);
|
||||
s9pk.load_images(&tmpdir).await?;
|
||||
s9pk.validate_and_filter(None)?;
|
||||
let tmp_path = s9pk_path.with_extension("s9pk.tmp");
|
||||
let mut tmp_file = File::create(&tmp_path).await?;
|
||||
s9pk.serialize(&mut tmp_file, true).await?;
|
||||
@@ -206,7 +104,7 @@ async fn edit_manifest(
|
||||
EditManifestParams { expression }: EditManifestParams,
|
||||
S9pkPath { s9pk: s9pk_path }: S9pkPath,
|
||||
) -> Result<Manifest, Error> {
|
||||
let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?, false).await?;
|
||||
let mut s9pk = S9pk::from_file(super::load(&ctx, &s9pk_path).await?).await?;
|
||||
let old = serde_json::to_value(s9pk.as_manifest()).with_kind(ErrorKind::Serialization)?;
|
||||
*s9pk.as_manifest_mut() = serde_json::from_value(apply_expr(old.into(), &expression)?.into())
|
||||
.with_kind(ErrorKind::Serialization)?;
|
||||
@@ -227,7 +125,7 @@ async fn file_tree(
|
||||
_: Empty,
|
||||
S9pkPath { s9pk }: S9pkPath,
|
||||
) -> Result<Vec<PathBuf>, Error> {
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?;
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?;
|
||||
Ok(s9pk.as_archive().contents().file_paths(""))
|
||||
}
|
||||
|
||||
@@ -244,7 +142,7 @@ async fn cat(
|
||||
) -> Result<(), Error> {
|
||||
use crate::s9pk::merkle_archive::source::FileSource;
|
||||
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?;
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?;
|
||||
tokio::io::copy(
|
||||
&mut s9pk
|
||||
.as_archive()
|
||||
@@ -266,6 +164,6 @@ async fn inspect_manifest(
|
||||
_: Empty,
|
||||
S9pkPath { s9pk }: S9pkPath,
|
||||
) -> Result<Manifest, Error> {
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?, false).await?;
|
||||
let s9pk = S9pk::from_file(super::load(&ctx, &s9pk).await?).await?;
|
||||
Ok(s9pk.as_manifest().clone())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user