Skip to content

Commit

Permalink
feat: add config options subcommand to show all config properties wit…
Browse files Browse the repository at this point in the history
…h help
  • Loading branch information
sandorex committed Oct 3, 2024
1 parent 92bc7d8 commit a806734
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 107 deletions.
6 changes: 6 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/cli/cli_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,8 @@ pub enum ConfigCommands {

/// Inspect a config, can be used to check if syntax is correct
Inspect(CmdConfigInspectArgs),

/// Show all options useable in a config
Options,
}

2 changes: 2 additions & 0 deletions src/commands/cmd_config.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod cmd_extract_config;
mod cmd_inspect_config;
mod cmd_options_config;

pub use cmd_extract_config::extract_config;
pub use cmd_inspect_config::inspect_config;
pub use cmd_options_config::show_config_options;
10 changes: 10 additions & 0 deletions src/commands/cmd_config/cmd_options_config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use code_docs::DocumentedStruct;
use crate::config::Config;

pub fn show_config_options() {
let docstring = Config::commented_fields().unwrap();

println!("Configuration options for each config:\n");
println!("{}", docstring);
}

156 changes: 79 additions & 77 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/// Contains everything related to container configuration
//! Contains everything related to container configuration

use serde::Deserialize;
use code_docs::{code_docs_struct, DocumentedStruct};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
Expand Down Expand Up @@ -56,6 +57,8 @@ const fn default_version() -> u64 { 1 }
impl ConfigFile {
/// Loads config from str, path is just for error message and can be anything
pub fn load_from_str(text: &str) -> Result<Self, ConfigError> {
// TODO load a table first and get the version then try parsing appropriate struct

let obj = toml::from_str::<ConfigFile>(text)
.map_err(|err| ConfigError::Generic(Box::new(err)) )?;

Expand All @@ -74,81 +77,80 @@ impl ConfigFile {
}
}

/// Single configuration for a container, contains default settings and optional settings per
/// engine that get applied over the default settings
#[derive(Debug, Clone, Default, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
// TODO figure out rules for local containers that need to be built
/// Name of the configuration
pub name: String,

/// Image used for the container
pub image: String,

/// Optional name to set for the container, otherwise randomly generated
pub container_name: Option<String>,

/// Dotfiles directory to use as /etc/skel
///
/// Environ vars are expanded
pub skel: Option<String>,

/// Should the container have access to internet
#[serde(default)]
pub network: bool,

/// Try to pass audio into the the container, security impact is unknown
#[serde(default)]
pub audio: bool,

/// Passes wayland compositor through, pokes holes in sandbox, allows r/w access to clipboard
#[serde(default)]
pub wayland: bool,

/// Pass through ssh-agent socket
#[serde(default)]
pub ssh_agent: bool,

/// Pass through session dbus socket
#[serde(default)]
pub session_bus: bool,

/// Run command on init (ran using `/bin/sh`)
#[serde(default)]
pub on_init: Vec<String>,

/// Copies files to container as init scripts (places them in `/init.d/`)
#[serde(default)]
pub on_init_file: Vec<String>,

/// Paths to persist between container invocation by mounting a volume
#[serde(default)]
pub persist: Vec<(String, String)>,

/// Environment variables to set
///
/// Environ vars are expanded
#[serde(default)]
pub env: HashMap<String, String>,

/// Args passed to the engine
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args: Vec<String>,

/// Args passed to the engine, if its podman
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args_podman: Vec<String>,

/// Args passed to the engine, if its docker
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args_docker: Vec<String>,
// save all the fields and docs so they can be printed as always up-to-date documentation
code_docs_struct! {
/// Single configuration for a container, contains default settings and optional settings per
/// engine that get applied over the default settings
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
// TODO redo these comments so they are easy to understand even for non-rust programmers
/// Name of the configuration
pub name: String,

/// Image used for the container
pub image: String,

/// Optional name to set for the container, otherwise randomly generated
pub container_name: Option<String>,

/// Dotfiles directory to use as /etc/skel
///
/// Environ vars are expanded
pub skel: Option<String>,

/// Should the container have access to internet
#[serde(default)]
pub network: bool,

/// Try to pass audio into the the container, security impact is unknown
#[serde(default)]
pub audio: bool,

/// Passes wayland compositor through, pokes holes in sandbox, allows r/w access to clipboard
#[serde(default)]
pub wayland: bool,

/// Pass through ssh-agent socket
#[serde(default)]
pub ssh_agent: bool,

/// Pass through session dbus socket
#[serde(default)]
pub session_bus: bool,

/// Run command on init (ran using `/bin/sh`)
#[serde(default)]
pub on_init: Vec<String>,

/// Copies files to container as init scripts (places them in `/init.d/`)
#[serde(default)]
pub on_init_file: Vec<String>,

/// Environment variables to set
///
/// Environ vars are expanded
#[serde(default)]
pub env: HashMap<String, String>,

/// Args passed to the engine
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args: Vec<String>,

/// Args passed to the engine, if its podman
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args_podman: Vec<String>,

/// Args passed to the engine, if its docker
///
/// Environ vars are expanded
#[serde(default)]
pub engine_args_docker: Vec<String>,
}
}

