summaryrefslogtreecommitdiffhomepage
path: root/tools/unitctl/unit-client-rs/src
diff options
context:
space:
mode:
authorAva Hahn <a.hahn@f5.com>2024-05-06 12:28:40 -0700
committeravahahn <110854134+avahahn@users.noreply.github.com>2024-05-08 13:30:08 -0700
commitcc9eb8e756e84cd3fd59baf5b80efab0ffd5757d (patch)
tree23b42d96a10774ef949367fc8393d3c106aafc87 /tools/unitctl/unit-client-rs/src
parent6ad1fa342813f2c9f00813108c3f2eec8824fd6f (diff)
downloadunit-cc9eb8e756e84cd3fd59baf5b80efab0ffd5757d.tar.gz
unit-cc9eb8e756e84cd3fd59baf5b80efab0ffd5757d.tar.bz2
tools/unitctl: enable passing IP addresses to the 'instances new' command
* use path seperator constant from rust std package * pass a ControlSocket into deploy_new_container instead of a string * parse and validate a ControlSocket from argument to instances new * conditionally mount control socket only if its a unix socket * use create_image in a way that actually pulls nonpresent images * possibly override container command if TCP socket passed in * handle more weird error cases * add a ton of validation cases in the CLI command handler * add a nice little progress bar :) Signed-off-by: Ava Hahn <a.hahn@f5.com>
Diffstat (limited to 'tools/unitctl/unit-client-rs/src')
-rw-r--r--tools/unitctl/unit-client-rs/src/unit_client.rs4
-rw-r--r--tools/unitctl/unit-client-rs/src/unitd_docker.rs177
-rw-r--r--tools/unitctl/unit-client-rs/src/unitd_instance.rs1
-rw-r--r--tools/unitctl/unit-client-rs/src/unitd_process.rs1
4 files changed, 115 insertions, 68 deletions
diff --git a/tools/unitctl/unit-client-rs/src/unit_client.rs b/tools/unitctl/unit-client-rs/src/unit_client.rs
index f76004cd..b8c73ec0 100644
--- a/tools/unitctl/unit-client-rs/src/unit_client.rs
+++ b/tools/unitctl/unit-client-rs/src/unit_client.rs
@@ -250,7 +250,7 @@ impl UnitClient {
Err(Box::new(UnitClientError::new(
hyper_error,
self.control_socket.to_string(),
- "".to_string(),
+ "/listeners".to_string(),
)))
} else {
Err(Box::new(UnitClientError::OpenAPIError { source: err }))
@@ -268,7 +268,7 @@ impl UnitClient {
Err(Box::new(UnitClientError::new(
hyper_error,
self.control_socket.to_string(),
- "".to_string(),
+ "/status".to_string(),
)))
} else {
Err(Box::new(UnitClientError::OpenAPIError { source: err }))
diff --git a/tools/unitctl/unit-client-rs/src/unitd_docker.rs b/tools/unitctl/unit-client-rs/src/unitd_docker.rs
index 4c86c870..b9199e40 100644
--- a/tools/unitctl/unit-client-rs/src/unitd_docker.rs
+++ b/tools/unitctl/unit-client-rs/src/unitd_docker.rs
@@ -1,19 +1,29 @@
use std::collections::HashMap;
use std::fs::read_to_string;
-use std::path::PathBuf;
+use std::path::{PathBuf, MAIN_SEPARATOR};
+use std::io::stderr;
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};
+use bollard::models::{
+ ContainerCreateResponse, HostConfig, Mount,
+ MountTypeEnum, ContainerSummary,
+};
use bollard::secret::ContainerInspectResponse;
-use bollard::{models::ContainerSummary, Docker};
+use bollard::Docker;
+
use regex::Regex;
+
use serde::ser::SerializeMap;
use serde::{Serialize, Serializer};
+use pbr::ProgressBar;
+
#[derive(Clone, Debug)]
pub struct UnitdContainer {
pub container_id: Option<String>,
@@ -121,6 +131,7 @@ impl Serialize for UnitdContainer {
where
S: Serializer,
{
+ // 5 = fields to serialize
let mut state = serializer.serialize_map(Some(5))?;
state.serialize_entry("container_id", &self.container_id)?;
state.serialize_entry("container_image", &self.container_image)?;
@@ -143,18 +154,23 @@ impl UnitdContainer {
// cant do this functionally because of the async call
let mut mapped = vec![];
for ctr in summary {
- if ctr.clone().image.or(Some(String::new())).unwrap().contains("unit") {
- 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 ctr.clone().image
+ .or(Some(String::new()))
+ .unwrap()
+ .contains("unit") {
+ 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
}
@@ -190,7 +206,7 @@ impl UnitdContainer {
* that doesnt actually exist
*/
if cfg!(target_os = "macos") {
- let mut abs = PathBuf::from("/");
+ 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()));
@@ -238,19 +254,27 @@ impl UnitdContainer {
* ON FAILURE returns wrapped error from Docker API
*/
pub async fn deploy_new_container(
- socket: &String,
+ socket: ControlSocket,
application: &String,
image: &String,
) -> Result<Vec<String>, UnitClientError> {
match Docker::connect_with_local_defaults() {
Ok(docker) => {
let mut mounts = vec![];
- mounts.push(Mount {
- typ: Some(MountTypeEnum::BIND),
- source: Some(socket.clone()),
- target: Some("/var/run".to_string()),
- ..Default::default()
- });
+ // 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();
+ mounts.push(Mount {
+ typ: Some(MountTypeEnum::BIND),
+ source: Some(mount_path),
+ target: Some("/var/run".to_string()),
+ ..Default::default()
+ });
+ }
+ // mount application dir
mounts.push(Mount {
typ: Some(MountTypeEnum::BIND),
source: Some(application.clone()),
@@ -259,40 +283,57 @@ pub async fn deploy_new_container(
..Default::default()
});
- 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(),
- })
- });
+ let mut pb = ProgressBar::on(stderr(), 10);
+ let mut totals = HashMap::new();
+ let mut stream = docker.create_image(
+ Some(CreateImageOptions {
+ from_image: image.as_str(),
+ ..Default::default()
+ }), 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);
+ }
+ } else {
+ if let Some(total) = info.progress_detail
+ .and_then(|detail| detail.total) {
+ totals.insert(id, total);
+ pb.total += total as u64;
+ }
+ }
+ }
+ }
+ }
+ pb.finish();
+ // create the new unit container
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),
- ..Default::default()
- }),
- ..Default::default()
- },
- )
- .await
- {
+ let host_conf = HostConfig {
+ mounts: Some(mounts),
+ network_mode: Some("host".to_string()),
+ ..Default::default()
+ };
+ let mut container_conf = Config {
+ image: Some(image.clone()),
+ ..Default::default()
+ };
+ if let ControlSocket::TcpSocket(ref uri) = socket {
+ let port = uri.port_u16().or(Some(80)).unwrap();
+ // override port
+ container_conf.cmd = Some(vec![
+ "unitd".to_string(),
+ "--no-daemon".to_string(),
+ "--control".to_string(),
+ format!("{}:{}", uri.host().unwrap(), port),
+ ]);
+ }
+ container_conf.host_config = Some(host_conf);
+ match docker.create_container::<String, String>(None, container_conf).await {
Err(err) => {
return Err(UnitClientError::UnitdDockerError {
message: err.to_string(),
@@ -301,6 +342,8 @@ pub async fn deploy_new_container(
Ok(response) => resp = response,
}
+ // create container gives us an ID
+ // but start container requires a name
let mut list_container_filters = HashMap::new();
list_container_filters.insert("id".to_string(), vec![resp.id]);
match docker
@@ -312,26 +355,28 @@ pub async fn deploy_new_container(
}))
.await
{
- Err(e) => Err(UnitClientError::UnitdDockerError { message: e.to_string() }),
+ // somehow our container doesnt exist
+ 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(),
});
- }
- 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(),
+ });
+ }
- match docker
- .start_container(
- info[0].names.clone().unwrap()[0].strip_prefix("/").unwrap(),
- None::<StartContainerOptions<String>>,
- )
- .await
- {
+ // start our container
+ 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(),
}),
diff --git a/tools/unitctl/unit-client-rs/src/unitd_instance.rs b/tools/unitctl/unit-client-rs/src/unitd_instance.rs
index a7fb1bdc..ace8e858 100644
--- a/tools/unitctl/unit-client-rs/src/unitd_instance.rs
+++ b/tools/unitctl/unit-client-rs/src/unitd_instance.rs
@@ -26,6 +26,7 @@ impl Serialize for UnitdInstance {
where
S: Serializer,
{
+ // 11 = fields to serialize
let mut state = serializer.serialize_map(Some(11))?;
let runtime_flags = self
.process
diff --git a/tools/unitctl/unit-client-rs/src/unitd_process.rs b/tools/unitctl/unit-client-rs/src/unitd_process.rs
index 47ffcb5d..3dc0c3af 100644
--- a/tools/unitctl/unit-client-rs/src/unitd_process.rs
+++ b/tools/unitctl/unit-client-rs/src/unitd_process.rs
@@ -27,6 +27,7 @@ impl Serialize for UnitdProcess {
where
S: Serializer,
{
+ // 6 = fields to serialize
let mut state = serializer.serialize_map(Some(6))?;
state.serialize_entry("pid", &self.process_id)?;
state.serialize_entry("user", &self.user)?;