Add image ls and image rm commands
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
use std::fs;
|
||||
|
||||
use crate::registry::{registry_base_dir, validate_image_name};
|
||||
use anyhow::Context;
|
||||
use clap::Subcommand;
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum ImageCmd {
|
||||
Ls,
|
||||
Rm { image: String },
|
||||
}
|
||||
|
||||
pub fn run(cmd: ImageCmd) -> anyhow::Result<()> {
|
||||
match cmd {
|
||||
ImageCmd::Ls => ls(),
|
||||
ImageCmd::Rm { image } => rm(&image),
|
||||
}
|
||||
}
|
||||
|
||||
fn rm(image: &str) -> Result<(), anyhow::Error> {
|
||||
validate_image_name(image)?;
|
||||
let image_dir = registry_base_dir()?.join(image);
|
||||
fs::remove_dir_all(image_dir).context("Failed to remove image dir")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ls() -> anyhow::Result<()> {
|
||||
let dir = fs::read_dir(registry_base_dir()?)?;
|
||||
for entry in dir {
|
||||
let entry = entry?.file_name();
|
||||
println!(" {}", entry.to_string_lossy());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
mod build;
|
||||
mod image;
|
||||
mod qemu;
|
||||
mod registry;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
use crate::image::ImageCmd;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(about = "Build bootable initrd VMs from container images")]
|
||||
struct Cli {
|
||||
@@ -27,6 +30,9 @@ enum Commands {
|
||||
/// Image tag / registry subdir name
|
||||
name: String,
|
||||
},
|
||||
/// List slim images in the registry
|
||||
#[command(subcommand)]
|
||||
Image(ImageCmd),
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
@@ -38,6 +44,9 @@ fn main() -> Result<()> {
|
||||
Commands::Run { name } => {
|
||||
qemu::run(&name)?;
|
||||
}
|
||||
Commands::Image(cmd) => {
|
||||
image::run(cmd)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+18
-23
@@ -1,8 +1,8 @@
|
||||
//! Registry: local storage of built VM artifacts under
|
||||
//! `$XDG_DATA_HOME/slim-rs/registry/<image>/`.
|
||||
|
||||
use anyhow::{Context, Result, bail};
|
||||
use std::path::PathBuf;
|
||||
use anyhow::{Context, Result, anyhow, bail};
|
||||
use std::{fs, io, path::PathBuf};
|
||||
|
||||
/// Validate a user-supplied image name before it is used in a registry path.
|
||||
///
|
||||
@@ -26,30 +26,25 @@ pub(crate) fn validate_image_name(name: &str) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn registry_base_dir() -> Result<PathBuf> {
|
||||
let base = xdg::BaseDirectories::with_prefix("slim-rs");
|
||||
base.create_data_directory("registry")
|
||||
.context("Failed to create XDG_DATA_HOME subdirectory")
|
||||
}
|
||||
|
||||
pub(crate) fn registry_dir(image: &str) -> Result<PathBuf> {
|
||||
validate_image_name(image)?;
|
||||
let base = xdg::BaseDirectories::with_prefix("slim-rs");
|
||||
let initrd_path = base
|
||||
.place_data_file(format!("registry/{image}/initrd"))
|
||||
.context("Failed to write to XDG_DATA_HOME")?;
|
||||
let reg_dir = initrd_path
|
||||
.parent()
|
||||
.expect("data file has a parent")
|
||||
.to_owned();
|
||||
let base = registry_base_dir()?;
|
||||
|
||||
let reg_dir = base.join(image);
|
||||
fs::create_dir(®_dir)
|
||||
.or_else(|e| {
|
||||
(e.kind() == io::ErrorKind::AlreadyExists)
|
||||
.then_some(())
|
||||
.ok_or(e)
|
||||
})
|
||||
.with_context(|| anyhow!("Failed to create {reg_dir:?}"))?;
|
||||
|
||||
// Defense in depth: the constructed path must stay inside the registry.
|
||||
let registry_root = base
|
||||
.place_data_file("registry/.root-check")
|
||||
.context("Failed to write to XDG_DATA_HOME")?
|
||||
.parent()
|
||||
.expect("data file has a parent")
|
||||
.to_owned();
|
||||
if !reg_dir.starts_with(®istry_root) {
|
||||
bail!(
|
||||
"invalid image name '{image}': registry path escapes {}",
|
||||
registry_root.display()
|
||||
);
|
||||
}
|
||||
Ok(reg_dir)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user