diff --git a/Cargo.lock b/Cargo.lock index ba811d48..ae421883 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,7 +82,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83" dependencies = [ "nom", - "vte", + "vte 0.10.1", ] [[package]] @@ -155,6 +155,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "async-broadcast" version = "0.7.1" @@ -665,7 +671,7 @@ dependencies = [ "crossterm", "strum 0.26.3", "strum_macros 0.26.4", - "unicode-width", + "unicode-width 0.1.13", ] [[package]] @@ -686,7 +692,7 @@ dependencies = [ "encode_unicode", "lazy_static", "libc", - "unicode-width", + "unicode-width 0.1.13", "windows-sys 0.52.0", ] @@ -1641,6 +1647,20 @@ dependencies = [ "serde", ] +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "unicode-width 0.2.0", + "vt100", + "web-time", +] + [[package]] name = "inout" version = "0.1.3" @@ -1896,7 +1916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -2083,6 +2103,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "numtoa" version = "0.1.0" @@ -2394,6 +2420,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + [[package]] name = "powerfmt" version = "0.2.0" @@ -3267,6 +3299,7 @@ dependencies = [ "futures", "helm-sys", "indexmap 2.5.0", + "indicatif", "k8s-openapi", "kube", "rand 0.8.5", @@ -3282,6 +3315,7 @@ dependencies = [ "tera", "tokio", "tracing", + "tracing-indicatif", "url", "urlencoding", "utoipa", @@ -3438,6 +3472,7 @@ dependencies = [ "dotenvy", "futures", "indexmap 2.5.0", + "indicatif", "lazy_static", "libc", "rand 0.8.5", @@ -3453,6 +3488,7 @@ dependencies = [ "termion", "tokio", "tracing", + "tracing-indicatif", "tracing-subscriber", "urlencoding", ] @@ -3930,6 +3966,18 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-indicatif" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8201ca430e0cd893ef978226fd3516c06d9c494181c8bf4e5b32e30ed4b40aa1" +dependencies = [ + "indicatif", + "tracing", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -4117,6 +4165,12 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.5" @@ -4228,13 +4282,36 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vt100" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84cd863bf0db7e392ba3bd04994be3473491b31e66340672af5d11943c6274de" +dependencies = [ + "itoa", + "log", + "unicode-width 0.1.13", + "vte 0.11.1", +] + [[package]] name = "vte" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cbce692ab4ca2f1f3047fcf732430249c0e971bfdd2b234cf2c47ad93af5983" dependencies = [ - "arrayvec", + "arrayvec 0.5.2", + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "arrayvec 0.7.6", "utf8parse", "vte_generate_state_changes", ] @@ -4413,7 +4490,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/Cargo.nix b/Cargo.nix index ef95c5d6..0224a012 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -332,7 +332,7 @@ rec { } { name = "vte"; - packageId = "vte"; + packageId = "vte 0.10.1"; } ]; features = { @@ -492,7 +492,7 @@ rec { }; resolvedDefaultFeatures = [ "derive" "derive_arbitrary" ]; }; - "arrayvec" = rec { + "arrayvec 0.5.2" = rec { crateName = "arrayvec"; version = "0.5.2"; edition = "2018"; @@ -505,6 +505,21 @@ rec { "serde" = [ "dep:serde" ]; }; }; + "arrayvec 0.7.6" = rec { + crateName = "arrayvec"; + version = "0.7.6"; + edition = "2018"; + sha256 = "0l1fz4ccgv6pm609rif37sl5nv5k6lbzi7kkppgzqzh1vwix20kw"; + authors = [ + "bluss" + ]; + features = { + "borsh" = [ "dep:borsh" ]; + "default" = [ "std" ]; + "serde" = [ "dep:serde" ]; + "zeroize" = [ "dep:zeroize" ]; + }; + }; "async-broadcast" = rec { crateName = "async-broadcast"; version = "0.7.1"; @@ -2276,7 +2291,7 @@ rec { } { name = "unicode-width"; - packageId = "unicode-width"; + packageId = "unicode-width 0.1.13"; } ]; features = { @@ -2339,7 +2354,7 @@ rec { } { name = "unicode-width"; - packageId = "unicode-width"; + packageId = "unicode-width 0.1.13"; optional = true; } { @@ -5257,6 +5272,55 @@ rec { }; resolvedDefaultFeatures = [ "default" "serde" "std" ]; }; + "indicatif" = rec { + crateName = "indicatif"; + version = "0.17.11"; + edition = "2021"; + sha256 = "0db2b2r79r9x8x4lysq1ci9xm13c0xg0sqn3z960yh2bk2430fqq"; + dependencies = [ + { + name = "console"; + packageId = "console"; + usesDefaultFeatures = false; + features = [ "ansi-parsing" ]; + } + { + name = "number_prefix"; + packageId = "number_prefix"; + } + { + name = "portable-atomic"; + packageId = "portable-atomic"; + } + { + name = "unicode-width"; + packageId = "unicode-width 0.2.0"; + optional = true; + } + { + name = "vt100"; + packageId = "vt100"; + optional = true; + } + { + name = "web-time"; + packageId = "web-time"; + target = { target, features }: ("wasm32" == target."arch" or null); + } + ]; + features = { + "default" = [ "unicode-width" "console/unicode-width" ]; + "futures" = [ "dep:futures-core" ]; + "improved_unicode" = [ "unicode-segmentation" "unicode-width" "console/unicode-width" ]; + "in_memory" = [ "vt100" ]; + "rayon" = [ "dep:rayon" ]; + "tokio" = [ "dep:tokio" ]; + "unicode-segmentation" = [ "dep:unicode-segmentation" ]; + "unicode-width" = [ "dep:unicode-width" ]; + "vt100" = [ "dep:vt100" ]; + }; + resolvedDefaultFeatures = [ "default" "in_memory" "unicode-width" "vt100" ]; + }; "inout" = rec { crateName = "inout"; version = "0.1.3"; @@ -6214,7 +6278,7 @@ rec { } { name = "windows-targets"; - packageId = "windows-targets 0.48.5"; + packageId = "windows-targets 0.52.6"; target = { target, features }: (target."windows" or false); } ]; @@ -6725,6 +6789,19 @@ rec { }; resolvedDefaultFeatures = [ "proc-macro-crate" "std" ]; }; + "number_prefix" = rec { + crateName = "number_prefix"; + version = "0.4.0"; + edition = "2015"; + sha256 = "1wvh13wvlajqxkb1filsfzbrnq0vrmrw298v2j3sy82z1rm282w3"; + authors = [ + "Benjamin Sago " + ]; + features = { + "default" = [ "std" ]; + }; + resolvedDefaultFeatures = [ "default" "std" ]; + }; "numtoa" = rec { crateName = "numtoa"; version = "0.1.0"; @@ -7704,6 +7781,19 @@ rec { ]; }; + "portable-atomic" = rec { + crateName = "portable-atomic"; + version = "1.11.0"; + edition = "2018"; + sha256 = "0glb2wngflvfmg789qbf6dbnwcf6ai212fs7n0lf1c66rd49n3im"; + libName = "portable_atomic"; + features = { + "critical-section" = [ "dep:critical-section" ]; + "default" = [ "fallback" ]; + "serde" = [ "dep:serde" ]; + }; + resolvedDefaultFeatures = [ "default" "fallback" ]; + }; "powerfmt" = rec { crateName = "powerfmt"; version = "0.2.0"; @@ -10584,6 +10674,10 @@ rec { packageId = "indexmap 2.5.0"; features = [ "serde" ]; } + { + name = "indicatif"; + packageId = "indicatif"; + } { name = "k8s-openapi"; packageId = "k8s-openapi"; @@ -10651,6 +10745,10 @@ rec { name = "tracing"; packageId = "tracing"; } + { + name = "tracing-indicatif"; + packageId = "tracing-indicatif"; + } { name = "url"; packageId = "url"; @@ -11297,6 +11395,10 @@ rec { packageId = "indexmap 2.5.0"; features = [ "serde" ]; } + { + name = "indicatif"; + packageId = "indicatif"; + } { name = "lazy_static"; packageId = "lazy_static"; @@ -11365,6 +11467,10 @@ rec { name = "tracing"; packageId = "tracing"; } + { + name = "tracing-indicatif"; + packageId = "tracing-indicatif"; + } { name = "tracing-subscriber"; packageId = "tracing-subscriber"; @@ -13123,6 +13229,33 @@ rec { }; resolvedDefaultFeatures = [ "default" "once_cell" "std" ]; }; + "tracing-indicatif" = rec { + crateName = "tracing-indicatif"; + version = "0.3.9"; + edition = "2021"; + sha256 = "188anka0xqrjbd7bzj41854rqvf02qszs9l2jzpr7n0c1r1wl0c2"; + libName = "tracing_indicatif"; + dependencies = [ + { + name = "indicatif"; + packageId = "indicatif"; + features = [ "in_memory" ]; + } + { + name = "tracing"; + packageId = "tracing"; + } + { + name = "tracing-core"; + packageId = "tracing-core"; + } + { + name = "tracing-subscriber"; + packageId = "tracing-subscriber"; + } + ]; + + }; "tracing-log" = rec { crateName = "tracing-log"; version = "0.2.0"; @@ -13712,7 +13845,7 @@ rec { features = { }; }; - "unicode-width" = rec { + "unicode-width 0.1.13" = rec { crateName = "unicode-width"; version = "0.1.13"; edition = "2021"; @@ -13730,6 +13863,25 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; + "unicode-width 0.2.0" = rec { + crateName = "unicode-width"; + version = "0.2.0"; + edition = "2021"; + sha256 = "1zd0r5vs52ifxn25rs06gxrgz8cmh4xpra922k0xlmrchib1kj0z"; + libName = "unicode_width"; + authors = [ + "kwantam " + "Manish Goregaokar " + ]; + features = { + "compiler_builtins" = [ "dep:compiler_builtins" ]; + "core" = [ "dep:core" ]; + "default" = [ "cjk" ]; + "rustc-dep-of-std" = [ "std" "core" "compiler_builtins" ]; + "std" = [ "dep:std" ]; + }; + resolvedDefaultFeatures = [ "cjk" "default" ]; + }; "unicode-xid" = rec { crateName = "unicode-xid"; version = "0.2.5"; @@ -14080,7 +14232,41 @@ rec { ]; }; - "vte" = rec { + "vt100" = rec { + crateName = "vt100"; + version = "0.15.2"; + edition = "2021"; + sha256 = "1pklc8y984axmxr0cd363srr2d27wd5rj15xlcmkjznvy0xqdkc4"; + authors = [ + "Jesse Luehrs " + ]; + dependencies = [ + { + name = "itoa"; + packageId = "itoa"; + } + { + name = "log"; + packageId = "log"; + } + { + name = "unicode-width"; + packageId = "unicode-width 0.1.13"; + } + { + name = "vte"; + packageId = "vte 0.11.1"; + } + ]; + devDependencies = [ + { + name = "vte"; + packageId = "vte 0.11.1"; + } + ]; + + }; + "vte 0.10.1" = rec { crateName = "vte"; version = "0.10.1"; edition = "2018"; @@ -14092,7 +14278,7 @@ rec { dependencies = [ { name = "arrayvec"; - packageId = "arrayvec"; + packageId = "arrayvec 0.5.2"; optional = true; usesDefaultFeatures = false; } @@ -14113,6 +14299,42 @@ rec { }; resolvedDefaultFeatures = [ "arrayvec" "default" "no_std" ]; }; + "vte 0.11.1" = rec { + crateName = "vte"; + version = "0.11.1"; + edition = "2021"; + sha256 = "15r1ff4j8ndqj9vsyil3wqwxhhl7jsz5g58f31n0h1wlpxgjn0pm"; + authors = [ + "Joe Wilm " + "Christian Duerr " + ]; + dependencies = [ + { + name = "arrayvec"; + packageId = "arrayvec 0.7.6"; + optional = true; + usesDefaultFeatures = false; + } + { + name = "utf8parse"; + packageId = "utf8parse"; + } + { + name = "vte_generate_state_changes"; + packageId = "vte_generate_state_changes"; + } + ]; + features = { + "ansi" = [ "log" ]; + "arrayvec" = [ "dep:arrayvec" ]; + "default" = [ "no_std" ]; + "log" = [ "dep:log" ]; + "nightly" = [ "utf8parse/nightly" ]; + "no_std" = [ "arrayvec" ]; + "serde" = [ "dep:serde" ]; + }; + resolvedDefaultFeatures = [ "arrayvec" "default" "no_std" ]; + }; "vte_generate_state_changes" = rec { crateName = "vte_generate_state_changes"; version = "0.1.2"; @@ -14999,7 +15221,7 @@ rec { dependencies = [ { name = "windows-sys"; - packageId = "windows-sys 0.48.0"; + packageId = "windows-sys 0.59.0"; target = { target, features }: (target."windows" or false); features = [ "Win32_Foundation" "Win32_Storage_FileSystem" "Win32_System_Console" "Win32_System_SystemInformation" ]; } @@ -15409,7 +15631,7 @@ rec { "Win32_Web" = [ "Win32" ]; "Win32_Web_InternetExplorer" = [ "Win32_Web" ]; }; - resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Globalization" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Com" "Win32_System_Console" "Win32_System_SystemInformation" "Win32_UI" "Win32_UI_Shell" "default" ]; + resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Globalization" "Win32_System" "Win32_System_Com" "Win32_UI" "Win32_UI_Shell" "default" ]; }; "windows-sys 0.52.0" = rec { crateName = "windows-sys"; @@ -15916,7 +16138,7 @@ rec { "Win32_Web" = [ "Win32" ]; "Win32_Web_InternetExplorer" = [ "Win32_Web" ]; }; - resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_System" "Win32_System_IO" "default" ]; + resolvedDefaultFeatures = [ "Win32" "Win32_Foundation" "Win32_Networking" "Win32_Networking_WinSock" "Win32_Storage" "Win32_Storage_FileSystem" "Win32_System" "Win32_System_Console" "Win32_System_IO" "Win32_System_SystemInformation" "default" ]; }; "windows-targets 0.48.5" = rec { crateName = "windows-targets"; diff --git a/Cargo.toml b/Cargo.toml index 7c48e18d..800e4df1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ directories = "5.0" dotenvy = "0.15" futures = "0.3" indexmap = { version = "2.2", features = ["serde"] } +indicatif = "0.17.11" k8s-openapi = { version = "0.24", default-features = false, features = ["v1_32"] } kube = { version = "0.99", default-features = false, features = ["client", "rustls-tls", "ws", "socks5", "http-proxy"] } lazy_static = "1.5" @@ -54,6 +55,7 @@ termion = "4.0" tokio = { version = "1.38", features = ["rt-multi-thread", "macros", "fs", "process", "io-std"] } tower-http = { version = "0.5", features = ["validate-request"] } tracing = "0.1" +tracing-indicatif = "0.3.9" tracing-subscriber = "0.3" url = "2.5" urlencoding = "2.1.3" diff --git a/rust/stackable-cockpit/Cargo.toml b/rust/stackable-cockpit/Cargo.toml index 87380fb8..1decc06f 100644 --- a/rust/stackable-cockpit/Cargo.toml +++ b/rust/stackable-cockpit/Cargo.toml @@ -31,11 +31,13 @@ stackable-operator.workspace = true tera.workspace = true tokio.workspace = true tracing.workspace = true +tracing-indicatif.workspace = true url.workspace = true urlencoding.workspace = true utoipa = { workspace = true, optional = true } which.workspace = true futures.workspace = true +indicatif.workspace = true [dev-dependencies] rstest.workspace = true diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index cd219166..c43578d3 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -3,7 +3,8 @@ use std::fmt::Display; use serde::{Deserialize, Serialize}; use snafu::{ResultExt, Snafu}; use tokio::task::block_in_place; -use tracing::{debug, error, info, instrument}; +use tracing::{Span, debug, error, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use url::Url; use crate::{ @@ -183,7 +184,7 @@ pub struct ChartVersion<'a> { /// /// This function expects the fully qualified Helm release name. In case of our /// operators this is: `-operator`. -#[instrument(skip(values_yaml), fields(with_values = values_yaml.is_some()))] +#[instrument(skip(values_yaml), fields(with_values = values_yaml.is_some(), indicatif.pb_show = true))] pub fn install_release_from_repo_or_registry( release_name: &str, ChartVersion { @@ -199,6 +200,7 @@ pub fn install_release_from_repo_or_registry( // but that requires a larger refactoring block_in_place(|| { debug!("Install Helm release from repo"); + Span::current().pb_set_message(format!("Installing {chart_name} Helm chart").as_str()); if check_release_exists(release_name, namespace)? { let release = get_release(release_name, namespace)?.ok_or(Error::InstallRelease { @@ -297,13 +299,14 @@ fn install_release( /// /// This function expects the fully qualified Helm release name. In case of our /// operators this is: `-operator`. -#[instrument] +#[instrument(fields(indicatif.pb_show = true))] pub fn uninstall_release( release_name: &str, namespace: &str, suppress_output: bool, ) -> Result { debug!("Uninstall Helm release"); + Span::current().pb_set_message(format!("Uninstalling {release_name}-operator").as_str()); if check_release_exists(release_name, namespace)? { let result = helm_sys::uninstall_helm_release(release_name, namespace, suppress_output); diff --git a/rust/stackable-cockpit/src/lib.rs b/rust/stackable-cockpit/src/lib.rs index e572b560..ef1b50b4 100644 --- a/rust/stackable-cockpit/src/lib.rs +++ b/rust/stackable-cockpit/src/lib.rs @@ -1,3 +1,7 @@ +use std::sync::LazyLock; + +use indicatif::ProgressStyle; + pub mod common; pub mod constants; pub mod engine; @@ -6,3 +10,13 @@ pub mod oci; pub mod platform; pub mod utils; pub mod xfer; + +pub static PROGRESS_BAR_STYLE: LazyLock = LazyLock::new(|| { + ProgressStyle::with_template("{span_child_prefix}Progress: {wide_bar} {pos}/{len}") + .expect("valid progress template") +}); + +pub static PROGRESS_SPINNER_STYLE: LazyLock = LazyLock::new(|| { + ProgressStyle::with_template("{span_child_prefix}{spinner} {msg}") + .expect("valid progress template") +}); diff --git a/rust/stackable-cockpit/src/platform/demo/spec.rs b/rust/stackable-cockpit/src/platform/demo/spec.rs index 75efd265..e2903516 100644 --- a/rust/stackable-cockpit/src/platform/demo/spec.rs +++ b/rust/stackable-cockpit/src/platform/demo/spec.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; -use tracing::{debug, info, instrument, warn}; +use tracing::{Span, debug, info, instrument, warn}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; #[cfg(feature = "openapi")] use utoipa::ToSchema; @@ -181,6 +182,7 @@ impl DemoSpec { stack_name = %self.stack, operator_namespace = %install_params.operator_namespace, demo_namespace = %install_params.demo_namespace, + indicatif.pb_show = true ))] async fn prepare_manifests( &self, @@ -189,6 +191,7 @@ impl DemoSpec { transfer_client: &xfer::Client, ) -> Result<(), Error> { info!("Installing demo manifests"); + Span::current().pb_set_message("Installing manifests"); let params = install_params .parameters diff --git a/rust/stackable-cockpit/src/platform/manifests.rs b/rust/stackable-cockpit/src/platform/manifests.rs index 7a5df63a..c1b3a566 100644 --- a/rust/stackable-cockpit/src/platform/manifests.rs +++ b/rust/stackable-cockpit/src/platform/manifests.rs @@ -2,9 +2,11 @@ use std::collections::HashMap; use snafu::{ResultExt, Snafu}; use stackable_operator::kvp::Labels; -use tracing::{debug, info, instrument}; +use tracing::{Span, debug, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use crate::{ + PROGRESS_BAR_STYLE, common::manifest::ManifestSpec, helm, utils::{ @@ -62,7 +64,7 @@ pub enum Error { pub trait InstallManifestsExt { // TODO (Techassi): This step shouldn't care about templating the manifests nor fetching them from remote - #[instrument(skip_all, fields(%namespace))] + #[instrument(skip_all, fields(%namespace, indicatif.pb_show = true))] #[allow(async_fn_in_trait)] async fn install_manifests( manifests: &[ManifestSpec], @@ -74,12 +76,18 @@ pub trait InstallManifestsExt { ) -> Result<(), Error> { debug!("Installing manifests"); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); + Span::current().pb_set_length(manifests.len() as u64); + let mut parameters = parameters.clone(); // We add the NAMESPACE parameter, so that stacks/demos can use that to render e.g. the // fqdn service names [which contain the namespace]. parameters.insert("NAMESPACE".to_owned(), namespace.to_owned()); for manifest in manifests { + let parameters = parameters.clone(); + let labels = labels.clone(); + match manifest { ManifestSpec::HelmChart(helm_file) => { debug!(helm_file, "Installing manifest from Helm chart"); @@ -142,9 +150,11 @@ pub trait InstallManifestsExt { client .deploy_manifests(&manifests, namespace, labels.clone()) .await - .context(DeployManifestSnafu)? + .context(DeployManifestSnafu)?; } } + + Span::current().pb_inc(1); } Ok(()) diff --git a/rust/stackable-cockpit/src/platform/operator/mod.rs b/rust/stackable-cockpit/src/platform/operator/mod.rs index 965afe37..55d4f692 100644 --- a/rust/stackable-cockpit/src/platform/operator/mod.rs +++ b/rust/stackable-cockpit/src/platform/operator/mod.rs @@ -3,7 +3,8 @@ use std::{fmt::Display, str::FromStr}; use semver::Version; use serde::Serialize; use snafu::{ResultExt, Snafu, ensure}; -use tracing::{info, instrument}; +use tracing::{Span, info, instrument}; +use tracing_indicatif::{indicatif_println, span_ext::IndicatifSpanExt}; use crate::{ constants::{ @@ -61,10 +62,15 @@ pub struct OperatorSpec { impl Display for OperatorSpec { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}{}", self.name, match &self.version { - Some(v) => format!("={v}"), - None => "".into(), - }) + write!( + f, + "{name}{version_selector}", + name = self.name, + version_selector = match &self.version { + Some(v) => format!("={v}"), + None => "".into(), + } + ) } } @@ -179,6 +185,7 @@ impl OperatorSpec { // display for the inner type if it exists. Otherwise we gte the Debug // impl for the whole Option. version = self.version.as_ref().map(tracing::field::display), + indicatif.pb_show = true ))] pub fn install( &self, @@ -186,6 +193,8 @@ impl OperatorSpec { chart_source: &ChartSourceType, ) -> Result<(), helm::Error> { info!(operator = %self, "Installing operator"); + Span::current() + .pb_set_message(format!("Installing {name}-operator", name = self.name).as_str()); let version = self.version.as_ref().map(|v| v.to_string()); let helm_name = self.helm_name(); @@ -221,7 +230,7 @@ impl OperatorSpec { { match helm::uninstall_release(&self.helm_name(), namespace.as_ref(), true) { Ok(status) => { - println!("{status}"); + indicatif_println!("{status}"); Ok(()) } Err(err) => Err(err), diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index 1bb8be6b..e17120d2 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -4,11 +4,12 @@ use serde::{Deserialize, Serialize}; use snafu::{ResultExt, Snafu}; use tokio::task::JoinError; use tracing::{Instrument, Span, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; #[cfg(feature = "openapi")] use utoipa::ToSchema; use crate::{ - helm, + PROGRESS_BAR_STYLE, helm, platform::{ operator::{self, ChartSourceType, OperatorSpec}, product, @@ -53,6 +54,7 @@ impl ReleaseSpec { %namespace, product.included = tracing::field::Empty, product.excluded = tracing::field::Empty, + indicatif.pb_show = true ))] pub async fn install( &self, @@ -62,6 +64,7 @@ impl ReleaseSpec { chart_source: &ChartSourceType, ) -> Result<()> { info!("Installing release"); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); include_products.iter().for_each(|product| { Span::current().record("product.included", product); @@ -70,11 +73,15 @@ impl ReleaseSpec { Span::current().record("product.excluded", product); }); + let operators = self.filter_products(include_products, exclude_products); + + Span::current().pb_set_length(operators.len() as u64); + let namespace = namespace.to_string(); - futures::stream::iter(self.filter_products(include_products, exclude_products)) + futures::stream::iter(operators) .map(|(product_name, product)| { let task_span = - tracing::debug_span!("install_operator", product_name = tracing::field::Empty); + tracing::info_span!("install_operator", product_name = tracing::field::Empty); let namespace = namespace.clone(); let chart_source = chart_source.clone(); @@ -103,15 +110,21 @@ impl ReleaseSpec { ) }) .buffer_unordered(10) - .map(|res| res.context(BackgroundTaskSnafu)?) + .map(|res| { + Span::current().pb_inc(1); + res.context(BackgroundTaskSnafu)? + }) .try_collect::<()>() .await } - #[instrument(skip_all)] + #[instrument(skip_all, fields(indicatif.pb_show = true))] pub fn uninstall(&self, namespace: &str) -> Result<()> { info!("Uninstalling release"); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); + Span::current().pb_set_length(self.products.len() as u64); + for (product_name, product_spec) in &self.products { info!("Uninstalling {product_name}-operator"); @@ -122,6 +135,8 @@ impl ReleaseSpec { // Uninstall operator helm::uninstall_release(&operator.helm_name(), namespace, true) .context(HelmUninstallSnafu)?; + + Span::current().pb_inc(1); } Ok(()) diff --git a/rust/stackable-cockpit/src/platform/stack/spec.rs b/rust/stackable-cockpit/src/platform/stack/spec.rs index 5e638935..e95fb628 100644 --- a/rust/stackable-cockpit/src/platform/stack/spec.rs +++ b/rust/stackable-cockpit/src/platform/stack/spec.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use snafu::{OptionExt, ResultExt, Snafu}; -use tracing::{debug, info, instrument, log::warn}; +use tracing::{Span, debug, info, instrument, log::warn}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; #[cfg(feature = "openapi")] use utoipa::ToSchema; @@ -194,7 +195,7 @@ impl StackSpec { .await } - #[instrument(skip_all, fields(release = %self.release, %operator_namespace))] + #[instrument(skip_all, fields(release = %self.release, %operator_namespace, indicatif.pb_show = true))] pub async fn install_release( &self, release_list: release::ReleaseList, @@ -203,6 +204,7 @@ impl StackSpec { chart_source: &ChartSourceType, ) -> Result<(), Error> { info!(self.release, "Trying to install release"); + Span::current().pb_set_message("Installing operators"); // Get the release by name let release = release_list @@ -218,7 +220,7 @@ impl StackSpec { .context(InstallReleaseSnafu) } - #[instrument(skip_all)] + #[instrument(skip_all, fields(indicatif.pb_show = true))] pub async fn prepare_manifests( &self, install_params: StackInstallParameters, @@ -226,6 +228,7 @@ impl StackSpec { transfer_client: &xfer::Client, ) -> Result<(), Error> { info!("Installing stack manifests"); + Span::current().pb_set_message("Installing manifests"); let parameters = install_params .parameters diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index c87b7118..629aa9e4 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -14,7 +14,8 @@ use serde::Deserialize; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{commons::listener::Listener, kvp::Labels}; use tokio::sync::RwLock; -use tracing::info; +use tracing::{Span, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; #[cfg(doc)] use crate::utils::k8s::ListParamsExt; @@ -98,12 +99,15 @@ impl Client { /// Deploys manifests defined the in raw `manifests` YAML string. This /// method will fail if it is unable to parse the manifests, unable to /// resolve GVKs or unable to patch the dynamic objects. + #[instrument(skip_all, fields(indicatif.pb_show = true))] pub async fn deploy_manifests( &self, manifests: &str, namespace: &str, labels: Labels, ) -> Result<()> { + Span::current().pb_set_message("Installing YAML manifest"); + // TODO (Techassi): Impl IntoIterator for Labels let labels: BTreeMap = labels.into(); diff --git a/rust/stackablectl/CHANGELOG.md b/rust/stackablectl/CHANGELOG.md index 8daf172d..05f8d199 100644 --- a/rust/stackablectl/CHANGELOG.md +++ b/rust/stackablectl/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - Pass the stack/demo namespace as a templating variable `NAMESPACE` to manifests. This should unblock demos to run in all namespaces, as they can template the namespace e.g. for the FQDN of services ([#355]). +- Add progress reporting ([#376]). ### Changed @@ -20,6 +21,7 @@ All notable changes to this project will be documented in this file. [#368]: https://github.com/stackabletech/stackable-cockpit/pull/368 [#373]: https://github.com/stackabletech/stackable-cockpit/pull/373 +[#376]: https://github.com/stackabletech/stackable-cockpit/pull/376 ## [25.3.0] - 2025-03-27 diff --git a/rust/stackablectl/Cargo.toml b/rust/stackablectl/Cargo.toml index 6fa40591..6d50927f 100644 --- a/rust/stackablectl/Cargo.toml +++ b/rust/stackablectl/Cargo.toml @@ -32,7 +32,9 @@ tera.workspace = true tokio.workspace = true tracing-subscriber.workspace = true tracing.workspace = true +tracing-indicatif.workspace = true futures.workspace = true +indicatif.workspace = true termion.workspace = true urlencoding.workspace = true libc.workspace = true diff --git a/rust/stackablectl/src/cmds/demo.rs b/rust/stackablectl/src/cmds/demo.rs index 3a4f4c80..9480e3dd 100644 --- a/rust/stackablectl/src/cmds/demo.rs +++ b/rust/stackablectl/src/cmds/demo.rs @@ -19,7 +19,8 @@ use stackable_cockpit::{ xfer::{self, cache::Cache}, }; use stackable_operator::kvp::{LabelError, Labels}; -use tracing::{debug, info, instrument}; +use tracing::{Span, debug, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use crate::{ args::{CommonClusterArgs, CommonClusterArgsError, CommonNamespaceArgs}, @@ -209,9 +210,10 @@ impl DemoArgs { } /// Print out a list of demos, either as a table (plain), JSON or YAML -#[instrument(skip_all)] +#[instrument(skip_all, fields(indicatif.pb_show = true))] async fn list_cmd(args: &DemoListArgs, cli: &Cli, list: demo::List) -> Result { info!("Listing demos"); + Span::current().pb_set_message("Fetching demo information"); match args.output_type { OutputType::Plain | OutputType::Table => { @@ -257,13 +259,14 @@ async fn list_cmd(args: &DemoListArgs, cli: &Cli, list: demo::List) -> Result Result { info!(demo_name = %args.demo_name, "Describing demo"); + Span::current().pb_set_message("Fetching demo information"); let demo = list.get(&args.demo_name).ok_or(CmdError::NoSuchDemo { name: args.demo_name.clone(), @@ -316,7 +319,8 @@ async fn describe_cmd( #[instrument(skip_all, fields( demo_name = %args.demo_name, skip_release = args.skip_release, - %release_branch + %release_branch, + indicatif.pb_show = true ))] async fn install_cmd( args: &DemoInstallArgs, @@ -326,6 +330,7 @@ async fn install_cmd( release_branch: &str, ) -> Result { info!(demo_name = %args.demo_name, "Installing demo"); + Span::current().pb_set_message("Installing demo"); // Init result output and progress output let mut output = cli.result(); diff --git a/rust/stackablectl/src/cmds/operator.rs b/rust/stackablectl/src/cmds/operator.rs index 207b7df4..84c269cd 100644 --- a/rust/stackablectl/src/cmds/operator.rs +++ b/rust/stackablectl/src/cmds/operator.rs @@ -25,7 +25,8 @@ use stackable_cockpit::{ k8s::{self, Client}, }, }; -use tracing::{debug, info, instrument}; +use tracing::{Span, debug, info, instrument}; +use tracing_indicatif::{indicatif_println, span_ext::IndicatifSpanExt}; use crate::{ args::{CommonClusterArgs, CommonClusterArgsError}, @@ -186,9 +187,10 @@ impl OperatorArgs { } } -#[instrument(skip_all)] +#[instrument(skip_all, fields(indicatif.pb_show = true))] async fn list_cmd(args: &OperatorListArgs, cli: &Cli) -> Result { debug!("Listing operators"); + Span::current().pb_set_message("Fetching operator information"); // Build map which maps artifacts to a chart source let source_index_files = @@ -243,9 +245,10 @@ async fn list_cmd(args: &OperatorListArgs, cli: &Cli) -> Result Result { debug!(operator_name = %args.operator_name, "Describing operator"); + Span::current().pb_set_message("Fetching operator information"); // Build map which maps artifacts to a chart source let source_index_files = @@ -302,9 +305,10 @@ async fn describe_cmd(args: &OperatorDescribeArgs, cli: &Cli) -> Result Result { info!("Installing operator(s)"); + Span::current().pb_set_message("Installing operator(s)"); args.local_cluster .install_if_needed() @@ -327,9 +331,8 @@ async fn install_cmd(args: &OperatorInstallArgs, cli: &Cli) -> Result Result Result { info!("Uninstalling operator(s)"); + Span::current().pb_set_message("Uninstalling operator(s)"); for operator in &args.operators { operator @@ -382,9 +386,10 @@ fn uninstall_cmd(args: &OperatorUninstallArgs, cli: &Cli) -> Result Result { info!("Listing installed operators"); + Span::current().pb_set_message("Fetching operator information"); type ReleaseList = IndexMap; diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index bbaee551..fc2e68e8 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -14,7 +14,8 @@ use stackable_cockpit::{ }, xfer::{self, cache::Cache}, }; -use tracing::{debug, info, instrument}; +use tracing::{Span, debug, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use crate::{ args::{CommonClusterArgs, CommonClusterArgsError}, @@ -150,13 +151,14 @@ impl ReleaseArgs { } } -#[instrument(skip(cli, release_list))] +#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] async fn list_cmd( args: &ReleaseListArgs, cli: &Cli, release_list: release::ReleaseList, ) -> Result { info!("Listing releases"); + Span::current().pb_set_message("Fetching release information"); match args.output_type { OutputType::Plain | OutputType::Table => { @@ -204,13 +206,14 @@ async fn list_cmd( } } -#[instrument(skip(cli, release_list))] +#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] async fn describe_cmd( args: &ReleaseDescribeArgs, cli: &Cli, release_list: release::ReleaseList, ) -> Result { info!(release = %args.release, "Describing release"); + Span::current().pb_set_message("Fetching release information"); let release = release_list.get(&args.release); @@ -265,13 +268,15 @@ async fn describe_cmd( } } -#[instrument(skip(cli, release_list))] +#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] async fn install_cmd( args: &ReleaseInstallArgs, cli: &Cli, release_list: release::ReleaseList, ) -> Result { info!(release = %args.release, "Installing release"); + Span::current().pb_set_message("Installing release"); + match release_list.get(&args.release) { Some(release) => { let mut output = cli.result(); @@ -314,12 +319,14 @@ async fn install_cmd( } } -#[instrument(skip(cli, release_list))] +#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] async fn uninstall_cmd( args: &ReleaseUninstallArgs, cli: &Cli, release_list: release::ReleaseList, ) -> Result { + Span::current().pb_set_message("Uninstalling release"); + match release_list.get(&args.release) { Some(release) => { release diff --git a/rust/stackablectl/src/cmds/stack.rs b/rust/stackablectl/src/cmds/stack.rs index be78c33a..01816929 100644 --- a/rust/stackablectl/src/cmds/stack.rs +++ b/rust/stackablectl/src/cmds/stack.rs @@ -19,7 +19,8 @@ use stackable_cockpit::{ xfer::{self, cache::Cache}, }; use stackable_operator::kvp::{LabelError, Labels}; -use tracing::{debug, info, instrument}; +use tracing::{Span, debug, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use crate::{ args::{CommonClusterArgs, CommonClusterArgsError, CommonNamespaceArgs}, @@ -191,13 +192,14 @@ impl StackArgs { } } -#[instrument(skip_all)] +#[instrument(skip_all, fields(indicatif.pb_show = true))] fn list_cmd( args: &StackListArgs, cli: &Cli, stack_list: stack::StackList, ) -> Result { info!("Listing stacks"); + Span::current().pb_set_message("Fetching stack information"); match args.output_type { OutputType::Plain | OutputType::Table => { @@ -241,13 +243,14 @@ fn list_cmd( } } -#[instrument(skip_all)] +#[instrument(skip_all, fields(indicatif.pb_show = true))] fn describe_cmd( args: &StackDescribeArgs, cli: &Cli, stack_list: stack::StackList, ) -> Result { info!(stack_name = %args.stack_name, "Describing stack"); + Span::current().pb_set_message("Fetching stack information"); match stack_list.get(&args.stack_name) { Some(stack) => match args.output_type { @@ -303,7 +306,7 @@ fn describe_cmd( } } -#[instrument(skip(cli, stack_list, transfer_client))] +#[instrument(skip(cli, stack_list, transfer_client), fields(indicatif.pb_show = true))] async fn install_cmd( args: &StackInstallArgs, cli: &Cli, @@ -311,6 +314,7 @@ async fn install_cmd( transfer_client: &xfer::Client, ) -> Result { info!(stack_name = %args.stack_name, "Installing stack"); + Span::current().pb_set_message("Installing stack"); let files = cli.get_release_files().context(PathOrUrlParseSnafu)?; let release_list = release::ReleaseList::build(&files, transfer_client) diff --git a/rust/stackablectl/src/cmds/stacklet.rs b/rust/stackablectl/src/cmds/stacklet.rs index 2422f65f..332d45a0 100644 --- a/rust/stackablectl/src/cmds/stacklet.rs +++ b/rust/stackablectl/src/cmds/stacklet.rs @@ -9,7 +9,8 @@ use stackable_cockpit::{ platform::stacklet::{self, get_credentials_for_product, list_stacklets}, utils::k8s::{self, Client, DisplayCondition}, }; -use tracing::{info, instrument}; +use tracing::{Span, info, instrument}; +use tracing_indicatif::span_ext::IndicatifSpanExt as _; use crate::{ args::CommonNamespaceArgs, @@ -89,9 +90,10 @@ impl StackletArgs { } } -#[instrument(skip_all)] +#[instrument(skip_all, fields(indicatif.pb_show = true))] async fn list_cmd(args: &StackletListArgs, cli: &Cli) -> Result { info!("Listing installed stacklets"); + Span::current().pb_set_message("Fetching stacklet information"); let client = Client::new().await.context(KubeClientCreateSnafu)?; @@ -202,9 +204,10 @@ async fn list_cmd(args: &StackletListArgs, cli: &Cli) -> Result Result { info!("Displaying stacklet credentials"); + Span::current().pb_set_message("Fetching stacklet information"); let client = Client::new().await.context(KubeClientCreateSnafu)?; diff --git a/rust/stackablectl/src/main.rs b/rust/stackablectl/src/main.rs index 09bc2269..9ad159d2 100644 --- a/rust/stackablectl/src/main.rs +++ b/rust/stackablectl/src/main.rs @@ -1,8 +1,19 @@ use clap::Parser; use dotenvy::dotenv; +use stackable_cockpit::PROGRESS_SPINNER_STYLE; use stackablectl::cli::{Cli, Error}; -use tracing::metadata::LevelFilter; -use tracing_subscriber::fmt; +use tracing::{Level, metadata::LevelFilter}; +use tracing_indicatif::{ + IndicatifLayer, + filter::{IndicatifFilter, hide_indicatif_span_fields}, + indicatif_eprintln, +}; +use tracing_subscriber::{ + Layer as _, + fmt::{self, format::DefaultFields}, + layer::SubscriberExt, + util::SubscriberInitExt, +}; #[snafu::report] #[tokio::main] @@ -16,21 +27,37 @@ async fn main() -> Result<(), Error> { .without_time() .with_target(false); - tracing_subscriber::fmt() - .with_max_level(match app.log_level { - Some(level) => LevelFilter::from_level(level), - None => LevelFilter::WARN, - }) - .event_format(format) - .pretty() - .init(); + let indicatif_layer = IndicatifLayer::new() + .with_span_field_formatter( + // If the `{span_fields}` interpolation is used in a template, then we want to hide the + // indicatif control fields "indicatif.pb_show" and "indicatif.pb_hide" + hide_indicatif_span_fields(DefaultFields::new()), + ) + .with_progress_style(PROGRESS_SPINNER_STYLE.clone()); + + if let Some(level) = app.log_level { + tracing_subscriber::registry() + .with( + fmt::layer() + .event_format(format) + .pretty() + .with_writer(indicatif_layer.get_stderr_writer()), + ) + .with(LevelFilter::from_level(level)) + .init(); + } else { + tracing_subscriber::registry() + .with(LevelFilter::from_level(Level::INFO)) + .with(indicatif_layer.with_filter(IndicatifFilter::new(false))) + .init(); + } // Load env vars from optional .env file match dotenv() { Ok(_) => (), Err(err) => { if !err.not_found() { - println!("{err}") + indicatif_eprintln!("{err}") } } }