From 6f85d780c6a49a4d1917e690e577a8107eb3ef4c Mon Sep 17 00:00:00 2001 From: leone Date: Thu, 2 Nov 2023 00:02:58 +0100 Subject: [PATCH 1/3] release/nightly: nproxy 1.25.0.2 (#613) --- bin/nproxy/changelog.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bin/nproxy/changelog.md b/bin/nproxy/changelog.md index 9c9e4f6d3..f64f13f65 100644 --- a/bin/nproxy/changelog.md +++ b/bin/nproxy/changelog.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.25.0.2] - 2023-11-01 + +### Changed + +- Upgrade dependencies + ## [1.25.0.1] - 2023-10-05 ### Changed From a64eb56f8e65200ddd02a04d95855243306403d5 Mon Sep 17 00:00:00 2001 From: leone Date: Thu, 2 Nov 2023 01:26:23 +0100 Subject: [PATCH 2/3] fix/nanocl: install and upgrade (#621) --- bin/nanocl/src/commands/install.rs | 5 +--- bin/nanocl/src/commands/uninstall.rs | 20 ++++++++++++-- bin/nanocl/src/commands/upgrade.rs | 41 ++++++++++++++-------------- bin/nanocl/src/models/mod.rs | 2 +- bin/nanocl/src/models/upgrade.rs | 3 ++ crates/nanocld_client/src/cargo.rs | 19 ++++++++----- installer.yml | 38 +++++++++++++------------- 7 files changed, 74 insertions(+), 54 deletions(-) diff --git a/bin/nanocl/src/commands/install.rs b/bin/nanocl/src/commands/install.rs index 665cf8a71..cdc2b1007 100644 --- a/bin/nanocl/src/commands/install.rs +++ b/bin/nanocl/src/commands/install.rs @@ -26,7 +26,7 @@ use crate::models::{ /// ## Return /// /// * [Result](Result) The result of the operation -/// * [Ok](()) The operation was successful +/// * [Ok](Ok<()>) The operation was successful /// * [Err](nanocl_error::io::IoError) An error occured /// pub async fn exec_install(args: &InstallOpts) -> IoResult<()> { @@ -106,9 +106,6 @@ pub async fn exec_install(args: &InstallOpts) -> IoResult<()> { let installer = utils::installer::get_template(args.template.clone()).await?; let data: liquid::Object = nanocld_args.clone().into(); let installer = utils::state::compile(&installer, &data)?; - - println!("{installer}"); - let deployment = serde_yaml::from_str::(&installer) .map_err(|err| { err.map_err_context(|| "Unable to extract deployment from installer") diff --git a/bin/nanocl/src/commands/uninstall.rs b/bin/nanocl/src/commands/uninstall.rs index 98f22356a..3ed3d4690 100644 --- a/bin/nanocl/src/commands/uninstall.rs +++ b/bin/nanocl/src/commands/uninstall.rs @@ -3,7 +3,7 @@ use nanocld_client::stubs::state::StateDeployment; use bollard_next::container::{InspectContainerOptions, RemoveContainerOptions}; -use crate::utils; +use crate::{utils, version}; use crate::models::UninstallOpts; /// ## Exec uninstall @@ -19,17 +19,31 @@ use crate::models::UninstallOpts; /// ## Return /// /// * [Result](Result) The result of the operation -/// * [Ok](()) The operation was successful +/// * [Ok](Ok<()>) The operation was successful /// * [Err](nanocl_error::io::IoError) An error occured /// pub async fn exec_uninstall(args: &UninstallOpts) -> IoResult<()> { let detected_host = utils::docker::detect_docker_host()?; - let (docker_host, _) = match &args.docker_host { + let (docker_host, is_docker_desktop) = match &args.docker_host { Some(docker_host) => (docker_host.to_owned(), args.is_docker_desktop), None => detected_host, }; let docker = utils::docker::connect(&docker_host)?; let installer = utils::installer::get_template(args.template.clone()).await?; + let data = liquid::object!({ + "docker_host": docker_host, + "state_dir": "/tmp/random", + "conf_dir": "/tmp/random", + "gateway": "127.0.0.1", + "hosts": "tcp://127.0.0.1:8585", + "hostname": "localhost", + "advertise_addr": "127.0.0.1:8585", + "is_docker_desktop": is_docker_desktop, + "gid": "0", + "home_dir": "/tmp/random", + "channel": version::CHANNEL.to_owned(), + }); + let installer = utils::state::compile(&installer, &data)?; let installer = serde_yaml::from_str::(&installer) .map_err(|err| err.map_err_context(|| "Unable to parse installer"))?; let cargoes = installer.cargoes.unwrap_or_default(); diff --git a/bin/nanocl/src/commands/upgrade.rs b/bin/nanocl/src/commands/upgrade.rs index c57a21aaf..2df304abf 100644 --- a/bin/nanocl/src/commands/upgrade.rs +++ b/bin/nanocl/src/commands/upgrade.rs @@ -1,12 +1,7 @@ -use std::collections::HashMap; - -use futures::StreamExt; -use indicatif::{ProgressBar, MultiProgress}; - use nanocl_error::io::{IoError, FromIo, IoResult}; use nanocld_client::stubs::cargo_config::CargoConfigPartial; -use crate::utils; +use crate::{utils, version}; use crate::config::CliConfig; use crate::models::UpgradeOpts; use super::cargo_image::exec_cargo_image_pull; @@ -23,23 +18,35 @@ use super::cargo_image::exec_cargo_image_pull; /// ## Return /// /// * [Result](Result) The result of the operation -/// * [Ok](()) The operation was successful +/// * [Ok](Ok<()>) The operation was successful /// * [Err](IoError) An error occured /// pub async fn exec_upgrade( cli_conf: &CliConfig, args: &UpgradeOpts, ) -> IoResult<()> { + let detected_host = utils::docker::detect_docker_host()?; + let (docker_host, is_docker_desktop) = match &args.docker_host { + Some(docker_host) => (docker_host.to_owned(), args.is_docker_desktop), + None => detected_host, + }; + let home_dir = std::env::var("HOME").map_err(|err| { + IoError::interupted("Unable to get $HOME env variable", &err.to_string()) + })?; let client = &cli_conf.client; let config = client.info().await?.config; let data = liquid::object!({ "advertise_addr": config.advertise_addr, "state_dir": config.state_dir, - "docker_host": config.docker_host, + "docker_host": docker_host, + "is_docker_desktop": is_docker_desktop, "gateway": config.gateway, "conf_dir": config.conf_dir, "hostname": config.hostname, + "hosts": config.hosts.join(" "), "gid": config.gid, + "home_dir": home_dir, + "channel": version::CHANNEL, }); let installer = utils::installer::get_template(args.template.clone()).await?; let installer = utils::state::compile(&installer, &data)?; @@ -60,18 +67,12 @@ pub async fn exec_upgrade( "is not specified".into(), ))?; exec_cargo_image_pull(client, &image).await?; - } - let data = - serde_json::from_value::(data).map_err(|err| { - err.map_err_context(|| "Unable to convert upgrade to json") - })?; - let mut stream = client.apply_state(&data).await?; - let multiprogress = MultiProgress::new(); - multiprogress.set_move_cursor(false); - let mut layers: HashMap = HashMap::new(); - while let Some(res) = stream.next().await { - let res = res?; - utils::state::update_progress(&multiprogress, &mut layers, &res.key, &res); + print!("Upgrading {}", cargo.name); + let _ = client + .put_cargo(&cargo.name.clone(), &cargo, Some("system".to_owned())) + .await; + ntex::time::sleep(std::time::Duration::from_secs(2)).await; + println!(" {} has been upgraded successfully!", cargo.name); } Ok(()) } diff --git a/bin/nanocl/src/models/mod.rs b/bin/nanocl/src/models/mod.rs index a6dca446a..079e1b537 100644 --- a/bin/nanocl/src/models/mod.rs +++ b/bin/nanocl/src/models/mod.rs @@ -75,7 +75,7 @@ pub enum Command { Install(InstallOpts), /// Uninstall nanocl components Uninstall(UninstallOpts), - /// Upgrade nanocl components + // TODO: Upgrade nanocl components Upgrade(UpgradeOpts), /// Show all processes managed by nanocl Ps(ProcessOpts), diff --git a/bin/nanocl/src/models/upgrade.rs b/bin/nanocl/src/models/upgrade.rs index bf1b747f9..4f33286e7 100644 --- a/bin/nanocl/src/models/upgrade.rs +++ b/bin/nanocl/src/models/upgrade.rs @@ -12,4 +12,7 @@ pub struct UpgradeOpts { /// Upgrade template to use for nanocl by default it's detected #[clap(short, long)] pub(crate) template: Option, + /// Specify if the docker host is docker desktop detected if docker context is desktop-linux + #[clap(long = "docker-desktop")] + pub(crate) is_docker_desktop: bool, } diff --git a/crates/nanocld_client/src/cargo.rs b/crates/nanocld_client/src/cargo.rs index d05db6f20..2433a610b 100644 --- a/crates/nanocld_client/src/cargo.rs +++ b/crates/nanocld_client/src/cargo.rs @@ -271,7 +271,7 @@ impl NanocldClient { /// /// ## Returns /// * [Result](Result) - /// * [Ok](Ok) - The cargo was patched + /// * [Ok](Ok<()>) - The cargo was patched /// * [Err](HttpClientError) - The cargo could not be patched /// /// ## Example @@ -306,13 +306,15 @@ impl NanocldClient { /// It will create a new cargo config and store old one in history /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to put - /// * [cargo](CargoConfigPatch) - The config to put the cargo with + /// * [cargo](CargoConfigPartial) - The config to put the cargo with /// * [namespace](Option) - The namespace to put the cargo from /// /// ## Returns + /// /// * [Result](Result) - /// * [Ok](Ok) - The cargo was put + /// * [Ok](Ok<()>) - The cargo was put /// * [Err](HttpClientError) - The cargo could not be put /// /// ## Example @@ -320,16 +322,16 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let cargo_config = CargoConfigPatch { + /// let cargo_config = CargoConfigPartial { /// name: "my-cargo-renamed".into(), /// }; - /// client.put_cargo("my-cargo", cargo, None).await.unwrap(); + /// client.put_cargo("my-cargo", &cargo, None).await.unwrap(); /// ``` /// pub async fn put_cargo( &self, name: &str, - config: CargoConfigPartial, + config: &CargoConfigPartial, namespace: Option, ) -> Result<(), HttpClientError> { self @@ -538,7 +540,10 @@ mod tests { .patch_cargo(CARGO_NAME, cargo_update, None) .await .unwrap(); - client.put_cargo(CARGO_NAME, new_cargo, None).await.unwrap(); + client + .put_cargo(CARGO_NAME, &new_cargo, None) + .await + .unwrap(); let histories = client.list_history_cargo(CARGO_NAME, None).await.unwrap(); assert!(histories.len() > 1); let history = histories.first().unwrap(); diff --git a/installer.yml b/installer.yml index c012dd045..a704ed29b 100644 --- a/installer.yml +++ b/installer.yml @@ -9,6 +9,25 @@ ApiVersion: v0.11 Namespace: system Cargoes: + - Name: nstore + Container: + Image: cockroachdb/cockroach:v23.1.11 + Tty: true + Hostname: nstore.nanocl.internal + Env: + - TZ=Europe/Paris + Cmd: + - start-single-node + - --insecure + - --listen-addr=:26257 + - --advertise-addr=${{ advertise_addr }}:26257 + HostConfig: + NetworkMode: system + Binds: + - ${{ state_dir }}/store/ca:/ca + - ${{ state_dir }}/store/certs:/certs + - ${{ state_dir }}/store/data:/cockroach/cockroach-data + - Name: nmetrics Container: Image: ghcr.io/nxthat/metrsd:0.3.1 @@ -123,25 +142,6 @@ Cargoes: # {% endif %} - ${{ state_dir }}/dns:/opt/dns - - Name: nstore - Container: - Image: cockroachdb/cockroach:v23.1.11 - Tty: true - Hostname: nstore.nanocl.internal - Env: - - TZ=Europe/Paris - Cmd: - - start-single-node - - --insecure - - --listen-addr=:26257 - - --advertise-addr=${{ advertise_addr }}:26257 - HostConfig: - NetworkMode: system - Binds: - - ${{ state_dir }}/store/ca:/ca - - ${{ state_dir }}/store/certs:/certs - - ${{ state_dir }}/store/data:/cockroach/cockroach-data - - Name: ndaemon Container: # {% if channel == "nightly" %} From fa4632aeb95e7a4b8c6e037b6b25d663aaec24d9 Mon Sep 17 00:00:00 2001 From: leone Date: Thu, 2 Nov 2023 05:28:27 +0100 Subject: [PATCH 3/3] refactor/nanocld_client: using references (#622) * refactor/nanocld_client: using references --- Cargo.lock | 8 +- bin/nanocl/src/commands/cargo.rs | 38 +-- bin/nanocl/src/commands/cargo_image.rs | 2 +- bin/nanocl/src/commands/state.rs | 13 +- bin/nanocl/src/commands/system.rs | 4 +- bin/nanocl/src/commands/upgrade.rs | 2 +- bin/nanocl/src/commands/vm.rs | 20 +- bin/nanocl/src/main.rs | 3 +- bin/nanocld/src/models/node.rs | 6 +- bin/nanocld/src/services/system.rs | 2 +- bin/nanocld/src/utils/cargo.rs | 4 +- bin/ncdns/src/event.rs | 25 +- bin/ncdns/src/main.rs | 13 +- bin/ncdns/src/server.rs | 19 +- bin/ncdns/src/services/rule.rs | 16 +- bin/ncdns/src/utils.rs | 8 +- bin/ncproxy/src/main.rs | 4 +- bin/ncproxy/src/server.rs | 11 +- bin/ncproxy/src/services/rule.rs | 16 +- bin/ncproxy/src/subsystem/event.rs | 59 ++-- bin/ncproxy/src/subsystem/init.rs | 14 +- bin/ncproxy/src/utils.rs | 18 +- crates/nanocl_stubs/src/cargo.rs | 4 +- crates/nanocl_stubs/src/generic.rs | 9 + crates/nanocld_client/src/cargo.rs | 332 ++++++++++++++--------- crates/nanocld_client/src/cargo_image.rs | 64 +++-- crates/nanocld_client/src/exec.rs | 48 ++-- crates/nanocld_client/src/http_client.rs | 143 ++++------ crates/nanocld_client/src/http_metric.rs | 37 ++- crates/nanocld_client/src/namespace.rs | 61 ++--- crates/nanocld_client/src/node.rs | 29 +- crates/nanocld_client/src/resource.rs | 120 +++++--- crates/nanocld_client/src/secret.rs | 72 +++-- crates/nanocld_client/src/state.rs | 35 ++- crates/nanocld_client/src/system.rs | 78 +++--- crates/nanocld_client/src/vm.rs | 217 +++++++++++++-- crates/nanocld_client/src/vm_image.rs | 130 ++++++++- 37 files changed, 1067 insertions(+), 617 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f8c2349a6..a1829c000 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2439,9 +2439,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "openssl" -version = "0.10.57" +version = "0.10.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +checksum = "a9dfc0783362704e97ef3bd24261995a699468440099ef95d869b4d9732f829a" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -2474,9 +2474,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.93" +version = "0.9.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +checksum = "2f55da20b29f956fb01f0add8683eb26ee13ebe3ebd935e49898717c6b4b2830" dependencies = [ "cc", "libc", diff --git a/bin/nanocl/src/commands/cargo.rs b/bin/nanocl/src/commands/cargo.rs index 5ef4ec0a0..245becf4d 100644 --- a/bin/nanocl/src/commands/cargo.rs +++ b/bin/nanocl/src/commands/cargo.rs @@ -46,7 +46,9 @@ async fn exec_cargo_create( ) -> IoResult<()> { let client = &cli_conf.client; let cargo = opts.clone().into(); - let item = client.create_cargo(&cargo, args.namespace.clone()).await?; + let item = client + .create_cargo(&cargo, args.namespace.as_deref()) + .await?; println!("{}", &item.key); Ok(()) } @@ -82,7 +84,7 @@ async fn exec_cargo_rm( force: Some(opts.force), }; for name in &opts.names { - client.delete_cargo(name, &query).await?; + client.delete_cargo(name, Some(&query)).await?; } Ok(()) } @@ -109,7 +111,7 @@ async fn exec_cargo_ls( opts: &CargoListOpts, ) -> IoResult<()> { let client = &cli_conf.client; - let items = client.list_cargo(args.namespace.clone()).await?; + let items = client.list_cargo(args.namespace.as_deref()).await?; let rows = items .into_iter() .map(CargoRow::from) @@ -150,7 +152,7 @@ async fn exec_cargo_start( ) -> IoResult<()> { let client = &cli_conf.client; client - .start_cargo(&opts.name, args.namespace.clone()) + .start_cargo(&opts.name, args.namespace.as_deref()) .await?; Ok(()) } @@ -178,7 +180,7 @@ async fn exec_cargo_stop( ) -> IoResult<()> { let client = &cli_conf.client; for name in &opts.names { - client.stop_cargo(name, args.namespace.clone()).await?; + client.stop_cargo(name, args.namespace.as_deref()).await?; } Ok(()) } @@ -206,7 +208,9 @@ async fn exec_cargo_restart( ) -> IoResult<()> { let client = &cli_conf.client; for name in &opts.names { - client.restart_cargo(name, args.namespace.clone()).await?; + client + .restart_cargo(name, args.namespace.as_deref()) + .await?; } Ok(()) } @@ -233,9 +237,8 @@ async fn exec_cargo_patch( opts: &CargoPatchOpts, ) -> IoResult<()> { let client = &cli_conf.client; - let cargo = opts.clone().into(); client - .patch_cargo(&opts.name, cargo, args.namespace.clone()) + .patch_cargo(&opts.name, &opts.clone().into(), args.namespace.as_deref()) .await?; Ok(()) } @@ -263,7 +266,7 @@ async fn exec_cargo_inspect( ) -> IoResult<()> { let client = &cli_conf.client; let cargo = client - .inspect_cargo(&opts.name, args.namespace.clone()) + .inspect_cargo(&opts.name, args.namespace.as_deref()) .await?; let display = opts .display @@ -297,12 +300,12 @@ async fn exec_cargo_exec( let client = &cli_conf.client; let exec: CreateExecOptions = opts.clone().into(); let result = client - .create_exec(&opts.name, exec, args.namespace.clone()) + .create_exec(&opts.name, &exec, args.namespace.as_deref()) .await?; let mut stream = client .start_exec( &result.id, - StartExecOptions { + &StartExecOptions { tty: opts.tty, ..Default::default() }, @@ -356,7 +359,7 @@ async fn exec_cargo_history( ) -> IoResult<()> { let client = &cli_conf.client; let histories = client - .list_history_cargo(&opts.name, args.namespace.clone()) + .list_history_cargo(&opts.name, args.namespace.as_deref()) .await?; utils::print::print_yml(histories)?; Ok(()) @@ -394,7 +397,7 @@ async fn exec_cargo_logs( stderr: None, stdout: None, }; - let mut stream = client.logs_cargo(&opts.name, &query).await?; + let mut stream = client.logs_cargo(&opts.name, Some(&query)).await?; while let Some(log) = stream.next().await { let log = match log { Ok(log) => log, @@ -455,7 +458,8 @@ async fn exec_cargo_stats( let mut tx = tx.clone(); let client = client.clone(); async move { - let Ok(mut stream) = client.stats_cargo(&name, &query).await else { + let Ok(mut stream) = client.stats_cargo(&name, Some(&query)).await + else { return; }; while let Some(stats) = stream.next().await { @@ -514,7 +518,7 @@ async fn exec_cargo_revert( ) -> IoResult<()> { let client = &cli_conf.client; let cargo = client - .revert_cargo(&opts.name, &opts.history_id, args.namespace.clone()) + .revert_cargo(&opts.name, &opts.history_id, args.namespace.as_deref()) .await?; utils::print::print_yml(cargo)?; Ok(()) @@ -547,10 +551,10 @@ async fn exec_cargo_run( exec_cargo_image_pull(client, &opts.image).await?; } let cargo = client - .create_cargo(&opts.clone().into(), args.namespace.clone()) + .create_cargo(&opts.clone().into(), args.namespace.as_deref()) .await?; client - .start_cargo(&cargo.name, Some(cargo.namespace_name)) + .start_cargo(&cargo.name, Some(&cargo.namespace_name)) .await?; Ok(()) } diff --git a/bin/nanocl/src/commands/cargo_image.rs b/bin/nanocl/src/commands/cargo_image.rs index 15c5e97d3..6536e9033 100644 --- a/bin/nanocl/src/commands/cargo_image.rs +++ b/bin/nanocl/src/commands/cargo_image.rs @@ -34,7 +34,7 @@ async fn exec_cargo_image_ls( client: &NanocldClient, opts: &CargoImageListOpts, ) -> IoResult<()> { - let items = client.list_cargo_image(Some(opts.clone().into())).await?; + let items = client.list_cargo_image(Some(&opts.clone().into())).await?; let rows = items .into_iter() .map(CargoImageRow::from) diff --git a/bin/nanocl/src/commands/state.rs b/bin/nanocl/src/commands/state.rs index 85e92f529..47b9cc6ca 100644 --- a/bin/nanocl/src/commands/state.rs +++ b/bin/nanocl/src/commands/state.rs @@ -223,7 +223,7 @@ pub async fn log_cargo( let cargo = match client .inspect_cargo( &cargo.name, - Some(opts.namespace.to_owned().unwrap_or("global".to_string())), + Some(opts.namespace.as_deref().unwrap_or("global")), ) .await { @@ -260,7 +260,7 @@ pub async fn log_cargo( timestamps, ..Default::default() }; - match client.logs_cargo(&name, &query).await { + match client.logs_cargo(&name, Some(&query)).await { Err(err) => { eprintln!("Cannot attach to cargo {name}: {err}"); } @@ -378,12 +378,10 @@ fn gen_client(host: &str, meta: &StateMeta) -> IoResult { .ok_or(IoError::not_found("Version", "is not specified"))?; paths.remove(paths.len() - 1); let url = paths.join("/"); - let url = Box::leak(url.into_boxed_str()); - NanocldClient::connect_to(url, Some(version.into())) + NanocldClient::connect_to(&url, Some(version.into())) } api_version if meta.api_version.starts_with('v') => { - let url = Box::leak(host.to_owned().into_boxed_str()); - NanocldClient::connect_to(url, Some(api_version)) + NanocldClient::connect_to(host, Some(api_version)) } _ => { let mut paths = meta @@ -399,8 +397,7 @@ fn gen_client(host: &str, meta: &StateMeta) -> IoResult { paths.remove(paths.len() - 1); let url = paths.join("/"); let url = format!("https://{url}"); - let url = Box::leak(url.into_boxed_str()); - NanocldClient::connect_to(url, Some(version.into())) + NanocldClient::connect_to(&url, Some(version.into())) } }; Ok(client) diff --git a/bin/nanocl/src/commands/system.rs b/bin/nanocl/src/commands/system.rs index da32c5e8f..ffc85e154 100644 --- a/bin/nanocl/src/commands/system.rs +++ b/bin/nanocl/src/commands/system.rs @@ -31,7 +31,7 @@ pub async fn exec_process( ) -> IoResult<()> { let client = &cli_conf.client; let opts = args.clone().into(); - let items = client.process(Some(opts)).await?; + let items = client.process(Some(&opts)).await?; let rows = items .into_iter() .map(ProcessRow::from) @@ -62,7 +62,7 @@ pub async fn exec_http( ) -> IoResult<()> { match &opts.command { SystemHttpCommand::Logs(opts) => { - let logs = client.list_http_metric(Some(opts.clone().into())).await?; + let logs = client.list_http_metric(Some(&opts.clone().into())).await?; utils::print::print_yml(logs)?; } } diff --git a/bin/nanocl/src/commands/upgrade.rs b/bin/nanocl/src/commands/upgrade.rs index 2df304abf..d5e01c455 100644 --- a/bin/nanocl/src/commands/upgrade.rs +++ b/bin/nanocl/src/commands/upgrade.rs @@ -69,7 +69,7 @@ pub async fn exec_upgrade( exec_cargo_image_pull(client, &image).await?; print!("Upgrading {}", cargo.name); let _ = client - .put_cargo(&cargo.name.clone(), &cargo, Some("system".to_owned())) + .put_cargo(&cargo.name.clone(), &cargo, Some("system")) .await; ntex::time::sleep(std::time::Duration::from_secs(2)).await; println!(" {} has been upgraded successfully!", cargo.name); diff --git a/bin/nanocl/src/commands/vm.rs b/bin/nanocl/src/commands/vm.rs index 9894567c8..0c6e552d2 100644 --- a/bin/nanocl/src/commands/vm.rs +++ b/bin/nanocl/src/commands/vm.rs @@ -47,7 +47,7 @@ pub async fn exec_vm_create( ) -> IoResult<()> { let client = &cli_conf.client; let vm = options.clone().into(); - let vm = client.create_vm(&vm, args.namespace.clone()).await?; + let vm = client.create_vm(&vm, args.namespace.as_deref()).await?; println!("{}", &vm.key); Ok(()) } @@ -75,7 +75,7 @@ pub async fn exec_vm_ls( opts: &VmListOpts, ) -> IoResult<()> { let client = &cli_conf.client; - let items = client.list_vm(args.namespace.clone()).await?; + let items = client.list_vm(args.namespace.as_deref()).await?; let rows = items.into_iter().map(VmRow::from).collect::>(); match opts.quiet { true => { @@ -114,7 +114,7 @@ pub async fn exec_vm_rm( ) -> IoResult<()> { let client = &cli_conf.client; for name in names { - client.delete_vm(name, args.namespace.clone()).await?; + client.delete_vm(name, args.namespace.as_deref()).await?; } Ok(()) } @@ -145,7 +145,7 @@ pub async fn exec_vm_inspect( ) -> IoResult<()> { let client = &cli_conf.client; let vm = client - .inspect_vm(&opts.name, args.namespace.clone()) + .inspect_vm(&opts.name, args.namespace.as_deref()) .await?; let display = opts .display @@ -179,7 +179,7 @@ pub async fn exec_vm_start( ) -> IoResult<()> { let client = &cli_conf.client; for name in names { - if let Err(err) = client.start_vm(name, args.namespace.clone()).await { + if let Err(err) = client.start_vm(name, args.namespace.as_deref()).await { eprintln!("Failed to start vm {}: {}", name, err); } } @@ -210,7 +210,7 @@ pub async fn exec_vm_stop( ) -> IoResult<()> { let client = &cli_conf.client; for name in names { - if let Err(err) = client.stop_vm(name, args.namespace.clone()).await { + if let Err(err) = client.stop_vm(name, args.namespace.as_deref()).await { eprintln!("Failed to stop vm {}: {}", name, err); } } @@ -242,8 +242,8 @@ pub async fn exec_vm_run( ) -> IoResult<()> { let client = &cli_conf.client; let vm = options.clone().into(); - let vm = client.create_vm(&vm, args.namespace.clone()).await?; - client.start_vm(&vm.name, args.namespace.clone()).await?; + let vm = client.create_vm(&vm, args.namespace.as_deref()).await?; + client.start_vm(&vm.name, args.namespace.as_deref()).await?; if options.attach { exec_vm_attach(cli_conf, args, &options.name).await?; } @@ -275,7 +275,7 @@ pub async fn exec_vm_patch( let client = &cli_conf.client; let vm = options.clone().into(); client - .patch_vm(&options.name, &vm, args.namespace.clone()) + .patch_vm(&options.name, &vm, args.namespace.as_deref()) .await?; Ok(()) } @@ -305,7 +305,7 @@ pub async fn exec_vm_attach( let client = &cli_conf.client; /// How often heartbeat pings are sent const HEARTBEAT_INTERVAL: Duration = Duration::from_secs(5); - let conn = client.attach_vm(name, args.namespace.clone()).await?; + let conn = client.attach_vm(name, args.namespace.as_deref()).await?; let (mut tx, mut rx) = mpsc::unbounded(); // start heartbeat task let sink = conn.sink(); diff --git a/bin/nanocl/src/main.rs b/bin/nanocl/src/main.rs index 727bccc96..571a7d8e5 100644 --- a/bin/nanocl/src/main.rs +++ b/bin/nanocl/src/main.rs @@ -55,8 +55,7 @@ fn create_cli_config(cli_args: &Cli) -> IoResult { .unwrap_or("http://ndaemon.nanocl.internal:8585".into()); } } - let url = Box::leak(host.clone().into_boxed_str()); - let client = NanocldClient::connect_to(url, None); + let client = NanocldClient::connect_to(&host, None); Ok(CliConfig { host, client, diff --git a/bin/nanocld/src/models/node.rs b/bin/nanocld/src/models/node.rs index 33a2b9c63..d73eacf74 100644 --- a/bin/nanocld/src/models/node.rs +++ b/bin/nanocld/src/models/node.rs @@ -31,9 +31,7 @@ impl NodeDbModel { /// * [client](NanocldClient) - The client for the node /// pub fn to_http_client(&self) -> NanocldClient { - let url = - Box::leak(format!("http://{}:8081", self.ip_address).into_boxed_str()); - - NanocldClient::connect_to(url, None) + let url = format!("http://{}:8081", self.ip_address); + NanocldClient::connect_to(&url, None) } } diff --git a/bin/nanocld/src/services/system.rs b/bin/nanocld/src/services/system.rs index ef77c5b9d..5912e26e8 100644 --- a/bin/nanocld/src/services/system.rs +++ b/bin/nanocld/src/services/system.rs @@ -137,7 +137,7 @@ pub(crate) async fn get_processes( for node in nodes { let client = node.to_http_client(); let node_containers = match client - .process(Some(ProccessQuery { + .process(Some(&ProccessQuery { all: false, namespace: qs.namespace.clone(), ..Default::default() diff --git a/bin/nanocld/src/utils/cargo.rs b/bin/nanocld/src/utils/cargo.rs index 74313e43f..ad11f004d 100644 --- a/bin/nanocld/src/utils/cargo.rs +++ b/bin/nanocld/src/utils/cargo.rs @@ -784,7 +784,7 @@ pub async fn list( for node in &nodes { let client = node.to_http_client(); let node_containers = match client - .list_cargo_instance(&cargo.name, Some(cargo.namespace_name.clone())) + .list_cargo_instance(&cargo.name, Some(&cargo.namespace_name)) .await { Ok(containers) => containers, @@ -857,7 +857,7 @@ pub async fn inspect_by_key( for node in &nodes { let client = node.to_http_client(); let node_containers = match client - .list_cargo_instance(&cargo.name, Some(cargo.namespace_name.clone())) + .list_cargo_instance(&cargo.name, Some(&cargo.namespace_name)) .await { Ok(containers) => containers, diff --git a/bin/ncdns/src/event.rs b/bin/ncdns/src/event.rs index 32e0f44db..86972f33a 100644 --- a/bin/ncdns/src/event.rs +++ b/bin/ncdns/src/event.rs @@ -1,16 +1,15 @@ use ntex::rt; use ntex::http; use futures::StreamExt; -use nanocl_utils::versioning; + use nanocl_error::http_client::HttpClientError; +use nanocl_utils::versioning; use nanocld_client::NanocldClient; use nanocld_client::stubs::system::Event; use nanocld_client::stubs::dns::ResourceDnsRule; use nanocld_client::stubs::resource::ResourcePartial; -use crate::dnsmasq::Dnsmasq; -use crate::utils::update_entries; -use crate::version; +use crate::{utils, version, dnsmasq}; async fn ensure_resource_config(client: &NanocldClient) { let formated_version = versioning::format_version(version::VERSION); @@ -59,7 +58,7 @@ async fn ensure_resource_config(client: &NanocldClient) { } } -async fn r#loop(dnsmasq: &Dnsmasq, client: &NanocldClient) { +async fn r#loop(dnsmasq: &dnsmasq::Dnsmasq, client: &NanocldClient) { loop { log::info!("Subscribing to nanocl daemon events.."); match client.watch_events().await { @@ -81,7 +80,8 @@ async fn r#loop(dnsmasq: &Dnsmasq, client: &NanocldClient) { log::warn!("Unable to serialize the DnsRule"); continue; }; - if let Err(err) = update_entries(&dns_rule, dnsmasq, client).await + if let Err(err) = + utils::update_entries(&dns_rule, dnsmasq, client).await { log::error!("Unable to update the DnsRule: {err}"); } @@ -93,7 +93,8 @@ async fn r#loop(dnsmasq: &Dnsmasq, client: &NanocldClient) { log::warn!("Unable to serialize the DnsRule"); continue; }; - if let Err(err) = update_entries(&dns_rule, dnsmasq, client).await + if let Err(err) = + utils::update_entries(&dns_rule, dnsmasq, client).await { log::error!("Unable to update the DnsRule: {err}"); } @@ -114,16 +115,10 @@ async fn r#loop(dnsmasq: &Dnsmasq, client: &NanocldClient) { } /// Spawn new thread with event loop to watch for nanocld events -pub(crate) fn spawn(dnsmasq: &Dnsmasq) { +pub(crate) fn spawn(dnsmasq: &dnsmasq::Dnsmasq, client: &NanocldClient) { let dnsmasq = dnsmasq.clone(); + let client = client.clone(); rt::Arbiter::new().exec_fn(move || { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } ntex::rt::spawn(async move { r#loop(&dnsmasq, &client).await; }); diff --git a/bin/ncdns/src/main.rs b/bin/ncdns/src/main.rs index 58e30cb0e..8ad13eab7 100644 --- a/bin/ncdns/src/main.rs +++ b/bin/ncdns/src/main.rs @@ -11,6 +11,8 @@ mod version; mod dnsmasq; mod services; +use nanocld_client::NanocldClient; + use cli::Cli; use dnsmasq::Dnsmasq; @@ -26,8 +28,15 @@ async fn run(cli: &Cli) -> IoResult<()> { // Spawn a new thread to listen events from nanocld let conf_dir = cli.conf_dir.to_owned().unwrap_or("/etc".into()); let dnsmasq = Dnsmasq::new(&conf_dir).with_dns(cli.dns.clone()).ensure()?; - event::spawn(&dnsmasq); - let server = server::generate(&cli.host, &dnsmasq)?; + #[allow(unused)] + let mut client = NanocldClient::connect_with_unix_default(); + #[cfg(any(feature = "dev", feature = "test"))] + { + client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); + } + event::spawn(&dnsmasq, &client); + let server = server::generate(&cli.host, &dnsmasq, &client)?; server.await?; Ok(()) } diff --git a/bin/ncdns/src/server.rs b/bin/ncdns/src/server.rs index 3c65f0c41..e7585176b 100644 --- a/bin/ncdns/src/server.rs +++ b/bin/ncdns/src/server.rs @@ -1,7 +1,8 @@ use ntex::web; -use nanocl_utils::ntex::middlewares; use nanocl_error::io::{IoResult, IoError}; +use nanocl_utils::ntex::middlewares; +use nanocld_client::NanocldClient; use crate::services; use crate::dnsmasq::Dnsmasq; @@ -9,16 +10,18 @@ use crate::dnsmasq::Dnsmasq; pub fn generate( host: &str, dnsmasq: &Dnsmasq, + client: &NanocldClient, ) -> IoResult { let dnsmasq = dnsmasq.clone(); + let client = client.clone(); let mut server = web::HttpServer::new(move || { web::App::new() .state(dnsmasq.clone()) + .state(client.clone()) .wrap(middlewares::SerializeError) .configure(services::ntex_config) .default_service(web::route().to(services::unhandled)) }); - match host { host if host.starts_with("unix://") => { let path = host.trim_start_matches("unix://"); @@ -35,14 +38,12 @@ pub fn generate( )) } } - #[cfg(feature = "dev")] { server = server.bind("0.0.0.0:8787")?; log::debug!("Running in dev mode, binding to: http://0.0.0.0:8787"); log::debug!("OpenAPI explorer available at: http://0.0.0.0:8787/explorer/"); } - Ok(server.run()) } @@ -55,9 +56,11 @@ mod tests { #[ntex::test] async fn generate_unix_and_tcp() -> IoResult<()> { let dnsmasq = Dnsmasq::new("/tmp/ncdns"); - let server = generate("unix:///tmp/ncdns.sock", &dnsmasq)?; + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); + let server = generate("unix:///tmp/ncdns.sock", &dnsmasq, &client)?; server.stop(true).await; - let server = generate("tcp://0.0.0.0:9987", &dnsmasq)?; + let server = generate("tcp://0.0.0.0:9987", &dnsmasq, &client)?; server.stop(true).await; Ok(()) } @@ -65,7 +68,9 @@ mod tests { #[test] fn generate_wrong_host() -> IoResult<()> { let dnsmasq = Dnsmasq::new("/tmp/ncdns"); - let server = generate("wrong://dsadsa", &dnsmasq); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); + let server = generate("wrong://dsadsa", &dnsmasq, &client); assert!(server.is_err()); Ok(()) } diff --git a/bin/ncdns/src/services/rule.rs b/bin/ncdns/src/services/rule.rs index 76aaf946c..ce8720f6b 100644 --- a/bin/ncdns/src/services/rule.rs +++ b/bin/ncdns/src/services/rule.rs @@ -27,14 +27,8 @@ pub(crate) async fn apply_rule( _path: web::types::Path<(String, String)>, dnsmasq: web::types::State, web::types::Json(payload): web::types::Json, + client: web::types::State, ) -> Result { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } utils::write_entries(&payload, &dnsmasq, &client).await?; utils::reload_service(&client).await?; Ok(web::HttpResponse::Ok().json(&payload)) @@ -56,14 +50,8 @@ pub(crate) async fn apply_rule( pub(crate) async fn remove_rule( path: web::types::Path<(String, String)>, dnsmasq: web::types::State, + client: web::types::State, ) -> Result { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } let rule = client.inspect_resource(&path.1).await?; let dns_rule = serde_json::from_value::(rule.data).map_err(|err| { diff --git a/bin/ncdns/src/utils.rs b/bin/ncdns/src/utils.rs index efb94ad1b..4b01281d1 100644 --- a/bin/ncdns/src/utils.rs +++ b/bin/ncdns/src/utils.rs @@ -59,7 +59,7 @@ async fn get_network_addr( /// Reload the dns service /// TODO: use a better way to reload the service, we may have to move from dnsmasq to something else pub(crate) async fn reload_service(client: &NanocldClient) -> IoResult<()> { - client.restart_cargo("ndns", Some("system".into())).await?; + client.restart_cargo("ndns", Some("system")).await?; Ok(()) } @@ -99,7 +99,7 @@ pub(crate) async fn update_entries( kind: Some("DnsRule".into()), ..Default::default() }; - let resources = client.list_resource(Some(query)).await.map_err(|err| { + let resources = client.list_resource(Some(&query)).await.map_err(|err| { err.map_err_context(|| "Unable to list resources from nanocl daemon") })?; let mut entries = Vec::new(); @@ -169,6 +169,7 @@ pub(crate) async fn remove_entries( pub mod tests { use nanocl_utils::logger; pub use nanocl_utils::ntex::test_client::*; + use nanocld_client::NanocldClient; use crate::{version, dnsmasq, services}; @@ -184,10 +185,13 @@ pub mod tests { before(); let dnsmasq = dnsmasq::Dnsmasq::new("/tmp/dnsmasq"); dnsmasq.ensure().unwrap(); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); // Create test server let srv = ntex::web::test::server(move || { ntex::web::App::new() .state(dnsmasq.clone()) + .state(client.clone()) .configure(services::ntex_config) }); TestClient::new(srv, version::VERSION) diff --git a/bin/ncproxy/src/main.rs b/bin/ncproxy/src/main.rs index 8dc3612ae..f162c6fd2 100644 --- a/bin/ncproxy/src/main.rs +++ b/bin/ncproxy/src/main.rs @@ -21,14 +21,14 @@ async fn main() -> std::io::Result<()> { version::VERSION, version::COMMIT_ID ); - let nginx = match subsystem::init(&cli).await { + let (nginx, client) = match subsystem::init(&cli).await { Err(err) => { log::error!("{err}"); err.exit(); } Ok(nginx) => nginx, }; - let server = server::generate(&nginx)?; + let server = server::generate(&nginx, &client)?; server.await?; Ok(()) } diff --git a/bin/ncproxy/src/server.rs b/bin/ncproxy/src/server.rs index 3033f8cdc..7eb27fe88 100644 --- a/bin/ncproxy/src/server.rs +++ b/bin/ncproxy/src/server.rs @@ -1,28 +1,31 @@ use ntex::web; +use nanocld_client::NanocldClient; use nanocl_utils::ntex::middlewares; use crate::services; use crate::nginx::Nginx; -pub fn generate(nginx: &Nginx) -> std::io::Result { +pub fn generate( + nginx: &Nginx, + client: &NanocldClient, +) -> std::io::Result { let nginx = nginx.clone(); + let client = client.clone(); let mut server = web::HttpServer::new(move || { web::App::new() .state(nginx.clone()) + .state(client.clone()) .wrap(middlewares::SerializeError) .configure(services::ntex_config) .default_service(web::route().to(services::unhandled)) }); - server = server.bind_uds("/run/nanocl/proxy.sock")?; - #[cfg(feature = "dev")] { server = server.bind("0.0.0.0:8686")?; log::debug!("Running in dev mode, binding to: http://0.0.0.0:8686"); log::debug!("OpenAPI explorer available at: http://0.0.0.0:8686/explorer/"); } - Ok(server.run()) } diff --git a/bin/ncproxy/src/services/rule.rs b/bin/ncproxy/src/services/rule.rs index d2f362111..74dab3ea7 100644 --- a/bin/ncproxy/src/services/rule.rs +++ b/bin/ncproxy/src/services/rule.rs @@ -25,14 +25,8 @@ pub async fn apply_rule( path: web::types::Path<(String, String)>, nginx: web::types::State, web::types::Json(payload): web::types::Json, + client: web::types::State, ) -> Result { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } utils::create_resource_conf(&path.1, &payload, &client, &nginx).await?; if let Err(err) = utils::reload_config(&client).await { nginx.delete_conf_file(&path.1).await; @@ -58,14 +52,8 @@ pub async fn apply_rule( pub async fn remove_rule( path: web::types::Path<(String, String)>, nginx: web::types::State, + client: web::types::State, ) -> Result { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } nginx.delete_conf_file(&path.1).await; utils::reload_config(&client).await?; Ok(web::HttpResponse::Ok().finish()) diff --git a/bin/ncproxy/src/subsystem/event.rs b/bin/ncproxy/src/subsystem/event.rs index c0982b416..4582684f2 100644 --- a/bin/ncproxy/src/subsystem/event.rs +++ b/bin/ncproxy/src/subsystem/event.rs @@ -11,15 +11,13 @@ use nanocld_client::NanocldClient; use nanocld_client::stubs::system::Event; use nanocld_client::stubs::resource::ResourcePartial; -use crate::utils; -use crate::version; -use crate::nginx::Nginx; +use crate::{utils, version, nginx}; /// Update the nginx configuration when a cargo is started, patched async fn update_cargo_rule( name: &str, namespace: &str, - nginx: &Nginx, + nginx: &nginx::Nginx, client: &NanocldClient, ) -> IoResult<()> { let resources = @@ -51,7 +49,7 @@ async fn update_cargo_rule( async fn delete_cargo_rule( name: &str, namespace: &str, - nginx: &Nginx, + nginx: &nginx::Nginx, client: &NanocldClient, ) -> IoResult<()> { let resources = @@ -76,7 +74,7 @@ async fn delete_cargo_rule( /// Update the nginx configuration when a resource is created, patched async fn update_resource_rule( resource: &ResourcePartial, - nginx: &Nginx, + nginx: &nginx::Nginx, client: &NanocldClient, ) -> IoResult<()> { let proxy_rule = utils::serialize_proxy_rule(resource)?; @@ -91,15 +89,15 @@ async fn update_resource_rule( } async fn on_event( - event: Event, - nginx: Nginx, - client: NanocldClient, + event: &Event, + nginx: &nginx::Nginx, + client: &NanocldClient, ) -> IoResult<()> { match event { Event::CargoStarted(ev) => { log::debug!("received cargo started event: {ev:#?}"); if let Err(err) = - update_cargo_rule(&ev.name, &ev.namespace_name, &nginx, &client).await + update_cargo_rule(&ev.name, &ev.namespace_name, nginx, client).await { log::warn!("{err}"); } @@ -107,7 +105,7 @@ async fn on_event( Event::CargoPatched(ev) => { log::debug!("received cargo patched event: {ev:#?}"); if let Err(err) = - update_cargo_rule(&ev.name, &ev.namespace_name, &nginx, &client).await + update_cargo_rule(&ev.name, &ev.namespace_name, nginx, client).await { log::warn!("{err}"); } @@ -115,7 +113,7 @@ async fn on_event( Event::CargoStopped(ev) => { log::debug!("received cargo stopped event: {ev:#?}"); if let Err(err) = - delete_cargo_rule(&ev.name, &ev.namespace_name, &nginx, &client).await + delete_cargo_rule(&ev.name, &ev.namespace_name, nginx, client).await { log::warn!("{err}"); } @@ -123,7 +121,7 @@ async fn on_event( Event::CargoDeleted(ev) => { log::debug!("received cargo deleted event: {ev:#?}"); if let Err(err) = - delete_cargo_rule(&ev.name, &ev.namespace_name, &nginx, &client).await + delete_cargo_rule(&ev.name, &ev.namespace_name, nginx, client).await { log::warn!("{err}"); } @@ -134,7 +132,7 @@ async fn on_event( } log::debug!("received resource created event: {ev:#?}"); let resource: ResourcePartial = ev.as_ref().clone().into(); - if let Err(err) = update_resource_rule(&resource, &nginx, &client).await { + if let Err(err) = update_resource_rule(&resource, nginx, client).await { log::warn!("{err}"); } } @@ -144,7 +142,7 @@ async fn on_event( } log::debug!("received resource patched event: {ev:#?}"); let resource: ResourcePartial = ev.as_ref().clone().into(); - if let Err(err) = update_resource_rule(&resource, &nginx, &client).await { + if let Err(err) = update_resource_rule(&resource, nginx, client).await { log::warn!("{err}"); } } @@ -154,26 +152,24 @@ async fn on_event( } log::debug!("received resource deleted event: {ev:#?}"); nginx.delete_conf_file(&ev.name).await; - utils::reload_config(&client).await?; + utils::reload_config(client).await?; } Event::SecretPatched(secret) => { let resources = - utils::list_resource_by_secret(&secret.key, &client).await?; + utils::list_resource_by_secret(&secret.key, client).await?; for resource in resources { let resource: ResourcePartial = resource.into(); - if let Err(err) = update_resource_rule(&resource, &nginx, &client).await - { + if let Err(err) = update_resource_rule(&resource, nginx, client).await { log::warn!("{err}"); } } } Event::SecretCreated(secret) => { let resources = - utils::list_resource_by_secret(&secret.key, &client).await?; + utils::list_resource_by_secret(&secret.key, client).await?; for resource in resources { let resource: ResourcePartial = resource.into(); - if let Err(err) = update_resource_rule(&resource, &nginx, &client).await - { + if let Err(err) = update_resource_rule(&resource, nginx, client).await { log::warn!("{err}"); } } @@ -231,7 +227,7 @@ async fn ensure_resource_config(client: &NanocldClient) { } } -async fn r#loop(client: &NanocldClient, nginx: &Nginx) { +async fn r#loop(nginx: &nginx::Nginx, client: &NanocldClient) { loop { log::info!("Subscribing to nanocl daemon events.."); match client.watch_events().await { @@ -240,15 +236,12 @@ async fn r#loop(client: &NanocldClient, nginx: &Nginx) { } Ok(mut stream) => { log::info!("Subscribed to nanocl daemon events"); - ensure_resource_config(client).await; - while let Some(event) = stream.next().await { let Ok(event) = event else { break; }; - if let Err(err) = on_event(event, nginx.clone(), client.clone()).await - { + if let Err(err) = on_event(&event, nginx, client).await { log::warn!("{err}"); } } @@ -260,18 +253,12 @@ async fn r#loop(client: &NanocldClient, nginx: &Nginx) { } /// Spawn new thread with event loop to watch for nanocld events -pub(crate) fn spawn(nginx: &Nginx) { +pub(crate) fn spawn(nginx: &nginx::Nginx, client: &NanocldClient) { let nginx = nginx.clone(); + let client = client.clone(); rt::Arbiter::new().exec_fn(move || { - #[allow(unused)] - let mut client = NanocldClient::connect_with_unix_default(); - #[cfg(any(feature = "dev", feature = "test"))] - { - client = - NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); - } ntex::rt::spawn(async move { - r#loop(&client, &nginx).await; + r#loop(&nginx, &client).await; }); }); } diff --git a/bin/ncproxy/src/subsystem/init.rs b/bin/ncproxy/src/subsystem/init.rs index f54b38874..182d33869 100644 --- a/bin/ncproxy/src/subsystem/init.rs +++ b/bin/ncproxy/src/subsystem/init.rs @@ -1,4 +1,5 @@ use nanocl_error::io::IoResult; +use nanocld_client::NanocldClient; use crate::cli::Cli; use crate::nginx::Nginx; @@ -6,10 +7,17 @@ use crate::nginx::Nginx; use super::event; use super::network_log; -pub async fn init(cli: &Cli) -> IoResult { +pub async fn init(cli: &Cli) -> IoResult<(Nginx, NanocldClient)> { + #[allow(unused)] + let mut client = NanocldClient::connect_with_unix_default(); + #[cfg(any(feature = "dev", feature = "test"))] + { + client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); + } let nginx = Nginx::new(&cli.conf_dir.clone().unwrap_or("/etc/nginx".into())); nginx.ensure().await?; - event::spawn(&nginx); + event::spawn(&nginx, &client); network_log::spawn(); - Ok(nginx) + Ok((nginx, client)) } diff --git a/bin/ncproxy/src/utils.rs b/bin/ncproxy/src/utils.rs index dcd080ef5..21d72f5ff 100644 --- a/bin/ncproxy/src/utils.rs +++ b/bin/ncproxy/src/utils.rs @@ -204,7 +204,7 @@ async fn gen_upstream( match target_kind.as_str() { "c" => { let cargo = client - .inspect_cargo(&target_name, Some(target_namespace.clone())) + .inspect_cargo(&target_name, Some(&target_namespace)) .await .map_err(|err| { err.map_err_context(|| { @@ -215,7 +215,7 @@ async fn gen_upstream( } "v" => { let vm = client - .inspect_vm(&target_name, Some(target_namespace.clone())) + .inspect_vm(&target_name, Some(&target_namespace)) .await .map_err(|err| { err.map_err_context(|| format!("Unable to inspect vm {target_name}")) @@ -578,13 +578,13 @@ pub(crate) async fn reload_config(client: &NanocldClient) -> IoResult<()> { ..Default::default() }; let start_res = client - .create_exec("nproxy", exec_options, Some("system".into())) + .create_exec("nproxy", &exec_options, Some("system")) .await .map_err(|err| err.map_err_context(|| "Unable to reload proxy configs"))?; let mut start_stream = client .start_exec( &start_res.id, - bollard_next::exec::StartExecOptions::default(), + &bollard_next::exec::StartExecOptions::default(), ) .await .map_err(|err| err.map_err_context(|| "Unable to reload proxy configs"))?; @@ -672,7 +672,7 @@ pub(crate) async fn list_resource_by_cargo( ..Default::default() }; let http_ressources = - client.list_resource(Some(query)).await.map_err(|err| { + client.list_resource(Some(&query)).await.map_err(|err| { err.map_err_context(|| "Unable to list resources from nanocl daemon") })?; let query = ResourceQuery { @@ -684,7 +684,7 @@ pub(crate) async fn list_resource_by_cargo( ..Default::default() }; let stream_resources = - client.list_resource(Some(query)).await.map_err(|err| { + client.list_resource(Some(&query)).await.map_err(|err| { err.map_err_context(|| "Unable to list resources from nanocl daemon") })?; let resources = http_ressources @@ -714,7 +714,7 @@ pub(crate) async fn list_resource_by_secret( kind: Some("ProxyRule".into()), ..Default::default() }; - let resources = client.list_resource(Some(query)).await.map_err(|err| { + let resources = client.list_resource(Some(&query)).await.map_err(|err| { err.map_err_context(|| "Unable to list resources from nanocl daemon") })?; log::debug!("matching resources for secret: {secret}:\n{:?}", resources); @@ -724,6 +724,7 @@ pub(crate) async fn list_resource_by_secret( #[cfg(test)] pub(crate) mod tests { use nanocl_utils::logger; + use nanocld_client::NanocldClient; pub use nanocl_utils::ntex::test_client::*; use crate::{version, nginx, services}; @@ -739,9 +740,12 @@ pub(crate) mod tests { before(); let nginx = nginx::Nginx::new("/tmp/nginx"); nginx.ensure().await.unwrap(); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); // Create test server let srv = ntex::web::test::server(move || { ntex::web::App::new() + .state(client.clone()) .state(nginx.clone()) .configure(services::ntex_config) }); diff --git a/crates/nanocl_stubs/src/cargo.rs b/crates/nanocl_stubs/src/cargo.rs index ce49338dd..d136d0fdf 100644 --- a/crates/nanocl_stubs/src/cargo.rs +++ b/crates/nanocl_stubs/src/cargo.rs @@ -264,9 +264,9 @@ impl From for StatsOptions { } impl CargoLogQuery { - pub fn of_namespace(nsp: String) -> CargoLogQuery { + pub fn of_namespace(nsp: &str) -> CargoLogQuery { CargoLogQuery { - namespace: Some(nsp), + namespace: Some(nsp.to_owned()), since: None, until: None, timestamps: None, diff --git a/crates/nanocl_stubs/src/generic.rs b/crates/nanocl_stubs/src/generic.rs index ee4eca7dc..fbf178650 100644 --- a/crates/nanocl_stubs/src/generic.rs +++ b/crates/nanocl_stubs/src/generic.rs @@ -10,6 +10,15 @@ pub struct GenericNspQuery { pub namespace: Option, } +impl GenericNspQuery { + /// Create a new query with an optional namespace + pub fn new(namespace: Option<&str>) -> Self { + Self { + namespace: namespace.map(|s| s.to_owned()), + } + } +} + /// Generic delete response #[derive(Debug)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] diff --git a/crates/nanocld_client/src/cargo.rs b/crates/nanocld_client/src/cargo.rs index 2433a610b..718d7cb44 100644 --- a/crates/nanocld_client/src/cargo.rs +++ b/crates/nanocld_client/src/cargo.rs @@ -16,16 +16,24 @@ use nanocl_stubs::cargo_config::{ use super::http_client::NanocldClient; impl NanocldClient { - /// ## Create a new cargo + /// ## Default path for cargoes + const CARGO_PATH: &str = "/cargoes"; + + /// ## Create cargo + /// + /// Create a new cargo in the system + /// Note that the cargo is not started by default /// /// ## Arguments - /// * [item](CargoConfigPartial) - The cargo config to create - /// * [namespace](Option) - The namespace to create the cargo in + /// + /// * [item](CargoConfigPartial) - A reference of a [cargo config partial](CargoConfigPartial) + /// * [namespace](Option) - The [namespace](str) to create the cargo in /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The created [cargo](Cargo) - /// * [Err](HttpClientError) - The cargo could not be created + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The created [cargo](Cargo) if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example /// @@ -40,206 +48,230 @@ impl NanocldClient { /// ..Default::default() /// } /// }; - /// let cargo = client.create_cargo(new_cargo, None).await; + /// let res = client.create_cargo(new_cargo, None).await; /// ``` /// pub async fn create_cargo( &self, item: &CargoConfigPartial, - namespace: Option, + namespace: Option<&str>, ) -> Result { let res = self .send_post( - format!("/{}/cargoes", &self.version), + Self::CARGO_PATH, Some(item), - Some(&GenericNspQuery { namespace }), + Some(&GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } /// ## Delete a cargo - /// Delete a cargo by it's name + /// + /// Delete a cargo by it's name and namespace /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to delete - /// * [namespace](Option) - The namespace to delete the cargo from + /// * [query](CargoDeleteQuery) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The cargo was deleted - /// * [Err](HttpClientError) - The cargo could not be deleted + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was deleted if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// client.delete_cargo("my-cargo", None).await.unwrap(); + /// let res = client.delete_cargo("my-cargo", None).await; /// ``` /// pub async fn delete_cargo( &self, name: &str, - query: &CargoDeleteQuery, + query: Option<&CargoDeleteQuery>, ) -> Result<(), HttpClientError> { self - .send_delete(format!("/{}/cargoes/{name}", &self.version), Some(query)) + .send_delete(&format!("{}/{name}", Self::CARGO_PATH), query) .await?; Ok(()) } /// ## Inspect a cargo + /// /// Inspect a cargo by it's name to get more information about it /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to inspect - /// * [namespace](Option) - The namespace to inspect the cargo from + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The desired [cargo](Cargo) - /// * [Err](HttpClientError) - The cargo could not be inspected + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The desired [cargo](Cargo) if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let cargo = client.inspect_cargo("my-cargo", None).await.unwrap(); + /// let res = client.inspect_cargo("my-cargo", None).await; /// ``` /// pub async fn inspect_cargo( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result { let res = self .send_get( - format!("/{}/cargoes/{name}/inspect", &self.version), - Some(GenericNspQuery { namespace }), + &format!("{}/{name}/inspect", Self::CARGO_PATH), + Some(GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } /// ## Start a cargo - /// Start a cargo by it's name + /// + /// Start a cargo by it's name and namespace /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to start - /// * [namespace](Option) - The namespace to start the cargo from + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The cargo was started - /// * [Err](HttpClientError) - The cargo could not be started + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was started if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// client.start_cargo("my-cargo", None).await.unwrap(); + /// let res = client.start_cargo("my-cargo", None).await; /// ``` /// pub async fn start_cargo( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/cargoes/{name}/start", &self.version), + &format!("{}/{name}/start", Self::CARGO_PATH), None::, - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) } /// # Stop a cargo - /// Stop a cargo by it's name + /// + /// Stop a cargo by it's name and namespace /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to stop - /// * [namespace](Option) - The namespace to stop the cargo from + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The cargo was stopped - /// * [Err](HttpClientError) - The cargo could not be stopped + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was stopped if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// client.stop_cargo("my-cargo", None).await.unwrap(); + /// let res = client.stop_cargo("my-cargo", None).await; /// ``` /// pub async fn stop_cargo( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/cargoes/{name}/stop", &self.version), + &format!("{}/{name}/stop", Self::CARGO_PATH), None::, - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) } /// # Restart a cargo - /// Restart a cargo by it's name + /// + /// Restart a cargo by it's name and namespace /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to restart - /// * [namespace](Option) - The namespace to restart the cargo from + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - The cargo was restarted - /// * [Err](HttpClientError) - The cargo could not be restarted + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was restarted if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// client.restart_cargo("my-cargo", None).await.unwrap(); + /// let res = client.restart_cargo("my-cargo", None).await; /// ``` /// pub async fn restart_cargo( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/cargoes/{name}/restart", &self.version), + &format!("{}/{name}/restart", Self::CARGO_PATH), None::, - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) } /// ## List cargoes + /// /// List all cargoes in a namespace /// /// ## Arguments - /// * [namespace](Option) - The namespace to list the cargoes from + /// + /// * [namespace](Option) - The [namespace](str) where the cargoes belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok) - A [Vec](Vec) of [cargoes](CargoSummary) - /// * [Err](HttpClientError) - The cargoes could not be listed + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [cargo summary](CargoSummary) if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// @@ -249,32 +281,33 @@ impl NanocldClient { /// pub async fn list_cargo( &self, - namespace: Option, + namespace: Option<&str>, ) -> Result, HttpClientError> { let res = self - .send_get( - format!("/{}/cargoes", &self.version), - Some(GenericNspQuery { namespace }), - ) + .send_get(Self::CARGO_PATH, Some(GenericNspQuery::new(namespace))) .await?; Self::res_json(res).await } /// ## Patch a cargo + /// /// Patch a cargo by it's name /// This will update the cargo's config by merging current config with new config and creating an history entry /// /// ## Arguments + /// /// * [name](str) - The name of the cargo to patch - /// * [cargo](CargoConfigPatch) - The config to patch the cargo with - /// * [namespace](Option) - The namespace to patch the cargo from + /// * [cargo](CargoConfigUpdate) - The config to patch the cargo with + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns - /// * [Result](Result) - /// * [Ok](Ok<()>) - The cargo was patched - /// * [Err](HttpClientError) - The cargo could not be patched + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was patched if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// @@ -288,36 +321,38 @@ impl NanocldClient { pub async fn patch_cargo( &self, name: &str, - config: CargoConfigUpdate, - namespace: Option, + config: &CargoConfigUpdate, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_patch( - format!("/{}/cargoes/{name}", &self.version), + &format!("{}/{name}", Self::CARGO_PATH), Some(config), - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) } /// ## Put a cargo + /// /// Put a cargo by it's name /// It will create a new cargo config and store old one in history /// /// ## Arguments /// - /// * [name](str) - The name of the cargo to put - /// * [cargo](CargoConfigPartial) - The config to put the cargo with - /// * [namespace](Option) - The namespace to put the cargo from + /// * [name](str) - The name of the cargo to update + /// * [cargo](CargoConfigPartial) - The config to update the cargo with + /// * [namespace](Option) - The [namespace](str) where the cargo belongs /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok<()>) - The cargo was put - /// * [Err](HttpClientError) - The cargo could not be put + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The cargo was put if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example + /// /// ```no_run,ignore /// use nanocld_client::NanocldClient; /// @@ -332,13 +367,13 @@ impl NanocldClient { &self, name: &str, config: &CargoConfigPartial, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_put( - format!("/{}/cargoes/{name}", &self.version), + &format!("{}/{name}", Self::CARGO_PATH), Some(config), - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) @@ -349,13 +384,13 @@ impl NanocldClient { /// ## Arguments /// /// * [name](str) - The name of the cargo to list the histories - /// * [namespace](Option) - The namespace where belong the cargo + /// * [namespace](Option) - The [namespace](str) where belong the cargo /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - A [Vec](Vec) of [CargoConfig](CargoConfig) - /// * [Err](HttpClientError) - The cargo could not be listed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [cargo config](CargoConfig) if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example /// @@ -369,12 +404,12 @@ impl NanocldClient { pub async fn list_history_cargo( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result, HttpClientError> { let res = self .send_get( - format!("/{}/cargoes/{name}/histories", &self.version), - Some(GenericNspQuery { namespace }), + &format!("{}/{name}/histories", Self::CARGO_PATH), + Some(GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await @@ -386,13 +421,13 @@ impl NanocldClient { /// /// * [name](str) - The name of the cargo to revert /// * [id](str) - The id of the history to revert to - /// * [namespace](Option) - The namespace where belong the cargo + /// * [namespace](Option) - The [namespace](str) where belong the cargo /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The [Cargo](Cargo) reverted - /// * [Err](HttpClientError) - The cargo could not be reverted + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) reverted if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed /// /// ## Example /// @@ -407,58 +442,55 @@ impl NanocldClient { &self, name: &str, id: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result { let res = self .send_patch( - format!("/{}/cargoes/{name}/histories/{id}/revert", &self.version), + &format!("{}/{name}/histories/{id}/revert", Self::CARGO_PATH), None::, - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } - /// ## Get the logs of a cargo - /// The logs are streamed as a [Receiver](Receiver) of [CargoOutput](CargoOutput) + /// ## Logs a cargo + /// + /// Get logs of a cargo by it's name + /// The logs are streamed as a [Receiver](Receiver) of [output log](OutputLog) /// /// ## Arguments /// /// * [name](str) - The name of the cargo to get the logs - /// * [namespace](Option) - The namespace where belong the cargo + /// * [query](Option) - The optional [query](CargoLogQuery) /// pub async fn logs_cargo( &self, name: &str, - query: &CargoLogQuery, + query: Option<&CargoLogQuery>, ) -> Result>, HttpClientError> { let res = self - .send_get( - format!("/{}/cargoes/{name}/logs", &self.version), - Some(query), - ) + .send_get(&format!("{}/{name}/logs", Self::CARGO_PATH), query) .await?; Ok(Self::res_stream(res).await) } /// ## Get the stats of a cargo - /// The stats are streamed as a [Receiver](Receiver) of [CargoOutput](CargoOutput) + /// + /// The stats are streamed as a [Receiver](Receiver) of [cargo stats](CargoStats) /// /// ## Arguments /// /// * [name](str) - The name of the cargo to get the stats - /// * [namespace](Option) - The namespace where belong the cargo + /// * [query](Option) - The option [query](CargoStatsQuery) /// pub async fn stats_cargo( &self, name: &str, - query: &CargoStatsQuery, + query: Option<&CargoStatsQuery>, ) -> Result>, HttpClientError> { let res = self - .send_get( - format!("/{}/cargoes/{name}/stats", &self.version), - Some(query), - ) + .send_get(&format!("{}/{name}/stats", Self::CARGO_PATH), query) .await?; Ok(Self::res_stream(res).await) } @@ -470,34 +502,73 @@ impl NanocldClient { /// ## Arguments /// /// * [name](str) - The name of the cargo to kill - /// * [options](CargoKillOptions) - The options to kill the cargo - /// * [namespace](Option) - The namespace to kill the cargo from + /// * [query](Option) - The optional [query](CargoKillOptions) + /// * [namespace](Option) - The [namespace](str) where belong the cargo + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The [cargo](Cargo) was killed if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.kill_cargo("my-cargo", None, None).await; + /// ``` /// pub async fn kill_cargo( &self, name: &str, - options: &CargoKillOptions, - namespace: Option, + query: Option<&CargoKillOptions>, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/cargoes/{name}/kill", &self.version), - Some(options), - Some(GenericNspQuery { namespace }), + &format!("{}/{name}/kill", Self::CARGO_PATH), + query, + Some(GenericNspQuery::new(namespace)), ) .await?; Ok(()) } + /// ## List cargo instance + /// + /// List all the instances of a cargo by it's name and namespace + /// + /// ## Arguments + /// + /// * [name](str) - The name of the cargo to list the instances + /// * [namespace](Option) - The [namespace](str) where belong the cargo + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [container summary](ContainerSummary) if the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_cargo_instance("my-cargo", None).await; + /// ``` + /// pub async fn list_cargo_instance( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result, HttpClientError> { let res = self .send_get( - format!("/{}/cargoes/{name}/instances", &self.version), - Some(GenericNspQuery { namespace }), + &format!("{}/{name}/instances", Self::CARGO_PATH), + Some(GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await @@ -515,7 +586,8 @@ mod tests { #[ntex::test] async fn basic() { const CARGO_NAME: &str = "client-test-cargo"; - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); client.list_cargo(None).await.unwrap(); let new_cargo = CargoConfigPartial { name: CARGO_NAME.into(), @@ -537,7 +609,7 @@ mod tests { ..Default::default() }; client - .patch_cargo(CARGO_NAME, cargo_update, None) + .patch_cargo(CARGO_NAME, &cargo_update, None) .await .unwrap(); client @@ -552,15 +624,13 @@ mod tests { .await .unwrap(); client.stop_cargo(CARGO_NAME, None).await.unwrap(); - client - .delete_cargo(CARGO_NAME, &CargoDeleteQuery::default()) - .await - .unwrap(); + client.delete_cargo(CARGO_NAME, None).await.unwrap(); } #[ntex::test] async fn create_cargo_wrong_image() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let new_cargo = CargoConfigPartial { name: "client-test-cargowi".into(), container: bollard_next::container::Config { @@ -580,7 +650,8 @@ mod tests { #[ntex::test] async fn create_cargo_duplicate_name() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let new_cargo = CargoConfigPartial { name: "client-test-cargodup".into(), container: bollard_next::container::Config { @@ -598,16 +669,17 @@ mod tests { _ => panic!("Wrong error type"), } client - .delete_cargo("client-test-cargodup", &CargoDeleteQuery::default()) + .delete_cargo("client-test-cargodup", None) .await .unwrap(); } #[ntex::test] async fn logs_cargo() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let mut rx = client - .logs_cargo("nstore", &CargoLogQuery::of_namespace("system".into())) + .logs_cargo("nstore", Some(&CargoLogQuery::of_namespace("system"))) .await .unwrap(); let _out = rx.next().await.unwrap().unwrap(); diff --git a/crates/nanocld_client/src/cargo_image.rs b/crates/nanocld_client/src/cargo_image.rs index d00470a43..68c9442f2 100644 --- a/crates/nanocld_client/src/cargo_image.rs +++ b/crates/nanocld_client/src/cargo_image.rs @@ -11,13 +11,22 @@ use nanocl_stubs::cargo_image::{CargoImagePartial, ListCargoImagesOptions}; use super::http_client::NanocldClient; impl NanocldClient { - /// ## List all cargo images + /// ## Default path for cargo images + const CARGO_IMAGE_PATH: &str = "/cargoes/images"; + + /// ## List cargo image + /// + /// List cargo images from the system + /// + /// ## Arguments + /// + /// * [opts](Option) - The optional [query](ListCargoImagesOptions) /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) * [Vec](Vec) of [ImageSummary](bollard_next::models::ImageSummary) - /// * [Err](Err) * [HttpClientError](HttpClientError) if the request failed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [image summary](bollard_next::models::ImageSummary) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -25,20 +34,18 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let images = client.list_cargo_image(None).await; + /// let res = client.list_cargo_image(None).await; /// ``` /// pub async fn list_cargo_image( &self, - opts: Option, + opts: Option<&ListCargoImagesOptions>, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/cargoes/images", &self.version), opts) - .await?; + let res = self.send_get(Self::CARGO_IMAGE_PATH, opts).await?; Self::res_json(res).await } - /// ## Create a cargo image + /// ## Create cargo image /// /// This method will create a cargo image and return a stream of [CreateImageInfo](bollard_next::models::CreateImageInfo) /// that can be used to follow the progress of the image creation. @@ -50,9 +57,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) * [mpsc::Receiver](mpsc::Receiver) of [CreateImageInfo](bollard_next::models::CreateImageInfo) as Stream - /// * [Err](Err) * [HttpClientError](HttpClientError) if the request failed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Receiver](mpsc::Receiver) of [CreateImageInfo](bollard_next::models::CreateImageInfo) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -62,7 +69,7 @@ impl NanocldClient { /// let client = NanocldClient::connect_to("http://localhost:8585", None); /// let mut stream = client.create_cargo_image("my-image").await; /// while let Some(info) = stream.try_next().await { - /// println!("{:?}", info); + /// println!("{info:?}"); /// } /// ``` /// @@ -75,7 +82,7 @@ impl NanocldClient { > { let res = self .send_post( - format!("/{}/cargoes/images", self.version), + Self::CARGO_IMAGE_PATH, Some(CargoImagePartial { name: name.to_owned(), }), @@ -85,9 +92,9 @@ impl NanocldClient { Ok(Self::res_stream(res).await) } - /// ## Delete a cargo image + /// ## Delete cargo image /// - /// This method will delete a cargo image by it's name. + /// Delete a cargo image by it's name. /// /// ## Arguments /// @@ -96,8 +103,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The image was successfully deleted - /// * [Err](Err) * [HttpClientError](HttpClientError) if the request failed + /// * [Ok](Ok) - The image was successfully deleted if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -114,16 +121,16 @@ impl NanocldClient { ) -> Result<(), HttpClientError> { self .send_delete( - format!("/{}/cargoes/images/{name}", self.version), + &format!("{}/{name}", Self::CARGO_IMAGE_PATH), None::, ) .await?; Ok(()) } - /// ## Inspect a cargo image + /// ## Inspect cargo image /// - /// This method will inspect a cargo image by it's name. + /// Return detailed information about a cargo image. /// /// ## Arguments /// @@ -131,9 +138,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) * [ImageInspect](bollard_next::models::ImageInspect) of the image - /// * [Err](Err) * [HttpClientError](HttpClientError) if the request failed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Image inspect](bollard_next::models::ImageInspect) of the image if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -150,7 +157,7 @@ impl NanocldClient { ) -> Result { let res = self .send_get( - format!("/{}/cargoes/images/{name}", self.version), + &format!("{}/{name}", Self::CARGO_IMAGE_PATH), None::, ) .await?; @@ -167,7 +174,7 @@ impl NanocldClient { { self .send_post_stream( - format!("/{}/cargoes/images/import", self.version), + &format!("{}/import", Self::CARGO_IMAGE_PATH), stream, None::, ) @@ -184,7 +191,8 @@ mod tests { #[ntex::test] async fn basic() { const IMAGE: &str = "busybox:1.26.1"; - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let mut stream = client.create_cargo_image(IMAGE).await.unwrap(); while let Some(_info) = stream.next().await {} client.list_cargo_image(None).await.unwrap(); diff --git a/crates/nanocld_client/src/exec.rs b/crates/nanocld_client/src/exec.rs index f72d938b6..27c3daac6 100644 --- a/crates/nanocld_client/src/exec.rs +++ b/crates/nanocld_client/src/exec.rs @@ -11,19 +11,22 @@ use nanocl_stubs::cargo::{CreateExecOptions, OutputLog}; use super::http_client::NanocldClient; impl NanocldClient { + /// ## Default path for exec commands + const EXEC_PATH: &str = "/exec"; + /// ## Create exec command inside a cargo /// /// ## Arguments /// /// * [name](str) - The name of the cargo to exec the command in /// * [exec](CreateExecOptions) - The config for the exec command - /// * [namespace](Option) - The namespace where belong the cargo + /// * [namespace](Option) - The [namespace](str) where belong the cargo /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The created exec command - /// * [Err](HttpClientError) - The command could not be executed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Created exec](CreateExecResults) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -43,20 +46,22 @@ impl NanocldClient { pub async fn create_exec( &self, name: &str, - exec: CreateExecOptions, - namespace: Option, + exec: &CreateExecOptions, + namespace: Option<&str>, ) -> Result { let res = self .send_post( - format!("/{}/cargoes/{name}/exec", &self.version), + &format!("/cargoes/{name}/exec"), Some(exec), - Some(GenericNspQuery { namespace }), + Some(GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } - /// ## Inspect an exec command inside a cargo + /// ## Inspect exec + /// + /// Inspect an exec command inside a cargo instance. /// /// ## Arguments /// @@ -65,8 +70,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - Infos of the inspected command - /// * [Err](HttpClientError) - The command could not be executed + /// * [Ok](Ok) - [Info](ExecInspectResponse) of the exec command if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -96,10 +101,7 @@ impl NanocldClient { id: &str, ) -> Result { let res = self - .send_get( - format!("/{}/exec/{id}/cargo/inspect", &self.version), - Some(()), - ) + .send_get(&format!("{}/{id}/cargo/inspect", Self::EXEC_PATH), Some(())) .await?; Self::res_json(res).await } @@ -110,13 +112,12 @@ impl NanocldClient { /// /// * [id](str) - Id of command to run /// * [exec](CreateExecOptions) - The config for the exec command - /// * [namespace](Option) - The namespace where belong the cargo /// /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - A [mpsc::Receiver](mpsc::Receiver) of [ExecOutput](ExecOutput) - /// * [Err](HttpClientError) - The command could not be executed + /// * [Ok](Ok) - [Receiver](mpsc::Receiver) of [output log](OutputLog) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -140,11 +141,11 @@ impl NanocldClient { pub async fn start_exec( &self, id: &str, - exec: StartExecOptions, + exec: &StartExecOptions, ) -> Result>, HttpClientError> { let res = self .send_post( - format!("/{}/exec/{id}/cargo/start", &self.version), + &format!("{}/{id}/cargo/start", &Self::EXEC_PATH), Some(exec), Some(()), ) @@ -162,17 +163,18 @@ mod tests { #[ntex::test] async fn exec_cargo() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let exec = CreateExecOptions { cmd: Some(vec!["echo".into(), "hello".into()]), ..Default::default() }; let result = client - .create_exec("nstore", exec, Some("system".into())) + .create_exec("nstore", &exec, Some("system")) .await .unwrap(); let mut rx = client - .start_exec(&result.id, StartExecOptions::default()) + .start_exec(&result.id, &StartExecOptions::default()) .await .unwrap(); while let Some(_out) = rx.next().await {} diff --git a/crates/nanocld_client/src/http_client.rs b/crates/nanocld_client/src/http_client.rs index 8dc5ee540..4d12543a3 100644 --- a/crates/nanocld_client/src/http_client.rs +++ b/crates/nanocld_client/src/http_client.rs @@ -5,24 +5,21 @@ use ntex::http; use ntex::util::{Bytes, Stream}; use ntex::channel::mpsc::Receiver; -use ntex::connect::openssl::SslMethod; use futures::{StreamExt, TryStreamExt}; use nanocl_error::io::FromIo; use nanocl_error::http::HttpError; use nanocl_error::http_client::HttpClientError; -use openssl::ssl::SslConnector; use crate::error::is_api_error; -const NANOCLD_DEFAULT_VERSION: &str = "0.11.0"; +pub const NANOCLD_DEFAULT_VERSION: &str = "0.11.0"; #[derive(Clone)] pub struct NanocldClient { pub url: String, pub version: String, pub unix_socket: Option, - pub client: http::client::Client, } impl std::fmt::Display for NanocldClient { @@ -33,65 +30,27 @@ impl std::fmt::Display for NanocldClient { impl NanocldClient { pub fn connect_with_unix_default() -> Self { - let client = http::client::Client::build() - .connector( - http::client::Connector::default() - .connector(ntex::service::fn_service(|_| async { - Ok::<_, _>(rt::unix_connect("/run/nanocl/nanocl.sock").await?) - })) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(), - ) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(); - NanocldClient { - client, unix_socket: Some(String::from("/run/nanocl/nanocl.sock")), version: format!("v{NANOCLD_DEFAULT_VERSION}"), - url: String::from("http://localhost"), + url: "http://localhost".to_owned(), } } - pub fn connect_to(url: &'static str, version: Option) -> Self { - let builder = SslConnector::builder(SslMethod::tls()).unwrap().build(); + pub fn connect_to(url: &str, version: Option) -> Self { match url { url if url.starts_with("http://") || url.starts_with("https://") => { - let client = http::client::Client::build() - .connector( - http::client::Connector::default() - .timeout(ntex::time::Millis::from_secs(100)) - .openssl(builder) - .finish(), - ) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(); NanocldClient { - url: url.into(), - client, + url: url.to_owned(), unix_socket: None, version: version.unwrap_or(format!("v{NANOCLD_DEFAULT_VERSION}")), } } url if url.starts_with("unix://") => { let path = url.trim_start_matches("unix://"); - let client = http::client::Client::build() - .connector( - http::client::Connector::default() - .connector(ntex::service::fn_service(move |_| async { - let path = url.trim_start_matches("unix://"); - Ok::<_, _>(rt::unix_connect(path).await?) - })) - .openssl(builder) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(), - ) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(); NanocldClient { - url: "http://localhost".into(), - client, - unix_socket: Some(path.into()), + url: "http://localhost".to_owned(), + unix_socket: Some(path.to_owned()), version: version.unwrap_or(format!("v{NANOCLD_DEFAULT_VERSION}")), } } @@ -103,6 +62,31 @@ impl NanocldClient { self.version = format!("v{version}") } + pub fn connect_with_unix_version(version: &str) -> Self { + NanocldClient { + unix_socket: Some(String::from("/run/nanocl/nanocl.sock")), + version: version.to_owned(), + url: String::from("http://localhost"), + } + } + + fn gen_client(&self) -> http::client::Client { + let mut client = http::client::Client::build(); + if let Some(unix_socket) = &self.unix_socket { + let unix_socket = unix_socket.clone(); + client = client.connector( + http::client::Connector::default() + .connector(ntex::service::fn_service(move |_| { + let unix_socket = unix_socket.clone(); + async { Ok::<_, _>(rt::unix_connect(unix_socket).await?) } + })) + .timeout(ntex::time::Millis::from_secs(100)) + .finish(), + ); + } + client.timeout(ntex::time::Millis::from_secs(100)).finish() + } + fn send_error( &self, err: http::client::error::SendRequestError, @@ -115,72 +99,55 @@ impl NanocldClient { HttpClientError::IoError(*err.map_err_context(|| url.to_string())) } - pub fn connect_with_unix_version(version: &str) -> Self { - let client = http::client::Client::build() - .connector( - http::client::Connector::default() - .connector(ntex::service::fn_service(|_| async { - Ok::<_, _>(rt::unix_connect("/run/nanocl/nanocl.sock").await?) - })) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(), - ) - .timeout(ntex::time::Millis::from_secs(100)) - .finish(); - NanocldClient { - client, - unix_socket: Some(String::from("/run/nanocl/nanocl.sock")), - version: version.to_owned(), - url: String::from("http://localhost"), - } - } - - fn gen_url(&self, url: String) -> String { - self.url.to_owned() + &url + fn gen_url(&self, url: &str) -> String { + format!("{}/{}{}", self.url, self.version, url) } - fn get(&self, url: String) -> http::client::ClientRequest { - self.client.get(self.gen_url(url)) + fn get(&self, url: &str) -> http::client::ClientRequest { + self + .gen_client() + .get(self.gen_url(url)) + .header("User-Agent", "nanocld_client") } - fn delete(&self, url: String) -> http::client::ClientRequest { + fn delete(&self, url: &str) -> http::client::ClientRequest { self - .client + .gen_client() .delete(self.gen_url(url)) .header("User-Agent", "nanocld_client") } - fn post(&self, url: String) -> http::client::ClientRequest { + fn post(&self, url: &str) -> http::client::ClientRequest { self - .client + .gen_client() .post(self.gen_url(url)) .header("User-Agent", "nanocld_client") } - fn patch(&self, url: String) -> http::client::ClientRequest { + fn patch(&self, url: &str) -> http::client::ClientRequest { self - .client + .gen_client() .patch(self.gen_url(url)) .header("User-Agent", "nanocld_client") } - fn put(&self, url: String) -> http::client::ClientRequest { + fn put(&self, url: &str) -> http::client::ClientRequest { self - .client + .gen_client() .put(self.gen_url(url)) .header("User-Agent", "nanocld_client") } - fn head(&self, url: String) -> http::client::ClientRequest { + fn head(&self, url: &str) -> http::client::ClientRequest { self - .client + .gen_client() .head(self.gen_url(url)) .header("User-Agent", "nanocld_client") } pub(crate) async fn send_get( &self, - url: String, + url: &str, query: Option, ) -> Result where @@ -202,7 +169,7 @@ impl NanocldClient { pub(crate) async fn send_post( &self, - url: String, + url: &str, body: Option, query: Option, ) -> Result @@ -230,7 +197,7 @@ impl NanocldClient { pub(crate) async fn send_post_stream( &self, - url: String, + url: &str, stream: S, query: Option, ) -> Result @@ -256,7 +223,7 @@ impl NanocldClient { pub(crate) async fn send_delete( &self, - url: String, + url: &str, query: Option, ) -> Result where @@ -276,7 +243,7 @@ impl NanocldClient { pub(crate) async fn send_patch( &self, - url: String, + url: &str, body: Option, query: Option, ) -> Result @@ -304,7 +271,7 @@ impl NanocldClient { pub(crate) async fn send_head( &self, - url: String, + url: &str, query: Option, ) -> Result where @@ -324,7 +291,7 @@ impl NanocldClient { pub(crate) async fn send_put( &self, - url: String, + url: &str, body: Option, query: Option, ) -> Result diff --git a/crates/nanocld_client/src/http_metric.rs b/crates/nanocld_client/src/http_metric.rs index f45afa890..b92409418 100644 --- a/crates/nanocld_client/src/http_metric.rs +++ b/crates/nanocld_client/src/http_metric.rs @@ -4,13 +4,37 @@ use nanocl_error::http_client::HttpClientError; use super::http_client::NanocldClient; impl NanocldClient { + /// ## Default path for http metrics + const HTTP_METRIC_PATH: &str = "/http_metrics"; + + /// ## List http metrics + /// + /// List http metric from the system + /// + /// ## Arguments + /// + /// * [query](Option) - The optional [query](HttpMetricListQuery) + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [http metrics](HttpMetric) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_http_metric(None).await; + /// ``` + /// pub async fn list_http_metric( &self, - query: Option, + query: Option<&HttpMetricListQuery>, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/http_metrics", &self.version), query) - .await?; + let res = self.send_get(Self::HTTP_METRIC_PATH, query).await?; Self::res_json(res).await } } @@ -22,8 +46,9 @@ mod tests { #[ntex::test] async fn list_metric() -> Result<(), HttpClientError> { - let client = NanocldClient::connect_to("http://localhost:8585", None); - let res = client.list_http_metric(None::).await; + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); + let res = client.list_http_metric(None).await; assert!(res.is_ok()); Ok(()) } diff --git a/crates/nanocld_client/src/namespace.rs b/crates/nanocld_client/src/namespace.rs index 62dc8725c..4ff6d1e4f 100644 --- a/crates/nanocld_client/src/namespace.rs +++ b/crates/nanocld_client/src/namespace.rs @@ -4,13 +4,18 @@ use nanocl_stubs::namespace::{Namespace, NamespaceSummary, NamespaceInspect}; use super::http_client::NanocldClient; impl NanocldClient { - /// ## List all namespaces + /// ## Default path for namespaces + const NAMESPACE_PATH: &str = "/namespaces"; + + /// ## List namespace + /// + /// List all namespaces from the system /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - A [Vec](Vec) of [namespaces](NamespaceSummary) - /// * [Err](HttpClientError) - The namespaces could not be listed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [namespace summary](NamespaceSummary) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -18,19 +23,19 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let namespaces = client.list_namespace().await; + /// let res = client.list_namespace().await; /// ``` /// pub async fn list_namespace( &self, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/namespaces", &self.version), None::) - .await?; + let res = self.send_get(Self::NAMESPACE_PATH, None::).await?; Self::res_json(res).await } - /// ## Create a new namespace + /// ## Create namespace + /// + /// Create a namespace by it's name /// /// ## Arguments /// @@ -38,9 +43,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The created [namespace](Namespace) - /// * [Err](HttpClientError) - The namespace could not be created + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Namespace](Namespace) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// pub async fn create_namespace( &self, @@ -48,18 +53,14 @@ impl NanocldClient { ) -> Result { let new_item = Namespace { name: name.into() }; let res = self - .send_post( - format!("/{}/namespaces", &self.version), - Some(new_item), - None::, - ) + .send_post(Self::NAMESPACE_PATH, Some(new_item), None::) .await?; Self::res_json(res).await } - /// ## Inspect a namespace + /// ## Inspect namespace /// - /// Inspect a namespace by it's name to get more information about it + /// Inspect a namespace by it's name to get detailed information about it. /// /// ## Arguments /// @@ -68,8 +69,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The desired [namespace](NamespaceInspect) - /// * [Err](HttpClientError) - The namespace could not be inspected + /// * [Ok](Ok) - [Namespace inspect](NamespaceInspect) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -77,7 +78,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let namespace = client.inspect_namespace("my-namespace").await?; + /// let res = client.inspect_namespace("my-namespace").await; /// ``` /// pub async fn inspect_namespace( @@ -86,7 +87,7 @@ impl NanocldClient { ) -> Result { let res = self .send_get( - format!("/{}/namespaces/{name}/inspect", &self.version), + &format!("{}/{name}/inspect", Self::NAMESPACE_PATH), None::, ) .await?; @@ -104,8 +105,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The namespace was deleted - /// * [Err](HttpClientError) - The namespace could not be deleted + /// * [Ok](Ok) - The namespace was deleted if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -113,7 +114,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// client.delete_namespace("my-namespace").await?; + /// let res = client.delete_namespace("my-namespace").await; /// ``` /// pub async fn delete_namespace( @@ -121,10 +122,7 @@ impl NanocldClient { name: &str, ) -> Result<(), HttpClientError> { self - .send_delete( - format!("/{}/namespaces/{name}", &self.version), - None::, - ) + .send_delete(&format!("{}/{name}", Self::NAMESPACE_PATH), None::) .await?; Ok(()) } @@ -137,7 +135,8 @@ mod tests { #[ntex::test] async fn basic() { const NAMESPACE: &str = "clientnt"; - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); client.list_namespace().await.unwrap(); let namespace = client.create_namespace(NAMESPACE).await.unwrap(); assert_eq!(namespace.name, NAMESPACE); diff --git a/crates/nanocld_client/src/node.rs b/crates/nanocld_client/src/node.rs index d92316ce5..a0709a181 100644 --- a/crates/nanocld_client/src/node.rs +++ b/crates/nanocld_client/src/node.rs @@ -5,10 +5,30 @@ use nanocl_stubs::node::Node; use super::http_client::NanocldClient; impl NanocldClient { + /// ## Default path for nodes + const NODE_PATH: &str = "/nodes"; + + /// ## List node + /// + /// List existing nodes in the system + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [node](Node) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_node().await; + /// ``` + /// pub async fn list_node(&self) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/nodes", &self.version), None::) - .await?; + let res = self.send_get(Self::NODE_PATH, None::).await?; Self::res_json(res).await } } @@ -19,7 +39,8 @@ mod tests { #[ntex::test] async fn basic() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let node = client.list_node().await; assert!(node.is_ok()); } diff --git a/crates/nanocld_client/src/resource.rs b/crates/nanocld_client/src/resource.rs index 08a5069c8..14ad95333 100644 --- a/crates/nanocld_client/src/resource.rs +++ b/crates/nanocld_client/src/resource.rs @@ -7,19 +7,22 @@ use nanocl_stubs::resource::{ use super::http_client::NanocldClient; impl NanocldClient { + /// ## Default path for resources + const RESOURCE_PATH: &str = "/resources"; + /// ## List resources /// - /// List all existing resources + /// List existing resources in the system. /// /// ## Arguments /// - /// * [query](ResourceQuery) - Query to filter resources + /// * [query](Option) - The optional [query](ResourceQuery) /// /// ## Returns /// /// * [Result](Result) - The result of the operation - /// * [Ok](Vec) - The resources - /// * [Err](HttpClientError) - An error if the operation failed + /// * [Ok](Ok) - [Vector](Vec) of [resource](Resource) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -27,32 +30,30 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let namespaces = client.list_resource().await; + /// let res = client.list_resource().await; /// ``` /// pub async fn list_resource( &self, - query: Option, + query: Option<&ResourceQuery>, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/resources", &self.version), query) - .await?; + let res = self.send_get(Self::RESOURCE_PATH, query).await?; Self::res_json(res).await } /// ## Create resource /// - /// Create a new resource + /// Create a new resource from a partial resource in the system. /// /// ## Arguments /// - /// * [data](ResourcePartial) - The data of the resource to create + /// * [data](ResourcePartial) - The partial /// /// ## Returns /// /// * [Result](Result) - The result of the operation - /// * [Ok](Resource) - The created resource - /// * [Err](HttpClientError) - An error if the operation failed + /// * [Ok](Ok) - [Resource] if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -61,7 +62,7 @@ impl NanocldClient { /// use nanocl_stubs::resource::ResourceKind; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let resource = client.create_resource(&ResourcePartial { + /// let res = client.create_resource(&ResourcePartial { /// name: "my-resource".into(), /// kind: String::from("Custom")s, /// // Your config @@ -74,11 +75,7 @@ impl NanocldClient { data: &ResourcePartial, ) -> Result { let res = self - .send_post( - format!("/{}/resources", &self.version), - Some(data), - None::, - ) + .send_post(Self::RESOURCE_PATH, Some(data), None::) .await?; Self::res_json(res).await } @@ -94,8 +91,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - The result of the operation - /// * [Ok](Resource) - The inspected resource - /// * [Err](HttpClientError) - An error if the operation failed + /// * [Ok](Ok) - [Resource](Resource) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -103,7 +100,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let resource = client.inspect_resource("my-resource").await; + /// let res = client.inspect_resource("my-resource").await; /// ``` /// pub async fn inspect_resource( @@ -111,10 +108,7 @@ impl NanocldClient { key: &str, ) -> Result { let res = self - .send_get( - format!("/{}/resources/{key}", &self.version), - None::, - ) + .send_get(&format!("{}/{key}", Self::RESOURCE_PATH), None::) .await?; Self::res_json(res).await } @@ -131,8 +125,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - The result of the operation - /// * [Ok](Resource) - The patched resource - /// * [Err](HttpClientError) - An error if the operation failed + /// * [Ok](Ok) - [Resource](Resource) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -140,7 +134,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let resource = client.patch_resource("my-resource", serde_json::json!({})).await; + /// let res = client.patch_resource("my-resource", serde_json::json!({})).await; /// ``` /// pub async fn put_resource( @@ -150,7 +144,7 @@ impl NanocldClient { ) -> Result { let res = self .send_patch( - format!("/{}/resources/{key}", &self.version), + &format!("{}/{key}", Self::RESOURCE_PATH), Some(config), None::, ) @@ -169,8 +163,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - The result of the operation - /// * [Ok](Ok(())) - The operation succeeded - /// * [Err](HttpClientError) - An error if the operation failed + /// * [Ok](Ok) - If operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -178,7 +172,7 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let resource = client.delete_resource("my-resource").await; + /// let res = client.delete_resource("my-resource").await; /// ``` /// pub async fn delete_resource( @@ -186,27 +180,72 @@ impl NanocldClient { key: &str, ) -> Result<(), HttpClientError> { self - .send_delete( - format!("/{}/resources/{key}", &self.version), - None::, - ) + .send_delete(&format!("{}/{key}", Self::RESOURCE_PATH), None::) .await?; Ok(()) } + /// ## List history resource + /// + /// List history of an existing resource + /// + /// ## Arguments + /// + /// * [key](str) - The key of the resource to list history + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [ResourceConfig](ResourceConfig) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_history_resource("my-resource").await; + /// ``` + /// pub async fn list_history_resource( &self, key: &str, ) -> Result, HttpClientError> { let res = self .send_get( - format!("/{}/resources/{key}/histories", &self.version), + &format!("{}/{key}/histories", Self::RESOURCE_PATH), None::, ) .await?; Self::res_json(res).await } + /// ## Revert resource + /// + /// Revert a resource to a previous version + /// + /// ## Arguments + /// + /// * [name](str) - The name of the resource to revert + /// * [key](str) - The key of the resource to revert + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Resource](Resource) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let history = client.list_history_resource("my-resource").await.unwrap().first().unwrap(); + /// let res = client.revert_resource("my-resource", history.key).await; + /// ``` + /// pub async fn revert_resource( &self, name: &str, @@ -214,7 +253,7 @@ impl NanocldClient { ) -> Result { let res = self .send_patch( - format!("/{}/resources/{name}/histories/{key}/revert", &self.version), + &format!("{}/{name}/histories/{key}/revert", Self::RESOURCE_PATH), None::, None::, ) @@ -231,7 +270,8 @@ mod tests { #[ntex::test] async fn basic() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); // list client.list_resource(None).await.unwrap(); let config = serde_json::json!({ diff --git a/crates/nanocld_client/src/secret.rs b/crates/nanocld_client/src/secret.rs index 324b08bdd..7542aaf04 100644 --- a/crates/nanocld_client/src/secret.rs +++ b/crates/nanocld_client/src/secret.rs @@ -4,17 +4,22 @@ use nanocl_stubs::secret::{Secret, SecretPartial, SecretUpdate, SecretQuery}; use super::http_client::NanocldClient; impl NanocldClient { - /// ## List all secrets + /// ## Default path for secrets + const SECRET_PATH: &str = "/secrets"; + + /// ## List secrets + /// + /// List existing secrets in the system. /// /// ## Arguments /// - /// * [query](SecretQuery) - Query to filter secrets + /// * [query](Option) - The optional [query](SecretQuery) /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - A [Vec](Vec) of [secrets](SecretSummary) - /// * [Err](HttpClientError) - The secrets could not be listed + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [secrets](Secret) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -22,46 +27,42 @@ impl NanocldClient { /// use nanocld_client::NanocldClient; /// /// let client = NanocldClient::connect_to("http://localhost:8585", None); - /// let secrets = client.list_secret(None).await; + /// let res = client.list_secret(None).await; /// ``` /// pub async fn list_secret( &self, - query: Option, + query: Option<&SecretQuery>, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/secrets", &self.version), query) - .await?; + let res = self.send_get(Self::SECRET_PATH, query).await?; Self::res_json(res).await } - /// ## Create a new secret + /// ## Create secret /// /// ## Arguments /// - /// * [secret](SecretPartial) - The key of the secret to create + /// * [secret](SecretPartial) - The secret to create /// /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The created [secret](Secret) - /// * [Err](HttpClientError) - The secret could not be created + /// * [Ok](Ok) - [Secret](Secret) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// pub async fn create_secret( &self, item: &SecretPartial, ) -> Result { let res = self - .send_post( - format!("/{}/secrets", &self.version), - Some(item), - None::, - ) + .send_post(Self::SECRET_PATH, Some(item), None::) .await?; Self::res_json(res).await } - /// ## Patch a secret + /// ## Patch secret + /// + /// Patch a secret by it's key to update it with new data /// /// ## Arguments /// @@ -70,24 +71,20 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The created [secret](Secret) - /// * [Err](HttpClientError) - The secret could not be created + /// * [Ok](Ok) - [Secret](Secret) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// pub async fn patch_secret( &self, item: &SecretUpdate, ) -> Result { let res = self - .send_patch( - format!("/{}/secrets", &self.version), - Some(item), - None::, - ) + .send_patch(Self::SECRET_PATH, Some(item), None::) .await?; Self::res_json(res).await } - /// ## Inspect a secret + /// ## Inspect secret /// /// Inspect a secret by it's key to get more information about it /// @@ -98,8 +95,8 @@ impl NanocldClient { /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The desired [secret](SecretInspect) - /// * [Err](HttpClientError) - The secret could not be inspected + /// * [Ok](Ok) - [Secret](Secret) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -116,7 +113,7 @@ impl NanocldClient { ) -> Result { let res = self .send_get( - format!("/{}/secrets/{key}/inspect", &self.version), + &format!("{}/{key}/inspect", Self::SECRET_PATH), None::, ) .await?; @@ -125,17 +122,17 @@ impl NanocldClient { /// ## Delete a secret /// - /// Delete a secret by it's key + /// Delete a [secret](Secret) by it's key /// /// ## Arguments /// - /// * [key](str) - The key of the secret to delete + /// * [key](str) - The key of the [secret](Secret) to delete /// /// ## Returns /// /// * [Result](Result) - /// * [Ok](Ok) - The secret was deleted - /// * [Err](HttpClientError) - The secret could not be deleted + /// * [Ok](Ok) - If operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -148,7 +145,7 @@ impl NanocldClient { /// pub async fn delete_secret(&self, key: &str) -> Result<(), HttpClientError> { self - .send_delete(format!("/{}/secrets/{key}", &self.version), None::) + .send_delete(&format!("{}/{key}", Self::SECRET_PATH), None::) .await?; Ok(()) } @@ -161,7 +158,8 @@ mod tests { #[ntex::test] async fn basic() { const SECRET_KEY: &str = "secret-test"; - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); client.list_secret(None).await.unwrap(); let secret = SecretPartial { key: SECRET_KEY.to_string(), diff --git a/crates/nanocld_client/src/state.rs b/crates/nanocld_client/src/state.rs index 90a7ae3df..928f264ed 100644 --- a/crates/nanocld_client/src/state.rs +++ b/crates/nanocld_client/src/state.rs @@ -8,13 +8,30 @@ use nanocl_stubs::state::StateStream; use crate::http_client::NanocldClient; impl NanocldClient { + /// ## Default path for state + const STATE_PATH: &str = "/state"; + + /// ## Apply state + /// + /// Apply a state to the system + /// + /// ## Arguments + /// + /// * [data](serde_json::Value) - The state to apply + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - A [stream](Receiver) of result of [state stream](StateStream) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// pub async fn apply_state( &self, data: &serde_json::Value, ) -> Result>, HttpClientError> { let res = self .send_put( - format!("/{}/state/apply", &self.version), + &format!("{}/apply", Self::STATE_PATH), Some(data), None::, ) @@ -22,13 +39,27 @@ impl NanocldClient { Ok(Self::res_stream(res).await) } + /// ## Remove state + /// + /// Remove a state from the system + /// + /// ## Arguments + /// + /// * [data](serde_json::Value) - The state to remove + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - A [stream](Receiver) of result of [state stream](StateStream) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// pub async fn remove_state( &self, data: &serde_json::Value, ) -> Result>, HttpClientError> { let res = self .send_put( - format!("/{}/state/remove", &self.version), + &format!("{}/remove", Self::STATE_PATH), Some(data), None::, ) diff --git a/crates/nanocld_client/src/system.rs b/crates/nanocld_client/src/system.rs index 5e9613a63..cfb91fd05 100644 --- a/crates/nanocld_client/src/system.rs +++ b/crates/nanocld_client/src/system.rs @@ -13,9 +13,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The [version](Version) of the daemon - /// * [Err](HttpClientError) - The version could not be retrieved + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Version](Version) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -27,9 +27,7 @@ impl NanocldClient { /// ``` /// pub async fn get_version(&self) -> Result { - let res = self - .send_get(format!("/{}/version", &self.version), None::) - .await?; + let res = self.send_get("/version", None::).await?; Self::res_json(res).await } @@ -40,9 +38,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - A [Receiver](mpsc::Receiver) of [Event](Event)s - /// * [Err](HttpClientError) - The events could not be retrieved + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - A [Receiver](mpsc::Receiver) of [events](Event) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -59,9 +57,7 @@ impl NanocldClient { pub async fn watch_events( &self, ) -> Result>, HttpClientError> { - let res = self - .send_get(format!("/{}/events", &self.version), None::) - .await?; + let res = self.send_get("/events", None::).await?; Ok(Self::res_stream(res).await) } @@ -71,9 +67,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The daemon is running - /// * [Err](HttpClientError) - The daemon is not running + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -85,9 +81,7 @@ impl NanocldClient { /// ``` /// pub async fn ping(&self) -> Result<(), HttpClientError> { - self - .send_head(format!("/{}/_ping", &self.version), None::) - .await?; + self.send_head("/_ping", None::).await?; Ok(()) } @@ -97,9 +91,9 @@ impl NanocldClient { /// /// ## Returns /// - /// * [Result](Result) - /// * [Ok](Ok) - The [HostInfo](HostInfo) - /// * [Err](HttpClientError) - The host info could not be retrieved + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [HostInfo](HostInfo) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed /// /// ## Example /// @@ -111,19 +105,38 @@ impl NanocldClient { /// ``` /// pub async fn info(&self) -> Result { - let res = self - .send_get(format!("/{}/info", &self.version), None::) - .await?; + let res = self.send_get("/info", None::).await?; Self::res_json(res).await } + /// ## Process + /// + /// List of current processes (vm, cargoes) managed by the daemon + /// + /// ## Arguments + /// + /// * [opts](Option) - The optional [query](ProccessQuery) + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [node container summary](NodeContainerSummary) if operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let processes = client.process(None).await; + /// ``` + /// pub async fn process( &self, - opts: Option, + opts: Option<&ProccessQuery>, ) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/processes", &self.version), opts) - .await?; + let res = self.send_get("/processes", opts).await?; Self::res_json(res).await } } @@ -134,14 +147,16 @@ mod tests { #[ntex::test] async fn get_version() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let version = client.get_version().await; assert!(version.is_ok()); } #[ntex::test] async fn watch_events() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let _stream = client.watch_events().await.unwrap(); // Todo : find a way to test this on CI because it's limited to 2 threads // let _event = stream.next().await.unwrap(); @@ -149,7 +164,8 @@ mod tests { #[ntex::test] async fn info() { - let client = NanocldClient::connect_to("http://localhost:8585", None); + let client = + NanocldClient::connect_to("http://ndaemon.nanocl.internal:8585", None); let info = client.info().await.unwrap(); assert!(info.docker.containers.unwrap() > 0); } diff --git a/crates/nanocld_client/src/vm.rs b/crates/nanocld_client/src/vm.rs index e546a2cf3..04348b91e 100644 --- a/crates/nanocld_client/src/vm.rs +++ b/crates/nanocld_client/src/vm.rs @@ -13,115 +13,282 @@ use nanocl_stubs::vm_config::{VmConfigPartial, VmConfigUpdate}; use crate::NanocldClient; impl NanocldClient { + /// ## Default path for vms + const VM_PATH: &str = "/vms"; + + /// ## Create vm + /// + /// Create a new virtual machine in the system. + /// + /// ## Arguments + /// + /// * [vm](VmConfigPartial) - The config for the vm + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vm](Vm) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// pub async fn create_vm( &self, vm: &VmConfigPartial, - namespace: Option, + namespace: Option<&str>, ) -> Result { let res = self .send_post( - format!("/{}/vms", self.version), + Self::VM_PATH, Some(vm), - Some(&GenericNspQuery { namespace }), + Some(&GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } + /// ## List vm + /// + /// List existing vms + /// + /// ## Arguments + /// + /// * [namespace](Option) - The [namespace](str) where belong the vms + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [vm summary](VmSummary) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_vm(None).await; + /// ``` + /// pub async fn list_vm( &self, - namespace: Option, + namespace: Option<&str>, ) -> Result, HttpClientError> { let res = self - .send_get( - format!("/{}/vms", self.version), - Some(&GenericNspQuery { namespace }), - ) + .send_get(Self::VM_PATH, Some(&GenericNspQuery::new(namespace))) .await?; Self::res_json(res).await } + /// ## Delete vm + /// + /// Delete a vm by it's name and namespace + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to delete + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - The operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.delete_vm("my-vm", None).await; + /// ``` + /// pub async fn delete_vm( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_delete( - format!("/{}/vms/{}", self.version, name), - Some(&GenericNspQuery { namespace }), + &format!("{}/{name}", Self::VM_PATH), + Some(&GenericNspQuery::new(namespace)), ) .await?; Ok(()) } + /// ## Inspect vm + /// + /// Inspect a vm by it's name and namespace + /// And get detailed information about it + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to inspect + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vm inspect](VmInspect) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.inspect_vm("my-vm", None).await; + /// ``` + /// pub async fn inspect_vm( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result { let res = self .send_get( - format!("/{}/vms/{}/inspect", self.version, name), - Some(&GenericNspQuery { namespace }), + &format!("{}/{name}/inspect", Self::VM_PATH), + Some(&GenericNspQuery::new(namespace)), ) .await?; Self::res_json(res).await } + /// ## Start vm + /// + /// Start a vm by it's name and namespace + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to start + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// pub async fn start_vm( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/vms/{}/start", self.version, name), + &format!("{}/{name}/start", Self::VM_PATH), None::, - Some(&GenericNspQuery { namespace }), + Some(&GenericNspQuery::new(namespace)), ) .await?; Ok(()) } + /// ## Stop vm + /// + /// Stop a vm by it's name and namespace + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to stop + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.stop_vm("my-vm", None).await; + /// ``` + /// pub async fn stop_vm( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_post( - format!("/{}/vms/{}/stop", self.version, name), + &format!("{}/{name}/stop", Self::VM_PATH), None::, - Some(&GenericNspQuery { namespace }), + Some(&GenericNspQuery::new(namespace)), ) .await?; Ok(()) } + /// ## Patch vm + /// + /// Patch a vm by it's name and namespace to update it's config + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to patch + /// * [vm](VmConfigUpdate) - The config to update the vm + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If the operation succeeded + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// pub async fn patch_vm( &self, name: &str, vm: &VmConfigUpdate, - namespace: Option, + namespace: Option<&str>, ) -> Result<(), HttpClientError> { self .send_patch( - format!("/{}/vms/{}", self.version, name), + &format!("{}/{name}", Self::VM_PATH), Some(vm), - Some(&GenericNspQuery { namespace }), + Some(&GenericNspQuery::new(namespace)), ) .await?; Ok(()) } + /// ## Attach vm + /// + /// Attach to a vm by it's name and namespace + /// and return websocket stream to send input and receive output from the vm tty + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm to attach + /// * [namespace](Option) - The [namespace](str) where belong the vm + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Websocket connection](WsConnection) to the vm tty if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if the operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.attach_vm("my-vm", None).await; + /// ``` + /// pub async fn attach_vm( &self, name: &str, - namespace: Option, + namespace: Option<&str>, ) -> Result, HttpClientError> { let qs = if let Some(namespace) = namespace { - format!("?namespace={}", namespace) + format!("?Namespace={}", namespace) } else { "".to_owned() }; diff --git a/crates/nanocld_client/src/vm_image.rs b/crates/nanocld_client/src/vm_image.rs index d07c3ff2c..cc148713f 100644 --- a/crates/nanocld_client/src/vm_image.rs +++ b/crates/nanocld_client/src/vm_image.rs @@ -12,6 +12,24 @@ use nanocl_stubs::vm_image::{VmImage, VmImageCloneStream, VmImageResizePayload}; use crate::NanocldClient; impl NanocldClient { + /// ## Default path for vm images + const VM_IMAGE_PATH: &str = "/vms/images"; + + /// ## Import a vm image + /// + /// This method will import a vm image from a stream of bytes. + /// + /// ## Arguments + /// + /// * [name](str) - The name of the image to import + /// * [stream](Stream) - The stream of bytes to import + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If the operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// pub async fn import_vm_image( &self, name: &str, @@ -23,7 +41,7 @@ impl NanocldClient { { self .send_post_stream( - format!("/{}/vms/images/{name}/import", self.version), + &format!("{}/{name}/import", Self::VM_IMAGE_PATH), stream, None::, ) @@ -31,26 +49,87 @@ impl NanocldClient { Ok(()) } + /// ## List vm images + /// + /// List existing vm images in the system. + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vector](Vec) of [vm images](VmImage) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.list_vm_image().await; + /// ``` + /// pub async fn list_vm_image(&self) -> Result, HttpClientError> { - let res = self - .send_get(format!("/{}/vms/images", self.version), None::) - .await?; + let res = self.send_get(Self::VM_IMAGE_PATH, None::).await?; Self::res_json(res).await } + /// ## Delete vm image + /// + /// Delete a vm image by it's name + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm image to delete + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - If the operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.delete_vm_image("my-image").await; + /// ``` + /// pub async fn delete_vm_image( &self, name: &str, ) -> Result<(), HttpClientError> { self - .send_delete( - format!("/{}/vms/images/{name}", self.version), - None::, - ) + .send_delete(&format!("{}/{name}", Self::VM_IMAGE_PATH), None::) .await?; Ok(()) } + /// ## Clone vm image + /// + /// Clone a vm image by it's name + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm image to clone + /// * [clone_name](str) - The name of the clone + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Stream](mpsc::Receiver) of vm image clone [status](VmImageCloneStream) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.clone_vm_image("my-image", "my-clone").await; + /// ``` + /// pub async fn clone_vm_image( &self, name: &str, @@ -61,7 +140,7 @@ impl NanocldClient { > { let res = self .send_post( - format!("/{}/vms/images/{name}/clone/{clone_name}", self.version), + &format!("{}/{name}/clone/{clone_name}", Self::VM_IMAGE_PATH), None::, None::, ) @@ -69,15 +148,42 @@ impl NanocldClient { Ok(Self::res_stream(res).await) } + /// ## Resize vm image + /// + /// Resize a vm image by it's name + /// + /// ## Arguments + /// + /// * [name](str) - The name of the vm image to resize + /// * [opts](VmImageResizePayload) - The options to resize the vm image + /// + /// ## Returns + /// + /// * [Result](Result) - The result of the operation + /// * [Ok](Ok) - [Vm image](VmImage) if operation was successful + /// * [Err](Err) - [Http client error](HttpClientError) if operation failed + /// + /// ## Example + /// + /// ```no_run,ignore + /// use nanocld_client::NanocldClient; + /// + /// let client = NanocldClient::connect_to("http://localhost:8585", None); + /// let res = client.resize_vm_image("my-image", VmImageResizePayload { + /// size: 45640, + /// shrink: false, + /// }).await; + /// ``` + /// pub async fn resize_vm_image( &self, name: &str, - payload: &VmImageResizePayload, + opts: &VmImageResizePayload, ) -> Result { let res = self .send_post( - format!("/{}/vms/images/{name}/resize", self.version), - Some(payload.clone()), + &format!("{}/{name}/resize", Self::VM_IMAGE_PATH), + Some(opts.clone()), None::, ) .await?;