impl Config {
Expand Down
73 changes: 43 additions & 30 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,53 +27,66 @@ macro_rules! ENV_VAR_PREFIX {

pub use util::ExitResult;

fn main() -> ExitCode {
let args = cli::Cli::parse();

// init does not need engine, just get it from environment if needed
if let CliCommands::Init(x) = args.cmd {
if !util::is_in_container() {
eprintln!("Running init outside a container is dangerous, qutting..");
return ExitCode::FAILURE;
}

return commands::container_init(x).to_exitcode();
}

fn get_engine(args: &cli::Cli) -> Result<Engine, ExitCode> {
// find and detect engine
let engine: Engine = if let Some(chosen) = args.engine {
let engine: Engine = if let Some(chosen) = &args.engine {
// test if engine exists in PATH or as a literal path
if !(util::executable_exists(&chosen) || std::path::Path::new(&chosen).exists()) {
eprintln!("Engine '{}' not found in PATH or filesystem", &chosen);
return ExitCode::FAILURE;
if !(util::executable_exists(chosen) || std::path::Path::new(chosen).exists()) {
eprintln!("Engine '{}' not found in PATH or filesystem", chosen);
return Err(ExitCode::FAILURE);
}

Engine::detect(&chosen).expect("Failed to detect engine kind")
Engine::detect(chosen).expect("Failed to detect engine kind")
} else if let Some(found) = Engine::find_available_engine() {
found
} else {
eprintln!("No compatible container engine found in PATH");
return ExitCode::FAILURE;
return Err(ExitCode::FAILURE);
};

// prevent running with docker for now
if let util::EngineKind::Docker = engine.kind {
eprintln!("Docker is not supported at the moment");
return ExitCode::FAILURE
return Err(ExitCode::FAILURE);
}

Ok(engine)
}

fn main() -> ExitCode {
let args = cli::Cli::parse();

// init does not need engine, just get it from environment if needed
if let CliCommands::Init(x) = args.cmd {
if !util::is_in_container() {
eprintln!("Running init outside a container is dangerous, qutting..");
return ExitCode::FAILURE;
}

return commands::container_init(x).to_exitcode();
}

match command(&args) {
Ok(_) => ExitCode::SUCCESS,
Err(x) => x,
}
}

match args.cmd {
CliCommands::Start(x) => commands::start_container(engine, args.dry_run, x),
CliCommands::Shell(x) => commands::open_shell(engine, args.dry_run, x),
CliCommands::Exec(x) => commands::container_exec(engine, args.dry_run, x),
CliCommands::Exists(x) => commands::container_exists(engine, x),
fn command(args: &cli::Cli) -> Result<(), ExitCode> {
// TODO there is a bit too much cloning here, not that it matters much
match &args.cmd {
CliCommands::Start(x) => commands::start_container(get_engine(&args)?, args.dry_run, x.clone()),
CliCommands::Shell(x) => commands::open_shell(get_engine(&args)?, args.dry_run, x.clone()),
CliCommands::Exec(x) => commands::container_exec(get_engine(&args)?, args.dry_run, x.clone()),
CliCommands::Exists(x) => commands::container_exists(get_engine(&args)?, x.clone()),
CliCommands::Config(subcmd) => match subcmd {
ConfigCommands::Extract(x) => commands::extract_config(engine, args.dry_run, &x),
ConfigCommands::Extract(x) => commands::extract_config(get_engine(&args)?, args.dry_run, &x),
ConfigCommands::Inspect(x) => commands::inspect_config(&x),
ConfigCommands::Options => { commands::show_config_options(); Ok(()) },
},
CliCommands::List => commands::print_containers(engine, args.dry_run),
CliCommands::Logs(x) => commands::print_logs(engine, x),
CliCommands::Kill(x) => commands::kill_container(engine, args.dry_run, x),
CliCommands::List => commands::print_containers(get_engine(&args)?, args.dry_run),
CliCommands::Logs(x) => commands::print_logs(get_engine(&args)?, x.clone()),
CliCommands::Kill(x) => commands::kill_container(get_engine(&args)?, args.dry_run, x.clone()),
CliCommands::Init(_) => unreachable!(), // this is handled before
}.to_exitcode()
}.map_err(|x| ExitCode::from(x))
}

0 comments on commit a806734

Please sign in to comment.