From 86528692280f89b04bc48347c9ba27dd1625230c Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 20:52:26 -0400 Subject: [PATCH 01/22] Implement no-upgrade-if-unchanged based on the --detailed-exit-code from helm-diff --- .../envs/dev/dev-cluster/config.yaml | 2 +- src/command.rs | 2 + src/helm.rs | 58 ++++++++++++--- src/main.rs | 71 +++++++++++++++---- src/output/text.rs | 1 + 5 files changed, 111 insertions(+), 23 deletions(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index bded123..93c88af 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: dev-cluster +context: minikube locked: false diff --git a/src/command.rs b/src/command.rs index a1e4dda..38f3978 100644 --- a/src/command.rs +++ b/src/command.rs @@ -22,6 +22,7 @@ pub struct CommandSuccess { pub stdout: String, pub stderr: String, pub duration: Duration, + pub exit_code: i32, // This field stores the exit code of the command to determine if it was successful or not, and whether or not there were any diffs. } impl CommandSuccess { @@ -189,6 +190,7 @@ impl CommandLine { cmd: self.clone(), stdout, stderr, + exit_code, duration, }), Err(kind) => Err(CommandError { diff --git a/src/helm.rs b/src/helm.rs index 53ee397..b6c5cdd 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -118,6 +118,8 @@ pub struct HelmResult { pub installation: Arc, pub result: CommandResult, pub command: Command, + // The exit code contains information about whether there were any diffs. + pub _exit_code: i32, } impl HelmResult { @@ -130,6 +132,7 @@ impl HelmResult { installation: installation.clone(), result, command, + _exit_code: 0, } } @@ -366,20 +369,29 @@ pub async fn template( } } +// The DiffResult struct is used to store the exit code of the diff command. +pub struct DiffResult { + pub _exit_code: i32, +} + /// Run the helm diff command. pub async fn diff( installation: &Arc, helm_repos: &HelmReposLock, tx: &MultiOutput, -) -> Result<()> { +) -> Result { + // Retrieve the helm chart for the given installation. let chart = helm_repos.get_helm_chart(&installation.chart_reference)?; + // Get the chart arguments from the chart. let (chart, mut chart_args) = get_args_from_chart(&chart); + // Construct the arguments for the helm diff command. let mut args = vec![ "diff".into(), "upgrade".into(), installation.name.clone().into(), chart, + "--detailed-exitcode".into(), // This flag ensures that the exit code will indicate if there are changes or errors. "--context=3".into(), "--no-color".into(), "--allow-unreleased".into(), @@ -389,22 +401,52 @@ pub async fn diff( installation.clone().context.clone().into(), ]; + // Append additional arguments from the installation configuration. args.append(&mut add_values_files(installation)); args.append(&mut chart_args); args.append(&mut get_template_parameters(installation)); + // Create a CommandLine instance with the helm path and the constructed arguments. let command_line = CommandLine(helm_path(), args); + // Run the command and await the result. let result = command_line.run().await; - let has_errors = result.is_err(); - let i_result = HelmResult::from_result(installation, result, Command::Diff); + // Check if there were any errors in the command execution. + let _has_errors = result.is_err(); + // Create a HelmResult instance from the command result. + let mut i_result = HelmResult::from_result(installation, result, Command::Diff); + + // Evaluate the detailed exit code - any non-zero exit code indicates changes (1) or errors (2). + let exit_code = if let Ok(command_success) = &i_result.result { + i_result._exit_code = command_success.exit_code; + match command_success.exit_code { + 0 => { + debug!("No changes detected!"); // Exit code 0 indicates no changes. + 0 + } + 1 => { + debug!("Errors encountered!"); // Exit code 1 indicates errors. + 1 + } + 2 => { + debug!("Changes detected!"); // Exit code 2 indicates changes. + 2 + } + _ => { + debug!("Unknown exit code"); // Any other exit code is considered unknown. + -1 + } + } + } else { + debug!("Other error encountered"); // If the command result is an error, return -1. + -1 + }; + + // Wrap the HelmResult in an Arc and send it via the MultiOutput channel. let i_result = Arc::new(i_result); tx.send(Message::InstallationResult(i_result)).await; - if has_errors { - Err(anyhow::anyhow!("diff operation failed")) - } else { - Ok(()) - } + // Return the exit code. Errors are no longer considered a failure and can be handled by the caller. + Ok(DiffResult { _exit_code: exit_code }) } /// Run the helm upgrade command. diff --git a/src/main.rs b/src/main.rs index c91d5aa..e6907ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,6 +56,7 @@ mod duration; mod utils; + /// An individual update #[derive(Clone, Debug)] pub struct Update { @@ -184,26 +185,61 @@ where Ok(rc) } +// Define the possible results of a job. +enum JobResult { + Unit, // Represents a unit result. + Diff(helm::DiffResult), // Represents a diff result. +} + +// Asynchronously run a job based on the provided command. async fn run_job( - command: &Request, - helm_repos: &HelmReposLock, - installation: &Arc, - tx: &MultiOutput, -) -> Result<()> { + command: &Request, // The command to execute. + helm_repos: &HelmReposLock, // The helm repositories lock. + installation: &Arc, // The installation details. + tx: &MultiOutput, // The multi-output channel. +) -> Result { match command { + // Handle the Upgrade request. Request::Upgrade { .. } => { + // Run the helm diff command. + let diff_result = helm::diff(installation, helm_repos, tx).await?; + // Check the exit code of the diff command. + if diff_result._exit_code == 0 || diff_result._exit_code == 2 { + // If no changes or only changes detected, return Unit. + return Ok(JobResult::Unit); + } + // Perform a dry-run upgrade. helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await + // Perform the actual upgrade. + helm::upgrade(installation, helm_repos, tx, false).await?; + // Return Unit result. + Ok(JobResult::Unit) } - Request::Diff { .. } => helm::diff(installation, helm_repos, tx).await, + // Handle the Diff request. + Request::Diff { .. } => { + // Run the helm diff command. + let diff_result = helm::diff(installation, helm_repos, tx).await?; + // Return the diff result. + Ok(JobResult::Diff(helm::DiffResult { _exit_code: diff_result._exit_code })) + }, Request::Test { .. } => { helm::outdated(installation, helm_repos, tx).await?; helm::lint(installation, tx).await?; - helm::template(installation, helm_repos, tx).await + helm::template(installation, helm_repos, tx).await?; + Ok(JobResult::Unit) + } + Request::Template { .. } => { + helm::template(installation, helm_repos, tx).await?; + Ok(JobResult::Unit) + } + Request::Outdated { .. } => { + helm::outdated(installation, helm_repos, tx).await?; + Ok(JobResult::Unit) + } + Request::Update { updates, .. } => { + helm::update(installation, tx, updates).await?; + Ok(JobResult::Unit) } - Request::Template { .. } => helm::template(installation, helm_repos, tx).await, - Request::Outdated { .. } => helm::outdated(installation, helm_repos, tx).await, - Request::Update { updates, .. } => helm::update(installation, tx, updates).await, } } @@ -824,8 +860,15 @@ async fn worker_thread( // Execute the job let result = run_job(command, helm_repos, &install, output).await; match &result { - Ok(()) => { - // Tell dispatcher job is done so it can update the dependancies + Ok(JobResult::Unit) => { + // Handle the unit case + tx_dispatch + .send(Dispatch::Done(HashIndex::get_hash_index(&install))) + .await?; + } + Ok(JobResult::Diff(helm::DiffResult { _exit_code })) => { + // Handle the diff result case + // You might want to log or process the diff_result here tx_dispatch .send(Dispatch::Done(HashIndex::get_hash_index(&install))) .await?; @@ -846,7 +889,7 @@ async fn worker_thread( output .send(Message::FinishedJob( install.clone(), - result.map_err(|err| err.to_string()), + result.map(|_| ()).map_err(|err| err.to_string()), stop - start, )) .await; diff --git a/src/output/text.rs b/src/output/text.rs index 20c88a1..3a3b9a4 100644 --- a/src/output/text.rs +++ b/src/output/text.rs @@ -216,6 +216,7 @@ fn process_message(msg: &Arc, state: &mut State) { command, result, installation, + _exit_code // This field is not used in this function, but it is part of the HelmResult struct. } = hr.as_ref(); let result_str = hr.result_line(); From f0fdbb3f38c5cd4245aa01220ce8631b33b1f12d Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 20:54:02 -0400 Subject: [PATCH 02/22] Revert config.yaml --- example/helm-values/envs/dev/dev-cluster/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index 93c88af..bded123 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: minikube +context: dev-cluster locked: false From 842f7ad91961e30236cd0f6c4d5028c8203064be Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:31:45 -0400 Subject: [PATCH 03/22] Add the comma causing the linting error from catgo fmt --- src/output/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output/text.rs b/src/output/text.rs index 3a3b9a4..f357696 100644 --- a/src/output/text.rs +++ b/src/output/text.rs @@ -216,7 +216,7 @@ fn process_message(msg: &Arc, state: &mut State) { command, result, installation, - _exit_code // This field is not used in this function, but it is part of the HelmResult struct. + _exit_code, // This field is not used in this function, but it is part of the HelmResult struct. } = hr.as_ref(); let result_str = hr.result_line(); From d28d4883962d50a17135b201afbad0fc9a1b4921 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:35:06 -0400 Subject: [PATCH 04/22] Remove the trailing { for cargo linting --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index e6907ae..ef455c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,8 +220,10 @@ async fn run_job( // Run the helm diff command. let diff_result = helm::diff(installation, helm_repos, tx).await?; // Return the diff result. - Ok(JobResult::Diff(helm::DiffResult { _exit_code: diff_result._exit_code })) - }, + Ok(JobResult::Diff(helm::DiffResult { + _exit_code: diff_result._exit_code, + })) + } Request::Test { .. } => { helm::outdated(installation, helm_repos, tx).await?; helm::lint(installation, tx).await?; From c397d38f2c08745eb4201e01f2e4a79eee52ffe9 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:35:49 -0400 Subject: [PATCH 05/22] Align the comment for cargo linting --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index ef455c0..ad5534e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -187,7 +187,7 @@ where // Define the possible results of a job. enum JobResult { - Unit, // Represents a unit result. + Unit, // Represents a unit result. Diff(helm::DiffResult), // Represents a diff result. } From d2f69c4f34376a32b5603c8a8d0715dc499de2a2 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:36:50 -0400 Subject: [PATCH 06/22] Align the comment for cargo linting --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index ad5534e..883f8c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -193,10 +193,10 @@ enum JobResult { // Asynchronously run a job based on the provided command. async fn run_job( - command: &Request, // The command to execute. - helm_repos: &HelmReposLock, // The helm repositories lock. + command: &Request, // The command to execute. + helm_repos: &HelmReposLock, // The helm repositories lock. installation: &Arc, // The installation details. - tx: &MultiOutput, // The multi-output channel. + tx: &MultiOutput, // The multi-output channel. ) -> Result { match command { // Handle the Upgrade request. From e4d0a9e30336dd9dabf58c920c429b82ee782a69 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:38:16 -0400 Subject: [PATCH 07/22] Code formatting for cargo linting --- src/helm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/helm.rs b/src/helm.rs index b6c5cdd..3bf705e 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -446,7 +446,9 @@ pub async fn diff( tx.send(Message::InstallationResult(i_result)).await; // Return the exit code. Errors are no longer considered a failure and can be handled by the caller. - Ok(DiffResult { _exit_code: exit_code }) + Ok(DiffResult { + _exit_code: exit_code + }) } /// Run the helm upgrade command. From a991aafdf0e289bbdf3a7635c3923a5e050a6d0d Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:38:38 -0400 Subject: [PATCH 08/22] Add the ',' for cargo linting --- src/helm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/helm.rs b/src/helm.rs index 3bf705e..c442d57 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -447,7 +447,7 @@ pub async fn diff( // Return the exit code. Errors are no longer considered a failure and can be handled by the caller. Ok(DiffResult { - _exit_code: exit_code + _exit_code: exit_code, }) } From 33ef24decf9e2ccafbab87b9952681a69997e2cc Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Sat, 24 Aug 2024 21:40:09 -0400 Subject: [PATCH 09/22] Remainder of cargo linting --- src/helm.rs | 2 +- src/main.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/helm.rs b/src/helm.rs index c442d57..df7eadd 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -446,7 +446,7 @@ pub async fn diff( tx.send(Message::InstallationResult(i_result)).await; // Return the exit code. Errors are no longer considered a failure and can be handled by the caller. - Ok(DiffResult { + Ok(DiffResult { _exit_code: exit_code, }) } diff --git a/src/main.rs b/src/main.rs index 883f8c9..05e1ce8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,7 +56,6 @@ mod duration; mod utils; - /// An individual update #[derive(Clone, Debug)] pub struct Update { From 02c006e87508b3b1860641d5fdb3ca61261bcd6d Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 12:14:19 -0400 Subject: [PATCH 10/22] Resolve CR --- .../envs/dev/dev-cluster/config.yaml | 2 +- src/helm.rs | 30 ++++----- src/main.rs | 64 +++++++------------ src/output/text.rs | 2 +- 4 files changed, 37 insertions(+), 61 deletions(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index bded123..93c88af 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: dev-cluster +context: minikube locked: false diff --git a/src/helm.rs b/src/helm.rs index df7eadd..600ca7a 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -119,7 +119,7 @@ pub struct HelmResult { pub result: CommandResult, pub command: Command, // The exit code contains information about whether there were any diffs. - pub _exit_code: i32, + pub exit_code: i32, } impl HelmResult { @@ -132,7 +132,7 @@ impl HelmResult { installation: installation.clone(), result, command, - _exit_code: 0, + exit_code: 0, } } @@ -370,9 +370,7 @@ pub async fn template( } // The DiffResult struct is used to store the exit code of the diff command. -pub struct DiffResult { - pub _exit_code: i32, -} +pub enum DiffResult { NoChanges, Changes, Errors, Unknown } /// Run the helm diff command. pub async fn diff( @@ -416,39 +414,37 @@ pub async fn diff( let mut i_result = HelmResult::from_result(installation, result, Command::Diff); // Evaluate the detailed exit code - any non-zero exit code indicates changes (1) or errors (2). - let exit_code = if let Ok(command_success) = &i_result.result { - i_result._exit_code = command_success.exit_code; + let diff_result = if let Ok(command_success) = &i_result.result { + i_result.exit_code = command_success.exit_code; match command_success.exit_code { 0 => { debug!("No changes detected!"); // Exit code 0 indicates no changes. - 0 + DiffResult::NoChanges } 1 => { debug!("Errors encountered!"); // Exit code 1 indicates errors. - 1 + DiffResult::Errors } 2 => { debug!("Changes detected!"); // Exit code 2 indicates changes. - 2 + DiffResult::Changes } _ => { debug!("Unknown exit code"); // Any other exit code is considered unknown. - -1 + DiffResult::Unknown } } } else { - debug!("Other error encountered"); // If the command result is an error, return -1. - -1 + debug!("Other exception encountered"); // If the command result is an error, return Unknown. + DiffResult::Unknown }; // Wrap the HelmResult in an Arc and send it via the MultiOutput channel. let i_result = Arc::new(i_result); tx.send(Message::InstallationResult(i_result)).await; - // Return the exit code. Errors are no longer considered a failure and can be handled by the caller. - Ok(DiffResult { - _exit_code: exit_code, - }) + // Return the diff result. + Ok(diff_result) } /// Run the helm upgrade command. diff --git a/src/main.rs b/src/main.rs index 05e1ce8..6685d8b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ mod command; mod helm; use helm::{HelmChart, Installation}; use helm::{HelmRepo, InstallationId}; +use helm::DiffResult; mod depends; use depends::{is_depends_ok, HashIndex, InstallationSet}; @@ -184,62 +185,49 @@ where Ok(rc) } -// Define the possible results of a job. -enum JobResult { - Unit, // Represents a unit result. - Diff(helm::DiffResult), // Represents a diff result. -} - -// Asynchronously run a job based on the provided command. async fn run_job( - command: &Request, // The command to execute. - helm_repos: &HelmReposLock, // The helm repositories lock. - installation: &Arc, // The installation details. - tx: &MultiOutput, // The multi-output channel. -) -> Result { + command: &Request, + helm_repos: &HelmReposLock, + installation: &Arc, + tx: &MultiOutput, +) -> Result<()> { match command { - // Handle the Upgrade request. Request::Upgrade { .. } => { - // Run the helm diff command. let diff_result = helm::diff(installation, helm_repos, tx).await?; - // Check the exit code of the diff command. - if diff_result._exit_code == 0 || diff_result._exit_code == 2 { - // If no changes or only changes detected, return Unit. - return Ok(JobResult::Unit); + match diff_result { + DiffResult::NoChanges | DiffResult::Changes => { + // If no changes or only changes detected, return Unit. + return Ok(()); + } + DiffResult::Errors | DiffResult::Unknown => { + // Handle errors or unknown cases if needed. + } } - // Perform a dry-run upgrade. helm::upgrade(installation, helm_repos, tx, true).await?; - // Perform the actual upgrade. helm::upgrade(installation, helm_repos, tx, false).await?; - // Return Unit result. - Ok(JobResult::Unit) + Ok(()) } - // Handle the Diff request. Request::Diff { .. } => { - // Run the helm diff command. - let diff_result = helm::diff(installation, helm_repos, tx).await?; - // Return the diff result. - Ok(JobResult::Diff(helm::DiffResult { - _exit_code: diff_result._exit_code, - })) + helm::diff(installation, helm_repos, tx).await?; + Ok(()) } Request::Test { .. } => { helm::outdated(installation, helm_repos, tx).await?; helm::lint(installation, tx).await?; helm::template(installation, helm_repos, tx).await?; - Ok(JobResult::Unit) + Ok(()) } Request::Template { .. } => { helm::template(installation, helm_repos, tx).await?; - Ok(JobResult::Unit) + Ok(()) } Request::Outdated { .. } => { helm::outdated(installation, helm_repos, tx).await?; - Ok(JobResult::Unit) + Ok(()) } Request::Update { updates, .. } => { helm::update(installation, tx, updates).await?; - Ok(JobResult::Unit) + Ok(()) } } } @@ -861,15 +849,7 @@ async fn worker_thread( // Execute the job let result = run_job(command, helm_repos, &install, output).await; match &result { - Ok(JobResult::Unit) => { - // Handle the unit case - tx_dispatch - .send(Dispatch::Done(HashIndex::get_hash_index(&install))) - .await?; - } - Ok(JobResult::Diff(helm::DiffResult { _exit_code })) => { - // Handle the diff result case - // You might want to log or process the diff_result here + Ok(()) => { tx_dispatch .send(Dispatch::Done(HashIndex::get_hash_index(&install))) .await?; diff --git a/src/output/text.rs b/src/output/text.rs index f357696..046044f 100644 --- a/src/output/text.rs +++ b/src/output/text.rs @@ -216,7 +216,7 @@ fn process_message(msg: &Arc, state: &mut State) { command, result, installation, - _exit_code, // This field is not used in this function, but it is part of the HelmResult struct. + exit_code, } = hr.as_ref(); let result_str = hr.result_line(); From a00597469a78f52b86db0796896fdd417eab8737 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 12:58:09 -0400 Subject: [PATCH 11/22] Add -b --bypass-skip-upgrade-on-no-changes #clap flag --- src/main.rs | 58 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6685d8b..6f099d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,7 @@ use std::collections::HashMap; use std::path::PathBuf; use std::str::{self, FromStr}; use std::sync::Arc; +use std::io::{self, Write}; use anyhow::Result; use anyhow::{anyhow, Context}; @@ -190,21 +191,39 @@ async fn run_job( helm_repos: &HelmReposLock, installation: &Arc, tx: &MultiOutput, + bypass_skip_upgrade_on_no_changes: bool, ) -> Result<()> { match command { Request::Upgrade { .. } => { let diff_result = helm::diff(installation, helm_repos, tx).await?; match diff_result { - DiffResult::NoChanges | DiffResult::Changes => { - // If no changes or only changes detected, return Unit. - return Ok(()); + DiffResult::NoChanges => { + if bypass_skip_upgrade_on_no_changes { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; + } else { + + print!("No changes detected. Upgrade anyway? (Y/n): "); + io::stdout().flush().unwrap(); + + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + let input = input.trim().to_lowercase(); + + if input == 'Y' || input == "y" || input == "yes" || input == "" { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; + } + } + } + DiffResult::Changes => { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; } DiffResult::Errors | DiffResult::Unknown => { // Handle errors or unknown cases if needed. } } - helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await?; Ok(()) } Request::Diff { .. } => { @@ -282,12 +301,17 @@ struct Args { /// Should we process releases that have auto set to false? #[clap(long, value_enum, default_value_t=AutoState::Yes)] auto: AutoState, + } #[derive(Subcommand, Debug, Clone)] enum Request { /// Upgrade/install releases. - Upgrade {}, + Upgrade { + /// Bypass skip upgrade on no changes. + #[clap(long, short = 'b')] + bypass_skip_upgrade_on_no_changes: bool, + }, /// Diff releases with current state. Diff {}, @@ -404,6 +428,13 @@ async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let args = Args::parse(); + + // Extract the bypass_skip_upgrade_on_no_changes flag if the command is Upgrade + let bypass_skip_upgrade_on_no_changes = if let Request::Upgrade { bypass_skip_upgrade_on_no_changes } = args.command { + bypass_skip_upgrade_on_no_changes + } else { + false + }; let output_types = if args.output.is_empty() { vec![OutputFormat::Text] @@ -449,7 +480,7 @@ async fn main() -> Result<()> { .await; // Save the error for now so we can clean up. - let rc = do_task(command, &args, &output_pipe).await; + let rc = do_task(command, &args, &output_pipe, bypass_skip_upgrade_on_no_changes).await; // Log the error. if let Err(err) = &rc { @@ -481,7 +512,7 @@ async fn main() -> Result<()> { rc } -async fn do_task(command: Arc, args: &Args, output: &output::MultiOutput) -> Result<()> { +async fn do_task(command: Arc, args: &Args, output: &output::MultiOutput, bypass_skip_upgrade_on_no_changes: bool) -> Result<()> { // let mut helm_repos = HelmRepos::new(); let (skipped_list, todo) = generate_todo(args)?; @@ -493,7 +524,7 @@ async fn do_task(command: Arc, args: &Args, output: &output::MultiOutpu } // let jobs: Jobs = (command, todo); - run_jobs_concurrently(command, todo, output, skipped).await + run_jobs_concurrently(command, todo, output, skipped, bypass_skip_upgrade_on_no_changes).await } type SkippedResult = Arc; @@ -688,6 +719,7 @@ async fn run_jobs_concurrently( todo: Vec>, output: &output::MultiOutput, skipped: InstallationSet, + bypass_skip_upgrade_on_no_changes: bool, ) -> Result<()> { let required_repos = if request.requires_helm_repos() { get_required_repos(&todo) @@ -696,7 +728,7 @@ async fn run_jobs_concurrently( }; let rc = with_helm_repos(required_repos, |repos| async { - run_jobs_concurrently_with_repos(request, todo, output, skipped, repos).await + run_jobs_concurrently_with_repos(request, todo, output, skipped, repos, bypass_skip_upgrade_on_no_changes).await }) .await; @@ -709,6 +741,7 @@ async fn run_jobs_concurrently_with_repos( output: &output::MultiOutput, skipped: InstallationSet, helm_repos: Arc, + bypass_skip_upgrade_on_no_changes: bool, ) -> Result<()> { let do_depends = request.do_depends(); // let skip_depends = !matches!(jobs.0, Task::Upgrade | Task::Test); @@ -731,7 +764,7 @@ async fn run_jobs_concurrently_with_repos( let request = request.clone(); let helm_repos = helm_repos.clone(); tokio::spawn(async move { - worker_thread(&request, &helm_repos, &tx_dispatch, &output).await + worker_thread(&request, &helm_repos, &tx_dispatch, &output, bypass_skip_upgrade_on_no_changes).await }) }) .collect(); @@ -827,6 +860,7 @@ async fn worker_thread( helm_repos: &HelmReposLock, tx_dispatch: &mpsc::Sender, output: &MultiOutput, + bypass_skip_upgrade_on_no_changes: bool, ) -> Result<()> { let mut errors = false; @@ -847,7 +881,7 @@ async fn worker_thread( .await; // Execute the job - let result = run_job(command, helm_repos, &install, output).await; + let result = run_job(command, helm_repos, &install, output, bypass_skip_upgrade_on_no_changes).await; match &result { Ok(()) => { tx_dispatch From c3c85546bde5a53ba84cbdb1ebf2b2a0e785a4b5 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 12:59:12 -0400 Subject: [PATCH 12/22] Based on result of char cargo test, change chars to strings --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 6f099d2..a7c3b1e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -210,7 +210,7 @@ async fn run_job( io::stdin().read_line(&mut input).unwrap(); let input = input.trim().to_lowercase(); - if input == 'Y' || input == "y" || input == "yes" || input == "" { + if input == "Y" || input == "y" || input == "yes" || input == "" { helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; } From 60148aea5965d0c59206b9cb0cd2711b752254d3 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 21:34:25 -0400 Subject: [PATCH 13/22] "Rust auto-linting, revert config.yaml context" --- .../envs/dev/dev-cluster/config.yaml | 2 +- src/helm.rs | 7 +- src/main.rs | 66 +++++++++++++++---- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index 93c88af..bded123 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: minikube +context: dev-cluster locked: false diff --git a/src/helm.rs b/src/helm.rs index 600ca7a..eb05cca 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -370,7 +370,12 @@ pub async fn template( } // The DiffResult struct is used to store the exit code of the diff command. -pub enum DiffResult { NoChanges, Changes, Errors, Unknown } +pub enum DiffResult { + NoChanges, + Changes, + Errors, + Unknown, +} /// Run the helm diff command. pub async fn diff( diff --git a/src/main.rs b/src/main.rs index a7c3b1e..f6b4e9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,10 +12,10 @@ extern crate lazy_static; use std::collections::HashMap; +use std::io::{self, Write}; use std::path::PathBuf; use std::str::{self, FromStr}; use std::sync::Arc; -use std::io::{self, Write}; use anyhow::Result; use anyhow::{anyhow, Context}; @@ -35,9 +35,9 @@ use tracing_subscriber::{Layer, Registry}; mod command; mod helm; +use helm::DiffResult; use helm::{HelmChart, Installation}; use helm::{HelmRepo, InstallationId}; -use helm::DiffResult; mod depends; use depends::{is_depends_ok, HashIndex, InstallationSet}; @@ -202,7 +202,6 @@ async fn run_job( helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; } else { - print!("No changes detected. Upgrade anyway? (Y/n): "); io::stdout().flush().unwrap(); @@ -301,7 +300,6 @@ struct Args { /// Should we process releases that have auto set to false? #[clap(long, value_enum, default_value_t=AutoState::Yes)] auto: AutoState, - } #[derive(Subcommand, Debug, Clone)] @@ -428,9 +426,12 @@ async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let args = Args::parse(); - + // Extract the bypass_skip_upgrade_on_no_changes flag if the command is Upgrade - let bypass_skip_upgrade_on_no_changes = if let Request::Upgrade { bypass_skip_upgrade_on_no_changes } = args.command { + let bypass_skip_upgrade_on_no_changes = if let Request::Upgrade { + bypass_skip_upgrade_on_no_changes, + } = args.command + { bypass_skip_upgrade_on_no_changes } else { false @@ -480,7 +481,13 @@ async fn main() -> Result<()> { .await; // Save the error for now so we can clean up. - let rc = do_task(command, &args, &output_pipe, bypass_skip_upgrade_on_no_changes).await; + let rc = do_task( + command, + &args, + &output_pipe, + bypass_skip_upgrade_on_no_changes, + ) + .await; // Log the error. if let Err(err) = &rc { @@ -512,7 +519,12 @@ async fn main() -> Result<()> { rc } -async fn do_task(command: Arc, args: &Args, output: &output::MultiOutput, bypass_skip_upgrade_on_no_changes: bool) -> Result<()> { +async fn do_task( + command: Arc, + args: &Args, + output: &output::MultiOutput, + bypass_skip_upgrade_on_no_changes: bool, +) -> Result<()> { // let mut helm_repos = HelmRepos::new(); let (skipped_list, todo) = generate_todo(args)?; @@ -524,7 +536,14 @@ async fn do_task(command: Arc, args: &Args, output: &output::MultiOutpu } // let jobs: Jobs = (command, todo); - run_jobs_concurrently(command, todo, output, skipped, bypass_skip_upgrade_on_no_changes).await + run_jobs_concurrently( + command, + todo, + output, + skipped, + bypass_skip_upgrade_on_no_changes, + ) + .await } type SkippedResult = Arc; @@ -726,9 +745,16 @@ async fn run_jobs_concurrently( } else { vec![] }; - let rc = with_helm_repos(required_repos, |repos| async { - run_jobs_concurrently_with_repos(request, todo, output, skipped, repos, bypass_skip_upgrade_on_no_changes).await + run_jobs_concurrently_with_repos( + request, + todo, + output, + skipped, + repos, + bypass_skip_upgrade_on_no_changes, + ) + .await }) .await; @@ -764,7 +790,14 @@ async fn run_jobs_concurrently_with_repos( let request = request.clone(); let helm_repos = helm_repos.clone(); tokio::spawn(async move { - worker_thread(&request, &helm_repos, &tx_dispatch, &output, bypass_skip_upgrade_on_no_changes).await + worker_thread( + &request, + &helm_repos, + &tx_dispatch, + &output, + bypass_skip_upgrade_on_no_changes, + ) + .await }) }) .collect(); @@ -881,7 +914,14 @@ async fn worker_thread( .await; // Execute the job - let result = run_job(command, helm_repos, &install, output, bypass_skip_upgrade_on_no_changes).await; + let result = run_job( + command, + helm_repos, + &install, + output, + bypass_skip_upgrade_on_no_changes, + ) + .await; match &result { Ok(()) => { tx_dispatch From bd41385c63349af729c320a00c01d872a96092c4 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 22:05:42 -0400 Subject: [PATCH 14/22] Implement --yes || -y for -b Upgrade suboption for CI environments --- .../envs/dev/dev-cluster/config.yaml | 2 +- src/main.rs | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index bded123..93c88af 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: dev-cluster +context: minikube locked: false diff --git a/src/main.rs b/src/main.rs index f6b4e9c..d20c1d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -192,6 +192,7 @@ async fn run_job( installation: &Arc, tx: &MultiOutput, bypass_skip_upgrade_on_no_changes: bool, + bypass_assume_yes: bool, ) -> Result<()> { match command { Request::Upgrade { .. } => { @@ -202,16 +203,21 @@ async fn run_job( helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; } else { - print!("No changes detected. Upgrade anyway? (Y/n): "); - io::stdout().flush().unwrap(); - - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - let input = input.trim().to_lowercase(); - - if input == "Y" || input == "y" || input == "yes" || input == "" { + if bypass_assume_yes { helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; + } else { + print!("No changes detected. Upgrade anyway? (Y/n): "); + io::stdout().flush().unwrap(); + + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + let input = input.trim().to_lowercase(); + + if input == "Y" || input == "y" || input == "yes" || input == "" { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; + } } } } @@ -309,6 +315,9 @@ enum Request { /// Bypass skip upgrade on no changes. #[clap(long, short = 'b')] bypass_skip_upgrade_on_no_changes: bool, + /// Assume yes at the prompt. + #[clap(long, short = 'y')] + yes: bool, }, /// Diff releases with current state. @@ -427,14 +436,15 @@ async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let args = Args::parse(); - // Extract the bypass_skip_upgrade_on_no_changes flag if the command is Upgrade - let bypass_skip_upgrade_on_no_changes = if let Request::Upgrade { + // Extract the bypass_skip_upgrade_on_no_changes and bypass_assume_yes flags if the command is Upgrade + let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes) = if let Request::Upgrade { bypass_skip_upgrade_on_no_changes, + yes, } = args.command { - bypass_skip_upgrade_on_no_changes + (bypass_skip_upgrade_on_no_changes, yes) } else { - false + (false, false) }; let output_types = if args.output.is_empty() { @@ -486,6 +496,7 @@ async fn main() -> Result<()> { &args, &output_pipe, bypass_skip_upgrade_on_no_changes, + bypass_assume_yes, ) .await; @@ -524,6 +535,7 @@ async fn do_task( args: &Args, output: &output::MultiOutput, bypass_skip_upgrade_on_no_changes: bool, + bypass_assume_yes: bool, ) -> Result<()> { // let mut helm_repos = HelmRepos::new(); @@ -542,6 +554,7 @@ async fn do_task( output, skipped, bypass_skip_upgrade_on_no_changes, + bypass_assume_yes, ) .await } @@ -739,6 +752,7 @@ async fn run_jobs_concurrently( output: &output::MultiOutput, skipped: InstallationSet, bypass_skip_upgrade_on_no_changes: bool, + bypass_assume_yes: bool, ) -> Result<()> { let required_repos = if request.requires_helm_repos() { get_required_repos(&todo) @@ -753,6 +767,7 @@ async fn run_jobs_concurrently( skipped, repos, bypass_skip_upgrade_on_no_changes, + bypass_assume_yes, ) .await }) @@ -768,6 +783,7 @@ async fn run_jobs_concurrently_with_repos( skipped: InstallationSet, helm_repos: Arc, bypass_skip_upgrade_on_no_changes: bool, + bypass_assume_yes: bool, ) -> Result<()> { let do_depends = request.do_depends(); // let skip_depends = !matches!(jobs.0, Task::Upgrade | Task::Test); @@ -796,6 +812,7 @@ async fn run_jobs_concurrently_with_repos( &tx_dispatch, &output, bypass_skip_upgrade_on_no_changes, + bypass_assume_yes, ) .await }) @@ -894,6 +911,7 @@ async fn worker_thread( tx_dispatch: &mpsc::Sender, output: &MultiOutput, bypass_skip_upgrade_on_no_changes: bool, + bypass_assume_yes: bool, ) -> Result<()> { let mut errors = false; @@ -920,6 +938,7 @@ async fn worker_thread( &install, output, bypass_skip_upgrade_on_no_changes, + bypass_assume_yes, ) .await; match &result { From bb0fd4391f9c0ea632e54dd573cb784a9ec42ae4 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 22:19:33 -0400 Subject: [PATCH 15/22] Implement -b -y || -n (--bypass-skip-upgrade-on-no-changes --no is the implicit case --- .../envs/dev/dev-cluster/config.yaml | 2 +- src/main.rs | 51 +++++++++++-------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/example/helm-values/envs/dev/dev-cluster/config.yaml b/example/helm-values/envs/dev/dev-cluster/config.yaml index 93c88af..bded123 100644 --- a/example/helm-values/envs/dev/dev-cluster/config.yaml +++ b/example/helm-values/envs/dev/dev-cluster/config.yaml @@ -1,2 +1,2 @@ -context: minikube +context: dev-cluster locked: false diff --git a/src/main.rs b/src/main.rs index d20c1d8..7324456 100644 --- a/src/main.rs +++ b/src/main.rs @@ -193,6 +193,7 @@ async fn run_job( tx: &MultiOutput, bypass_skip_upgrade_on_no_changes: bool, bypass_assume_yes: bool, + bypass_assume_no: bool, ) -> Result<()> { match command { Request::Upgrade { .. } => { @@ -200,24 +201,21 @@ async fn run_job( match diff_result { DiffResult::NoChanges => { if bypass_skip_upgrade_on_no_changes { - helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await?; - } else { if bypass_assume_yes { helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; - } else { - print!("No changes detected. Upgrade anyway? (Y/n): "); - io::stdout().flush().unwrap(); - - let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); - let input = input.trim().to_lowercase(); - - if input == "Y" || input == "y" || input == "yes" || input == "" { - helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await?; - } + } // bypass_assume_no is an implicit case + } else { + print!("No changes detected. Upgrade anyway? (Y/n): "); + io::stdout().flush().unwrap(); + + let mut input = String::new(); + io::stdin().read_line(&mut input).unwrap(); + let input = input.trim().to_lowercase(); + + if input == "y" || input == "yes" || input == "" { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; } } } @@ -315,9 +313,12 @@ enum Request { /// Bypass skip upgrade on no changes. #[clap(long, short = 'b')] bypass_skip_upgrade_on_no_changes: bool, - /// Assume yes at the prompt. - #[clap(long, short = 'y')] + /// Assume yes or no. + #[clap(long, short = 'y', conflicts_with = "no", default_value_t = false)] yes: bool, + /// Assume no. + #[clap(long, short = 'n', conflicts_with = "yes", default_value_t = false)] + no: bool, }, /// Diff releases with current state. @@ -437,14 +438,15 @@ async fn main() -> Result<()> { let args = Args::parse(); // Extract the bypass_skip_upgrade_on_no_changes and bypass_assume_yes flags if the command is Upgrade - let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes) = if let Request::Upgrade { + let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes, bypass_assume_no) = if let Request::Upgrade { bypass_skip_upgrade_on_no_changes, yes, + no, } = args.command { - (bypass_skip_upgrade_on_no_changes, yes) + (bypass_skip_upgrade_on_no_changes, yes, no) } else { - (false, false) + (false, false, false) }; let output_types = if args.output.is_empty() { @@ -497,6 +499,7 @@ async fn main() -> Result<()> { &output_pipe, bypass_skip_upgrade_on_no_changes, bypass_assume_yes, + bypass_assume_no, ) .await; @@ -536,6 +539,7 @@ async fn do_task( output: &output::MultiOutput, bypass_skip_upgrade_on_no_changes: bool, bypass_assume_yes: bool, + bypass_assume_no: bool, ) -> Result<()> { // let mut helm_repos = HelmRepos::new(); @@ -555,6 +559,7 @@ async fn do_task( skipped, bypass_skip_upgrade_on_no_changes, bypass_assume_yes, + bypass_assume_no, ) .await } @@ -753,6 +758,7 @@ async fn run_jobs_concurrently( skipped: InstallationSet, bypass_skip_upgrade_on_no_changes: bool, bypass_assume_yes: bool, + bypass_assume_no: bool, ) -> Result<()> { let required_repos = if request.requires_helm_repos() { get_required_repos(&todo) @@ -768,6 +774,7 @@ async fn run_jobs_concurrently( repos, bypass_skip_upgrade_on_no_changes, bypass_assume_yes, + bypass_assume_no, ) .await }) @@ -784,6 +791,7 @@ async fn run_jobs_concurrently_with_repos( helm_repos: Arc, bypass_skip_upgrade_on_no_changes: bool, bypass_assume_yes: bool, + bypass_assume_no: bool, ) -> Result<()> { let do_depends = request.do_depends(); // let skip_depends = !matches!(jobs.0, Task::Upgrade | Task::Test); @@ -813,6 +821,7 @@ async fn run_jobs_concurrently_with_repos( &output, bypass_skip_upgrade_on_no_changes, bypass_assume_yes, + bypass_assume_no, ) .await }) @@ -912,6 +921,7 @@ async fn worker_thread( output: &MultiOutput, bypass_skip_upgrade_on_no_changes: bool, bypass_assume_yes: bool, + bypass_assume_no: bool, ) -> Result<()> { let mut errors = false; @@ -939,6 +949,7 @@ async fn worker_thread( output, bypass_skip_upgrade_on_no_changes, bypass_assume_yes, + bypass_assume_no, ) .await; match &result { From 04adaecea3e4278f00a164ddeb481ee56a0a3a31 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 22:23:16 -0400 Subject: [PATCH 16/22] Cargo linting fix --- src/main.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7324456..83b7dd6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -438,16 +438,17 @@ async fn main() -> Result<()> { let args = Args::parse(); // Extract the bypass_skip_upgrade_on_no_changes and bypass_assume_yes flags if the command is Upgrade - let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes, bypass_assume_no) = if let Request::Upgrade { - bypass_skip_upgrade_on_no_changes, - yes, - no, - } = args.command - { - (bypass_skip_upgrade_on_no_changes, yes, no) - } else { - (false, false, false) - }; + let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes, bypass_assume_no) = + if let Request::Upgrade { + bypass_skip_upgrade_on_no_changes, + yes, + no, + } = args.command + { + (bypass_skip_upgrade_on_no_changes, yes, no) + } else { + (false, false, false) + }; let output_types = if args.output.is_empty() { vec![OutputFormat::Text] From 6fb180ebbfedd99174df79162fb2e8cb0938bae0 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 22:30:53 -0400 Subject: [PATCH 17/22] Update documentation --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index cd58fe4..3b7851f 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,35 @@ Only do this if you really want to deploy: cargo run -- --vdir ./example/helm-values upgrade ``` +## Additional Options + +### Bypass Upgrade on No Changes + +The `-b` or `--bypass-upgrade-on-no-changes` option allows you to bypass the upgrade process if no changes are detected. This can be useful to save time and resources when you are confident that no changes have been made to the release values. + +### Suboptions + +#### Yes + +The `-y` or `--yes` suboption can be used in conjunction with the `--bypass-upgrade-on-no-changes` option to automatically proceed with the upgrade even if no changes are detected. This is useful for automated scripts where manual intervention is not possible. + +#### No + +The `-n` or `--no` suboption can be used in conjunction with the `--bypass-upgrade-on-no-changes` option to automatically skip the upgrade if no changes are detected. This is useful when you want to ensure that upgrades are only performed when necessary without manual intervention. + +Example usage: + +```sh +# Bypass upgrade on no changes and automatically proceed with the upgrade +cargo run -- --vdir ./example/helm-values upgrade --bypass-upgrade-on-no-changes --yes +cargo run -- --vdir ./example/helm-values upgrade -b -y + + +# Bypass upgrade on no changes and automatically skip the upgrade +cargo run -- --vdir ./example/helm-values upgrade --bypass-upgrade-on-no-changes --no +cargo run -- --vdir ./example/helm-values upgrade -b -y +``` + ## Config layout You can have zero or more environments. From 56b802c4122e8eecf7642eb901900edaef88bfd0 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 22:43:05 -0400 Subject: [PATCH 18/22] Fix some clippy errors introduced - partial reversion in Main function --- src/main.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 83b7dd6..581c39d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ extern crate lazy_static; use std::collections::HashMap; -use std::io::{self, Write}; +use std::io::{self}; use std::path::PathBuf; use std::str::{self, FromStr}; use std::sync::Arc; @@ -207,13 +207,15 @@ async fn run_job( } // bypass_assume_no is an implicit case } else { print!("No changes detected. Upgrade anyway? (Y/n): "); - io::stdout().flush().unwrap(); let mut input = String::new(); - io::stdin().read_line(&mut input).unwrap(); + if let Err(e) = io::stdin().read_line(&mut input) { + eprintln!("Failed to read input: {e}"); + return Err(anyhow::anyhow!("Failed to read input")); + } let input = input.trim().to_lowercase(); - if input == "y" || input == "yes" || input == "" { + if input == "y" || input == "yes" || input.is_empty() { helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; } @@ -975,7 +977,7 @@ async fn worker_thread( output .send(Message::FinishedJob( install.clone(), - result.map(|_| ()).map_err(|err| err.to_string()), + result.map_err(|err| err.to_string()), stop - start, )) .await; From ffbf8ccf690c0a5fa98c6584e17726fc745eae62 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Mon, 26 Aug 2024 23:44:00 -0400 Subject: [PATCH 19/22] Add the UpgradeControl enum --- src/main.rs | 140 +++++++++++++++++++++------------------------------- 1 file changed, 57 insertions(+), 83 deletions(-) diff --git a/src/main.rs b/src/main.rs index 581c39d..04c5e99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,6 +67,14 @@ pub struct Update { pub value: String, } +/// For command line options, we need to control the upgrade process. +#[derive(Clone, Copy)] // Add Clone and Copy traits +enum UpgradeControl { + BypassAndAssumeYes, + BypassAndAssumeNo, + Normal, +} + impl FromStr for Update { type Err = anyhow::Error; @@ -191,33 +199,35 @@ async fn run_job( helm_repos: &HelmReposLock, installation: &Arc, tx: &MultiOutput, - bypass_skip_upgrade_on_no_changes: bool, - bypass_assume_yes: bool, - bypass_assume_no: bool, + upgrade_control: &UpgradeControl, ) -> Result<()> { match command { Request::Upgrade { .. } => { let diff_result = helm::diff(installation, helm_repos, tx).await?; match diff_result { DiffResult::NoChanges => { - if bypass_skip_upgrade_on_no_changes { - if bypass_assume_yes { + match upgrade_control { + UpgradeControl::BypassAndAssumeYes => { helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; - } // bypass_assume_no is an implicit case - } else { - print!("No changes detected. Upgrade anyway? (Y/n): "); - - let mut input = String::new(); - if let Err(e) = io::stdin().read_line(&mut input) { - eprintln!("Failed to read input: {e}"); - return Err(anyhow::anyhow!("Failed to read input")); } - let input = input.trim().to_lowercase(); - - if input == "y" || input == "yes" || input.is_empty() { - helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await?; + UpgradeControl::BypassAndAssumeNo => { + // Do nothing, as bypass_assume_no is an implicit case + } + UpgradeControl::Normal => { + print!("No changes detected. Upgrade anyway? (Y/n): "); + + let mut input = String::new(); + if let Err(e) = io::stdin().read_line(&mut input) { + eprintln!("Failed to read input: {e}"); + return Err(anyhow::anyhow!("Failed to read input")); + } + let input = input.trim().to_lowercase(); + + if input == "y" || input == "yes" || input.is_empty() { + helm::upgrade(installation, helm_repos, tx, true).await?; + helm::upgrade(installation, helm_repos, tx, false).await?; + } } } } @@ -440,17 +450,26 @@ async fn main() -> Result<()> { let args = Args::parse(); // Extract the bypass_skip_upgrade_on_no_changes and bypass_assume_yes flags if the command is Upgrade - let (bypass_skip_upgrade_on_no_changes, bypass_assume_yes, bypass_assume_no) = - if let Request::Upgrade { - bypass_skip_upgrade_on_no_changes, - yes, - no, - } = args.command - { - (bypass_skip_upgrade_on_no_changes, yes, no) + let upgrade_control = if let Request::Upgrade { + bypass_skip_upgrade_on_no_changes, + yes, + no, + } = args.command + { + if bypass_skip_upgrade_on_no_changes { + if yes { + UpgradeControl::BypassAndAssumeYes + } else if no { + UpgradeControl::BypassAndAssumeNo + } else { + UpgradeControl::Normal + } } else { - (false, false, false) - }; + UpgradeControl::Normal + } + } else { + UpgradeControl::Normal + }; let output_types = if args.output.is_empty() { vec![OutputFormat::Text] @@ -496,15 +515,7 @@ async fn main() -> Result<()> { .await; // Save the error for now so we can clean up. - let rc = do_task( - command, - &args, - &output_pipe, - bypass_skip_upgrade_on_no_changes, - bypass_assume_yes, - bypass_assume_no, - ) - .await; + let rc = do_task(command, &args, &output_pipe, upgrade_control).await; // Log the error. if let Err(err) = &rc { @@ -540,9 +551,7 @@ async fn do_task( command: Arc, args: &Args, output: &output::MultiOutput, - bypass_skip_upgrade_on_no_changes: bool, - bypass_assume_yes: bool, - bypass_assume_no: bool, + upgrade_control: UpgradeControl, ) -> Result<()> { // let mut helm_repos = HelmRepos::new(); @@ -555,16 +564,7 @@ async fn do_task( } // let jobs: Jobs = (command, todo); - run_jobs_concurrently( - command, - todo, - output, - skipped, - bypass_skip_upgrade_on_no_changes, - bypass_assume_yes, - bypass_assume_no, - ) - .await + run_jobs_concurrently(command, todo, output, skipped, &upgrade_control).await } type SkippedResult = Arc; @@ -759,9 +759,7 @@ async fn run_jobs_concurrently( todo: Vec>, output: &output::MultiOutput, skipped: InstallationSet, - bypass_skip_upgrade_on_no_changes: bool, - bypass_assume_yes: bool, - bypass_assume_no: bool, + upgrade_control: &UpgradeControl, ) -> Result<()> { let required_repos = if request.requires_helm_repos() { get_required_repos(&todo) @@ -769,17 +767,8 @@ async fn run_jobs_concurrently( vec![] }; let rc = with_helm_repos(required_repos, |repos| async { - run_jobs_concurrently_with_repos( - request, - todo, - output, - skipped, - repos, - bypass_skip_upgrade_on_no_changes, - bypass_assume_yes, - bypass_assume_no, - ) - .await + run_jobs_concurrently_with_repos(request, todo, output, skipped, repos, *upgrade_control) + .await }) .await; @@ -792,9 +781,7 @@ async fn run_jobs_concurrently_with_repos( output: &output::MultiOutput, skipped: InstallationSet, helm_repos: Arc, - bypass_skip_upgrade_on_no_changes: bool, - bypass_assume_yes: bool, - bypass_assume_no: bool, + upgrade_control: UpgradeControl, ) -> Result<()> { let do_depends = request.do_depends(); // let skip_depends = !matches!(jobs.0, Task::Upgrade | Task::Test); @@ -822,9 +809,7 @@ async fn run_jobs_concurrently_with_repos( &helm_repos, &tx_dispatch, &output, - bypass_skip_upgrade_on_no_changes, - bypass_assume_yes, - bypass_assume_no, + &upgrade_control, ) .await }) @@ -922,9 +907,7 @@ async fn worker_thread( helm_repos: &HelmReposLock, tx_dispatch: &mpsc::Sender, output: &MultiOutput, - bypass_skip_upgrade_on_no_changes: bool, - bypass_assume_yes: bool, - bypass_assume_no: bool, + upgrade_control: &UpgradeControl, ) -> Result<()> { let mut errors = false; @@ -945,16 +928,7 @@ async fn worker_thread( .await; // Execute the job - let result = run_job( - command, - helm_repos, - &install, - output, - bypass_skip_upgrade_on_no_changes, - bypass_assume_yes, - bypass_assume_no, - ) - .await; + let result = run_job(command, helm_repos, &install, output, upgrade_control).await; match &result { Ok(()) => { tx_dispatch From d6d3a31ead90c55ad99564495c63a6b5805e086a Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Tue, 27 Aug 2024 00:06:16 -0400 Subject: [PATCH 20/22] Suppress exit_code 2 --- src/command.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/command.rs b/src/command.rs index 38f3978..0292105 100644 --- a/src/command.rs +++ b/src/command.rs @@ -177,7 +177,7 @@ impl CommandLine { let kind = match output { Err(err) => Err(CommandErrorKind::FailedToStart { err }), Ok(output) => { - if output.status.success() { + if output.status.success() || exit_code == 2 { Ok(()) } else { Err(CommandErrorKind::BadExitCode {}) From 12810ef2fe159f39951e6f9d8403c76765abf005 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Tue, 27 Aug 2024 00:20:21 -0400 Subject: [PATCH 21/22] Suppress the single exit_code warning for unused variables in text.rs --- src/output/text.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/output/text.rs b/src/output/text.rs index 046044f..7797c16 100644 --- a/src/output/text.rs +++ b/src/output/text.rs @@ -216,6 +216,7 @@ fn process_message(msg: &Arc, state: &mut State) { command, result, installation, + #[allow(unused_variables)] // Suppress warning for this variable exit_code, } = hr.as_ref(); let result_str = hr.result_line(); From b304ef882f2064e0f66900d3e8a22e1e0a61c189 Mon Sep 17 00:00:00 2001 From: Tyler Maginnis Date: Tue, 27 Aug 2024 00:44:52 -0400 Subject: [PATCH 22/22] Remove UI prompt intererfered with TUI. Alter the way specific debug messages are written to avoid interfering with TUI mode --- src/helm.rs | 13 ++++++++----- src/main.rs | 20 ++------------------ 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/src/helm.rs b/src/helm.rs index eb05cca..e4af019 100644 --- a/src/helm.rs +++ b/src/helm.rs @@ -376,6 +376,9 @@ pub enum DiffResult { Errors, Unknown, } +async fn log_debug_message(tx: &MultiOutput, message: &str) { + tx.send(Message::Log(log!(Level::DEBUG, message))).await; +} /// Run the helm diff command. pub async fn diff( @@ -423,24 +426,24 @@ pub async fn diff( i_result.exit_code = command_success.exit_code; match command_success.exit_code { 0 => { - debug!("No changes detected!"); // Exit code 0 indicates no changes. + log_debug_message(tx, "No changes detected!").await; // Exit code 0 indicates no changes. DiffResult::NoChanges } 1 => { - debug!("Errors encountered!"); // Exit code 1 indicates errors. + log_debug_message(tx, "Errors encountered!").await; // Exit code 1 indicates errors. DiffResult::Errors } 2 => { - debug!("Changes detected!"); // Exit code 2 indicates changes. + log_debug_message(tx, "Changes detected!").await; // Exit code 2 indicates changes. DiffResult::Changes } _ => { - debug!("Unknown exit code"); // Any other exit code is considered unknown. + log_debug_message(tx, "Unknown exit code").await; // Any other exit code is considered unknown. DiffResult::Unknown } } } else { - debug!("Other exception encountered"); // If the command result is an error, return Unknown. + log_debug_message(tx, "Other exception encountered").await; // If the command result is an error, return Unknown. DiffResult::Unknown }; diff --git a/src/main.rs b/src/main.rs index 04c5e99..6899028 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,6 @@ extern crate lazy_static; use std::collections::HashMap; -use std::io::{self}; use std::path::PathBuf; use std::str::{self, FromStr}; use std::sync::Arc; @@ -211,23 +210,8 @@ async fn run_job( helm::upgrade(installation, helm_repos, tx, true).await?; helm::upgrade(installation, helm_repos, tx, false).await?; } - UpgradeControl::BypassAndAssumeNo => { - // Do nothing, as bypass_assume_no is an implicit case - } - UpgradeControl::Normal => { - print!("No changes detected. Upgrade anyway? (Y/n): "); - - let mut input = String::new(); - if let Err(e) = io::stdin().read_line(&mut input) { - eprintln!("Failed to read input: {e}"); - return Err(anyhow::anyhow!("Failed to read input")); - } - let input = input.trim().to_lowercase(); - - if input == "y" || input == "yes" || input.is_empty() { - helm::upgrade(installation, helm_repos, tx, true).await?; - helm::upgrade(installation, helm_repos, tx, false).await?; - } + UpgradeControl::BypassAndAssumeNo | UpgradeControl::Normal => { + // Do nothing, as these are implicit or default cases } } }