From 55e80a59eedb343c9b88735dbc645bafa3dd262d Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Thu, 15 May 2025 11:22:00 +0200 Subject: [PATCH 01/17] add release upgrade functionality --- .../src/platform/release/spec.rs | 76 ++++++++++++++++++ .../stackable-cockpit/src/utils/k8s/client.rs | 43 ++++++++++ rust/stackablectl/src/cmds/release.rs | 80 +++++++++++++++++++ 3 files changed, 199 insertions(+) diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index 1bb8be6b..1f443cda 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -13,6 +13,7 @@ use crate::{ operator::{self, ChartSourceType, OperatorSpec}, product, }, + utils::k8s::{self, Client}, }; type Result<T, E = Error> = std::result::Result<T, E>; @@ -30,6 +31,15 @@ pub enum Error { #[snafu(display("failed to launch background task"))] BackgroundTask { source: JoinError }, + + #[snafu(display("failed to deploy manifests using the kube client"))] + DeployManifest { source: k8s::Error }, + + #[snafu(display("failed to access the CRDs from source"))] + AccessCRDs { source: reqwest::Error }, + + #[snafu(display("failed to read CRD manifests"))] + ReadManifests { source: reqwest::Error }, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -108,6 +118,72 @@ impl ReleaseSpec { .await } + /// Upgrades a release by upgrading individual operators. + #[instrument(skip_all, fields( + %namespace, + ))] + pub async fn upgrade_crds( + &self, + include_products: &[String], + exclude_products: &[String], + namespace: &str, + k8s_client: &Client, + ) -> Result<()> { + debug!("Upgrading CRDs for release"); + + include_products.iter().for_each(|product| { + Span::current().record("product.included", product); + }); + exclude_products.iter().for_each(|product| { + Span::current().record("product.excluded", product); + }); + + let client = reqwest::Client::new(); + + let operators = self.filter_products(include_products, exclude_products); + + Span::current().pb_set_style( + &ProgressStyle::with_template("Upgrading CRDs {wide_bar} {pos}/{len}").unwrap(), + ); + Span::current().pb_set_length(operators.len() as u64); + + for (product_name, product) in operators { + Span::current().record("product_name", &product_name); + debug!("Upgrading CRDs for {product_name}-operator"); + + let release = match product.version.pre.as_str() { + "dev" => "main".to_string(), + _ => { + format!("{}", product.version) + } + }; + + let request_url = format!( + "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release}/deploy/helm/{product_name}-operator/crds/crds.yaml" + ); + + // Get CRD manifests + // TODO bei nicht 200 Status, Fehler werfen + let response = client + .get(request_url) + .send() + .await + .context(AccessCRDsSnafu)?; + let crd_manifests = response.text().await.context(ReadManifestsSnafu)?; + + // Upgrade CRDs + k8s_client + .replace_crds(&crd_manifests) + .await + .context(DeployManifestSnafu)?; + + debug!("Upgraded {product_name}-operator CRDs"); + Span::current().pb_inc(1); + } + + Ok(()) + } + #[instrument(skip_all)] pub fn uninstall(&self, namespace: &str) -> Result<()> { info!("Uninstalling release"); diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index c87b7118..9c0228b8 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -65,6 +65,9 @@ pub enum Error { #[snafu(display("failed to retrieve cluster information"))] ClusterInformation { source: cluster::Error }, + #[snafu(display("failed to retrieve previous resource version information"))] + ResourceVersion { source: kube::error::Error }, + #[snafu(display("invalid or empty secret data in '{secret_name}'"))] InvalidSecretData { secret_name: String }, @@ -148,6 +151,46 @@ impl Client { Ok(()) } + pub async fn replace_crds(&self, crds: &str) -> Result<()> { + for crd in serde_yaml::Deserializer::from_str(crds) { + let mut object = DynamicObject::deserialize(crd).context(DeserializeYamlSnafu)?; + + let object_type = object.types.as_ref().ok_or( + ObjectTypeSnafu { + object: object.clone(), + } + .build(), + )?; + + let gvk = Self::gvk_of_typemeta(object_type); + let (resource, _) = self + .resolve_gvk(&gvk) + .await? + .context(GVKUnkownSnafu { gvk })?; + + // CRDs are cluster scoped + let api: Api<DynamicObject> = Api::all_with(self.client.clone(), &resource); + + if let Some(resource) = api + .get_opt(&object.name_any()) + .await + .context(KubeClientFetchSnafu)? + { + object.metadata.resource_version = resource.resource_version(); + api.replace(&object.name_any(), &PostParams::default(), &object) + .await + .context(KubeClientPatchSnafu)?; + } else { + // Create CRD if a previous version wasn't found + api.create(&PostParams::default(), &object) + .await + .context(KubeClientCreateSnafu)?; + } + } + + Ok(()) + } + /// Lists objects by looking up a GVK via the discovery. It returns an /// optional list of dynamic objects. The method returns [`Ok(None)`] /// if the client was unable to resolve the GVK. An error is returned diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index bbaee551..0a386cb7 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -44,6 +44,9 @@ pub enum ReleaseCommands { /// Uninstall a release #[command(aliases(["rm", "un"]))] Uninstall(ReleaseUninstallArgs), + + // Upgrade a release + Upgrade(ReleaseUpgradeArgs), } #[derive(Debug, Args)] @@ -83,6 +86,25 @@ pub struct ReleaseInstallArgs { local_cluster: CommonClusterArgs, } +#[derive(Debug, Args)] +pub struct ReleaseUpgradeArgs { + /// Release to upgrade to + #[arg(name = "RELEASE")] + release: String, + + /// List of product operators to upgrade + #[arg(short, long = "include", group = "products")] + included_products: Vec<String>, + + /// Blacklist of product operators to install + #[arg(short, long = "exclude", group = "products")] + excluded_products: Vec<String>, + + /// Namespace in the cluster used to deploy the operators + #[arg(long, default_value = DEFAULT_OPERATOR_NAMESPACE, visible_aliases(["operator-ns"]))] + pub operator_namespace: String, +} + #[derive(Debug, Args)] pub struct ReleaseUninstallArgs { /// Name of the release to uninstall @@ -111,6 +133,9 @@ pub enum CmdError { #[snafu(display("failed to install release"))] ReleaseInstall { source: release::Error }, + #[snafu(display("failed to upgrade CRDs for release"))] + CrdUpgrade { source: release::Error }, + #[snafu(display("failed to uninstall release"))] ReleaseUninstall { source: release::Error }, @@ -146,6 +171,7 @@ impl ReleaseArgs { ReleaseCommands::Describe(args) => describe_cmd(args, cli, release_list).await, ReleaseCommands::Install(args) => install_cmd(args, cli, release_list).await, ReleaseCommands::Uninstall(args) => uninstall_cmd(args, cli, release_list).await, + ReleaseCommands::Upgrade(args) => upgrade_cmd(args, cli, release_list).await, } } } @@ -314,6 +340,60 @@ async fn install_cmd( } } +#[instrument(skip(cli, release_list))] +async fn upgrade_cmd( + args: &ReleaseUpgradeArgs, + cli: &Cli, + release_list: release::ReleaseList, +) -> Result<String, CmdError> { + debug!(release = %args.release, "Upgrading release"); + Span::current().pb_set_style(&ProgressStyle::with_template("").unwrap()); + + match release_list.get(&args.release) { + Some(release) => { + let mut output = cli.result(); + let client = Client::new().await.context(KubeClientCreateSnafu)?; + + // Uninstall the old operator release first + release + .uninstall(&args.operator_namespace) + .context(ReleaseUninstallSnafu)?; + + // Upgrade the CRDs for all the operators to be upgraded + release + .upgrade_crds( + &args.included_products, + &args.excluded_products, + &args.operator_namespace, + &client, + ) + .await + .context(CrdUpgradeSnafu)?; + + // Install the new operator release + release + .install( + &args.included_products, + &args.excluded_products, + &args.operator_namespace, + &ChartSourceType::from(cli.chart_type()), + ) + .await + .context(ReleaseInstallSnafu)?; + + output + .with_command_hint( + "stackablectl operator installed", + "list installed operators", + ) + .with_output(format!("Upgraded to release '{}'", args.release)); + + Ok(output.render()) + } + None => Ok("No such release".into()), + } +} + #[instrument(skip(cli, release_list))] async fn uninstall_cmd( args: &ReleaseUninstallArgs, From 20a3ef032d7f54abc2ad45bf44e4d81ba50a85c4 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Wed, 21 May 2025 16:35:02 +0200 Subject: [PATCH 02/17] error handling and include/exclude operators in/from upgrading --- .../src/platform/release/spec.rs | 43 +++++++++++-------- .../stackable-cockpit/src/utils/k8s/client.rs | 13 +++--- rust/stackablectl/src/cmds/release.rs | 13 ++++-- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index 1f443cda..1e738cb6 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -35,6 +35,9 @@ pub enum Error { #[snafu(display("failed to deploy manifests using the kube client"))] DeployManifest { source: k8s::Error }, + #[snafu(display("failed to construct a valid request"))] + ConstructRequest { source: reqwest::Error }, + #[snafu(display("failed to access the CRDs from source"))] AccessCRDs { source: reqwest::Error }, @@ -129,7 +132,7 @@ impl ReleaseSpec { namespace: &str, k8s_client: &Client, ) -> Result<()> { - debug!("Upgrading CRDs for release"); + info!("Upgrading CRDs for release"); include_products.iter().for_each(|product| { Span::current().record("product.included", product); @@ -139,19 +142,12 @@ impl ReleaseSpec { }); let client = reqwest::Client::new(); - let operators = self.filter_products(include_products, exclude_products); - Span::current().pb_set_style( - &ProgressStyle::with_template("Upgrading CRDs {wide_bar} {pos}/{len}").unwrap(), - ); - Span::current().pb_set_length(operators.len() as u64); - for (product_name, product) in operators { - Span::current().record("product_name", &product_name); - debug!("Upgrading CRDs for {product_name}-operator"); + info!("Upgrading CRDs for {product_name}-operator"); - let release = match product.version.pre.as_str() { + let release_branch = match product.version.pre.as_str() { "dev" => "main".to_string(), _ => { format!("{}", product.version) @@ -159,16 +155,18 @@ impl ReleaseSpec { }; let request_url = format!( - "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release}/deploy/helm/{product_name}-operator/crds/crds.yaml" + "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release_branch}/deploy/helm/{product_name}-operator/crds/crds.yaml" ); - // Get CRD manifests - // TODO bei nicht 200 Status, Fehler werfen + // Get CRD manifests from request_url let response = client .get(request_url) .send() .await + .context(ConstructRequestSnafu)? + .error_for_status() .context(AccessCRDsSnafu)?; + let crd_manifests = response.text().await.context(ReadManifestsSnafu)?; // Upgrade CRDs @@ -177,18 +175,29 @@ impl ReleaseSpec { .await .context(DeployManifestSnafu)?; - debug!("Upgraded {product_name}-operator CRDs"); - Span::current().pb_inc(1); + info!("Upgraded {product_name}-operator CRDs"); } Ok(()) } #[instrument(skip_all)] - pub fn uninstall(&self, namespace: &str) -> Result<()> { + pub fn uninstall(&self, + include_products: &[String], + exclude_products: &[String], + namespace: &str) -> Result<()> { info!("Uninstalling release"); - for (product_name, product_spec) in &self.products { + include_products.iter().for_each(|product| { + Span::current().record("product.included", product); + }); + exclude_products.iter().for_each(|product| { + Span::current().record("product.excluded", product); + }); + + let operators = self.filter_products(include_products, exclude_products); + + for (product_name, product_spec) in operators { info!("Uninstalling {product_name}-operator"); // Create operator spec diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 9c0228b8..471dacd0 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -37,6 +37,9 @@ pub enum Error { #[snafu(display("failed to patch/create Kubernetes object"))] KubeClientPatch { source: kube::error::Error }, + #[snafu(display("failed to replace Kubernetes object"))] + KubeClientReplace { source: kube::error::Error }, + #[snafu(display("failed to deserialize YAML data"))] DeserializeYaml { source: serde_yaml::Error }, @@ -65,9 +68,6 @@ pub enum Error { #[snafu(display("failed to retrieve cluster information"))] ClusterInformation { source: cluster::Error }, - #[snafu(display("failed to retrieve previous resource version information"))] - ResourceVersion { source: kube::error::Error }, - #[snafu(display("invalid or empty secret data in '{secret_name}'"))] InvalidSecretData { secret_name: String }, @@ -151,6 +151,9 @@ impl Client { Ok(()) } + /// Replaces CRDs defined the in raw `crds` YAML string. This + /// method will fail if it is unable to parse the CRDs, unable to + /// resolve GVKs or unable to replace/create the dynamic objects. pub async fn replace_crds(&self, crds: &str) -> Result<()> { for crd in serde_yaml::Deserializer::from_str(crds) { let mut object = DynamicObject::deserialize(crd).context(DeserializeYamlSnafu)?; @@ -179,12 +182,12 @@ impl Client { object.metadata.resource_version = resource.resource_version(); api.replace(&object.name_any(), &PostParams::default(), &object) .await - .context(KubeClientPatchSnafu)?; + .context(KubeClientReplaceSnafu)?; } else { // Create CRD if a previous version wasn't found api.create(&PostParams::default(), &object) .await - .context(KubeClientCreateSnafu)?; + .context(KubeClientPatchSnafu)?; } } diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 0a386cb7..11b981d9 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -346,8 +346,7 @@ async fn upgrade_cmd( cli: &Cli, release_list: release::ReleaseList, ) -> Result<String, CmdError> { - debug!(release = %args.release, "Upgrading release"); - Span::current().pb_set_style(&ProgressStyle::with_template("").unwrap()); + info!(release = %args.release, "Upgrading release"); match release_list.get(&args.release) { Some(release) => { @@ -356,7 +355,10 @@ async fn upgrade_cmd( // Uninstall the old operator release first release - .uninstall(&args.operator_namespace) + .uninstall( + &args.included_products, + &args.excluded_products, + &args.operator_namespace) .context(ReleaseUninstallSnafu)?; // Upgrade the CRDs for all the operators to be upgraded @@ -403,7 +405,10 @@ async fn uninstall_cmd( match release_list.get(&args.release) { Some(release) => { release - .uninstall(&args.operator_namespace) + .uninstall( + &Vec::new(), + &Vec::new(), + &args.operator_namespace) .context(ReleaseUninstallSnafu)?; let mut result = cli.result(); From 45ab412ed6d10d32a39638b33792f540726859d3 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Thu, 22 May 2025 08:36:32 +0200 Subject: [PATCH 03/17] docs and autogenerated parts --- .../stackablectl/pages/commands/release.adoc | 68 ++++-- .../partials/commands/release.adoc | 1 + extra/completions/_stackablectl | 55 +++++ extra/completions/stackablectl.bash | 195 +++++++++++++++++- extra/completions/stackablectl.elv | 32 +++ extra/completions/stackablectl.fish | 49 +++-- extra/completions/stackablectl.nu | 32 +++ .../src/platform/release/spec.rs | 6 +- rust/stackablectl/src/cmds/release.rs | 12 +- 9 files changed, 403 insertions(+), 47 deletions(-) diff --git a/docs/modules/stackablectl/pages/commands/release.adoc b/docs/modules/stackablectl/pages/commands/release.adoc index fb3c750e..a1ef3be1 100644 --- a/docs/modules/stackablectl/pages/commands/release.adoc +++ b/docs/modules/stackablectl/pages/commands/release.adoc @@ -14,25 +14,35 @@ To list the available Stackable releases run the following command: [source,console] ---- $ stackablectl release list -┌───┬─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────┐ -│ # ┆ RELEASE ┆ RELEASE DATE ┆ DESCRIPTION │ -╞═══╪═════════╪══════════════╪═════════════════════════════════════════════════════════════════════════════╡ -│ 1 ┆ 23.7 ┆ 2023-07-26 ┆ Sixth release focusing on resources and pod overrides │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 2 ┆ 23.4 ┆ 2023-05-17 ┆ Fifth release focusing on affinities and product status │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 3 ┆ 23.1 ┆ 2023-01-27 ┆ Fourth release focusing on image selection and logging │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 4 ┆ 22.11 ┆ 2022-11-14 ┆ Third release focusing on resource management │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 5 ┆ 22.09 ┆ 2022-09-09 ┆ Second release focusing on security and OpenShift support │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 6 ┆ 22.06 ┆ 2022-06-30 ┆ First official release of the Stackable Data Platform │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 7 ┆ latest ┆ 2023-07-26 ┆ Always pointing to the latest stable version of the Stackable Data Platform │ -├╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ -│ 8 ┆ dev ┆ 2023-01-27 ┆ Development versions from main branch. Not stable! │ -└───┴─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────┘ +┌────┬─────────┬──────────────┬─────────────────────────────────────────────────────────────────────────────────┐ +│ # ┆ RELEASE ┆ RELEASE DATE ┆ DESCRIPTION │ +╞════╪═════════╪══════════════╪═════════════════════════════════════════════════════════════════════════════════╡ +│ 1 ┆ 25.3 ┆ 2025-03-24 ┆ The March 2025 release │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 2 ┆ 24.11 ┆ 2024-11-18 ┆ The November 2024 release │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 3 ┆ 24.7 ┆ 2024-07-24 ┆ Security focused release to reduce CVEs and to build product images from source │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 4 ┆ 24.3 ┆ 2024-03-20 ┆ Security focused release to improve platform authentication and authorization │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 5 ┆ 23.11 ┆ 2023-11-30 ┆ Seventh release focusing on PodDisruptionBudgets and graceful shutdown │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 6 ┆ 23.7 ┆ 2023-07-26 ┆ Sixth release focusing on resources and pod overrides │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 7 ┆ 23.4 ┆ 2023-05-17 ┆ Fifth release focusing on affinities and product status │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 8 ┆ 23.1 ┆ 2023-01-27 ┆ Fourth release focusing on image selection and logging │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 9 ┆ 22.11 ┆ 2022-11-14 ┆ Third release focusing on resource management │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 10 ┆ 22.09 ┆ 2022-09-09 ┆ Second release focusing on security and OpenShift support │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 11 ┆ 22.06 ┆ 2022-06-30 ┆ First official release of the Stackable Data Platform │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 12 ┆ latest ┆ 2025-03-24 ┆ Always pointing to the latest stable version of the Stackable Data Platform │ +├╌╌╌╌┼╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┤ +│ 13 ┆ dev ┆ 1970-01-01 ┆ Development versions from main branch. Not stable! │ +└────┴─────────┴──────────────┴─────────────────────────────────────────────────────────────────────────────────┘ ---- Detailed information of a release can be queried with the `stackablectl release describe` command: @@ -128,3 +138,23 @@ Installed product trino=23.7.0 Installed product zookeeper=23.7.0 Installed release 23.7 ---- + +== Upgrading Releases + +As described in the xref:home::release-notes.adoc[Upgrade sections of the Release Notes], the upgrade process can be achieved by the following three steps: + +. Uninstalling the previous release with `stackablectl release uninstall <RELEASE>` +. Replacing the CRDs with `kubectl replace` +. Installing the next release with `stackablectl release install <RELEASE>` + +For convenience `stackablectl` also provides an upgrade functionality which executes those steps by itself. +To upgrade a release, run the following command: + +[source,console] +---- +$ stackablectl release upgrade 25.3 + +Upgraded to release '25.3' + +Use "stackablectl operator installed" to list installed operators. +---- diff --git a/docs/modules/stackablectl/partials/commands/release.adoc b/docs/modules/stackablectl/partials/commands/release.adoc index a2030bd4..eac12e67 100644 --- a/docs/modules/stackablectl/partials/commands/release.adoc +++ b/docs/modules/stackablectl/partials/commands/release.adoc @@ -10,6 +10,7 @@ Commands: describe Print out detailed release information install Install a specific release uninstall Uninstall a release + upgrade Upgrade a release help Print this message or the help of the given subcommand(s) Options: diff --git a/extra/completions/_stackablectl b/extra/completions/_stackablectl index 25c69764..768a339a 100644 --- a/extra/completions/_stackablectl +++ b/extra/completions/_stackablectl @@ -418,6 +418,35 @@ repo\:"index.yaml-based repositories\: resolution (dev, test, stable) is based o ':RELEASE -- Name of the release to uninstall:' \ && ret=0 ;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +'*-i+[List of product operators to upgrade]:INCLUDED_PRODUCTS: ' \ +'*--include=[List of product operators to upgrade]:INCLUDED_PRODUCTS: ' \ +'*-e+[Blacklist of product operators to install]:EXCLUDED_PRODUCTS: ' \ +'*--exclude=[Blacklist of product operators to install]:EXCLUDED_PRODUCTS: ' \ +'--operator-namespace=[Namespace in the cluster used to deploy the operators]:OPERATOR_NAMESPACE: ' \ +'--operator-ns=[Namespace in the cluster used to deploy the operators]:OPERATOR_NAMESPACE: ' \ +'-l+[Log level this application uses]:LOG_LEVEL: ' \ +'--log-level=[Log level this application uses]:LOG_LEVEL: ' \ +'*-d+[Provide one or more additional (custom) demo file(s)]:DEMO_FILE:_files' \ +'*--demo-file=[Provide one or more additional (custom) demo file(s)]:DEMO_FILE:_files' \ +'*-s+[Provide one or more additional (custom) stack file(s)]:STACK_FILE:_files' \ +'*--stack-file=[Provide one or more additional (custom) stack file(s)]:STACK_FILE:_files' \ +'*-r+[Provide one or more additional (custom) release file(s)]:RELEASE_FILE:_files' \ +'*--release-file=[Provide one or more additional (custom) release file(s)]:RELEASE_FILE:_files' \ +'--helm-repo-stable=[Provide a custom Helm stable repository URL]:URL:_urls' \ +'--helm-repo-test=[Provide a custom Helm test repository URL]:URL:_urls' \ +'--helm-repo-dev=[Provide a custom Helm dev repository URL]:URL:_urls' \ +'--chart-source=[Source the charts from either a OCI registry or from index.yaml-based repositories]:CHART_SOURCE:((oci\:"OCI registry" +repo\:"index.yaml-based repositories\: resolution (dev, test, stable) is based on the version and thus will be operator-specific"))' \ +'--no-cache[Do not cache the remote (default) demo, stack and release files]' \ +'-h[Print help (see more with '\''--help'\'')]' \ +'--help[Print help (see more with '\''--help'\'')]' \ +'-V[Print version]' \ +'--version[Print version]' \ +':RELEASE -- Upgrade to the specified release:' \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ ":: :_stackablectl__release__help_commands" \ @@ -446,6 +475,10 @@ _arguments "${_arguments_options[@]}" : \ _arguments "${_arguments_options[@]}" : \ && ret=0 ;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 +;; (help) _arguments "${_arguments_options[@]}" : \ && ret=0 @@ -1315,6 +1348,10 @@ _arguments "${_arguments_options[@]}" : \ (uninstall) _arguments "${_arguments_options[@]}" : \ && ret=0 +;; +(upgrade) +_arguments "${_arguments_options[@]}" : \ +&& ret=0 ;; esac ;; @@ -1820,6 +1857,7 @@ _stackablectl__help__release_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ ) _describe -t commands 'stackablectl help release commands' commands "$@" } @@ -1843,6 +1881,11 @@ _stackablectl__help__release__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl help release uninstall commands' commands "$@" } +(( $+functions[_stackablectl__help__release__upgrade_commands] )) || +_stackablectl__help__release__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl help release upgrade commands' commands "$@" +} (( $+functions[_stackablectl__help__stack_commands] )) || _stackablectl__help__stack_commands() { local commands; commands=( @@ -1971,6 +2014,7 @@ _stackablectl__release_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'stackablectl release commands' commands "$@" @@ -1987,6 +2031,7 @@ _stackablectl__release__help_commands() { 'describe:Print out detailed release information' \ 'install:Install a specific release' \ 'uninstall:Uninstall a release' \ +'upgrade:Upgrade a release' \ 'help:Print this message or the help of the given subcommand(s)' \ ) _describe -t commands 'stackablectl release help commands' commands "$@" @@ -2016,6 +2061,11 @@ _stackablectl__release__help__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl release help uninstall commands' commands "$@" } +(( $+functions[_stackablectl__release__help__upgrade_commands] )) || +_stackablectl__release__help__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl release help upgrade commands' commands "$@" +} (( $+functions[_stackablectl__release__install_commands] )) || _stackablectl__release__install_commands() { local commands; commands=() @@ -2031,6 +2081,11 @@ _stackablectl__release__uninstall_commands() { local commands; commands=() _describe -t commands 'stackablectl release uninstall commands' commands "$@" } +(( $+functions[_stackablectl__release__upgrade_commands] )) || +_stackablectl__release__upgrade_commands() { + local commands; commands=() + _describe -t commands 'stackablectl release upgrade commands' commands "$@" +} (( $+functions[_stackablectl__stack_commands] )) || _stackablectl__stack_commands() { local commands; commands=( diff --git a/extra/completions/stackablectl.bash b/extra/completions/stackablectl.bash index 095ed06b..bd247a0c 100644 --- a/extra/completions/stackablectl.bash +++ b/extra/completions/stackablectl.bash @@ -201,6 +201,9 @@ _stackablectl() { stackablectl__help__release,uninstall) cmd="stackablectl__help__release__uninstall" ;; + stackablectl__help__release,upgrade) + cmd="stackablectl__help__release__upgrade" + ;; stackablectl__help__stack,describe) cmd="stackablectl__help__stack__describe" ;; @@ -267,6 +270,9 @@ _stackablectl() { stackablectl__release,uninstall) cmd="stackablectl__release__uninstall" ;; + stackablectl__release,upgrade) + cmd="stackablectl__release__upgrade" + ;; stackablectl__release__help,describe) cmd="stackablectl__release__help__describe" ;; @@ -282,6 +288,9 @@ _stackablectl() { stackablectl__release__help,uninstall) cmd="stackablectl__release__help__uninstall" ;; + stackablectl__release__help,upgrade) + cmd="stackablectl__release__help__upgrade" + ;; stackablectl__stack,describe) cmd="stackablectl__stack__describe" ;; @@ -2883,7 +2892,7 @@ _stackablectl() { return 0 ;; stackablectl__help__release) - opts="list describe install uninstall" + opts="list describe install uninstall upgrade" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2952,6 +2961,20 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__help__release__upgrade) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; stackablectl__help__stack) opts="list describe install" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -3985,7 +4008,7 @@ _stackablectl() { return 0 ;; stackablectl__release) - opts="-l -d -s -r -h -V --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install uninstall help" + opts="-l -d -s -r -h -V --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install uninstall upgrade help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4249,7 +4272,7 @@ _stackablectl() { return 0 ;; stackablectl__release__help) - opts="list describe install uninstall help" + opts="list describe install uninstall upgrade help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -4332,6 +4355,20 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__release__help__upgrade) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 4 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; stackablectl__release__install) opts="-i -e -c -l -d -s -r -h -V --include --exclude --operator-ns --operator-namespace --cluster --cluster-name --cluster-nodes --cluster-cp-nodes --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version <RELEASE>" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -4776,6 +4813,158 @@ _stackablectl() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + stackablectl__release__upgrade) + opts="-i -e -l -d -s -r -h -V --include --exclude --operator-ns --operator-namespace --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version <RELEASE>" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --include) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -i) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --exclude) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -e) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --operator-namespace) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --operator-ns) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -l) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --demo-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -d) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --stack-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -s) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --release-file) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + -r) + local oldifs + if [ -n "${IFS+x}" ]; then + oldifs="$IFS" + fi + IFS=$'\n' + COMPREPLY=($(compgen -f "${cur}")) + if [ -n "${oldifs+x}" ]; then + IFS="$oldifs" + fi + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o filenames + fi + return 0 + ;; + --helm-repo-stable) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --helm-repo-test) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --helm-repo-dev) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --chart-source) + COMPREPLY=($(compgen -W "oci repo" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; stackablectl__stack) opts="-l -d -s -r -h -V --release --log-level --no-cache --demo-file --stack-file --release-file --helm-repo-stable --helm-repo-test --helm-repo-dev --chart-source --help --version list describe install help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/extra/completions/stackablectl.elv b/extra/completions/stackablectl.elv index f943defe..2e55ca7d 100644 --- a/extra/completions/stackablectl.elv +++ b/extra/completions/stackablectl.elv @@ -224,6 +224,7 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' cand help 'Print this message or the help of the given subcommand(s)' } &'stackablectl;release;list'= { @@ -319,11 +320,37 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand -V 'Print version' cand --version 'Print version' } + &'stackablectl;release;upgrade'= { + cand -i 'List of product operators to upgrade' + cand --include 'List of product operators to upgrade' + cand -e 'Blacklist of product operators to install' + cand --exclude 'Blacklist of product operators to install' + cand --operator-namespace 'Namespace in the cluster used to deploy the operators' + cand --operator-ns 'Namespace in the cluster used to deploy the operators' + cand -l 'Log level this application uses' + cand --log-level 'Log level this application uses' + cand -d 'Provide one or more additional (custom) demo file(s)' + cand --demo-file 'Provide one or more additional (custom) demo file(s)' + cand -s 'Provide one or more additional (custom) stack file(s)' + cand --stack-file 'Provide one or more additional (custom) stack file(s)' + cand -r 'Provide one or more additional (custom) release file(s)' + cand --release-file 'Provide one or more additional (custom) release file(s)' + cand --helm-repo-stable 'Provide a custom Helm stable repository URL' + cand --helm-repo-test 'Provide a custom Helm test repository URL' + cand --helm-repo-dev 'Provide a custom Helm dev repository URL' + cand --chart-source 'Source the charts from either a OCI registry or from index.yaml-based repositories' + cand --no-cache 'Do not cache the remote (default) demo, stack and release files' + cand -h 'Print help (see more with ''--help'')' + cand --help 'Print help (see more with ''--help'')' + cand -V 'Print version' + cand --version 'Print version' + } &'stackablectl;release;help'= { cand list 'List available releases' cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' cand help 'Print this message or the help of the given subcommand(s)' } &'stackablectl;release;help;list'= { @@ -334,6 +361,8 @@ set edit:completion:arg-completer[stackablectl] = {|@words| } &'stackablectl;release;help;uninstall'= { } + &'stackablectl;release;help;upgrade'= { + } &'stackablectl;release;help;help'= { } &'stackablectl;stack'= { @@ -916,6 +945,7 @@ set edit:completion:arg-completer[stackablectl] = {|@words| cand describe 'Print out detailed release information' cand install 'Install a specific release' cand uninstall 'Uninstall a release' + cand upgrade 'Upgrade a release' } &'stackablectl;help;release;list'= { } @@ -925,6 +955,8 @@ set edit:completion:arg-completer[stackablectl] = {|@words| } &'stackablectl;help;release;uninstall'= { } + &'stackablectl;help;release;upgrade'= { + } &'stackablectl;help;stack'= { cand list 'List available stacks' cand describe 'Describe a specific stack' diff --git a/extra/completions/stackablectl.fish b/extra/completions/stackablectl.fish index 7fe840ee..28cb3fa0 100644 --- a/extra/completions/stackablectl.fish +++ b/extra/completions/stackablectl.fish @@ -132,22 +132,23 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __fish_seen_subcommand_from help" -f -a "uninstall" -d 'Uninstall one or more operators' complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __fish_seen_subcommand_from help" -f -a "installed" -d 'List installed operators' complete -c stackablectl -n "__fish_stackablectl_using_subcommand operator; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s l -l log-level -d 'Log level this application uses' -r -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s h -l help -d 'Print help (see more with \'--help\')' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -s V -l version -d 'Print version' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "list" -d 'List available releases' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "describe" -d 'Print out detailed release information' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "install" -d 'Install a specific release' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "uninstall" -d 'Uninstall a release' -complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "list" -d 'List available releases' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "describe" -d 'Print out detailed release information' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "install" -d 'Install a specific release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "upgrade" -d 'Upgrade a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and not __fish_seen_subcommand_from list describe install uninstall upgrade help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s o -l output -r -f -a "{plain\t'Print output formatted as plain text',table\t'Print output formatted as a table',json\t'Print output formatted as JSON',yaml\t'Print output formatted as YAML'}" complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s l -l log-level -d 'Log level this application uses' -r complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from list" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F @@ -202,10 +203,25 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and _ complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -s h -l help -d 'Print help (see more with \'--help\')' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from uninstall" -s V -l version -d 'Print version' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s i -l include -d 'List of product operators to upgrade' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s e -l exclude -d 'Blacklist of product operators to install' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l operator-namespace -l operator-ns -d 'Namespace in the cluster used to deploy the operators' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s l -l log-level -d 'Log level this application uses' -r +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s d -l demo-file -d 'Provide one or more additional (custom) demo file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s s -l stack-file -d 'Provide one or more additional (custom) stack file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s r -l release-file -d 'Provide one or more additional (custom) release file(s)' -r -F +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-stable -d 'Provide a custom Helm stable repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-test -d 'Provide a custom Helm test repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l helm-repo-dev -d 'Provide a custom Helm dev repository URL' -r -f +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l chart-source -d 'Source the charts from either a OCI registry or from index.yaml-based repositories' -r -f -a "{oci\t'OCI registry',repo\t'index.yaml-based repositories: resolution (dev, test, stable) is based on the version and thus will be operator-specific'}" +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -l no-cache -d 'Do not cache the remote (default) demo, stack and release files' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s h -l help -d 'Print help (see more with \'--help\')' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from upgrade" -s V -l version -d 'Print version' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "list" -d 'List available releases' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "describe" -d 'Print out detailed release information' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "install" -d 'Install a specific release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "upgrade" -d 'Upgrade a release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand release; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' complete -c stackablectl -n "__fish_stackablectl_using_subcommand stack; and not __fish_seen_subcommand_from list describe install help" -l release -d 'Target a specific Stackable release' -r complete -c stackablectl -n "__fish_stackablectl_using_subcommand stack; and not __fish_seen_subcommand_from list describe install help" -s l -l log-level -d 'Log level this application uses' -r @@ -534,6 +550,7 @@ complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fi complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "describe" -d 'Print out detailed release information' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "install" -d 'Install a specific release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "uninstall" -d 'Uninstall a release' +complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from release" -f -a "upgrade" -d 'Upgrade a release' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "list" -d 'List available stacks' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "describe" -d 'Describe a specific stack' complete -c stackablectl -n "__fish_stackablectl_using_subcommand help; and __fish_seen_subcommand_from stack" -f -a "install" -d 'Install a specific stack' diff --git a/extra/completions/stackablectl.nu b/extra/completions/stackablectl.nu index 448f1f63..6d899b1b 100644 --- a/extra/completions/stackablectl.nu +++ b/extra/completions/stackablectl.nu @@ -315,6 +315,30 @@ module completions { --version(-V) # Print version ] + def "nu-complete stackablectl release upgrade chart_source" [] { + [ "oci" "repo" ] + } + + # Upgrade a release + export extern "stackablectl release upgrade" [ + RELEASE: string # Upgrade to the specified release + --include(-i): string # List of product operators to upgrade + --exclude(-e): string # Blacklist of product operators to install + --operator-namespace: string # Namespace in the cluster used to deploy the operators + --operator-ns: string # Namespace in the cluster used to deploy the operators + --log-level(-l): string # Log level this application uses + --no-cache # Do not cache the remote (default) demo, stack and release files + --demo-file(-d): string # Provide one or more additional (custom) demo file(s) + --stack-file(-s): string # Provide one or more additional (custom) stack file(s) + --release-file(-r): string # Provide one or more additional (custom) release file(s) + --helm-repo-stable: string # Provide a custom Helm stable repository URL + --helm-repo-test: string # Provide a custom Helm test repository URL + --helm-repo-dev: string # Provide a custom Helm dev repository URL + --chart-source: string@"nu-complete stackablectl release upgrade chart_source" # Source the charts from either a OCI registry or from index.yaml-based repositories + --help(-h) # Print help (see more with '--help') + --version(-V) # Print version + ] + # Print this message or the help of the given subcommand(s) export extern "stackablectl release help" [ ] @@ -335,6 +359,10 @@ module completions { export extern "stackablectl release help uninstall" [ ] + # Upgrade a release + export extern "stackablectl release help upgrade" [ + ] + # Print this message or the help of the given subcommand(s) export extern "stackablectl release help help" [ ] @@ -967,6 +995,10 @@ module completions { export extern "stackablectl help release uninstall" [ ] + # Upgrade a release + export extern "stackablectl help release upgrade" [ + ] + # Interact with stacks, which are ready-to-use product combinations export extern "stackablectl help stack" [ ] diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index 1e738cb6..7a2512b3 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -182,10 +182,12 @@ impl ReleaseSpec { } #[instrument(skip_all)] - pub fn uninstall(&self, + pub fn uninstall( + &self, include_products: &[String], exclude_products: &[String], - namespace: &str) -> Result<()> { + namespace: &str, + ) -> Result<()> { info!("Uninstalling release"); include_products.iter().for_each(|product| { diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 11b981d9..95baed09 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -45,7 +45,7 @@ pub enum ReleaseCommands { #[command(aliases(["rm", "un"]))] Uninstall(ReleaseUninstallArgs), - // Upgrade a release + /// Upgrade a release Upgrade(ReleaseUpgradeArgs), } @@ -88,7 +88,7 @@ pub struct ReleaseInstallArgs { #[derive(Debug, Args)] pub struct ReleaseUpgradeArgs { - /// Release to upgrade to + /// Upgrade to the specified release #[arg(name = "RELEASE")] release: String, @@ -358,7 +358,8 @@ async fn upgrade_cmd( .uninstall( &args.included_products, &args.excluded_products, - &args.operator_namespace) + &args.operator_namespace, + ) .context(ReleaseUninstallSnafu)?; // Upgrade the CRDs for all the operators to be upgraded @@ -405,10 +406,7 @@ async fn uninstall_cmd( match release_list.get(&args.release) { Some(release) => { release - .uninstall( - &Vec::new(), - &Vec::new(), - &args.operator_namespace) + .uninstall(&Vec::new(), &Vec::new(), &args.operator_namespace) .context(ReleaseUninstallSnafu)?; let mut result = cli.result(); From 8dd56cd4c2381203027d3d6a743224765bd1c29c Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Thu, 22 May 2025 17:35:08 +0200 Subject: [PATCH 04/17] only upgrade installed operators and specifically included ones --- rust/stackablectl/src/cmds/release.rs | 38 ++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 95baed09..919f2957 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -7,8 +7,14 @@ use snafu::{ResultExt, Snafu}; use stackable_cockpit::{ common::list, constants::DEFAULT_OPERATOR_NAMESPACE, - platform::{namespace, operator::ChartSourceType, release}, + helm::{self, Release}, + platform::{ + namespace, + operator::{self, ChartSourceType}, + release, + }, utils::{ + self, k8s::{self, Client}, path::PathOrUrlParseError, }, @@ -118,6 +124,9 @@ pub struct ReleaseUninstallArgs { #[derive(Debug, Snafu)] pub enum CmdError { + #[snafu(display("Helm error"))] + HelmError { source: helm::Error }, + #[snafu(display("failed to serialize YAML output"))] SerializeYamlOutput { source: serde_yaml::Error }, @@ -353,19 +362,40 @@ async fn upgrade_cmd( let mut output = cli.result(); let client = Client::new().await.context(KubeClientCreateSnafu)?; + // Get all currently installed operators to only upgrade those + let installed_charts: Vec<Release> = + helm::list_releases(&args.operator_namespace).context(HelmSnafu)?; + + let mut operators: Vec<String> = operator::VALID_OPERATORS + .iter() + .filter(|operator| { + installed_charts + .iter() + .any(|release| release.name == utils::operator_chart_name(operator)) + }) + .map(|operator| operator.to_string()) + .collect(); + // Uninstall the old operator release first release .uninstall( - &args.included_products, + &operators, &args.excluded_products, &args.operator_namespace, ) .context(ReleaseUninstallSnafu)?; + // If operators were added to args.included_products, install them as well + for product in &args.included_products { + if !operators.contains(product) { + operators.push(product.clone()); + } + } + // Upgrade the CRDs for all the operators to be upgraded release .upgrade_crds( - &args.included_products, + &operators, &args.excluded_products, &args.operator_namespace, &client, @@ -376,7 +406,7 @@ async fn upgrade_cmd( // Install the new operator release release .install( - &args.included_products, + &operators, &args.excluded_products, &args.operator_namespace, &ChartSourceType::from(cli.chart_type()), From ec6de2fd77b7cd526bd5074f563f9cd413859d0a Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Thu, 22 May 2025 17:56:14 +0200 Subject: [PATCH 05/17] update docs --- .../modules/stackablectl/pages/commands/release.adoc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/modules/stackablectl/pages/commands/release.adoc b/docs/modules/stackablectl/pages/commands/release.adoc index a1ef3be1..80059440 100644 --- a/docs/modules/stackablectl/pages/commands/release.adoc +++ b/docs/modules/stackablectl/pages/commands/release.adoc @@ -158,3 +158,15 @@ Upgraded to release '25.3' Use "stackablectl operator installed" to list installed operators. ---- + +The above command only upgrades the currently installed operators. +To include additional operators in the installation step, use the `--include`/`-i` subcommands and specify the desired operators. + +For example +[source,console] +---- +$ stackablectl release upgrade 25.3 -i druid -i nifi +---- +would upgrade the existing operators as well as install the Stackable operators for Apache Druid and Apache NiFi. + +Likewise, operators can be exluded from the upgrade using the `--exclude`/`-e` subcommands. From a2a2cc493e5a8579cb3b7505098fd185e227e9ba Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Fri, 23 May 2025 09:16:21 +0200 Subject: [PATCH 06/17] add changelog entry --- rust/stackablectl/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/stackablectl/CHANGELOG.md b/rust/stackablectl/CHANGELOG.md index 8daf172d..c16b6480 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 release upgrade functionality to `stackablectl release` command through `upgrade` subcommand ([#379]). ### 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 +[#379]: https://github.com/stackabletech/stackable-cockpit/pull/379 ## [25.3.0] - 2025-03-27 From f301c91261b7eb8f862d74be952f00c65322b1d2 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Sun, 25 May 2025 16:11:40 +0200 Subject: [PATCH 07/17] add progress reporting to upgrade --- .../src/platform/release/spec.rs | 72 +++++++++++-------- rust/stackablectl/src/cmds/release.rs | 3 +- 2 files changed, 45 insertions(+), 30 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index bbe501ba..73964088 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -134,6 +134,7 @@ impl ReleaseSpec { /// Upgrades a release by upgrading individual operators. #[instrument(skip_all, fields( %namespace, + indicatif.pb_show = true ))] pub async fn upgrade_crds( &self, @@ -143,6 +144,7 @@ impl ReleaseSpec { k8s_client: &Client, ) -> Result<()> { info!("Upgrading CRDs for release"); + Span::current().pb_set_style(&PROGRESS_BAR_STYLE); include_products.iter().for_each(|product| { Span::current().record("product.included", product); @@ -154,38 +156,50 @@ impl ReleaseSpec { let client = reqwest::Client::new(); let operators = self.filter_products(include_products, exclude_products); + Span::current().pb_set_length(operators.len() as u64); + for (product_name, product) in operators { info!("Upgrading CRDs for {product_name}-operator"); + let iter_span = tracing::info_span!("upgrade_crds_iter", indicatif.pb_show = true); + + let client = client.clone(); + async move { + Span::current().pb_set_message(format!("Ugrading CRDs for {product_name}-operator").as_str()); + + let release_branch = match product.version.pre.as_str() { + "dev" => "main".to_string(), + _ => { + format!("{}", product.version) + } + }; - let release_branch = match product.version.pre.as_str() { - "dev" => "main".to_string(), - _ => { - format!("{}", product.version) - } - }; - - let request_url = format!( - "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release_branch}/deploy/helm/{product_name}-operator/crds/crds.yaml" - ); - - // Get CRD manifests from request_url - let response = client - .get(request_url) - .send() - .await - .context(ConstructRequestSnafu)? - .error_for_status() - .context(AccessCRDsSnafu)?; - - let crd_manifests = response.text().await.context(ReadManifestsSnafu)?; - - // Upgrade CRDs - k8s_client - .replace_crds(&crd_manifests) - .await - .context(DeployManifestSnafu)?; - - info!("Upgraded {product_name}-operator CRDs"); + let request_url = format!( + "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release_branch}/deploy/helm/{product_name}-operator/crds/crds.yaml" + ); + + // Get CRD manifests from request_url + let response = client + .get(request_url) + .send() + .await + .context(ConstructRequestSnafu)? + .error_for_status() + .context(AccessCRDsSnafu)?; + + let crd_manifests = response.text().await.context(ReadManifestsSnafu)?; + + // Upgrade CRDs + k8s_client + .replace_crds(&crd_manifests) + .await + .context(DeployManifestSnafu)?; + + info!("Upgraded {product_name}-operator CRDs"); + + Ok::<(), Error>(()) + }.instrument(iter_span).await?; + + Span::current().pb_inc(1); } Ok(()) diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index e4bb5c29..23f50c7c 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -354,13 +354,14 @@ async fn install_cmd( } } -#[instrument(skip(cli, release_list))] +#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] async fn upgrade_cmd( args: &ReleaseUpgradeArgs, cli: &Cli, release_list: release::ReleaseList, ) -> Result<String, CmdError> { info!(release = %args.release, "Upgrading release"); + Span::current().pb_set_message("Upgrading release"); match release_list.get(&args.release) { Some(release) => { From 2e6b8a5a878c179850661cd8a08fd63a4d3ff46d Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Mon, 26 May 2025 13:52:05 +0200 Subject: [PATCH 08/17] use to_string() instead format --- rust/stackable-cockpit/src/platform/release/spec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index 73964088..be214ca4 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -169,7 +169,7 @@ impl ReleaseSpec { let release_branch = match product.version.pre.as_str() { "dev" => "main".to_string(), _ => { - format!("{}", product.version) + product.version.to_string() } }; From 9f6f8e6af47fbc8668b9dd8609aaedf76b5d8245 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 09:57:36 +0200 Subject: [PATCH 09/17] use client with cache to fetch CRDs --- .../src/platform/release/spec.rs | 45 ++++++++++--------- rust/stackable-cockpit/src/xfer/mod.rs | 2 + rust/stackablectl/src/cmds/release.rs | 8 +++- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index be214ca4..d9693625 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -14,7 +14,11 @@ use crate::{ operator::{self, ChartSourceType, OperatorSpec}, product, }, - utils::k8s::{self, Client}, + utils::{ + k8s::{self, Client}, + path::{IntoPathOrUrl as _, PathOrUrlParseError}, + }, + xfer::{self, processor::Text}, }; type Result<T, E = Error> = std::result::Result<T, E>; @@ -24,6 +28,17 @@ pub enum Error { #[snafu(display("failed to parse operator spec"))] OperatorSpecParse { source: operator::SpecParseError }, + /// This error indicates that parsing a string into a path or URL failed. + #[snafu(display("failed to parse '{path_or_url}' as path/url"))] + ParsePathOrUrl { + source: PathOrUrlParseError, + path_or_url: String, + }, + + /// This error indicates that receiving remote content failed. + #[snafu(display("failed to receive remote content"))] + FileTransfer { source: xfer::Error }, + #[snafu(display("failed to install release using Helm"))] HelmInstall { source: helm::Error }, @@ -35,15 +50,6 @@ pub enum Error { #[snafu(display("failed to deploy manifests using the kube client"))] DeployManifest { source: k8s::Error }, - - #[snafu(display("failed to construct a valid request"))] - ConstructRequest { source: reqwest::Error }, - - #[snafu(display("failed to access the CRDs from source"))] - AccessCRDs { source: reqwest::Error }, - - #[snafu(display("failed to read CRD manifests"))] - ReadManifests { source: reqwest::Error }, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -142,6 +148,7 @@ impl ReleaseSpec { exclude_products: &[String], namespace: &str, k8s_client: &Client, + transfer_client: &xfer::Client, ) -> Result<()> { info!("Upgrading CRDs for release"); Span::current().pb_set_style(&PROGRESS_BAR_STYLE); @@ -153,7 +160,6 @@ impl ReleaseSpec { Span::current().record("product.excluded", product); }); - let client = reqwest::Client::new(); let operators = self.filter_products(include_products, exclude_products); Span::current().pb_set_length(operators.len() as u64); @@ -162,7 +168,6 @@ impl ReleaseSpec { info!("Upgrading CRDs for {product_name}-operator"); let iter_span = tracing::info_span!("upgrade_crds_iter", indicatif.pb_show = true); - let client = client.clone(); async move { Span::current().pb_set_message(format!("Ugrading CRDs for {product_name}-operator").as_str()); @@ -173,20 +178,18 @@ impl ReleaseSpec { } }; - let request_url = format!( + let request_url = &format!( "https://raw.githubusercontent.com/stackabletech/{product_name}-operator/{release_branch}/deploy/helm/{product_name}-operator/crds/crds.yaml" ); + let request_url = request_url.into_path_or_url().context(ParsePathOrUrlSnafu { + path_or_url: request_url.clone(), + })?; // Get CRD manifests from request_url - let response = client - .get(request_url) - .send() + let crd_manifests: String = transfer_client + .get(&request_url, &Text) .await - .context(ConstructRequestSnafu)? - .error_for_status() - .context(AccessCRDsSnafu)?; - - let crd_manifests = response.text().await.context(ReadManifestsSnafu)?; + .context(FileTransferSnafu)?; // Upgrade CRDs k8s_client diff --git a/rust/stackable-cockpit/src/xfer/mod.rs b/rust/stackable-cockpit/src/xfer/mod.rs index 87dde643..3792305b 100644 --- a/rust/stackable-cockpit/src/xfer/mod.rs +++ b/rust/stackable-cockpit/src/xfer/mod.rs @@ -120,6 +120,8 @@ impl Client { .client .execute(req) .await + .context(FetchRemoteContentSnafu)? + .error_for_status() .context(FetchRemoteContentSnafu)?; result.text().await.context(FetchRemoteContentSnafu) diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 23f50c7c..8af8fcad 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -181,7 +181,9 @@ impl ReleaseArgs { ReleaseCommands::Describe(args) => describe_cmd(args, cli, release_list).await, ReleaseCommands::Install(args) => install_cmd(args, cli, release_list).await, ReleaseCommands::Uninstall(args) => uninstall_cmd(args, cli, release_list).await, - ReleaseCommands::Upgrade(args) => upgrade_cmd(args, cli, release_list).await, + ReleaseCommands::Upgrade(args) => { + upgrade_cmd(args, cli, release_list, &transfer_client).await + } } } } @@ -354,11 +356,12 @@ async fn install_cmd( } } -#[instrument(skip(cli, release_list), fields(indicatif.pb_show = true))] +#[instrument(skip(cli, release_list, transfer_client), fields(indicatif.pb_show = true))] async fn upgrade_cmd( args: &ReleaseUpgradeArgs, cli: &Cli, release_list: release::ReleaseList, + transfer_client: &xfer::Client, ) -> Result<String, CmdError> { info!(release = %args.release, "Upgrading release"); Span::current().pb_set_message("Upgrading release"); @@ -405,6 +408,7 @@ async fn upgrade_cmd( &args.excluded_products, &args.operator_namespace, &client, + transfer_client, ) .await .context(CrdUpgradeSnafu)?; From 69b75c7e863d6f79e96d7dc1e098092f127ce8b1 Mon Sep 17 00:00:00 2001 From: Xenia <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 10:10:11 +0200 Subject: [PATCH 10/17] Update rust/stackablectl/src/cmds/release.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- rust/stackablectl/src/cmds/release.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 8af8fcad..635152ff 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -356,7 +356,7 @@ async fn install_cmd( } } -#[instrument(skip(cli, release_list, transfer_client), fields(indicatif.pb_show = true))] +#[instrument(skip_all, fields(indicatif.pb_show = true))] async fn upgrade_cmd( args: &ReleaseUpgradeArgs, cli: &Cli, From 5e66494f1cec9d166883361f938a52ee3792e627 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 11:07:48 +0200 Subject: [PATCH 11/17] use Debug impl for String in Error --- rust/stackable-cockpit/src/platform/manifests.rs | 2 +- rust/stackable-cockpit/src/platform/release/spec.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/stackable-cockpit/src/platform/manifests.rs b/rust/stackable-cockpit/src/platform/manifests.rs index c1b3a566..d9d23794 100644 --- a/rust/stackable-cockpit/src/platform/manifests.rs +++ b/rust/stackable-cockpit/src/platform/manifests.rs @@ -22,7 +22,7 @@ use crate::{ #[derive(Debug, Snafu)] pub enum Error { /// This error indicates that parsing a string into a path or URL failed. - #[snafu(display("failed to parse '{path_or_url}' as path/url"))] + #[snafu(display("failed to parse {path_or_url:?} as path/url"))] ParsePathOrUrl { source: PathOrUrlParseError, path_or_url: String, diff --git a/rust/stackable-cockpit/src/platform/release/spec.rs b/rust/stackable-cockpit/src/platform/release/spec.rs index d9693625..13f589a9 100644 --- a/rust/stackable-cockpit/src/platform/release/spec.rs +++ b/rust/stackable-cockpit/src/platform/release/spec.rs @@ -29,7 +29,7 @@ pub enum Error { OperatorSpecParse { source: operator::SpecParseError }, /// This error indicates that parsing a string into a path or URL failed. - #[snafu(display("failed to parse '{path_or_url}' as path/url"))] + #[snafu(display("failed to parse {path_or_url:?} as path/url"))] ParsePathOrUrl { source: PathOrUrlParseError, path_or_url: String, From 5ee5c354d97d5b02e145eb141b1ccf5118efbdcf Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 12:49:11 +0200 Subject: [PATCH 12/17] add gvk info to KubeClientReplace Error --- rust/stackable-cockpit/src/utils/k8s/client.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 092e6091..7b3f220f 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -39,7 +39,10 @@ pub enum Error { KubeClientPatch { source: kube::error::Error }, #[snafu(display("failed to replace Kubernetes object"))] - KubeClientReplace { source: kube::error::Error }, + KubeClientReplace { + source: kube::error::Error, + gvk: GroupVersionKind, + }, #[snafu(display("failed to deserialize YAML data"))] DeserializeYaml { source: serde_yaml::Error }, @@ -48,7 +51,7 @@ pub enum Error { GVKDiscoveryRun { source: kube::error::Error }, #[snafu(display("GVK {gvk:?} is not known"))] - GVKUnkown { gvk: GroupVersionKind }, + GVKUnknown { gvk: GroupVersionKind }, #[snafu(display("failed to deploy manifest because type of object {object:?} is not set"))] ObjectType { object: DynamicObject }, @@ -131,7 +134,7 @@ impl Client { let (resource, capabilities) = self .resolve_gvk(&gvk) .await? - .context(GVKUnkownSnafu { gvk })?; + .context(GVKUnknownSnafu { gvk })?; let api: Api<DynamicObject> = match capabilities.scope { Scope::Cluster => { @@ -173,7 +176,7 @@ impl Client { let (resource, _) = self .resolve_gvk(&gvk) .await? - .context(GVKUnkownSnafu { gvk })?; + .context(GVKUnknownSnafu { gvk: gvk.clone() })?; // CRDs are cluster scoped let api: Api<DynamicObject> = Api::all_with(self.client.clone(), &resource); @@ -186,7 +189,7 @@ impl Client { object.metadata.resource_version = resource.resource_version(); api.replace(&object.name_any(), &PostParams::default(), &object) .await - .context(KubeClientReplaceSnafu)?; + .context(KubeClientReplaceSnafu { gvk })?; } else { // Create CRD if a previous version wasn't found api.create(&PostParams::default(), &object) From b4003986e817e0063e997f7dc080c82f7e9f0648 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 13:58:08 +0200 Subject: [PATCH 13/17] return error on no release found, exit main with error if error from run --- rust/stackablectl/src/cmds/release.rs | 19 +++++++++++++++---- rust/stackablectl/src/main.rs | 3 ++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 635152ff..2d83ad26 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -140,6 +140,9 @@ pub enum CmdError { #[snafu(display("failed to build release list"))] BuildList { source: list::Error }, + #[snafu(display("no release '{release}'"))] + NoSuchRelease { release: String }, + #[snafu(display("failed to install release"))] ReleaseInstall { source: release::Error }, @@ -301,7 +304,9 @@ async fn describe_cmd( OutputType::Json => serde_json::to_string(&release).context(SerializeJsonOutputSnafu), OutputType::Yaml => serde_yaml::to_string(&release).context(SerializeYamlOutputSnafu), }, - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } @@ -352,7 +357,9 @@ async fn install_cmd( Ok(output.render()) } - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } @@ -433,7 +440,9 @@ async fn upgrade_cmd( Ok(output.render()) } - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } @@ -459,6 +468,8 @@ async fn uninstall_cmd( Ok(result.render()) } - None => Ok("No such release".into()), + None => Err(CmdError::NoSuchRelease { + release: args.release.clone(), + }), } } diff --git a/rust/stackablectl/src/main.rs b/rust/stackablectl/src/main.rs index 9ad159d2..8d6efb9d 100644 --- a/rust/stackablectl/src/main.rs +++ b/rust/stackablectl/src/main.rs @@ -68,7 +68,8 @@ async fn main() -> Result<(), Error> { let mut output = app.error(); output.with_error_report(err); - eprint!("{}", output.render()) + eprint!("{}", output.render()); + std::process::exit(1); } } From 17a2764f479a5f91c4be6100e9f3ab227f164204 Mon Sep 17 00:00:00 2001 From: Nick Larsen <nick.larsen@stackable.tech> Date: Tue, 27 May 2025 14:33:34 +0200 Subject: [PATCH 14/17] chore: use indicatif_(e)println --- rust/stackablectl/src/main.rs | 6 +++--- rust/stackablectl/src/output/mod.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/stackablectl/src/main.rs b/rust/stackablectl/src/main.rs index 8d6efb9d..d6f96b24 100644 --- a/rust/stackablectl/src/main.rs +++ b/rust/stackablectl/src/main.rs @@ -6,7 +6,7 @@ use tracing::{Level, metadata::LevelFilter}; use tracing_indicatif::{ IndicatifLayer, filter::{IndicatifFilter, hide_indicatif_span_fields}, - indicatif_eprintln, + indicatif_eprintln, indicatif_println, }; use tracing_subscriber::{ Layer as _, @@ -63,12 +63,12 @@ async fn main() -> Result<(), Error> { } match app.run().await { - Ok(result) => print!("{result}"), + Ok(result) => indicatif_println!("{result}"), Err(err) => { let mut output = app.error(); output.with_error_report(err); - eprint!("{}", output.render()); + indicatif_eprintln!("{error}", error = output.render()); std::process::exit(1); } } diff --git a/rust/stackablectl/src/output/mod.rs b/rust/stackablectl/src/output/mod.rs index eb1fe85f..9053ca87 100644 --- a/rust/stackablectl/src/output/mod.rs +++ b/rust/stackablectl/src/output/mod.rs @@ -65,7 +65,7 @@ where let mut index = 1; while let Some(source) = error.source() { - writeln!(report, " {}: {}", index, source)?; + writeln!(report, " {index}: {source}")?; error = source; index += 1; } From eee8119f80a18797737f5054d966bfd375ff5c3a Mon Sep 17 00:00:00 2001 From: Nick Larsen <nick.larsen@stackable.tech> Date: Tue, 27 May 2025 15:18:26 +0200 Subject: [PATCH 15/17] chore: Reuse existing error variant, and reformat it --- rust/stackable-cockpit/src/utils/k8s/client.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 7b3f220f..51fca425 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -50,18 +50,16 @@ pub enum Error { #[snafu(display("failed to run GVK discovery"))] GVKDiscoveryRun { source: kube::error::Error }, - #[snafu(display("GVK {gvk:?} is not known"))] - GVKUnknown { gvk: GroupVersionKind }, - #[snafu(display("failed to deploy manifest because type of object {object:?} is not set"))] ObjectType { object: DynamicObject }, - #[snafu(display("failed to deploy manifest because GVK {group}/{kind}@{version} cannot be resolved", + // Using output close to Display for ObjectRef https://docs.rs/kube-runtime/0.99.0/src/kube_runtime/reflector/object_ref.rs.html#292-296 + #[snafu(display("failed to resolve GVK: {kind}.{version}.{group}", group = gvk.group, version = gvk.version, kind = gvk.kind ))] - DiscoveryResolve { gvk: GroupVersionKind }, + GVKResolve { gvk: GroupVersionKind }, #[snafu(display("failed to convert byte string into UTF-8 string"))] ByteStringConvert { source: FromUtf8Error }, @@ -134,7 +132,7 @@ impl Client { let (resource, capabilities) = self .resolve_gvk(&gvk) .await? - .context(GVKUnknownSnafu { gvk })?; + .context(GVKResolveSnafu { gvk })?; let api: Api<DynamicObject> = match capabilities.scope { Scope::Cluster => { @@ -176,7 +174,7 @@ impl Client { let (resource, _) = self .resolve_gvk(&gvk) .await? - .context(GVKUnknownSnafu { gvk: gvk.clone() })?; + .with_context(|| GVKResolveSnafu { gvk: gvk.clone() })?; // CRDs are cluster scoped let api: Api<DynamicObject> = Api::all_with(self.client.clone(), &resource); @@ -189,7 +187,7 @@ impl Client { object.metadata.resource_version = resource.resource_version(); api.replace(&object.name_any(), &PostParams::default(), &object) .await - .context(KubeClientReplaceSnafu { gvk })?; + .with_context(|_| KubeClientReplaceSnafu { gvk })?; } else { // Create CRD if a previous version wasn't found api.create(&PostParams::default(), &object) From 96df19872c7ad6bdc0bfc380948c435951ae688a Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 16:47:44 +0200 Subject: [PATCH 16/17] use Debug impl for String in Errors --- rust/stackable-cockpit/src/engine/minikube/mod.rs | 2 +- rust/stackable-cockpit/src/platform/demo/spec.rs | 4 ++-- rust/stackable-cockpit/src/platform/service.rs | 12 ++++++------ rust/stackable-cockpit/src/platform/stacklet/mod.rs | 2 +- rust/stackable-cockpit/src/utils/k8s/client.rs | 8 ++++---- rust/stackable-cockpit/src/utils/params.rs | 2 +- rust/stackablectl/src/cmds/demo.rs | 6 +++--- rust/stackablectl/src/cmds/operator.rs | 4 ++-- rust/stackablectl/src/cmds/release.rs | 4 ++-- rust/stackablectl/src/cmds/stack.rs | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) diff --git a/rust/stackable-cockpit/src/engine/minikube/mod.rs b/rust/stackable-cockpit/src/engine/minikube/mod.rs index dab6e959..308de131 100644 --- a/rust/stackable-cockpit/src/engine/minikube/mod.rs +++ b/rust/stackable-cockpit/src/engine/minikube/mod.rs @@ -10,7 +10,7 @@ use crate::{ #[derive(Debug, Snafu)] pub enum Error { #[snafu(display( - "failed to determine if a Minikube cluster named '{cluster_name}' already exists" + "failed to determine if a Minikube cluster named {cluster_name:?} already exists" ))] CheckCluster { source: std::io::Error, diff --git a/rust/stackable-cockpit/src/platform/demo/spec.rs b/rust/stackable-cockpit/src/platform/demo/spec.rs index e2903516..2f031e89 100644 --- a/rust/stackable-cockpit/src/platform/demo/spec.rs +++ b/rust/stackable-cockpit/src/platform/demo/spec.rs @@ -29,13 +29,13 @@ pub type DemoParameter = Parameter; #[derive(Debug, Snafu)] pub enum Error { - #[snafu(display("no stack named '{name}'"))] + #[snafu(display("no stack named {name:?}"))] NoSuchStack { name: String }, #[snafu(display("demo resource requests error"), context(false))] DemoResourceRequests { source: ResourceRequestsError }, - #[snafu(display("cannot install demo in namespace '{requested}', only '{}' supported", supported.join(", ")))] + #[snafu(display("cannot install demo in namespace {requested:?}, only '{}' supported", supported.join(", ")))] UnsupportedNamespace { requested: String, supported: Vec<String>, diff --git a/rust/stackable-cockpit/src/platform/service.rs b/rust/stackable-cockpit/src/platform/service.rs index f82d50fe..73ff576c 100644 --- a/rust/stackable-cockpit/src/platform/service.rs +++ b/rust/stackable-cockpit/src/platform/service.rs @@ -20,22 +20,22 @@ pub enum Error { #[snafu(display("failed to fetch data from Kubernetes API"))] KubeClientFetch { source: k8s::Error }, - #[snafu(display("missing namespace for service '{service}'"))] + #[snafu(display("missing namespace for service {service:?}"))] MissingServiceNamespace { service: String }, - #[snafu(display("missing spec for service '{service}'"))] + #[snafu(display("missing spec for service {service:?}"))] MissingServiceSpec { service: String }, - #[snafu(display("failed to get status of node '{node_name}'"))] + #[snafu(display("failed to get status of node {node_name:?}"))] GetNodeStatus { node_name: String }, - #[snafu(display("failed to get address of node '{node_name}'"))] + #[snafu(display("failed to get address of node {node_name:?}"))] GetNodeAddress { node_name: String }, - #[snafu(display("failed to find an ExternalIP or InternalIP for node '{node_name}'"))] + #[snafu(display("failed to find an ExternalIP or InternalIP for node {node_name:?}"))] NoIpForNode { node_name: String }, - #[snafu(display("failed to find node '{node_name}' in node_name_ip_mapping"))] + #[snafu(display("failed to find node {node_name:?} in node_name_ip_mapping"))] NodeMissingInIpMapping { node_name: String }, } diff --git a/rust/stackable-cockpit/src/platform/stacklet/mod.rs b/rust/stackable-cockpit/src/platform/stacklet/mod.rs index b2f915c0..1279ddc4 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/mod.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/mod.rs @@ -50,7 +50,7 @@ pub enum Error { #[snafu(display("failed to fetch data from the Kubernetes API"))] KubeClientFetch { source: k8s::Error }, - #[snafu(display("no namespace set for custom resource '{crd_name}'"))] + #[snafu(display("no namespace set for custom resource {crd_name:?}"))] CustomCrdNamespace { crd_name: String }, #[snafu(display("failed to deserialize cluster conditions from JSON"))] diff --git a/rust/stackable-cockpit/src/utils/k8s/client.rs b/rust/stackable-cockpit/src/utils/k8s/client.rs index 51fca425..e678b71b 100644 --- a/rust/stackable-cockpit/src/utils/k8s/client.rs +++ b/rust/stackable-cockpit/src/utils/k8s/client.rs @@ -64,19 +64,19 @@ pub enum Error { #[snafu(display("failed to convert byte string into UTF-8 string"))] ByteStringConvert { source: FromUtf8Error }, - #[snafu(display("missing namespace for service '{service}'"))] + #[snafu(display("missing namespace for service {service:?}"))] MissingServiceNamespace { service: String }, #[snafu(display("failed to retrieve cluster information"))] ClusterInformation { source: cluster::Error }, - #[snafu(display("invalid or empty secret data in '{secret_name}'"))] + #[snafu(display("invalid or empty secret data in {secret_name:?}"))] InvalidSecretData { secret_name: String }, - #[snafu(display("no username key in credentials secret '{secret_name}'"))] + #[snafu(display("no username key in credentials secret {secret_name:?}"))] NoUsernameKey { secret_name: String }, - #[snafu(display("no password key in credentials secret '{secret_name}'"))] + #[snafu(display("no password key in credentials secret {secret_name:?}"))] NoPasswordKey { secret_name: String }, } diff --git a/rust/stackable-cockpit/src/utils/params.rs b/rust/stackable-cockpit/src/utils/params.rs index 623aeac4..6526b7be 100644 --- a/rust/stackable-cockpit/src/utils/params.rs +++ b/rust/stackable-cockpit/src/utils/params.rs @@ -34,7 +34,7 @@ pub enum IntoParametersError { #[snafu(display("failed to parse raw parameter"))] RawParse { source: RawParameterParseError }, - #[snafu(display("invalid parameter '{parameter}', expected one of {expected}"))] + #[snafu(display("invalid parameter {parameter:?}, expected one of {expected:?}"))] InvalidParameter { parameter: String, expected: String }, } diff --git a/rust/stackablectl/src/cmds/demo.rs b/rust/stackablectl/src/cmds/demo.rs index 9480e3dd..2b433035 100644 --- a/rust/stackablectl/src/cmds/demo.rs +++ b/rust/stackablectl/src/cmds/demo.rs @@ -125,13 +125,13 @@ pub enum CmdError { #[snafu(display("failed to serialize JSON output"))] SerializeJsonOutput { source: serde_json::Error }, - #[snafu(display("no demo with name '{name}'"))] + #[snafu(display("no demo with name {name:?}"))] NoSuchDemo { name: String }, - #[snafu(display("no stack with name '{name}'"))] + #[snafu(display("no stack with name {name:?}"))] NoSuchStack { name: String }, - #[snafu(display("no release '{release}'"))] + #[snafu(display("no release {release:?}"))] NoSuchRelease { release: String }, #[snafu(display("failed to get latest release"))] diff --git a/rust/stackablectl/src/cmds/operator.rs b/rust/stackablectl/src/cmds/operator.rs index 84c269cd..501203b9 100644 --- a/rust/stackablectl/src/cmds/operator.rs +++ b/rust/stackablectl/src/cmds/operator.rs @@ -141,7 +141,7 @@ pub enum CmdError { version: String, }, - #[snafu(display("unknown repository name '{name}'"))] + #[snafu(display("unknown repository name {name:?}"))] UnknownRepoName { name: String }, #[snafu(display("Helm error"))] @@ -159,7 +159,7 @@ pub enum CmdError { #[snafu(display("failed to create Kubernetes client"))] KubeClientCreate { source: k8s::Error }, - #[snafu(display("failed to create namespace '{namespace}'"))] + #[snafu(display("failed to create namespace {namespace:?}"))] NamespaceCreate { source: namespace::Error, namespace: String, diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 2d83ad26..00d26ab0 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -140,7 +140,7 @@ pub enum CmdError { #[snafu(display("failed to build release list"))] BuildList { source: list::Error }, - #[snafu(display("no release '{release}'"))] + #[snafu(display("no release {release:?}"))] NoSuchRelease { release: String }, #[snafu(display("failed to install release"))] @@ -158,7 +158,7 @@ pub enum CmdError { #[snafu(display("failed to create Kubernetes client"))] KubeClientCreate { source: k8s::Error }, - #[snafu(display("failed to create namespace '{namespace}'"))] + #[snafu(display("failed to create namespace {namespace:?}"))] NamespaceCreate { source: namespace::Error, namespace: String, diff --git a/rust/stackablectl/src/cmds/stack.rs b/rust/stackablectl/src/cmds/stack.rs index 01816929..6860c04f 100644 --- a/rust/stackablectl/src/cmds/stack.rs +++ b/rust/stackablectl/src/cmds/stack.rs @@ -121,7 +121,7 @@ pub enum CmdError { #[snafu(display("failed to serialize JSON output"))] SerializeJsonOutput { source: serde_json::Error }, - #[snafu(display("no release '{release}'"))] + #[snafu(display("no release {release:?}"))] NoSuchRelease { release: String }, #[snafu(display("failed to get latest release"))] From 1572f2febc5b860e269ec95320d30a7ccd60ad55 Mon Sep 17 00:00:00 2001 From: xeniape <xenia.fischer@stackable.tech> Date: Tue, 27 May 2025 20:08:45 +0200 Subject: [PATCH 17/17] refactor unnamed interpolations --- rust/stackable-cockpit/src/helm.rs | 48 +++++-------------- .../src/platform/cluster/resource_request.rs | 10 ++-- .../src/platform/credentials.rs | 7 ++- .../src/platform/demo/spec.rs | 2 +- .../src/platform/stack/spec.rs | 2 +- .../src/platform/stacklet/mod.rs | 5 +- .../src/utils/k8s/conditions.rs | 18 +++++-- .../stackable-cockpit/src/utils/k8s/labels.rs | 2 +- rust/stackable-cockpit/src/utils/mod.rs | 2 +- rust/stackable-cockpit/src/utils/params.rs | 2 +- rust/stackablectl/src/cmds/debug.rs | 4 +- rust/stackablectl/src/cmds/demo.rs | 27 +++++++---- rust/stackablectl/src/cmds/operator.rs | 17 ++++--- rust/stackablectl/src/cmds/release.rs | 20 ++++++-- rust/stackablectl/src/cmds/stack.rs | 27 +++++++---- rust/stackablectl/src/cmds/stacklet.rs | 16 ++++--- rust/stackablectl/src/output/mod.rs | 2 +- rust/stackablectl/src/output/result.rs | 6 +-- rust/xtask/src/docs.rs | 4 +- web/build.rs | 4 +- 20 files changed, 128 insertions(+), 97 deletions(-) diff --git a/rust/stackable-cockpit/src/helm.rs b/rust/stackable-cockpit/src/helm.rs index c43578d3..4cca0bba 100644 --- a/rust/stackable-cockpit/src/helm.rs +++ b/rust/stackable-cockpit/src/helm.rs @@ -122,8 +122,7 @@ impl Display for InstallReleaseStatus { } => { write!( f, - "The release {} ({}) is already installed (requested {}), skipping.", - release_name, current_version, requested_version + "The release {release_name} ({current_version}) is already installed (requested {requested_version}), skipping." ) } InstallReleaseStatus::ReleaseAlreadyInstalledUnspecified { @@ -132,16 +131,11 @@ impl Display for InstallReleaseStatus { } => { write!( f, - "The release {} ({}) is already installed and no specific version was requested, skipping.", - release_name, current_version + "The release {release_name} ({current_version}) is already installed and no specific version was requested, skipping." ) } InstallReleaseStatus::Installed(release_name) => { - write!( - f, - "The release {} was successfully installed.", - release_name - ) + write!(f, "The release {release_name} was successfully installed.") } } } @@ -157,17 +151,12 @@ impl Display for UninstallReleaseStatus { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { UninstallReleaseStatus::NotInstalled(release_name) => { - write!( - f, - "The release {} is not installed, skipping.", - release_name - ) + write!(f, "The release {release_name} is not installed, skipping.") } UninstallReleaseStatus::Uninstalled(release_name) => { write!( f, - "The release {} was successfully uninstalled.", - release_name + "The release {release_name} was successfully uninstalled." ) } } @@ -282,10 +271,7 @@ fn install_release( ); if let Some(error) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_install_helm_release encountered an error: {}", - error - ); + error!("Go wrapper function go_install_helm_release encountered an error: {error}"); return Err(Error::InstallRelease { source: InstallReleaseError::HelmWrapper { error }, @@ -312,10 +298,7 @@ pub fn uninstall_release( let result = helm_sys::uninstall_helm_release(release_name, namespace, suppress_output); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_uninstall_helm_release encountered an error: {}", - err - ); + error!("Go wrapper function go_uninstall_helm_release encountered an error: {err}"); return Err(Error::UninstallRelease { error: err }); } @@ -325,10 +308,7 @@ pub fn uninstall_release( )); } - info!( - "The Helm release {} is not installed, skipping.", - release_name - ); + info!("The Helm release {release_name} is not installed, skipping."); Ok(UninstallReleaseStatus::NotInstalled( release_name.to_string(), @@ -352,10 +332,7 @@ pub fn list_releases(namespace: &str) -> Result<Vec<Release>, Error> { let result = helm_sys::list_helm_releases(namespace); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_helm_list_releases encountered an error: {}", - err - ); + error!("Go wrapper function go_helm_list_releases encountered an error: {err}"); return Err(Error::ListReleases { error: err }); } @@ -381,10 +358,7 @@ pub fn add_repo(repository_name: &str, repository_url: &str) -> Result<(), Error let result = helm_sys::add_helm_repository(repository_name, repository_url); if let Some(err) = helm_sys::to_helm_error(&result) { - error!( - "Go wrapper function go_add_helm_repo encountered an error: {}", - err - ); + error!("Go wrapper function go_add_helm_repo encountered an error: {err}"); return Err(Error::AddRepo { error: err }); } @@ -403,7 +377,7 @@ where let url = Url::parse(repo_url.as_ref()).context(UrlParseSnafu)?; let url = url.join(HELM_REPO_INDEX_FILE).context(UrlParseSnafu)?; - debug!("Using {} to retrieve Helm index file", url); + debug!("Using {url} to retrieve Helm index file"); // TODO (Techassi): Use the FileTransferClient for that let index_file_content = reqwest::get(url) diff --git a/rust/stackable-cockpit/src/platform/cluster/resource_request.rs b/rust/stackable-cockpit/src/platform/cluster/resource_request.rs index b254bd75..d3ada92c 100644 --- a/rust/stackable-cockpit/src/platform/cluster/resource_request.rs +++ b/rust/stackable-cockpit/src/platform/cluster/resource_request.rs @@ -32,8 +32,10 @@ impl Display for ResourceRequests { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "CPU: {}, Memory: {}, PVC space: {}", - self.cpu.0, self.memory.0, self.pvc.0 + "CPU: {cpu}, Memory: {memory}, PVC space: {pvc}", + cpu = self.cpu.0, + memory = self.memory.0, + pvc = self.pvc.0 ) } } @@ -67,8 +69,8 @@ pub enum ResourceRequestsError { #[derive(Debug, Snafu)] pub enum ResourceRequestsValidationError { #[snafu(display( - "The {object_name} requires {} CPU core(s), but there are only {} CPU core(s) available in the cluster", - required.as_cpu_count(), available.as_cpu_count() + "The {object_name} requires {required_cpu} CPU core(s), but there are only {available_cpu} CPU core(s) available in the cluster", + required_cpu = required.as_cpu_count(), available_cpu = available.as_cpu_count() ))] InsufficientCpu { available: CpuQuantity, diff --git a/rust/stackable-cockpit/src/platform/credentials.rs b/rust/stackable-cockpit/src/platform/credentials.rs index f03caaa9..5b96da02 100644 --- a/rust/stackable-cockpit/src/platform/credentials.rs +++ b/rust/stackable-cockpit/src/platform/credentials.rs @@ -25,7 +25,12 @@ pub struct Credentials { impl Display for Credentials { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.username, self.password) + write!( + f, + "{username}:{password}", + username = self.username, + password = self.password + ) } } diff --git a/rust/stackable-cockpit/src/platform/demo/spec.rs b/rust/stackable-cockpit/src/platform/demo/spec.rs index 2f031e89..6c4c5589 100644 --- a/rust/stackable-cockpit/src/platform/demo/spec.rs +++ b/rust/stackable-cockpit/src/platform/demo/spec.rs @@ -35,7 +35,7 @@ pub enum Error { #[snafu(display("demo resource requests error"), context(false))] DemoResourceRequests { source: ResourceRequestsError }, - #[snafu(display("cannot install demo in namespace {requested:?}, only '{}' supported", supported.join(", ")))] + #[snafu(display("cannot install demo in namespace {requested:?}, only {supported:?} supported", supported = supported.join(", ")))] UnsupportedNamespace { requested: String, supported: Vec<String>, diff --git a/rust/stackable-cockpit/src/platform/stack/spec.rs b/rust/stackable-cockpit/src/platform/stack/spec.rs index e95fb628..c5fd2939 100644 --- a/rust/stackable-cockpit/src/platform/stack/spec.rs +++ b/rust/stackable-cockpit/src/platform/stack/spec.rs @@ -49,7 +49,7 @@ pub enum Error { /// This error indicates that the stack doesn't support being installed in /// the provided namespace. - #[snafu(display("unable install stack in namespace {requested:?}, only '{}' supported", supported.join(", ")))] + #[snafu(display("unable install stack in namespace {requested:?}, only {supported:?} supported", supported = supported.join(", ")))] UnsupportedNamespace { requested: String, supported: Vec<String>, diff --git a/rust/stackable-cockpit/src/platform/stacklet/mod.rs b/rust/stackable-cockpit/src/platform/stacklet/mod.rs index 1279ddc4..7c4cd449 100644 --- a/rust/stackable-cockpit/src/platform/stacklet/mod.rs +++ b/rust/stackable-cockpit/src/platform/stacklet/mod.rs @@ -197,6 +197,9 @@ fn gvk_from_product_name(product_name: &str) -> GroupVersionKind { GroupVersionKind { group: format!("{product_name}.stackable.tech"), version: "v1alpha1".into(), - kind: format!("{}Cluster", product_name.capitalize()), + kind: format!( + "{product_name}Cluster", + product_name = product_name.capitalize() + ), } } diff --git a/rust/stackable-cockpit/src/utils/k8s/conditions.rs b/rust/stackable-cockpit/src/utils/k8s/conditions.rs index 06abc174..82f52118 100644 --- a/rust/stackable-cockpit/src/utils/k8s/conditions.rs +++ b/rust/stackable-cockpit/src/utils/k8s/conditions.rs @@ -43,7 +43,11 @@ impl ConditionsExt for Vec<Condition> { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), Some(c.message.clone()), c.is_good(), ) @@ -57,7 +61,11 @@ impl ConditionsExt for Vec<DeploymentCondition> { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), c.message.clone(), c.is_good(), ) @@ -79,7 +87,11 @@ impl ConditionsExt for Vec<StatefulSetCondition> { self.iter() .map(|c| { DisplayCondition::new( - format!("{}: {}", c.type_, c.status), + format!( + "{condition_type}: {status}", + condition_type = c.type_, + status = c.status + ), c.message.clone(), c.is_good(), ) diff --git a/rust/stackable-cockpit/src/utils/k8s/labels.rs b/rust/stackable-cockpit/src/utils/k8s/labels.rs index 7f9d5b17..d79c127b 100644 --- a/rust/stackable-cockpit/src/utils/k8s/labels.rs +++ b/rust/stackable-cockpit/src/utils/k8s/labels.rs @@ -37,7 +37,7 @@ pub trait ListParamsExt { impl ListParamsExt for ListParams { fn add_label(&mut self, label: impl Into<String>) { match self.label_selector.as_mut() { - Some(labels) => labels.push_str(format!(",{}", label.into()).as_str()), + Some(labels) => labels.push_str(format!(",{label}", label = label.into()).as_str()), None => self.label_selector = Some(label.into()), } } diff --git a/rust/stackable-cockpit/src/utils/mod.rs b/rust/stackable-cockpit/src/utils/mod.rs index a42bcc0d..ee2018a4 100644 --- a/rust/stackable-cockpit/src/utils/mod.rs +++ b/rust/stackable-cockpit/src/utils/mod.rs @@ -8,5 +8,5 @@ pub mod templating; /// Returns the name of the operator used in the Helm repository. pub fn operator_chart_name(name: &str) -> String { - format!("{}-operator", name) + format!("{name}-operator") } diff --git a/rust/stackable-cockpit/src/utils/params.rs b/rust/stackable-cockpit/src/utils/params.rs index 6526b7be..d688009d 100644 --- a/rust/stackable-cockpit/src/utils/params.rs +++ b/rust/stackable-cockpit/src/utils/params.rs @@ -106,7 +106,7 @@ pub enum RawParameterParseError { impl Display for RawParameter { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}={}", self.name, self.value) + write!(f, "{name}={value}", name = self.name, value = self.value) } } diff --git a/rust/stackablectl/src/cmds/debug.rs b/rust/stackablectl/src/cmds/debug.rs index cf0feaa1..83ad165c 100644 --- a/rust/stackablectl/src/cmds/debug.rs +++ b/rust/stackablectl/src/cmds/debug.rs @@ -340,8 +340,8 @@ impl Drop for AsyncStdin { let status = unsafe { libc::fcntl(self.fd.as_raw_fd(), libc::F_SETFL, self.old_flags) }; if status == -1 { panic!( - "unable to revert stdin flags: {}", - std::io::Error::last_os_error() + "unable to revert stdin flags: {error}", + error = std::io::Error::last_os_error() ); } } diff --git a/rust/stackablectl/src/cmds/demo.rs b/rust/stackablectl/src/cmds/demo.rs index 2b433035..bffc38a0 100644 --- a/rust/stackablectl/src/cmds/demo.rs +++ b/rust/stackablectl/src/cmds/demo.rs @@ -302,7 +302,10 @@ async fn describe_cmd( result .with_command_hint( - format!("stackablectl demo install {}", args.demo_name), + format!( + "stackablectl demo install {demo_name}", + demo_name = args.demo_name + ), "install the demo", ) .with_command_hint("stackablectl demo list", "list all available demos") @@ -398,11 +401,11 @@ async fn install_cmd( })?; let operator_cmd = format!( - "stackablectl operator installed{}", - if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { + "stackablectl operator installed{option}", + option = if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { format!( - " --operator-namespace {}", - args.namespaces.operator_namespace + " --operator-namespace {namespace}", + namespace = args.namespaces.operator_namespace ) } else { "".into() @@ -410,9 +413,12 @@ async fn install_cmd( ); let stacklet_cmd = format!( - "stackablectl stacklet list{}", - if args.namespaces.namespace != DEFAULT_NAMESPACE { - format!(" --namespace {}", args.namespaces.namespace) + "stackablectl stacklet list{option}", + option = if args.namespaces.namespace != DEFAULT_NAMESPACE { + format!( + " --namespace {namespace}", + namespace = args.namespaces.namespace + ) } else { "".into() } @@ -421,7 +427,10 @@ async fn install_cmd( output .with_command_hint(operator_cmd, "display the installed operators") .with_command_hint(stacklet_cmd, "display the installed stacklets") - .with_output(format!("Installed demo '{}'", args.demo_name)); + .with_output(format!( + "Installed demo {demo_name:?}", + demo_name = args.demo_name + )); Ok(output.render()) } diff --git a/rust/stackablectl/src/cmds/operator.rs b/rust/stackablectl/src/cmds/operator.rs index 501203b9..cb2e903c 100644 --- a/rust/stackablectl/src/cmds/operator.rs +++ b/rust/stackablectl/src/cmds/operator.rs @@ -292,7 +292,10 @@ async fn describe_cmd(args: &OperatorDescribeArgs, cli: &Cli) -> Result<String, result .with_command_hint( - format!("stackablectl operator install {}", args.operator_name), + format!( + "stackablectl operator install {operator_name}", + operator_name = args.operator_name + ), "install the operator", ) .with_command_hint("stackablectl operator list", "list all available operators") @@ -343,9 +346,9 @@ async fn install_cmd(args: &OperatorInstallArgs, cli: &Cli) -> Result<String, Cm "list installed operators", ) .with_output(format!( - "Installed {} {}", - args.operators.len(), - if args.operators.len() == 1 { + "Installed {num_of_operators} {suffix}", + num_of_operators = args.operators.len(), + suffix = if args.operators.len() == 1 { "operator" } else { "operators" @@ -374,9 +377,9 @@ fn uninstall_cmd(args: &OperatorUninstallArgs, cli: &Cli) -> Result<String, CmdE "list remaining installed operators", ) .with_output(format!( - "Uninstalled {} {}", - args.operators.len(), - if args.operators.len() == 1 { + "Uninstalled {num_of_operators} {suffix}", + num_of_operators = args.operators.len(), + suffix = if args.operators.len() == 1 { "operator" } else { "operators" diff --git a/rust/stackablectl/src/cmds/release.rs b/rust/stackablectl/src/cmds/release.rs index 00d26ab0..cad3b538 100644 --- a/rust/stackablectl/src/cmds/release.rs +++ b/rust/stackablectl/src/cmds/release.rs @@ -293,7 +293,10 @@ async fn describe_cmd( result .with_command_hint( - format!("stackablectl release install {}", args.release), + format!( + "stackablectl release install {release}", + release = args.release + ), "install the release", ) .with_command_hint("stackablectl release list", "list all available releases") @@ -353,7 +356,10 @@ async fn install_cmd( "stackablectl operator installed", "list installed operators", ) - .with_output(format!("Installed release '{}'", args.release)); + .with_output(format!( + "Installed release {release:?}", + release = args.release + )); Ok(output.render()) } @@ -436,7 +442,10 @@ async fn upgrade_cmd( "stackablectl operator installed", "list installed operators", ) - .with_output(format!("Upgraded to release '{}'", args.release)); + .with_output(format!( + "Upgraded to release {release:?}", + release = args.release + )); Ok(output.render()) } @@ -464,7 +473,10 @@ async fn uninstall_cmd( result .with_command_hint("stackablectl release list", "list available releases") - .with_output(format!("Uninstalled release '{}'", args.release)); + .with_output(format!( + "Uninstalled release {release:?}", + release = args.release + )); Ok(result.render()) } diff --git a/rust/stackablectl/src/cmds/stack.rs b/rust/stackablectl/src/cmds/stack.rs index 6860c04f..a027166d 100644 --- a/rust/stackablectl/src/cmds/stack.rs +++ b/rust/stackablectl/src/cmds/stack.rs @@ -291,7 +291,10 @@ fn describe_cmd( result .with_command_hint( - format!("stackablectl stack install {}", args.stack_name), + format!( + "stackablectl stack install {stack_name}", + stack_name = args.stack_name + ), "install the stack", ) .with_command_hint("stackablectl stack list", "list all available stacks") @@ -361,11 +364,11 @@ async fn install_cmd( })?; let operator_cmd = format!( - "stackablectl operator installed{}", - if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { + "stackablectl operator installed{option}", + option = if args.namespaces.operator_namespace != DEFAULT_OPERATOR_NAMESPACE { format!( - " --operator-namespace {}", - args.namespaces.operator_namespace + " --operator-namespace {namespace}", + namespace = args.namespaces.operator_namespace ) } else { "".into() @@ -373,9 +376,12 @@ async fn install_cmd( ); let stacklet_cmd = format!( - "stackablectl stacklet list{}", - if args.namespaces.namespace != DEFAULT_NAMESPACE { - format!(" --namespace {}", args.namespaces.namespace) + "stackablectl stacklet list{option}", + option = if args.namespaces.namespace != DEFAULT_NAMESPACE { + format!( + " --namespace {namespace}", + namespace = args.namespaces.namespace + ) } else { "".into() } @@ -384,7 +390,10 @@ async fn install_cmd( output .with_command_hint(operator_cmd, "display the installed operators") .with_command_hint(stacklet_cmd, "display the installed stacklets") - .with_output(format!("Installed stack '{}'", args.stack_name)); + .with_output(format!( + "Installed stack {stack_name:?}", + stack_name = args.stack_name + )); Ok(output.render()) } diff --git a/rust/stackablectl/src/cmds/stacklet.rs b/rust/stackablectl/src/cmds/stacklet.rs index 332d45a0..62c8aff2 100644 --- a/rust/stackablectl/src/cmds/stacklet.rs +++ b/rust/stackablectl/src/cmds/stacklet.rs @@ -191,7 +191,7 @@ async fn list_cmd(args: &StackletListArgs, cli: &Cli) -> Result<String, CmdError .with_output(format!( "{table}{errors}", errors = if !error_list.is_empty() { - format!("\n\n{}", error_list.join("\n")) + format!("\n\n{error_list}", error_list = error_list.join("\n")) } else { "".into() } @@ -230,11 +230,13 @@ async fn credentials_cmd(args: &StackletCredentialsArgs) -> Result<String, CmdEr .add_row(vec!["PASSWORD", &credentials.password]); let output = format!( - "Credentials for {} ({}) in namespace '{}':", - args.product_name, args.stacklet_name, args.namespace + "Credentials for {product_name} ({stacklet_name}) in namespace {namespace:?}:", + product_name = args.product_name, + stacklet_name = args.stacklet_name, + namespace = args.namespace ); - Ok(format!("{}\n\n{}", output, table)) + Ok(format!("{output}\n\n{table}")) } None => Ok("No credentials".into()), } @@ -280,7 +282,7 @@ fn render_condition_error( ) -> Option<String> { if !is_good.unwrap_or(true) { let message = message.unwrap_or("-".into()); - return Some(format!("[{}]: {}", error_index, message)); + return Some(format!("[{error_index}]: {message}")); } None @@ -294,7 +296,7 @@ fn color_condition(condition: &str, is_good: Option<bool>, error_index: usize) - if is_good { condition.to_owned() } else { - format!("{}: See [{}]", condition, error_index) + format!("{condition}: See [{error_index}]") } } None => condition.to_owned(), @@ -308,6 +310,6 @@ fn render_errors(errors: Vec<String>) -> Option<String> { } else if errors.len() == 1 { Some(errors[0].clone()) } else { - Some(format!("{}\n---\n", errors.join("\n"))) + Some(format!("{errors}\n---\n", errors = errors.join("\n"))) } } diff --git a/rust/stackablectl/src/output/mod.rs b/rust/stackablectl/src/output/mod.rs index 9053ca87..20ef91ef 100644 --- a/rust/stackablectl/src/output/mod.rs +++ b/rust/stackablectl/src/output/mod.rs @@ -55,7 +55,7 @@ where let mut report = String::new(); // Print top most error - write!(report, "An unrecoverable error occured: {}\n\n", self)?; + write!(report, "An unrecoverable error occured: {self}\n\n")?; writeln!( report, "Caused by these errors (recent errors listed first):" diff --git a/rust/stackablectl/src/output/result.rs b/rust/stackablectl/src/output/result.rs index 28409bc9..1f36d397 100644 --- a/rust/stackablectl/src/output/result.rs +++ b/rust/stackablectl/src/output/result.rs @@ -55,9 +55,9 @@ impl ResultContext { description: impl Into<String>, ) -> &mut Self { self.command_hints.push(format!( - "Use \"{}\" to {}.", - command.into(), - description.into() + "Use \"{command}\" to {description}.", + command = command.into(), + description = description.into() )); self diff --git a/rust/xtask/src/docs.rs b/rust/xtask/src/docs.rs index 6f7f4b8b..d36feb65 100644 --- a/rust/xtask/src/docs.rs +++ b/rust/xtask/src/docs.rs @@ -43,8 +43,8 @@ pub fn generate() -> Result<(), GenDocsError> { .unwrap() .join(DOCS_BASE_PATH) .join(format!( - "{}.adoc", - if cmd.get_name() == cli.get_name() { + "{name}.adoc", + name = if cmd.get_name() == cli.get_name() { "index" } else { cmd.get_name() diff --git a/web/build.rs b/web/build.rs index 3e8de5e0..40145831 100644 --- a/web/build.rs +++ b/web/build.rs @@ -47,8 +47,8 @@ fn main() { } write!( File::create(out_dir.join("vite-asset-map.rs")).unwrap(), - "{}", - asset_map.build() + "{asset_map}", + asset_map = asset_map.build() ) .unwrap(); }