diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/unitctl/README.md | 40 | ||||
-rw-r--r-- | tools/unitctl/unit-client-rs/src/unitd_docker.rs | 145 | ||||
-rw-r--r-- | tools/unitctl/unit-client-rs/src/unitd_instance.rs | 13 | ||||
-rw-r--r-- | tools/unitctl/unitctl/src/cmd/instances.rs | 28 | ||||
-rw-r--r-- | tools/unitctl/unitctl/src/main.rs | 8 | ||||
-rw-r--r-- | tools/unitctl/unitctl/src/unitctl.rs | 14 |
6 files changed, 140 insertions, 108 deletions
diff --git a/tools/unitctl/README.md b/tools/unitctl/README.md index 7292cd86..e514cd56 100644 --- a/tools/unitctl/README.md +++ b/tools/unitctl/README.md @@ -36,9 +36,9 @@ desired. ## Features (Current) -### Consumes alternative configuration formats Like YAML and converts them -### Syntactic highlighting of JSON output -### Interpretation of UNIT errors with (arguably more) useful error messages +- Consumes alternative configuration formats Like YAML and converts them +- Syntactic highlighting of JSON output +- Interpretation of UNIT errors with (arguably more) useful error messages ### Lists all running UNIT processes and provides details about each process. ``` @@ -52,6 +52,30 @@ unitd instance [pid: 79489, version: 1.32.0]: Configure options: --prefix=/opt/unit --user=elijah --group=elijah --openssl ``` +### Start a new UNIT process via docker +``` +$ unitctl instances new /tmp/2 $(pwd) 'unit:wasm' +Pulling and starting a container from unit:wasm +Will mount /tmp/2 to /var/run for socket access +Will READ ONLY mount /home/ava/repositories/nginx/unit/tools/unitctl to /www for application access +Note: Container will be on host network + +``` + +To the subcommand `unitctl instances new` the user must provide three things: +1. **A directory such as `/tmp/2`.** + The UNIT container will mount this to `/var/run` internally. + Thus, the control socket and pid file will be accessible from the host. +2. **A path to an application.** + In the example, `$(pwd)` is provided. The UNIT container will mount + this READ ONLY to `/www/`. This will allow the user to configure + their UNIT container to expose an application stored on the host. +3. **An image tag.** + In the example, `unit:wasm` is used. This will be the image that unitctl + will deploy. Custom repos and images can be deployed in this manner. + +After deployment the user will have one UNIT container running on the host network. + ### Lists active listeners from running UNIT processes ``` unitctl listeners @@ -109,13 +133,6 @@ $ unitctl edit } ``` -### Display interactive OpenAPI control panel -``` -$ unitctl ui -Starting UI server on http://127.0.0.1:3000/control-ui/ -Press Ctrl-C to stop the server -``` - ### Import configuration, certificates, and NJS modules from directory ``` $ unitctl import /opt/unit/config @@ -124,6 +141,7 @@ Imported /opt/unit/config/hello.js -> /js_modules/hello.js Imported /opt/unit/config/put.json -> /config Imported 3 files ``` + ### Wait for socket to become available ``` $ unitctl --wait-timeout-seconds=3 --wait-max-tries=4 import /opt/unit/config` @@ -131,4 +149,4 @@ Waiting for 3s control socket to be available try 2/4... Waiting for 3s control socket to be available try 3/4... Waiting for 3s control socket to be available try 4/4... Timeout waiting for unit to start has been exceeded -```
\ No newline at end of file +``` diff --git a/tools/unitctl/unit-client-rs/src/unitd_docker.rs b/tools/unitctl/unit-client-rs/src/unitd_docker.rs index 47f017a9..2b51ddd3 100644 --- a/tools/unitctl/unit-client-rs/src/unitd_docker.rs +++ b/tools/unitctl/unit-client-rs/src/unitd_docker.rs @@ -2,18 +2,17 @@ use std::collections::HashMap; use std::fs::read_to_string; use std::path::PathBuf; -use crate::unitd_process::UnitdProcess; +use crate::futures::StreamExt; use crate::unit_client::UnitClientError; -use bollard::{models::ContainerSummary, Docker}; -use bollard::container::{Config, StartContainerOptions, ListContainersOptions}; +use crate::unitd_process::UnitdProcess; +use bollard::container::{Config, ListContainersOptions, StartContainerOptions}; use bollard::image::CreateImageOptions; +use bollard::models::{ContainerCreateResponse, HostConfig, Mount, MountTypeEnum}; use bollard::secret::ContainerInspectResponse; -use bollard::models::{HostConfig, MountTypeEnum, Mount, ContainerCreateResponse}; +use bollard::{models::ContainerSummary, Docker}; use regex::Regex; use serde::ser::SerializeMap; use serde::{Serialize, Serializer}; -use crate::futures::StreamExt; - #[derive(Clone, Debug)] pub struct UnitdContainer { @@ -75,7 +74,8 @@ impl From<&UnitdContainer> for UnitdProcess { cmd.strip_prefix("/usr/local/bin/docker-entrypoint.sh") .or_else(|| Some("")) .unwrap() - .to_string()) + .to_string() + ) )) }); let mut cmds = vec![]; @@ -197,13 +197,15 @@ impl UnitdContainer { pub fn rewrite_socket(&self, command: String) -> String { command .split(" ") - .map(|tok| if tok.starts_with("unix:") { - format!("unix:{}", self.host_path( - tok.strip_prefix("unix:") - .unwrap() - .to_string())) - } else { - tok.to_string() + .map(|tok| { + if tok.starts_with("unix:") { + format!( + "unix:{}", + self.host_path(tok.strip_prefix("unix:").unwrap().to_string()) + ) + } else { + tok.to_string() + } }) .collect::<Vec<_>>() .join(" ") @@ -227,18 +229,18 @@ impl UnitdContainer { pub async fn deploy_new_container( socket: &String, application: &String, - image: &String + image: &String, ) -> Result<Vec<String>, UnitClientError> { match Docker::connect_with_local_defaults() { Ok(docker) => { let mut mounts = vec![]; - mounts.push(Mount{ + mounts.push(Mount { typ: Some(MountTypeEnum::BIND), source: Some(socket.clone()), target: Some("/var/run".to_string()), ..Default::default() }); - mounts.push(Mount{ + mounts.push(Mount { typ: Some(MountTypeEnum::BIND), source: Some(application.clone()), target: Some("/www".to_string()), @@ -246,65 +248,88 @@ pub async fn deploy_new_container( ..Default::default() }); - let _ = docker.create_image( - Some(CreateImageOptions { - from_image: image.as_str(), - ..Default::default() - }), None, None) + let _ = docker + .create_image( + Some(CreateImageOptions { + from_image: image.as_str(), + ..Default::default() + }), + None, + None, + ) .next() .await .unwrap() - .or_else(|err| Err(UnitClientError::UnitdDockerError{message: err.to_string()})); + .or_else(|err| { + Err(UnitClientError::UnitdDockerError { + message: err.to_string(), + }) + }); let resp: ContainerCreateResponse; - match docker.create_container::<String, String>( - None, Config { - image: Some(image.clone()), - host_config: Some(HostConfig { - network_mode: Some("host".to_string()), - mounts: Some(mounts), + match docker + .create_container::<String, String>( + None, + Config { + image: Some(image.clone()), + host_config: Some(HostConfig { + network_mode: Some("host".to_string()), + mounts: Some(mounts), + ..Default::default() + }), ..Default::default() - }), ..Default::default()}) - .await { - Err(err) => return Err(UnitClientError::UnitdDockerError{message: err.to_string()}), - Ok(response) => resp = response, + }, + ) + .await + { + Err(err) => { + return Err(UnitClientError::UnitdDockerError { + message: err.to_string(), + }) } + Ok(response) => resp = response, + } let mut list_container_filters = HashMap::new(); list_container_filters.insert("id".to_string(), vec![resp.id]); - match docker.list_containers::<String>( - Some(ListContainersOptions{ + match docker + .list_containers::<String>(Some(ListContainersOptions { all: true, limit: None, size: false, filters: list_container_filters, })) - .await { - Err(e) => Err(UnitClientError::UnitdDockerError{message: e.to_string()}), - Ok(info) => { - if info.len() < 1 { - return Err(UnitClientError::UnitdDockerError{message: "couldnt find new container".to_string()}); - } - if info[0].names.is_none() || info[0].names.clone().unwrap().len() < 1 { - return Err(UnitClientError::UnitdDockerError{message: "new container has no name".to_string()}); - } + .await + { + Err(e) => Err(UnitClientError::UnitdDockerError { message: e.to_string() }), + Ok(info) => { + if info.len() < 1 { + return Err(UnitClientError::UnitdDockerError { + message: "couldnt find new container".to_string(), + }); + } + if info[0].names.is_none() || info[0].names.clone().unwrap().len() < 1 { + return Err(UnitClientError::UnitdDockerError { + message: "new container has no name".to_string(), + }); + } - match docker.start_container( - info[0] - .names - .clone() - .unwrap()[0] - .strip_prefix("/") - .unwrap(), - None::<StartContainerOptions<String>> - ).await { - Err(err) => Err(UnitClientError::UnitdDockerError{message: err.to_string()}), - Ok(_) => Ok(resp.warnings) - } + match docker + .start_container( + info[0].names.clone().unwrap()[0].strip_prefix("/").unwrap(), + None::<StartContainerOptions<String>>, + ) + .await + { + Err(err) => Err(UnitClientError::UnitdDockerError { + message: err.to_string(), + }), + Ok(_) => Ok(resp.warnings), } } - }, - Err(e) => Err(UnitClientError::UnitdDockerError{message: e.to_string()}) + } + } + Err(e) => Err(UnitClientError::UnitdDockerError { message: e.to_string() }), } } @@ -372,7 +397,7 @@ mod tests { assert_eq!( ctr.rewrite_socket("unitd --no-daemon --control unix:/var/run/control.unit.sock".to_string()), - "unitd --no-daemon --control unix:/tmp/control.unit.sock".to_string()); - + "unitd --no-daemon --control unix:/tmp/control.unit.sock".to_string() + ); } } diff --git a/tools/unitctl/unit-client-rs/src/unitd_instance.rs b/tools/unitctl/unit-client-rs/src/unitd_instance.rs index 86f8e73d..a7fb1bdc 100644 --- a/tools/unitctl/unit-client-rs/src/unitd_instance.rs +++ b/tools/unitctl/unit-client-rs/src/unitd_instance.rs @@ -121,13 +121,10 @@ impl UnitdInstance { Ok(_) if process.container.is_some() => { let mut err = vec![]; // double check that it is running - let running = process.container - .as_ref() - .unwrap() - .container_is_running(); + let running = process.container.as_ref().unwrap().container_is_running(); if running.is_none() || !running.unwrap() { - err.push(UnitClientError::UnitdProcessParseError{ + err.push(UnitClientError::UnitdProcessParseError { message: "process container is not running".to_string(), pid: process.process_id, }); @@ -138,10 +135,8 @@ impl UnitdInstance { configure_options: None, errors: err, } - }, - Ok(unitd_path) => match UnitdConfigureOptions::new( - &unitd_path.clone() - .into_path_buf()) { + } + Ok(unitd_path) => match UnitdConfigureOptions::new(&unitd_path.clone().into_path_buf()) { Ok(configure_options) => UnitdInstance { process: process.to_owned(), configure_options: Some(configure_options), diff --git a/tools/unitctl/unitctl/src/cmd/instances.rs b/tools/unitctl/unitctl/src/cmd/instances.rs index cd59b436..b9af75f6 100644 --- a/tools/unitctl/unitctl/src/cmd/instances.rs +++ b/tools/unitctl/unitctl/src/cmd/instances.rs @@ -1,16 +1,16 @@ -use crate::{OutputFormat, UnitctlError}; use crate::unitctl::{InstanceArgs, InstanceCommands}; -use unit_client_rs::unitd_instance::UnitdInstance; -use unit_client_rs::unitd_docker::deploy_new_container; +use crate::{OutputFormat, UnitctlError}; use std::path::PathBuf; +use unit_client_rs::unitd_docker::deploy_new_container; +use unit_client_rs::unitd_instance::UnitdInstance; pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> { if let Some(cmd) = args.command { match cmd { - InstanceCommands::New{ + InstanceCommands::New { ref socket, ref application, - ref image + ref image, } => { println!("Pulling and starting a container from {}", image); println!("Will mount {} to /var/run for socket access", socket); @@ -20,15 +20,15 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> { eprintln!("application and socket paths must be directories"); Err(UnitctlError::NoFilesImported) } else { - deploy_new_container(socket, application, image) - .await - .map_or_else(|e| Err(UnitctlError::UnitClientError{source: e}), - |warn| { - for i in warn { - println!("warning from docker: {}", i); - } - Ok(()) - }) + deploy_new_container(socket, application, image).await.map_or_else( + |e| Err(UnitctlError::UnitClientError { source: e }), + |warn| { + for i in warn { + println!("warning from docker: {}", i); + } + Ok(()) + }, + ) } } } diff --git a/tools/unitctl/unitctl/src/main.rs b/tools/unitctl/unitctl/src/main.rs index 9c42bdf0..12322873 100644 --- a/tools/unitctl/unitctl/src/main.rs +++ b/tools/unitctl/unitctl/src/main.rs @@ -30,9 +30,9 @@ async fn main() -> Result<(), UnitctlError> { match cli.command { Commands::Instances(args) => instances::cmd(args).await, - Commands::Edit{output_format} => edit::cmd(&cli, output_format).await, + Commands::Edit { output_format } => edit::cmd(&cli, output_format).await, - Commands::Import{ref directory} => import::cmd(&cli, directory).await, + Commands::Import { ref directory } => import::cmd(&cli, directory).await, Commands::Execute { ref output_format, @@ -41,9 +41,9 @@ async fn main() -> Result<(), UnitctlError> { ref path, } => execute_cmd::cmd(&cli, output_format, input_file, method, path).await, - Commands::Status{output_format} => status::cmd(&cli, output_format).await, + Commands::Status { output_format } => status::cmd(&cli, output_format).await, - Commands::Listeners{output_format}=> listeners::cmd(&cli, output_format).await, + Commands::Listeners { output_format } => listeners::cmd(&cli, output_format).await, } .map_err(|error| { eprint_error(&error); diff --git a/tools/unitctl/unitctl/src/unitctl.rs b/tools/unitctl/unitctl/src/unitctl.rs index eb7da1b0..b1bdbbd1 100644 --- a/tools/unitctl/unitctl/src/unitctl.rs +++ b/tools/unitctl/unitctl/src/unitctl.rs @@ -2,7 +2,7 @@ extern crate clap; use crate::output_format::OutputFormat; use clap::error::ErrorKind::ValueValidation; -use clap::{Error as ClapError, Parser, Subcommand, Args}; +use clap::{Args, Error as ClapError, Parser, Subcommand}; use std::path::PathBuf; use unit_client_rs::control_socket_address::ControlSocket; @@ -137,16 +137,10 @@ pub struct InstanceArgs { pub enum InstanceCommands { #[command(about = "deploy a new docker instance of unitd")] New { - #[arg( - required = true, - help = "Path to mount control socket to host", - )] + #[arg(required = true, help = "Path to mount control socket to host")] socket: String, - #[arg( - required = true, - help = "Path to mount application into container", - )] + #[arg(required = true, help = "Path to mount application into container")] application: String, #[arg( @@ -154,7 +148,7 @@ pub enum InstanceCommands { default_value = env!("CARGO_PKG_VERSION"), )] image: String, - } + }, } fn parse_control_socket_address(s: &str) -> Result<ControlSocket, ClapError> { |