11 KiB
11 KiB
RFS Wrapper Implementation Plan
Overview
We'll create a Rust wrapper for the RFS (Remote File System) tool that follows the builder pattern, similar to the existing implementations for buildah and nerdctl in the codebase. This wrapper will provide a fluent API for mounting, unmounting, listing mounts, configuring mount options, and packing directories into filesystem layers.
Module Structure
src/virt/rfs/
├── mod.rs # Module exports and common types
├── cmd.rs # Command execution functions
├── mount.rs # Mount operations
├── pack.rs # Packing operations
├── builder.rs # Builder pattern implementation
├── types.rs # Type definitions
└── error.rs # Error handling
Implementation Details
1. Error Handling
// error.rs
#[derive(Debug)]
pub enum RfsError {
CommandFailed(String),
InvalidArgument(String),
MountFailed(String),
UnmountFailed(String),
ListFailed(String),
PackFailed(String),
Other(String),
}
impl std::fmt::Display for RfsError {
// Implementation
}
impl std::error::Error for RfsError {
// Implementation
}
2. Command Execution
// cmd.rs
use crate::process::{run_command, CommandResult};
use super::error::RfsError;
pub fn execute_rfs_command(args: &[&str]) -> Result<CommandResult, RfsError> {
// Implementation similar to buildah and nerdctl
}
3. Types
// types.rs
#[derive(Debug, Clone)]
pub struct Mount {
pub id: String,
pub source: String,
pub target: String,
pub fs_type: String,
pub options: Vec<String>,
}
#[derive(Debug, Clone)]
pub enum MountType {
Local,
SSH,
S3,
WebDAV,
// Other mount types
}
#[derive(Debug, Clone)]
pub struct StoreSpec {
pub spec_type: String,
pub options: std::collections::HashMap<String, String>,
}
impl StoreSpec {
pub fn new(spec_type: &str) -> Self {
Self {
spec_type: spec_type.to_string(),
options: std::collections::HashMap::new(),
}
}
pub fn with_option(mut self, key: &str, value: &str) -> Self {
self.options.insert(key.to_string(), value.to_string());
self
}
pub fn to_string(&self) -> String {
let mut result = self.spec_type.clone();
if !self.options.is_empty() {
result.push_str(":");
let options: Vec<String> = self.options
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect();
result.push_str(&options.join(","));
}
result
}
}
4. Builder Pattern
// builder.rs
use std::collections::HashMap;
use super::{Mount, MountType, RfsError, execute_rfs_command, StoreSpec};
#[derive(Clone)]
pub struct RfsBuilder {
source: String,
target: String,
mount_type: MountType,
options: HashMap<String, String>,
mount_id: Option<String>,
debug: bool,
}
impl RfsBuilder {
pub fn new(source: &str, target: &str, mount_type: MountType) -> Self {
Self {
source: source.to_string(),
target: target.to_string(),
mount_type,
options: HashMap::new(),
mount_id: None,
debug: false,
}
}
pub fn with_option(mut self, key: &str, value: &str) -> Self {
self.options.insert(key.to_string(), value.to_string());
self
}
pub fn with_options(mut self, options: HashMap<&str, &str>) -> Self {
for (key, value) in options {
self.options.insert(key.to_string(), value.to_string());
}
self
}
pub fn with_debug(mut self, debug: bool) -> Self {
self.debug = debug;
self
}
pub fn mount(self) -> Result<Mount, RfsError> {
// Implementation
}
pub fn unmount(&self) -> Result<(), RfsError> {
// Implementation
}
// Other methods
}
// Packing functionality
pub struct PackBuilder {
directory: String,
output: String,
store_specs: Vec<StoreSpec>,
debug: bool,
}
impl PackBuilder {
pub fn new(directory: &str, output: &str) -> Self {
Self {
directory: directory.to_string(),
output: output.to_string(),
store_specs: Vec::new(),
debug: false,
}
}
pub fn with_store_spec(mut self, store_spec: StoreSpec) -> Self {
self.store_specs.push(store_spec);
self
}
pub fn with_store_specs(mut self, store_specs: Vec<StoreSpec>) -> Self {
self.store_specs.extend(store_specs);
self
}
pub fn with_debug(mut self, debug: bool) -> Self {
self.debug = debug;
self
}
pub fn pack(self) -> Result<(), RfsError> {
// Implementation for packing a directory into a filesystem layer
let mut args = vec!["pack"];
// Add output file
args.push("-m");
args.push(&self.output);
// Add store specs
if !self.store_specs.is_empty() {
args.push("-s");
let specs: Vec<String> = self.store_specs
.iter()
.map(|spec| spec.to_string())
.collect();
args.push(&specs.join(","));
}
// Add directory
args.push(&self.directory);
// Convert to string slices for the command
let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
// Execute the command
let result = execute_rfs_command(&args_str)?;
// Check for errors
if !result.success {
return Err(RfsError::PackFailed(result.stderr));
}
Ok(())
}
}
5. Mount Operations
// mount.rs
use super::{RfsBuilder, Mount, RfsError, execute_rfs_command};
pub fn list_mounts() -> Result<Vec<Mount>, RfsError> {
// Implementation
}
pub fn unmount_all() -> Result<(), RfsError> {
// Implementation
}
// Other mount-related functions
6. Pack Operations
// pack.rs
use super::{PackBuilder, StoreSpec, RfsError, execute_rfs_command};
pub fn pack_directory(directory: &str, output: &str, store_specs: &[StoreSpec]) -> Result<(), RfsError> {
PackBuilder::new(directory, output)
.with_store_specs(store_specs.to_vec())
.pack()
}
// Other pack-related functions
7. Module Exports
// mod.rs
mod cmd;
mod error;
mod mount;
mod pack;
mod builder;
mod types;
pub use error::RfsError;
pub use builder::{RfsBuilder, PackBuilder};
pub use types::{Mount, MountType, StoreSpec};
pub use mount::{list_mounts, unmount_all};
pub use pack::pack_directory;
// Re-export the execute_rfs_command function for use in other modules
pub(crate) use cmd::execute_rfs_command;
Usage Examples
Mounting Example
use crate::virt::rfs::{RfsBuilder, MountType};
// Create a new RFS mount with builder pattern
let mount = RfsBuilder::new("user@example.com:/remote/path", "/local/mount/point", MountType::SSH)
.with_option("port", "2222")
.with_option("identity_file", "/path/to/key")
.with_debug(true)
.mount()?;
// List all mounts
let mounts = list_mounts()?;
for mount in mounts {
println!("Mount ID: {}, Source: {}, Target: {}", mount.id, mount.source, mount.target);
}
// Unmount
mount.unmount()?;
Packing Example
use crate::virt::rfs::{PackBuilder, StoreSpec};
// Create store specifications
let store_spec1 = StoreSpec::new("file")
.with_option("path", "/path/to/store");
let store_spec2 = StoreSpec::new("s3")
.with_option("bucket", "my-bucket")
.with_option("region", "us-east-1");
// Pack a directory with builder pattern
let result = PackBuilder::new("/path/to/directory", "output.fl")
.with_store_spec(store_spec1)
.with_store_spec(store_spec2)
.with_debug(true)
.pack()?;
// Or use the convenience function
pack_directory("/path/to/directory", "output.fl", &[store_spec1, store_spec2])?;
Rhai Integration
We'll also need to create a Rhai module to expose the RFS functionality to Rhai scripts:
// src/rhai/rfs.rs
use rhai::{Engine, EvalAltResult, RegisterFn};
use crate::virt::rfs::{RfsBuilder, MountType, list_mounts, unmount_all, PackBuilder, StoreSpec};
pub fn register(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
// Register RFS functions
engine.register_fn("rfs_mount", rfs_mount);
engine.register_fn("rfs_unmount", rfs_unmount);
engine.register_fn("rfs_list_mounts", rfs_list_mounts);
engine.register_fn("rfs_unmount_all", rfs_unmount_all);
engine.register_fn("rfs_pack", rfs_pack);
Ok(())
}
// Function implementations
fn rfs_mount(source: &str, target: &str, mount_type: &str, options_map: rhai::Map) -> Result<(), Box<EvalAltResult>> {
// Implementation
}
fn rfs_unmount(target: &str) -> Result<(), Box<EvalAltResult>> {
// Implementation
}
fn rfs_list_mounts() -> Result<rhai::Array, Box<EvalAltResult>> {
// Implementation
}
fn rfs_unmount_all() -> Result<(), Box<EvalAltResult>> {
// Implementation
}
fn rfs_pack(directory: &str, output: &str, store_specs: &str) -> Result<(), Box<EvalAltResult>> {
// Implementation
}
Implementation Flow
Here's a diagram showing the flow of the implementation:
classDiagram
class RfsBuilder {
+String source
+String target
+MountType mount_type
+HashMap options
+Option~String~ mount_id
+bool debug
+new(source, target, mount_type)
+with_option(key, value)
+with_options(options)
+with_debug(debug)
+mount()
+unmount()
}
class PackBuilder {
+String directory
+String output
+Vec~StoreSpec~ store_specs
+bool debug
+new(directory, output)
+with_store_spec(store_spec)
+with_store_specs(store_specs)
+with_debug(debug)
+pack()
}
class Mount {
+String id
+String source
+String target
+String fs_type
+Vec~String~ options
}
class MountType {
<<enumeration>>
Local
SSH
S3
WebDAV
}
class StoreSpec {
+String spec_type
+HashMap options
+new(spec_type)
+with_option(key, value)
+to_string()
}
class RfsError {
<<enumeration>>
CommandFailed
InvalidArgument
MountFailed
UnmountFailed
ListFailed
PackFailed
Other
}
RfsBuilder --> Mount : creates
RfsBuilder --> RfsError : may throw
RfsBuilder --> MountType : uses
PackBuilder --> RfsError : may throw
PackBuilder --> StoreSpec : uses
Mount --> RfsError : may throw
Implementation Steps
- Create the directory structure for the RFS module
- Implement the error handling module
- Implement the command execution module
- Define the types for mounts, mount operations, and store specifications
- Implement the builder pattern for RFS operations (mount and pack)
- Implement the mount operations
- Implement the pack operations
- Create the module exports
- Add Rhai integration
- Write tests for the implementation
- Update documentation