summaryrefslogtreecommitdiffhomepage
path: root/tools
diff options
context:
space:
mode:
authorAva Hahn <a.hahn@f5.com>2024-06-14 21:04:15 -0700
committerAva Hahn <110854134+avahahn@users.noreply.github.com>2024-06-18 16:21:10 -0700
commite0c15ae4575335fb079e2d33fc853a547b2380c9 (patch)
treece2a4e6eb80f79d1bb2d89c5667c71dbf11d375e /tools
parentd96d583328f614c658d42f5bb0d2a0f81621327e (diff)
downloadunit-e0c15ae4575335fb079e2d33fc853a547b2380c9.tar.gz
unit-e0c15ae4575335fb079e2d33fc853a547b2380c9.tar.bz2
tools/unitctl: implement application subcommand
* application subcommand UI schema * application subcommand handler * additions to unit-client-rs to expose application API * elaborate on OpenAPI error handling * adds wasm and wasi app schemas to OpenAPI Schema * updates tools/unitctl OpenAPI library * many linter fixes * README.md updates Signed-off-by: Ava Hahn <a.hahn@f5.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/unitctl/GNUmakefile4
-rw-r--r--tools/unitctl/README.md26
-rw-r--r--tools/unitctl/unit-client-rs/src/control_socket_address.rs2
-rw-r--r--tools/unitctl/unit-client-rs/src/unit_client.rs48
-rw-r--r--tools/unitctl/unit-client-rs/src/unitd_docker.rs111
-rw-r--r--tools/unitctl/unit-openapi/.openapi-generator/FILES18
-rw-r--r--tools/unitctl/unit-openapi/.openapi-generator/VERSION2
-rw-r--r--tools/unitctl/unit-openapi/README.md10
-rw-r--r--tools/unitctl/unit-openapi/src/lib.rs4
-rw-r--r--tools/unitctl/unitctl/src/cmd/applications.rs36
-rw-r--r--tools/unitctl/unitctl/src/cmd/instances.rs77
-rw-r--r--tools/unitctl/unitctl/src/cmd/mod.rs1
-rw-r--r--tools/unitctl/unitctl/src/main.rs7
-rw-r--r--tools/unitctl/unitctl/src/unitctl.rs41
14 files changed, 251 insertions, 136 deletions
diff --git a/tools/unitctl/GNUmakefile b/tools/unitctl/GNUmakefile
index e7cb379a..9992a322 100644
--- a/tools/unitctl/GNUmakefile
+++ b/tools/unitctl/GNUmakefile
@@ -23,7 +23,7 @@ CARGO ?= cargo
DOCKER ?= docker
DOCKER_BUILD_FLAGS ?= --load
CHECKSUM ?= sha256sum
-OPENAPI_GENERATOR_VERSION ?= 6.6.0
+OPENAPI_GENERATOR_VERSION ?= 7.6.0
# Define platform targets based off of the current host OS
# If running MacOS, then build for MacOS platform targets installed in rustup
@@ -137,7 +137,7 @@ openapi-clean: ## Clean up generated OpenAPI files
$Q find "$(CURDIR)/unit-openapi/src/apis" \
! -name 'error.rs' -type f -exec rm -f {} +
$Q $(info $(M) cleaning up generated OpenAPI models code)
- $Q rm -rf "$(CURDIR)/unit-openapi/src/models/*"
+ $Q rm -rf "$(CURDIR)/unit-openapi/src/models"
include $(CURDIR)/build/package.mk
include $(CURDIR)/build/container.mk
diff --git a/tools/unitctl/README.md b/tools/unitctl/README.md
index 2e5a2da1..dca16e63 100644
--- a/tools/unitctl/README.md
+++ b/tools/unitctl/README.md
@@ -31,13 +31,13 @@ their own makefile targets. Alternatively, all available binary targets can be
built with `make all`. See the below example for illustration:
```
-[ava@calliope cli]$ make list-targets
+$ make list-targets
x86_64-unknown-linux-gnu
-[ava@calliope cli]$ make x86_64-unknown-linux-gnu
+$ make x86_64-unknown-linux-gnu
▶ building unitctl with flags [--quiet --release --bin unitctl --target x86_64-unknown-linux-gnu]
-[ava@calliope cli]$ file ./target/x86_64-unknown-linux-gnu/release/unitctl
+$ file ./target/x86_64-unknown-linux-gnu/release/unitctl
./target/x86_64-unknown-linux-gnu/release/unitctl: ELF 64-bit LSB pie executable,
x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=ef4b094ffd549b39a8cb27a7ba2cc0dbad87a3bc, for GNU/Linux 4.4.0,
@@ -91,6 +91,26 @@ To the subcommand `unitctl instances new` the user must provide three things:
After deployment the user will have one Unit container running on the host network.
+### Lists active applications and provides means to restart them
+Listing applications:
+```
+$ unitctl app list
+{
+ "wasm": {
+ "type": "wasm-wasi-component",
+ "component": "/www/wasmapp-proxy-component.wasm"
+ }
+}
+```
+
+Restarting an application:
+```
+$ unitctl app reload wasm
+{
+ "success": "Ok"
+}
+```
+
### Lists active listeners from running Unit processes
```
unitctl listeners
diff --git a/tools/unitctl/unit-client-rs/src/control_socket_address.rs b/tools/unitctl/unit-client-rs/src/control_socket_address.rs
index 402d2293..438ab0ad 100644
--- a/tools/unitctl/unit-client-rs/src/control_socket_address.rs
+++ b/tools/unitctl/unit-client-rs/src/control_socket_address.rs
@@ -34,7 +34,6 @@ impl ControlSocketScheme {
}
}
-
impl Display for ControlSocket {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
@@ -312,7 +311,6 @@ impl ControlSocket {
}
}
-
#[cfg(test)]
mod tests {
use rand::distributions::{Alphanumeric, DistString};
diff --git a/tools/unitctl/unit-client-rs/src/unit_client.rs b/tools/unitctl/unit-client-rs/src/unit_client.rs
index b8c73ec0..b3f07308 100644
--- a/tools/unitctl/unit-client-rs/src/unit_client.rs
+++ b/tools/unitctl/unit-client-rs/src/unit_client.rs
@@ -15,9 +15,11 @@ use serde::{Deserialize, Serialize};
use crate::control_socket_address::ControlSocket;
use unit_openapi::apis::configuration::Configuration;
-use unit_openapi::apis::{Error as OpenAPIError, StatusApi};
-use unit_openapi::apis::{ListenersApi, ListenersApiClient, StatusApiClient};
-use unit_openapi::models::{ConfigListener, Status};
+use unit_openapi::apis::{
+ ApplicationsApi, ApplicationsApiClient, AppsApi, AppsApiClient, Error as OpenAPIError, ListenersApi,
+ ListenersApiClient, StatusApi, StatusApiClient,
+};
+use unit_openapi::models::{ConfigApplication, ConfigListener, Status};
const USER_AGENT: &str = concat!("UNIT CLI/", env!("CARGO_PKG_VERSION"), "/rust");
@@ -276,6 +278,46 @@ impl UnitClient {
})
}
+ pub fn applications_api(&self) -> Box<dyn ApplicationsApi + 'static> {
+ new_openapi_client!(self, ApplicationsApiClient, ApplicationsApi)
+ }
+
+ pub async fn applications(&self) -> Result<HashMap<String, ConfigApplication>, Box<UnitClientError>> {
+ self.applications_api().get_applications().await.or_else(|err| {
+ if let OpenAPIError::Hyper(hyper_error) = err {
+ Err(Box::new(UnitClientError::new(
+ hyper_error,
+ self.control_socket.to_string(),
+ "/applications".to_string(),
+ )))
+ } else {
+ Err(Box::new(UnitClientError::OpenAPIError { source: err }))
+ }
+ })
+ }
+
+ pub async fn per_application_api(&self) -> Box<dyn AppsApi + 'static> {
+ new_openapi_client!(self, AppsApiClient, AppsApi)
+ }
+
+ pub async fn restart_application(&self, name: &String) -> Result<HashMap<String, String>, Box<UnitClientError>> {
+ self.per_application_api()
+ .await
+ .get_app_restart(name.as_str())
+ .await
+ .or_else(|err| {
+ if let OpenAPIError::Hyper(hyper_error) = err {
+ Err(Box::new(UnitClientError::new(
+ hyper_error,
+ self.control_socket.to_string(),
+ format!("/control/applications/{}/restart", name),
+ )))
+ } else {
+ Err(Box::new(UnitClientError::OpenAPIError { source: err }))
+ }
+ })
+ }
+
pub async fn is_running(&self) -> bool {
self.status().await.is_ok()
}
diff --git a/tools/unitctl/unit-client-rs/src/unitd_docker.rs b/tools/unitctl/unit-client-rs/src/unitd_docker.rs
index 6881893d..0d318096 100644
--- a/tools/unitctl/unit-client-rs/src/unitd_docker.rs
+++ b/tools/unitctl/unit-client-rs/src/unitd_docker.rs
@@ -1,19 +1,16 @@
use std::collections::HashMap;
use std::fs::read_to_string;
-use std::path::{PathBuf, MAIN_SEPARATOR};
use std::io::stderr;
+use std::path::{PathBuf, MAIN_SEPARATOR};
+use crate::control_socket_address::ControlSocket;
use crate::futures::StreamExt;
use crate::unit_client::UnitClientError;
use crate::unitd_process::UnitdProcess;
-use crate::control_socket_address::ControlSocket;
use bollard::container::{Config, ListContainersOptions, StartContainerOptions};
use bollard::image::CreateImageOptions;
-use bollard::models::{
- ContainerCreateResponse, HostConfig, Mount,
- MountTypeEnum, ContainerSummary,
-};
+use bollard::models::{ContainerCreateResponse, ContainerSummary, HostConfig, Mount, MountTypeEnum};
use bollard::secret::ContainerInspectResponse;
use bollard::Docker;
@@ -156,22 +153,18 @@ impl UnitdContainer {
// cant do this functionally because of the async call
let mut mapped = vec![];
for ctr in summary {
- if unitd_command_re.is_match(&ctr.clone().command
- .or(Some(String::new()))
- .unwrap()) {
- let mut c = UnitdContainer::from(&ctr);
- if let Some(names) = ctr.names {
- if names.len() > 0 {
- let name = names[0].strip_prefix("/")
- .or(Some(names[0].as_str())).unwrap();
- if let Ok(cir) = docker
- .inspect_container(name, None).await {
- c.details = Some(cir);
- }
+ if unitd_command_re.is_match(&ctr.clone().command.or(Some(String::new())).unwrap()) {
+ let mut c = UnitdContainer::from(&ctr);
+ if let Some(names) = ctr.names {
+ if names.len() > 0 {
+ let name = names[0].strip_prefix("/").or(Some(names[0].as_str())).unwrap();
+ if let Ok(cir) = docker.inspect_container(name, None).await {
+ c.details = Some(cir);
}
}
- mapped.push(c);
}
+ mapped.push(c);
+ }
}
mapped
}
@@ -196,11 +189,11 @@ impl UnitdContainer {
// either return translated path or original prefixed with "container"
if keys.len() > 0 {
- let mut matches = self.mounts[&keys[0]]
- .clone()
- .join(cp.as_path()
- .strip_prefix(keys[0].clone())
- .expect("error checking path prefix"));
+ let mut matches = self.mounts[&keys[0]].clone().join(
+ cp.as_path()
+ .strip_prefix(keys[0].clone())
+ .expect("error checking path prefix"),
+ );
/* Observed on M1 Mac that Docker on OSX
* adds a bunch of garbage to the mount path
* converting it into a useless directory
@@ -208,15 +201,14 @@ impl UnitdContainer {
*/
if cfg!(target_os = "macos") {
let mut abs = PathBuf::from(String::from(MAIN_SEPARATOR));
- let m = matches.strip_prefix("/host_mnt/private")
- .unwrap_or(matches.strip_prefix("/host_mnt")
- .unwrap_or(matches.as_path()));
+ let m = matches
+ .strip_prefix("/host_mnt/private")
+ .unwrap_or(matches.strip_prefix("/host_mnt").unwrap_or(matches.as_path()));
// make it absolute again
abs.push(m);
matches = abs;
}
- matches.to_string_lossy()
- .to_string()
+ matches.to_string_lossy().to_string()
} else {
format!("<container>:{}", cp.display())
}
@@ -264,10 +256,7 @@ pub async fn deploy_new_container(
let mut mounts = vec![];
// if a unix socket is specified, mounts its directory
if socket.is_local_socket() {
- let mount_path = PathBuf::from(socket.clone())
- .as_path()
- .to_string_lossy()
- .to_string();
+ let mount_path = PathBuf::from(socket.clone()).as_path().to_string_lossy().to_string();
mounts.push(Mount {
typ: Some(MountTypeEnum::BIND),
source: Some(mount_path),
@@ -290,22 +279,22 @@ pub async fn deploy_new_container(
Some(CreateImageOptions {
from_image: image.as_str(),
..Default::default()
- }), None, None
+ }),
+ None,
+ None,
);
while let Some(res) = stream.next().await {
if let Ok(info) = res {
if let Some(id) = info.id {
if let Some(_) = totals.get_mut(&id) {
- if let Some(delta) = info.progress_detail
- .and_then(|detail| detail.current) {
- pb.add(delta as u64);
- }
+ if let Some(delta) = info.progress_detail.and_then(|detail| detail.current) {
+ pb.add(delta as u64);
+ }
} else {
- if let Some(total) = info.progress_detail
- .and_then(|detail| detail.total) {
- totals.insert(id, total);
- pb.total += total as u64;
- }
+ if let Some(total) = info.progress_detail.and_then(|detail| detail.total) {
+ totals.insert(id, total);
+ pb.total += total as u64;
+ }
}
}
}
@@ -357,27 +346,27 @@ pub async fn deploy_new_container(
.await
{
// somehow our container doesnt exist
- Err(e) => Err(UnitClientError::UnitdDockerError{
- message: e.to_string()
- }),
+ Err(e) => Err(UnitClientError::UnitdDockerError { message: e.to_string() }),
// here it is!
Ok(info) => {
if info.len() < 1 {
return Err(UnitClientError::UnitdDockerError {
message: "couldnt find new container".to_string(),
});
- } else 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(),
- });
- }
+ } else 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(),
+ });
+ }
// start our container
- match docker.start_container(
- info[0].names.clone().unwrap()[0].strip_prefix(MAIN_SEPARATOR).unwrap(),
- None::<StartContainerOptions<String>>,
- ).await {
+ match docker
+ .start_container(
+ info[0].names.clone().unwrap()[0].strip_prefix(MAIN_SEPARATOR).unwrap(),
+ None::<StartContainerOptions<String>>,
+ )
+ .await
+ {
Err(err) => Err(UnitClientError::UnitdDockerError {
message: err.to_string(),
}),
@@ -439,14 +428,8 @@ mod tests {
ctr.host_path("/path/to/conf".to_string())
);
if cfg!(target_os = "macos") {
- assert_eq!(
- "/6/test".to_string(),
- ctr.host_path("/var/test".to_string())
- );
- assert_eq!(
- "/7/test".to_string(),
- ctr.host_path("/var/var/test".to_string())
- );
+ assert_eq!("/6/test".to_string(), ctr.host_path("/var/test".to_string()));
+ assert_eq!("/7/test".to_string(), ctr.host_path("/var/var/test".to_string()));
}
}
diff --git a/tools/unitctl/unit-openapi/.openapi-generator/FILES b/tools/unitctl/unit-openapi/.openapi-generator/FILES
index 4f177f5f..f487c081 100644
--- a/tools/unitctl/unit-openapi/.openapi-generator/FILES
+++ b/tools/unitctl/unit-openapi/.openapi-generator/FILES
@@ -26,21 +26,18 @@ docs/ConfigApplicationCommonLimits.md
docs/ConfigApplicationCommonProcesses.md
docs/ConfigApplicationCommonProcessesAnyOf.md
docs/ConfigApplicationExternal.md
-docs/ConfigApplicationExternalAllOf.md
docs/ConfigApplicationJava.md
-docs/ConfigApplicationJavaAllOf.md
docs/ConfigApplicationPerl.md
-docs/ConfigApplicationPerlAllOf.md
docs/ConfigApplicationPhp.md
-docs/ConfigApplicationPhpAllOf.md
docs/ConfigApplicationPhpAllOfOptions.md
docs/ConfigApplicationPhpAllOfTargets.md
docs/ConfigApplicationPython.md
-docs/ConfigApplicationPythonAllOf.md
docs/ConfigApplicationPythonAllOfPath.md
docs/ConfigApplicationPythonAllOfTargets.md
docs/ConfigApplicationRuby.md
-docs/ConfigApplicationRubyAllOf.md
+docs/ConfigApplicationWasi.md
+docs/ConfigApplicationWasm.md
+docs/ConfigApplicationWasmAllOfAccess.md
docs/ConfigListener.md
docs/ConfigListenerForwarded.md
docs/ConfigListenerForwardedSource.md
@@ -114,21 +111,18 @@ src/models/config_application_common_limits.rs
src/models/config_application_common_processes.rs
src/models/config_application_common_processes_any_of.rs
src/models/config_application_external.rs
-src/models/config_application_external_all_of.rs
src/models/config_application_java.rs
-src/models/config_application_java_all_of.rs
src/models/config_application_perl.rs
-src/models/config_application_perl_all_of.rs
src/models/config_application_php.rs
-src/models/config_application_php_all_of.rs
src/models/config_application_php_all_of_options.rs
src/models/config_application_php_all_of_targets.rs
src/models/config_application_python.rs
-src/models/config_application_python_all_of.rs
src/models/config_application_python_all_of_path.rs
src/models/config_application_python_all_of_targets.rs
src/models/config_application_ruby.rs
-src/models/config_application_ruby_all_of.rs
+src/models/config_application_wasi.rs
+src/models/config_application_wasm.rs
+src/models/config_application_wasm_all_of_access.rs
src/models/config_listener.rs
src/models/config_listener_forwarded.rs
src/models/config_listener_forwarded_source.rs
diff --git a/tools/unitctl/unit-openapi/.openapi-generator/VERSION b/tools/unitctl/unit-openapi/.openapi-generator/VERSION
index cd802a1e..93c8ddab 100644
--- a/tools/unitctl/unit-openapi/.openapi-generator/VERSION
+++ b/tools/unitctl/unit-openapi/.openapi-generator/VERSION
@@ -1 +1 @@
-6.6.0 \ No newline at end of file
+7.6.0
diff --git a/tools/unitctl/unit-openapi/README.md b/tools/unitctl/unit-openapi/README.md
index 05aba6d9..5bad3fa4 100644
--- a/tools/unitctl/unit-openapi/README.md
+++ b/tools/unitctl/unit-openapi/README.md
@@ -22,6 +22,7 @@ This API client was generated by the [OpenAPI Generator](https://openapi-generat
- API version: 0.2.0
- Package version: 0.4.0-beta
+- Generator version: 7.6.0
- Build package: `org.openapitools.codegen.languages.RustClientCodegen`
## Installation
@@ -354,21 +355,18 @@ Class | Method | HTTP request | Description
- [ConfigApplicationCommonProcesses](docs/ConfigApplicationCommonProcesses.md)
- [ConfigApplicationCommonProcessesAnyOf](docs/ConfigApplicationCommonProcessesAnyOf.md)
- [ConfigApplicationExternal](docs/ConfigApplicationExternal.md)
- - [ConfigApplicationExternalAllOf](docs/ConfigApplicationExternalAllOf.md)
- [ConfigApplicationJava](docs/ConfigApplicationJava.md)
- - [ConfigApplicationJavaAllOf](docs/ConfigApplicationJavaAllOf.md)
- [ConfigApplicationPerl](docs/ConfigApplicationPerl.md)
- - [ConfigApplicationPerlAllOf](docs/ConfigApplicationPerlAllOf.md)
- [ConfigApplicationPhp](docs/ConfigApplicationPhp.md)
- - [ConfigApplicationPhpAllOf](docs/ConfigApplicationPhpAllOf.md)
- [ConfigApplicationPhpAllOfOptions](docs/ConfigApplicationPhpAllOfOptions.md)
- [ConfigApplicationPhpAllOfTargets](docs/ConfigApplicationPhpAllOfTargets.md)
- [ConfigApplicationPython](docs/ConfigApplicationPython.md)
- - [ConfigApplicationPythonAllOf](docs/ConfigApplicationPythonAllOf.md)
- [ConfigApplicationPythonAllOfPath](docs/ConfigApplicationPythonAllOfPath.md)
- [ConfigApplicationPythonAllOfTargets](docs/ConfigApplicationPythonAllOfTargets.md)
- [ConfigApplicationRuby](docs/ConfigApplicationRuby.md)
- - [ConfigApplicationRubyAllOf](docs/ConfigApplicationRubyAllOf.md)
+ - [ConfigApplicationWasi](docs/ConfigApplicationWasi.md)
+ - [ConfigApplicationWasm](docs/ConfigApplicationWasm.md)
+ - [ConfigApplicationWasmAllOfAccess](docs/ConfigApplicationWasmAllOfAccess.md)
- [ConfigListener](docs/ConfigListener.md)
- [ConfigListenerForwarded](docs/ConfigListenerForwarded.md)
- [ConfigListenerForwardedSource](docs/ConfigListenerForwardedSource.md)
diff --git a/tools/unitctl/unit-openapi/src/lib.rs b/tools/unitctl/unit-openapi/src/lib.rs
index a71f18d6..5435cfdb 100644
--- a/tools/unitctl/unit-openapi/src/lib.rs
+++ b/tools/unitctl/unit-openapi/src/lib.rs
@@ -1,6 +1,6 @@
#![allow(clippy::all)]
-#[macro_use]
-extern crate serde_derive;
+#![allow(unused_imports)]
+#![allow(clippy::too_many_arguments)]
extern crate futures;
extern crate hyper;
diff --git a/tools/unitctl/unitctl/src/cmd/applications.rs b/tools/unitctl/unitctl/src/cmd/applications.rs
new file mode 100644
index 00000000..f4c44105
--- /dev/null
+++ b/tools/unitctl/unitctl/src/cmd/applications.rs
@@ -0,0 +1,36 @@
+use crate::unitctl::{ApplicationArgs, ApplicationCommands, UnitCtl};
+use crate::{wait, UnitctlError};
+use crate::requests::send_empty_body_deserialize_response;
+use unit_client_rs::unit_client::UnitClient;
+
+pub(crate) async fn cmd(cli: &UnitCtl, args: &ApplicationArgs) -> Result<(), UnitctlError> {
+ let control_socket = wait::wait_for_socket(cli).await?;
+ let client = UnitClient::new(control_socket);
+
+ match &args.command {
+ ApplicationCommands::Reload { ref name } => client
+ .restart_application(name)
+ .await
+ .map_err(|e| UnitctlError::UnitClientError { source: *e })
+ .and_then(|r| args.output_format.write_to_stdout(&r)),
+
+ /* we should be able to use this but the openapi generator library
+ * is fundamentally incorrect and provides a broken API for the
+ * applications endpoint.
+ ApplicationCommands::List {} => client
+ .applications()
+ .await
+ .map_err(|e| UnitctlError::UnitClientError { source: *e })
+ .and_then(|response| args.output_format.write_to_stdout(&response)),*/
+
+ ApplicationCommands::List {} => {
+ args.output_format.write_to_stdout(
+ &send_empty_body_deserialize_response(
+ &client,
+ "GET",
+ "/config/applications",
+ ).await?
+ )
+ },
+ }
+}
diff --git a/tools/unitctl/unitctl/src/cmd/instances.rs b/tools/unitctl/unitctl/src/cmd/instances.rs
index ee58f697..e532a151 100644
--- a/tools/unitctl/unitctl/src/cmd/instances.rs
+++ b/tools/unitctl/unitctl/src/cmd/instances.rs
@@ -1,11 +1,11 @@
use crate::unitctl::{InstanceArgs, InstanceCommands};
-use crate::{OutputFormat, UnitctlError};
use crate::unitctl_error::ControlSocketErrorKind;
+use crate::{OutputFormat, UnitctlError};
use std::path::PathBuf;
+use unit_client_rs::control_socket_address::ControlSocket;
use unit_client_rs::unitd_docker::deploy_new_container;
use unit_client_rs::unitd_instance::UnitdInstance;
-use unit_client_rs::control_socket_address::ControlSocket;
pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> {
if let Some(cmd) = args.command {
@@ -22,37 +22,38 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> {
} else if !PathBuf::from(application).as_path().exists() {
eprintln!("application path must exist");
Err(UnitctlError::NoFilesImported)
-
} else {
let addr = ControlSocket::parse_address(socket);
if let Err(e) = addr {
- return Err(UnitctlError::UnitClientError{source: e});
+ return Err(UnitctlError::UnitClientError { source: e });
}
// validate we arent processing an abstract socket
if let ControlSocket::UnixLocalAbstractSocket(_) = addr.as_ref().unwrap() {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
message: "cannot pass abstract socket to docker container".to_string(),
- })
+ });
}
// warn user of OSX docker limitations
if let ControlSocket::UnixLocalSocket(ref sock_path) = addr.as_ref().unwrap() {
if cfg!(target_os = "macos") {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
- message: format!("Docker on OSX will break unix sockets mounted {} {}",
- "in containers, see the following link for more information",
- "https://github.com/docker/for-mac/issues/483"),
- })
+ message: format!(
+ "Docker on macOS will break unix domain sockets mounted {} {}",
+ "in containers, see the following link for more information",
+ "https://github.com/docker/for-mac/issues/483"
+ ),
+ });
}
if !sock_path.is_dir() {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
message: "user must specify a directory of UNIX socket directory".to_string(),
- })
+ });
}
}
@@ -60,28 +61,30 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> {
if let ControlSocket::TcpSocket(uri) = addr.as_ref().unwrap() {
if let Some(host) = uri.host() {
if host != "127.0.0.1" {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
message: "TCP URI must point to 127.0.0.1".to_string(),
- })
+ });
}
} else {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
message: "TCP URI must point to a host".to_string(),
- })
+ });
}
if let Some(port) = uri.port_u16() {
if port < 1025 {
- eprintln!("warning! you are asking docker to forward a privileged port. {}",
- "please make sure docker has access to it");
+ eprintln!(
+ "warning! you are asking docker to forward a privileged port. {}",
+ "please make sure docker has access to it"
+ );
}
} else {
- return Err(UnitctlError::ControlSocketError{
+ return Err(UnitctlError::ControlSocketError {
kind: ControlSocketErrorKind::General,
message: "TCP URI must specify a port".to_string(),
- })
+ });
}
if uri.path() != "/" {
@@ -95,11 +98,13 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> {
eprintln!("> Will READ ONLY mount {} to /www for application access", application);
eprintln!("> Container will be on host network");
match addr.as_ref().unwrap() {
- ControlSocket::UnixLocalSocket(path) =>
- eprintln!("> Will mount directory containing {} to /var/www for control API",
- path.as_path().to_string_lossy()),
- ControlSocket::TcpSocket(uri) =>
- eprintln!("> Will forward port {} for control API", uri.port_u16().unwrap()),
+ ControlSocket::UnixLocalSocket(path) => eprintln!(
+ "> Will mount directory containing {} to /var/www for control API",
+ path.as_path().to_string_lossy()
+ ),
+ ControlSocket::TcpSocket(uri) => {
+ eprintln!("> Will forward port {} for control API", uri.port_u16().unwrap())
+ }
_ => unimplemented!(), // abstract socket case ruled out previously
}
@@ -108,15 +113,17 @@ pub(crate) async fn cmd(args: InstanceArgs) -> Result<(), UnitctlError> {
}
// do the actual deployment
- deploy_new_container(addr.unwrap(), application, image).await.map_or_else(
- |e| Err(UnitctlError::UnitClientError { source: e }),
- |warn| {
- for i in warn {
- eprintln!("warning! from docker: {}", i);
- }
- Ok(())
- },
- )
+ deploy_new_container(addr.unwrap(), application, image)
+ .await
+ .map_or_else(
+ |e| Err(UnitctlError::UnitClientError { source: e }),
+ |warn| {
+ for i in warn {
+ eprintln!("warning! from docker: {}", i);
+ }
+ Ok(())
+ },
+ )
}
}
}
diff --git a/tools/unitctl/unitctl/src/cmd/mod.rs b/tools/unitctl/unitctl/src/cmd/mod.rs
index 989a0109..07c50912 100644
--- a/tools/unitctl/unitctl/src/cmd/mod.rs
+++ b/tools/unitctl/unitctl/src/cmd/mod.rs
@@ -1,3 +1,4 @@
+pub(crate) mod applications;
pub(crate) mod edit;
pub(crate) mod execute;
pub(crate) mod import;
diff --git a/tools/unitctl/unitctl/src/main.rs b/tools/unitctl/unitctl/src/main.rs
index 12322873..6c9faaf7 100644
--- a/tools/unitctl/unitctl/src/main.rs
+++ b/tools/unitctl/unitctl/src/main.rs
@@ -8,7 +8,7 @@ extern crate unit_client_rs;
use clap::Parser;
-use crate::cmd::{edit, execute as execute_cmd, import, instances, listeners, status};
+use crate::cmd::{applications, edit, execute as execute_cmd, import, instances, listeners, status};
use crate::output_format::OutputFormat;
use crate::unitctl::{Commands, UnitCtl};
use crate::unitctl_error::UnitctlError;
@@ -30,6 +30,8 @@ async fn main() -> Result<(), UnitctlError> {
match cli.command {
Commands::Instances(args) => instances::cmd(args).await,
+ Commands::App(ref args) => applications::cmd(&cli, args).await,
+
Commands::Edit { output_format } => edit::cmd(&cli, output_format).await,
Commands::Import { ref directory } => import::cmd(&cli, directory).await,
@@ -67,6 +69,9 @@ fn eprint_error(error: &UnitctlError) {
eprintln!("{}", source);
eprintln!("Try running again with the same permissions as the unit control socket");
}
+ UnitClientError::OpenAPIError { source } => {
+ eprintln!("OpenAPI Error: {}", source);
+ }
_ => {
eprintln!("Unit client error: {}", source);
}
diff --git a/tools/unitctl/unitctl/src/unitctl.rs b/tools/unitctl/unitctl/src/unitctl.rs
index b1bdbbd1..47f33820 100644
--- a/tools/unitctl/unitctl/src/unitctl.rs
+++ b/tools/unitctl/unitctl/src/unitctl.rs
@@ -41,9 +41,9 @@ pub(crate) struct UnitCtl {
#[derive(Debug, Subcommand)]
pub(crate) enum Commands {
- #[command(about = "List all running UNIT processes")]
+ #[command(about = "List all running Unit processes")]
Instances(InstanceArgs),
- #[command(about = "Open current UNIT configuration in editor")]
+ #[command(about = "Open current Unit configuration in editor")]
Edit {
#[arg(
required = false,
@@ -60,7 +60,7 @@ pub(crate) enum Commands {
#[arg(required = true, help = "Directory to import from")]
directory: PathBuf,
},
- #[command(about = "Sends raw JSON payload to UNIT")]
+ #[command(about = "Sends raw JSON payload to Unit")]
Execute {
#[arg(
required = false,
@@ -90,7 +90,7 @@ pub(crate) enum Commands {
#[arg(required = true, short = 'p', long = "path")]
path: String,
},
- #[command(about = "Get the current status of UNIT")]
+ #[command(about = "Get the current status of Unit")]
Status {
#[arg(
required = false,
@@ -114,6 +114,8 @@ pub(crate) enum Commands {
)]
output_format: OutputFormat,
},
+ #[command(about = "List all configured Unit applications")]
+ App(ApplicationArgs),
}
#[derive(Debug, Args)]
@@ -135,7 +137,7 @@ pub struct InstanceArgs {
#[derive(Debug, Subcommand)]
#[command(args_conflicts_with_subcommands = true)]
pub enum InstanceCommands {
- #[command(about = "deploy a new docker instance of unitd")]
+ #[command(about = "deploy a new docker instance of Unit")]
New {
#[arg(required = true, help = "Path to mount control socket to host")]
socket: String,
@@ -151,6 +153,35 @@ pub enum InstanceCommands {
},
}
+#[derive(Debug, Args)]
+pub struct ApplicationArgs {
+ #[arg(
+ required = false,
+ global = true,
+ short = 't',
+ long = "output-format",
+ default_value = "text",
+ help = "Output format: text, yaml, json, json-pretty (default)"
+ )]
+ pub output_format: OutputFormat,
+
+ #[command(subcommand)]
+ pub command: ApplicationCommands,
+}
+
+#[derive(Debug, Subcommand)]
+#[command(args_conflicts_with_subcommands = true)]
+pub enum ApplicationCommands {
+ #[command(about = "reload a running application")]
+ Reload {
+ #[arg(required = true, help = "name of application")]
+ name: String,
+ },
+
+ #[command(about = "list running applications")]
+ List {},
+}
+
fn parse_control_socket_address(s: &str) -> Result<ControlSocket, ClapError> {
ControlSocket::try_from(s).map_err(|e| ClapError::raw(ValueValidation, e.to_string()))
}