feat: oci compliance work (#85)

* chore: rework oci crate to be more composable

* feat: image pull is now internally explicit

* feat: utilize vfs for assembling oci images

* feat: rework oci to preserve permissions via a vfs
This commit is contained in:
Alex Zenla
2024-04-15 10:24:14 -07:00
committed by GitHub
parent 24c71e9725
commit 89055ef77c
33 changed files with 1500 additions and 1164 deletions

View File

@ -1,7 +1,7 @@
use anyhow::Result;
use backhand::{FilesystemWriter, NodeHeader};
use krata::launchcfg::LaunchInfo;
use krataoci::compiler::ImageInfo;
use krataoci::packer::OciImagePacked;
use log::trace;
use std::fs;
use std::fs::File;
@ -9,28 +9,24 @@ use std::path::PathBuf;
use uuid::Uuid;
pub struct ConfigBlock<'a> {
pub image_info: &'a ImageInfo,
pub image: &'a OciImagePacked,
pub file: PathBuf,
pub dir: PathBuf,
}
impl ConfigBlock<'_> {
pub fn new<'a>(uuid: &Uuid, image_info: &'a ImageInfo) -> Result<ConfigBlock<'a>> {
pub fn new<'a>(uuid: &Uuid, image: &'a OciImagePacked) -> Result<ConfigBlock<'a>> {
let mut dir = std::env::temp_dir().clone();
dir.push(format!("krata-cfg-{}", uuid));
fs::create_dir_all(&dir)?;
let mut file = dir.clone();
file.push("config.squashfs");
Ok(ConfigBlock {
image_info,
file,
dir,
})
Ok(ConfigBlock { image, file, dir })
}
pub fn build(&self, launch_config: &LaunchInfo) -> Result<()> {
trace!("build launch_config={:?}", launch_config);
let manifest = self.image_info.config.to_string()?;
let manifest = self.image.config.to_string()?;
let launch = serde_json::to_string(launch_config)?;
let mut writer = FilesystemWriter::default();
writer.push_dir(

View File

@ -10,8 +10,7 @@ use krata::launchcfg::{
LaunchInfo, LaunchNetwork, LaunchNetworkIpv4, LaunchNetworkIpv6, LaunchNetworkResolver,
LaunchPackedFormat, LaunchRoot,
};
use krataoci::packer::OciPackerFormat;
use krataoci::progress::OciProgressContext;
use krataoci::packer::OciImagePacked;
use tokio::sync::Semaphore;
use uuid::Uuid;
use xenclient::{DomainChannel, DomainConfig, DomainDisk, DomainNetworkInterface};
@ -19,24 +18,19 @@ use xenstore::XsdInterface;
use crate::cfgblk::ConfigBlock;
use crate::RuntimeContext;
use krataoci::{
cache::ImageCache,
compiler::{ImageInfo, OciImageCompiler},
name::ImageName,
};
use super::{GuestInfo, GuestState};
pub struct GuestLaunchRequest<'a> {
pub struct GuestLaunchRequest {
pub format: LaunchPackedFormat,
pub uuid: Option<Uuid>,
pub name: Option<&'a str>,
pub image: &'a str,
pub name: Option<String>,
pub vcpus: u32,
pub mem: u64,
pub env: HashMap<String, String>,
pub run: Option<Vec<String>>,
pub debug: bool,
pub image: OciImagePacked,
}
pub struct GuestLauncher {
@ -48,26 +42,13 @@ impl GuestLauncher {
Ok(Self { launch_semaphore })
}
pub async fn launch<'r>(
pub async fn launch(
&mut self,
context: &RuntimeContext,
request: GuestLaunchRequest<'r>,
request: GuestLaunchRequest,
) -> Result<GuestInfo> {
let uuid = request.uuid.unwrap_or_else(Uuid::new_v4);
let xen_name = format!("krata-{uuid}");
let image_info = self
.compile(
&uuid.to_string(),
request.image,
&context.image_cache,
&context.oci_progress_context,
match request.format {
LaunchPackedFormat::Squashfs => OciPackerFormat::Squashfs,
LaunchPackedFormat::Erofs => OciPackerFormat::Erofs,
},
)
.await?;
let mut gateway_mac = MacAddr6::random();
gateway_mac.set_local(true);
gateway_mac.set_multicast(false);
@ -90,6 +71,7 @@ impl GuestLauncher {
hostname: Some(
request
.name
.as_ref()
.map(|x| x.to_string())
.unwrap_or_else(|| format!("krata-{}", uuid)),
),
@ -116,11 +98,12 @@ impl GuestLauncher {
run: request.run,
};
let cfgblk = ConfigBlock::new(&uuid, &image_info)?;
let cfgblk = ConfigBlock::new(&uuid, &request.image)?;
cfgblk.build(&launch_config)?;
let image_squashfs_path = image_info
let image_squashfs_path = request
.image
.path
.to_str()
.ok_or_else(|| anyhow!("failed to convert image path to string"))?;
@ -158,7 +141,6 @@ impl GuestLauncher {
cfgblk_dir_path,
),
),
("krata/image".to_string(), request.image.to_string()),
(
"krata/network/guest/ipv4".to_string(),
format!("{}/{}", guest_ipv4, ipv4_network_mask),
@ -185,8 +167,8 @@ impl GuestLauncher {
),
];
if let Some(name) = request.name {
extra_keys.push(("krata/name".to_string(), name.to_string()));
if let Some(name) = request.name.as_ref() {
extra_keys.push(("krata/name".to_string(), name.clone()));
}
let config = DomainConfig {
@ -227,10 +209,10 @@ impl GuestLauncher {
};
match context.xen.create(&config).await {
Ok(created) => Ok(GuestInfo {
name: request.name.map(|x| x.to_string()),
name: request.name.as_ref().map(|x| x.to_string()),
uuid,
domid: created.domid,
image: request.image.to_string(),
image: request.image.digest,
loops: vec![],
guest_ipv4: Some(IpNetwork::new(
IpAddr::V4(guest_ipv4),
@ -261,19 +243,6 @@ impl GuestLauncher {
}
}
async fn compile(
&self,
id: &str,
image: &str,
image_cache: &ImageCache,
progress: &OciProgressContext,
format: OciPackerFormat,
) -> Result<ImageInfo> {
let image = ImageName::parse(image)?;
let compiler = OciImageCompiler::new(image_cache, None, progress.clone())?;
compiler.compile(id, &image, format).await
}
async fn allocate_ipv4(&self, context: &RuntimeContext) -> Result<Ipv4Addr> {
let network = Ipv4Network::new(Ipv4Addr::new(10, 75, 80, 0), 24)?;
let mut used: Vec<Ipv4Addr> = vec![];

View File

@ -17,7 +17,6 @@ use self::{
autoloop::AutoLoop,
launch::{GuestLaunchRequest, GuestLauncher},
};
use krataoci::{cache::ImageCache, progress::OciProgressContext};
pub mod autoloop;
pub mod cfgblk;
@ -51,8 +50,6 @@ pub struct GuestInfo {
#[derive(Clone)]
pub struct RuntimeContext {
pub oci_progress_context: OciProgressContext,
pub image_cache: ImageCache,
pub autoloop: AutoLoop,
pub xen: XenClient,
pub kernel: String,
@ -60,7 +57,7 @@ pub struct RuntimeContext {
}
impl RuntimeContext {
pub async fn new(oci_progress_context: OciProgressContext, store: String) -> Result<Self> {
pub async fn new(store: String) -> Result<Self> {
let mut image_cache_path = PathBuf::from(&store);
image_cache_path.push("cache");
fs::create_dir_all(&image_cache_path)?;
@ -68,13 +65,10 @@ impl RuntimeContext {
let xen = XenClient::open(0).await?;
image_cache_path.push("image");
fs::create_dir_all(&image_cache_path)?;
let image_cache = ImageCache::new(&image_cache_path)?;
let kernel = RuntimeContext::detect_guest_file(&store, "kernel")?;
let initrd = RuntimeContext::detect_guest_file(&store, "initrd")?;
Ok(RuntimeContext {
oci_progress_context,
image_cache,
autoloop: AutoLoop::new(LoopControl::open()?),
xen,
kernel,
@ -254,24 +248,22 @@ impl RuntimeContext {
#[derive(Clone)]
pub struct Runtime {
oci_progress_context: OciProgressContext,
store: Arc<String>,
context: RuntimeContext,
launch_semaphore: Arc<Semaphore>,
}
impl Runtime {
pub async fn new(oci_progress_context: OciProgressContext, store: String) -> Result<Self> {
let context = RuntimeContext::new(oci_progress_context.clone(), store.clone()).await?;
pub async fn new(store: String) -> Result<Self> {
let context = RuntimeContext::new(store.clone()).await?;
Ok(Self {
oci_progress_context,
store: Arc::new(store),
context,
launch_semaphore: Arc::new(Semaphore::new(1)),
})
}
pub async fn launch<'a>(&self, request: GuestLaunchRequest<'a>) -> Result<GuestInfo> {
pub async fn launch(&self, request: GuestLaunchRequest) -> Result<GuestInfo> {
let mut launcher = GuestLauncher::new(self.launch_semaphore.clone())?;
launcher.launch(&self.context, request).await
}
@ -328,7 +320,7 @@ impl Runtime {
}
pub async fn dupe(&self) -> Result<Runtime> {
Runtime::new(self.oci_progress_context.clone(), (*self.store).clone()).await
Runtime::new((*self.store).clone()).await
}
}