474 lines
11 KiB
Markdown
474 lines
11 KiB
Markdown
# 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
// 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
|
|
|
|
```rust
|
|
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
|
|
|
|
```rust
|
|
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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```mermaid
|
|
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
|
|
|
|
1. Create the directory structure for the RFS module
|
|
2. Implement the error handling module
|
|
3. Implement the command execution module
|
|
4. Define the types for mounts, mount operations, and store specifications
|
|
5. Implement the builder pattern for RFS operations (mount and pack)
|
|
6. Implement the mount operations
|
|
7. Implement the pack operations
|
|
8. Create the module exports
|
|
9. Add Rhai integration
|
|
10. Write tests for the implementation
|
|
11. Update documentation |