Skip to content

Commit

Permalink
cli: polish CLI usage patterns (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford authored Aug 10, 2023
1 parent 6215e28 commit 0d7f879
Show file tree
Hide file tree
Showing 15 changed files with 247 additions and 191 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Supports all of the current WASI subsystems:
- [Filesystem](#filesystem): Mount a read-only filesystem, configure host filesystem preopen remappings or pass-through.
- [Random](#random): Allow / Deny
- [Sockets](#sockets): Allow / Deny
- [Stdio](#stdio): Allow / Deny
- [Stdio](#stdio): Allow / Deny / Ignore

While current virtualization support is limited, the goal for this project is to support a wide range of WASI virtualization configuration use cases.

Expand Down Expand Up @@ -120,14 +120,14 @@ wasi-virt component.wasm --allow-sockets -o virt.wasm

```sh
# Ignore all stdio entirely
wasi-virt component.wasm --allow-stdio -o virt.wasm
wasi-virt component.wasm --stdio=ignore -o virt.wasm

# Throw an error if attempting any stdio
# (this is the default)
wasi-virt component.wasm --deny-stdio -o virt.wasm
wasi-virt component.wasm --stdio=deny -o virt.wasm

# Allow stderr only
wasi-virt component.wasm --allow-stderr -o virt.wasm
# Configure stderr / stdout / stdin individually
wasi-virt component.wasm --stderr=allow -o virt.wasm
```

## API
Expand All @@ -142,7 +142,7 @@ fn main() {
let mut virt = WasiVirt::new_reactor();

// allow all subsystems initially
virt.all(true);
virt.allow_all();

// ignore stdio
virt.stdio().ignore();
Expand Down
Binary file modified lib/virtual_adapter.wasm
Binary file not shown.
151 changes: 72 additions & 79 deletions src/bin/wasi-virt.rs
Original file line number Diff line number Diff line change
@@ -1,93 +1,89 @@
use anyhow::{Context, Result};
use clap::Parser;
use clap::{ArgAction, Parser};
use std::{env, error::Error, fs, path::PathBuf, time::SystemTime};
use wasi_virt::WasiVirt;
use wasi_virt::{StdioCfg, WasiVirt};
use wasm_compose::composer::ComponentComposer;

#[derive(Parser, Debug)]
#[command(verbatim_doc_comment, author, version, about)]
/// WASI Virt CLI
///
/// Creates a virtualization component with the provided virtualization configuration.
///
/// This virtualization component can then be composed into a WASI component via:
///
/// wasm-tools compose component.wasm -d virt.wasm -o final.wasm
///
#[command(verbatim_doc_comment, author, version, about, long_about = None)]
/// WASI Virt
struct Args {
/// Virtualization TOML configuration
///
/// As defined in [`VirtOpts`]
#[arg(short, long, verbatim_doc_comment)]
config: Option<String>,

/// Allow the component to exit
#[arg(long)]
allow_exit: Option<bool>,

// STDIO
/// Enable all stdio
#[arg(long)]
allow_stdio: Option<bool>,
/// Enable stdin
#[arg(long)]
allow_stdin: Option<bool>,
/// Enable stdout
#[arg(long)]
allow_stdout: Option<bool>,
/// Enable stderr
#[arg(long)]
allow_stderr: Option<bool>,

// ENV
/// Allow host access to all environment variables, or to a specific comma-separated list of variable names.
#[arg(long, num_args(0..), use_value_delimiter(true), require_equals(true), value_name("ENV_VAR"))]
allow_env: Option<Vec<String>>,
/// Optional Wasm binary to compose the virtualization into.
/// If not provided, only the virtualization component itself will be generated,
/// which can then be composed via `wasm-tools compose -d virt.wasm component.wasm`
#[arg(required(false), value_name("component.wasm"), verbatim_doc_comment)]
compose: Option<String>,

/// Set environment variable overrides
#[arg(short, long, use_value_delimiter(true), value_name("ENV=VAR"), value_parser = parse_key_val::<String, String>)]
env: Option<Vec<(String, String)>>,
/// Output virtualization component Wasm file
#[arg(short, long, value_name("virt.wasm"))]
out: String,

// FS
/// Enable all subsystem passthrough (encapsulation is the default)
#[arg(long)]
allow_fs: Option<bool>,

/// Configure runtime preopen mappings
#[arg(long, value_name("preopen=hostpreopen"), value_parser = parse_key_val::<String, String>)]
preopen: Option<Vec<(String, String)>>,

/// Mount a virtual directory globbed from the local filesystem
#[arg(long, value_name("preopen=virtualdir"), value_parser = parse_key_val::<String, String>)]
mount: Option<Vec<(String, String)>>,
allow_all: bool,

// CLOCKS
#[arg(long)]
/// Enable clocks
#[arg(long, action = ArgAction::SetTrue)]
allow_clocks: Option<bool>,

/// Allow the component to exit
#[arg(long, action = ArgAction::SetTrue)]
allow_exit: Option<bool>,

// HTTP
#[arg(long)]
/// Enable HTTP
#[arg(long, action = ArgAction::SetTrue)]
allow_http: Option<bool>,

// RANDOM
#[arg(long)]
/// Enable Random
#[arg(long, action = ArgAction::SetTrue)]
allow_random: Option<bool>,

// SOCKETS
#[arg(long)]
/// Enable Sockets
#[arg(long, action = ArgAction::SetTrue)]
allow_sockets: Option<bool>,

// Enable all subsystem passthrough
#[arg(long)]
allow_all: Option<bool>,
// ENV
/// Allow unrestricted access to host environment variables, or to a comma-separated list of variable names.
#[arg(long, num_args(0..), use_value_delimiter(true), require_equals(true), value_name("ENV_VAR"), help_heading = "Env")]
allow_env: Option<Vec<String>>,

/// Wasm binary to compose the virtualization with
/// If not provided, the virtualization component itself will only generated.
#[arg(required(false))]
compose: Option<String>,
/// Set environment variable overrides
#[arg(short, long, use_value_delimiter(true), value_name("ENV=VAR"), value_parser = parse_key_val::<String, String>, help_heading = "Env")]
env: Option<Vec<(String, String)>>,

/// Output virtualization component Wasm file
#[arg(short, long)]
out: String,
// FS
/// Allow unrestricted access to host preopens
#[arg(long, action = ArgAction::SetTrue, help_heading = "Fs")]
allow_fs: Option<bool>,

/// Mount a virtual directory globbed from the local filesystem
#[arg(long, value_name("preopen=virtualdir"), value_parser = parse_key_val::<String, String>, help_heading = "Fs")]
mount: Option<Vec<(String, String)>>,

/// Configure runtime preopen mappings
#[arg(long, value_name("preopen=hostpreopen"), value_parser = parse_key_val::<String, String>, help_heading = "Fs")]
preopen: Option<Vec<(String, String)>>,

// STDIO
/// Enable all stdio
#[arg(long, action = ArgAction::SetTrue, help_heading = "Stdio")]
allow_stdio: Option<bool>,
/// Configure all stdio
#[arg(long, value_enum, value_name("cfg"), num_args(0..=1), require_equals(true), default_missing_value("allow"), help_heading = "Stdio")]
stdio: Option<StdioCfg>,
/// Configure stderr
#[arg(long, value_enum, value_name("cfg"), num_args(0..=1), require_equals(true), default_missing_value("allow"), help_heading = "Stdio")]
stderr: Option<StdioCfg>,
/// Configure stdin
#[arg(long, value_enum, value_name("cfg"), num_args(0..=1), require_equals(true), default_missing_value("allow"), help_heading = "Stdio")]
stdin: Option<StdioCfg>,
/// Configure stdout
#[arg(long, value_enum, value_name("cfg"), num_args(0..=1), require_equals(true), default_missing_value("allow"), help_heading = "Stdio")]
stdout: Option<StdioCfg>,
}

// parser for KEY=VAR env vars
Expand All @@ -114,16 +110,17 @@ fn timestamp() -> u64 {
fn main() -> Result<()> {
let args = Args::parse();

let mut virt_opts = if let Some(config) = &args.config {
toml::from_str(&fs::read_to_string(&config)?)?
} else {
WasiVirt::default()
};
let mut virt_opts = WasiVirt::default();

// By default, we virtualize all subsystems
// This ensures full encapsulation in the default (no argument) case
let allow_all = args.allow_all.unwrap_or(false);
let allow_all = args.allow_all;
let allow_stdio = args.allow_stdio.unwrap_or(allow_all);
let stdio = if allow_stdio {
StdioCfg::Allow
} else {
StdioCfg::Deny
};

// clocks
virt_opts.clocks(args.allow_clocks.unwrap_or(allow_all));
Expand All @@ -138,15 +135,11 @@ fn main() -> Result<()> {
virt_opts.sockets(args.allow_sockets.unwrap_or(allow_all));

// stdio
virt_opts.stdio().stdin(args.stdin.unwrap_or(stdio.clone()));
virt_opts
.stdio()
.stdin(args.allow_stdin.unwrap_or(allow_stdio));
virt_opts
.stdio()
.stdout(args.allow_stdout.unwrap_or(allow_stdio));
virt_opts
.stdio()
.stderr(args.allow_stderr.unwrap_or(allow_stdio));
.stdout(args.stdout.unwrap_or(stdio.clone()));
virt_opts.stdio().stderr(args.stderr.unwrap_or(stdio));

// exit
virt_opts.exit(args.allow_exit.unwrap_or_default());
Expand Down
38 changes: 21 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod virt_io;
mod walrus_ops;

pub use virt_env::{HostEnv, VirtEnv};
pub use virt_io::{FsEntry, VirtFs, VirtualFiles};
pub use virt_io::{FsEntry, StdioCfg, VirtFs, VirtualFiles};

/// Virtualization options
///
Expand Down Expand Up @@ -67,22 +67,26 @@ impl WasiVirt {
Self::default()
}

pub fn all(&mut self, allow: bool) {
self.clocks(allow);
self.http(allow);
self.sockets(allow);
self.exit(allow);
if allow {
self.env().allow_all();
} else {
self.env().deny_all();
}
if allow {
self.fs().allow_host_preopens();
} else {
self.fs().deny_host_preopens();
}
self.stdio().all(allow);
pub fn allow_all(&mut self) {
self.clocks(true);
self.http(true);
self.sockets(true);
self.exit(true);
self.random(true);
self.env().allow_all();
self.fs().allow_host_preopens();
self.stdio().allow();
}

pub fn deny_all(&mut self) {
self.clocks(false);
self.http(false);
self.sockets(false);
self.exit(false);
self.random(false);
self.env().deny_all();
self.fs().deny_host_preopens();
self.stdio().ignore();
}

pub fn clocks(&mut self, allow: bool) {
Expand Down
Loading

0 comments on commit 0d7f879

Please sign in to comment.