From 9867c0c99e4b95073579aa779ddca751ac9019b9 Mon Sep 17 00:00:00 2001 From: Benjamin Chastanier Date: Fri, 25 Jun 2021 10:05:42 +0200 Subject: [PATCH] typo: fix digital ocean typo in error message --- src/cmd/helm.rs | 344 ++++++++++++++++++++++++++++++++++++++++----- src/cmd/kubectl.rs | 16 +++ 2 files changed, 324 insertions(+), 36 deletions(-) diff --git a/src/cmd/helm.rs b/src/cmd/helm.rs index cc4d4c10..cbffab35 100644 --- a/src/cmd/helm.rs +++ b/src/cmd/helm.rs @@ -4,14 +4,18 @@ use std::path::Path; use tracing::{error, info, span, Level}; use crate::cloud_provider::helm::{get_chart_namespace, ChartInfo}; -use crate::cmd::structs::{Helm, HelmChart, HelmHistoryRow}; +use crate::cmd::helm::HelmLockErrors::{IncorrectFormatDate, NotYetExpired, ParsingError}; +use crate::cmd::kubectl::{kubectl_exec_delete_secret, kubectl_exec_get_secrets}; +use crate::cmd::structs::{Helm, HelmChart, HelmHistoryRow, Item, KubernetesList}; use crate::cmd::utilities::exec_with_envs_and_output; use crate::error::{SimpleError, SimpleErrorKind}; -use chrono::Duration; +use chrono::{DateTime, Duration, Utc}; +use core::time; use retry::delay::Fixed; use retry::Error::Operation; use retry::OperationResult; use std::fs::File; +use std::thread; const HELM_DEFAULT_TIMEOUT_IN_SECONDS: u32 = 300; @@ -218,41 +222,225 @@ pub fn helm_exec_upgrade

( where P: AsRef, { - let timeout = format!( - "{}s", - match timeout { - Timeout::Value(v) => v + HELM_DEFAULT_TIMEOUT_IN_SECONDS, - Timeout::Default => HELM_DEFAULT_TIMEOUT_IN_SECONDS, - } - ); + let timeout_i64 = match timeout { + Timeout::Value(v) => v + HELM_DEFAULT_TIMEOUT_IN_SECONDS, + Timeout::Default => HELM_DEFAULT_TIMEOUT_IN_SECONDS, + } as i64; + let timeout_string = format!("{}s", &timeout_i64); - helm_exec_with_output( - vec![ - "upgrade", - "--kubeconfig", - kubernetes_config.as_ref().to_str().unwrap(), - "--create-namespace", - "--install", - "--history-max", - "50", - "--timeout", - timeout.as_str(), - "--wait", - "--namespace", - namespace, - release_name, - chart_root_dir.as_ref().to_str().unwrap(), - ], - envs, - |out| match out { - Ok(line) => info!("{}", line.as_str()), - Err(err) => error!("{}", err), - }, - |out| match out { - Ok(line) => error!("{}", line.as_str()), - Err(err) => error!("{}", err), - }, - ) + let result = retry::retry(Fixed::from_millis(15000).take(3), || { + let mut clean_lock = false; + match helm_exec_with_output( + vec![ + "upgrade", + "--kubeconfig", + kubernetes_config.as_ref().to_str().unwrap(), + "--create-namespace", + "--install", + "--history-max", + "50", + "--timeout", + timeout_string.as_str(), + "--wait", + "--namespace", + namespace, + release_name, + chart_root_dir.as_ref().to_str().unwrap(), + ], + envs.clone(), + |out| match out { + Ok(line) => info!("{}", line.as_str()), + Err(err) => error!("{}", err), + }, + |out| match out { + Ok(line) => { + error!("{}", line.as_str()); + if line.contains("another operation (install/upgrade/rollback) is in progress") { + clean_lock = true; + } + } + Err(err) => error!("{}", err), + }, + ) { + Ok(_) => { + if clean_lock { + return match clean_helm_lock( + &kubernetes_config, + &namespace, + &release_name, + timeout_i64.clone(), + envs.clone(), + ) { + Ok(_) => { + let e = SimpleError { + kind: SimpleErrorKind::Other, + message: Some("Helm lock detected and cleaned".to_string()), + }; + OperationResult::Retry(e) + } + Err(e) => OperationResult::Err(e), + }; + }; + OperationResult::Ok(()) + } + Err(e) => OperationResult::Retry(e), + } + }); + + match result { + Ok(_) => Ok(()), + Err(Operation { error, .. }) => return Err(error), + Err(retry::Error::Internal(e)) => return Err(SimpleError::new(SimpleErrorKind::Other, Some(e))), + } +} + +pub fn clean_helm_lock

( + kubernetes_config: P, + namespace: &str, + release_name: &str, + timeout: i64, + envs: Vec<(&str, &str)>, +) -> Result<(), SimpleError> +where + P: AsRef, +{ + let selector = format!("name={}", release_name); + let timeout_i64 = timeout; + + let result = retry::retry(Fixed::from_millis(3000).take(5), || { + // get secrets for this helm deployment + let result = match kubectl_exec_get_secrets(&kubernetes_config, namespace, &selector, envs.clone()) { + Ok(x) => x, + Err(e) => return OperationResult::Retry(e), + }; + + // get helm release name (secret) containing the lock and clean if possible + match helm_get_secret_lock_name(&result, timeout_i64.clone()) { + Ok(x) => return OperationResult::Ok(x), + Err(e) => match e.kind { + ParsingError => OperationResult::Retry(SimpleError { + kind: SimpleErrorKind::Other, + message: Some(e.message), + }), + IncorrectFormatDate => OperationResult::Retry(SimpleError { + kind: SimpleErrorKind::Other, + message: Some(e.message), + }), + NotYetExpired => { + if e.wait_before_release_lock.is_none() { + return OperationResult::Retry(SimpleError { + kind: SimpleErrorKind::Other, + message: Some( + "missing helm time to wait information, before releasing the lock".to_string(), + ), + }); + }; + + let time_to_wait = e.wait_before_release_lock.unwrap() as u64; + // wait 2min max to avoid the customer to re-launch a job or exit + if time_to_wait < 120 { + info!("waiting {}s before retrying the deployment...", time_to_wait); + thread::sleep(time::Duration::from_secs(time_to_wait)); + } else { + return OperationResult::Err(SimpleError { + kind: SimpleErrorKind::Other, + message: Some(e.message), + }); + } + + // retrieve now the secret + match helm_get_secret_lock_name(&result, timeout_i64.clone()) { + Ok(x) => OperationResult::Ok(x), + Err(e) => OperationResult::Err(SimpleError { + kind: SimpleErrorKind::Other, + message: Some(e.message), + }), + } + } + }, + } + }); + + match result { + Err(err) => { + return match err { + retry::Error::Operation { .. } => Err(SimpleError { + kind: SimpleErrorKind::Other, + message: Some(format!( + "internal error while trying to deploy helm chart {}", + release_name + )), + }), + retry::Error::Internal(err) => Err(SimpleError::new(SimpleErrorKind::Other, Some(err))), + } + } + Ok(x) => { + if let Err(e) = kubectl_exec_delete_secret(&kubernetes_config, x.as_str(), envs.clone()) { + return Err(e); + }; + Ok(()) + } + } +} + +pub enum HelmDeploymentErrors { + SimpleError, + HelmLockError, +} + +#[derive(Debug)] +pub enum HelmLockErrors { + ParsingError, + IncorrectFormatDate, + NotYetExpired, +} + +#[derive(Debug)] +pub struct HelmLockError { + kind: HelmLockErrors, + message: String, + wait_before_release_lock: Option, +} + +/// Get helm secret name containing the lock +pub fn helm_get_secret_lock_name(secrets_items: &KubernetesList, timeout: i64) -> Result { + match secrets_items.items.last() { + None => Err(HelmLockError { + kind: ParsingError, + message: "couldn't parse the list of secrets, it's certainly empty".to_string(), + wait_before_release_lock: None, + }), + Some(x) => { + let creation_time = match DateTime::parse_from_rfc3339(&x.metadata.creation_timestamp) { + Ok(x) => x, + Err(e) => { + return Err(HelmLockError { + kind: IncorrectFormatDate, + message: format!("incorrect format date input from secrets. {:?}", e), + wait_before_release_lock: None, + }) + } + }; + let now = Utc::now().timestamp(); + let max_timeout = creation_time.timestamp() + timeout; + + // not yet expired + if &now < &max_timeout { + let time_to_wait = &max_timeout - &now; + return Err(HelmLockError { + kind: NotYetExpired, + message: format!( + "helm lock has not yet expired, please wait {}s before retrying", + &time_to_wait + ), + wait_before_release_lock: Some(time_to_wait), + }); + } + + //expired + Ok(x.metadata.name.to_string()) + } + } } pub fn helm_exec_uninstall_with_chart_info

( @@ -676,3 +864,87 @@ where _ => Ok(()), } } + +#[cfg(test)] +mod tests { + use crate::cmd::helm::helm_get_secret_lock_name; + use crate::cmd::structs::{Item, KubernetesList}; + use chrono::{DateTime, NaiveDateTime, Utc}; + + #[test] + fn test_helm_lock_get_name() { + let json_content = r#" +{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "data": { + "release": "SDRzSUFBQUFBQUFDLzZSWWFYT2pPTGYrS3k3dTE4UU5PSFRIcm5vL0dEb0dPVFlaT3pFQ1RVOTFTWUlBdGdRMG00UDc5bisvSmNCYmIzUG5uVXFsclBWc2VwNmpJNzVLQ2VhQk5KRm9tZ2QrVWtnM1VweThwdExrcS9RYTUwWDUyUTh5bGphQkwwMGtWVmFWVy9uOXJYcjNJbzhubWp5UlB3enZGZlc5ZkQrKy80Q2tHNG5oZjdqQkQxaFF0a3ZiVGtIek9DdmpOSkVtRWtpS0VqTTJvQ25QeENMcFJpcEtYRmFGTkpHS0tndnlJdkFEWC9wMkk5RUk1NlV3bUFjbDluR0pSZnZhcTF1YUpxOXhLTjFJZFpBWG5RSjVxQXpsSDlST0IxYkErS0NWT1hoTjg4RmpSWUk4Q2NwQWhBWm5zWE1TVUt2dFNPWmNpcFJ1cExMSmhHcWNaU3ltdUpYNzdVWmlLZDFKazZSaTdFWXFBNTR4TEVSTy9qeVplaHA4OXprS21IQndXR1pNR05pNkpBV2pvbjRNczFsZzZSSGhxOUEzSXdaTUxTSndFeExWNi90MjZybHplUkdtWHhZalpXd2s1WWZGczc1REVHV0ViMExBN1pweVowZVNaZW1wNHdweGxpeDRLeU1HaGpKK1hXVWZna1lPa2Vsd3ozVUszNWhXS3pXS2FMS3FYdmlzUk0vVHl1R3p3b2ZPb2R2bi9PRnpwNkdjN2RDemZnK3NkZU5EYlFzK2VnZGc3VVBmbW1mRXNSWEVVUllZMDNqeERNSlhWMzRNUmtVSlRLZENoajUrWGFXUHZXOGZLWGNpMzl5RVhtZHo1TU0zR1poSUllYWJCcXlaNHNHM0RISEdrS0ZIMUpxR3BMWHA3dEZSTjBLWFFyamQ3WGRYb2IxZGhsNXIrekwwdUxQMVhPZUFudlVEVVJVR0hrckY0MDVEdUNNamQ5bkwwVG1HVG9Hc1plaTVjd2JNdDR4QUppTW9ZbjB2NHB2UlpwcDZ5YTZMOWNQNmFkUG9sUWVWakhBYTBwSE92R1pYR1R2R2dUVm54SFFpcW02T3NyZEUxV1FQc29vMitoYWJzOFkzMnIwTW1Fd0dscDhSY3g5NmZCUDZydDM1MStoUjUvdis1T2RqUEw0Nkl3dzFCb3o1bG96bURKbmFZUUh0bW5DVW9VYmpQbndydW4wZ1hMZ25MR1Fvdmp6RE5yYXRIUzhqeEdneXo1QzVPWitSb1VFUHZpbklYVllvY1FwaS91c3pMMFJNenZMWFhRdytwbU5ncmhuaU00VllxM0N4NjNHODYvQzlZQ2dpbHNOb28vM2FWcE54WU5vMVNkWVJodHJoSk52UW5oRjhZNTVyczZPOEMvOStuRE4wbVNaTzVUVlRkZGwwL21Hb3ZQZ1FjZXlHSVlqbEdGaW5lREppMmV5aXY2ZWNWYjdwaFNCMkRvdG41d0RpYWJYaFRvSGc3SUNldGFmT3B0NjJMbDRLNGNzV3I2MXV6c3JOeU9HSXN6dGdnQkxFK3ZuTW9iYTcwczFYL3o4K2RSdytZckhMSCtJTVZWYVR1TVdhUWxWbkI4eTVCcXgxS3ZEYVk3VHc0SndSNHg5aHI5OTdoYnVmeHVYNzNITHNPMmZiN2dXWHFQa1dlZW9tQkhFUkEyUCtDdUovbjJ1SXFwUkV2UXVKT1lzUmZEc1ljVnFmMXdzc3NnbzlUMk5QSFRjSXJpdmFLSUxESEVOYUhmZUFlRnFLUGRoMEN2S3NIYkF4N256L21JYkJxQWd4MUxiRWNuYS9rdE9mU3d5TXU2T3RmN3VIcWdKTHRreEc4MlY3TnRieWNuL0hBMFA3MkoyQnBsTkxoOGlkSHpBY1Z5MTIrR3hQRFMzMzRaeFJyakhmRkhtQTFZdVdVM1pHMUx2M3dDby8vRnJHUHFTdVUvdFgzRHZHTnhONXVjSWpKMGJ1dkVMdVduQTJJODI0OUtBV0lkWFpMZUJjZTRyMUQwRnp4WXNYNU01VkRLKzRkTUozMjIvU3g4M0pkL0JmbmR2UFk2Zjh6dTZXcDUyOWVrYTRYZmh3L1N2Y2Q1eU9wNytQczhvcU9scEhoTnZzSjNIbzhzTnZZckFhelpubnJsblAwLzRPR2ZPK2Z6akYwWnh0UFhXc2tLUy92MXliL2NOWU5UNW5XN1E1eWxrLzlmNlYzMkh0ZEVmOHVHZFZlU2Q3citxUjdtNDNvcitMNmZVOUZ1OHU3NEtUcnl0b2I4bklxWHlqUDROZjUrY0wzZk56Ky9KT1RHeEdFNVI1cXFON3FsMzdVSlA3R3VsbmVML095K0YvL2lOOXUvbEpUZGNWb0J4bnc0WmZGSFZZWlJYNm1JYXJ2aGg3Z2JPOUFBNTJMc25vdlJsY1liNDUyM251T25vSzA3QXYvTjZMQzY4djZCNUJYMURRa1M2UzVYdGdpaUpuVTlJUk8vaW1VeHJ4OUVTYVZvWXhEUkVzRHdzK0t6MDNxd21maldpamJZa3ExNTQ2THFrNXJoRFU1S2RZUHgyTWtBRU1QWCt5bHFYbjZ2dW5XRzhCdmhEZzJXZG4reDVhdXppR2J3TGs5OTIrYWZXMDNSeUFWVHdLM2QyL0xpN3ltcmIybjhaU0JHZUZiNGFYWTFkRUFxWmQrSzR0SXhkVXhCeHZQYmdQTWJ3clBiamUwVmlMYUNLS0o3YTNqKzBybmRNUVdIb3RDajFSS0NCb0s1UnZ2cHQzOW5TMGJoQ2NsYjN0Unp1NEI5OEszNHdhTW5JU2JPZ1ppWlVJbWV0bXdXY05OUjlDN0U3VnZuMXAvL2hTRHJYbU5ZR09qRTFIb2MzMC9kUExnN0xjcDJjOUpxcHA0a2VVcjhKRlBLMlJ1OTR1Um5ORzFYSGh4MXBMMGt0NW5qcmJZbk1UTGcvVFM1MEZVY2Y3SzczY0tZZzYyMTJPRVhNY0lYTWVFWE5XZVdvZkIwdnVmMXZTTkI3VWt1dmlYNGtJbnlVSXJsOEZMbHJ3ZDlob0U1cm9mMy9XeUowM1pEUS9uSFNiZHVTcEVRTWZsMmNici94dWsrNFhvcklLR0NBRThYUjNRZjdUaGVLcTY0b3E0d2E1ZGswc3dadGxTOVR2L1RpVFZwRDByeHVweHF3U2I3Q3ZFc2NKRGdQL3MzZ0FULzc4NitaeTRITWVGQ2tUNzBZeDllMUdLbWdVY0h4OHk3M0c3UG9kTjR3Q3h1TXdTZlBnekhQUTZDdlBYY3ZpY3FHTkxwTkd6NUNxMVZRVS9HckVTS3pIUG1RRk1sbUZHbjN2UVR2M29NOW9jL2NJR24yRFRYWUFscTFRUzY5cHNoYnRGTUczQXBoK1FWUVFFamlUUFZVa1UxcDBqNCtaakYzRWdLVkh2aGxlejV1ekNrM1RMVEExaHRTWjNGN3FSclJ2K2VwRzR2RXh3dTQ2QmNaRHRvajFQd2gvMDRESUJjLzYvdWdEc0hSR1k3MFFSZUFpVEt2MXpIN2RqTloxeTZOR1B4Vlh6czUrQWVZNkUveGVjRC96amZ2dTEyUUo0ZU1HcmRMS1M3Sm1zZTkrTWZRcmtXdU1XRXRSSTlaR3ljWFl3ZWQzdFJFdlJiNHNpYkRibkVlZVdpclUwTnRjUS9mcGwwVmlqK2cwL2JMZzh3aTMvWFhaOWNjTmh2VHhNV2xqQ2oxM25wR1Jjd0FQN0dIdExoOFhpZDRRTldQZWFQVzRFSThiK0ZBYmNWcjVwcktuZlB6RmlEV1ZxbmFOekUzZEpYcUJodjdMd2c4Z2t0S1VENHRJK2cyWUpHWFkva2sza2pLVWgvSlFrWVJFanBQNE5TaEthU0xkM3Q1K1N2NW44SnhXT1EwbWcrdXZHZTkrY2J0OFNuWng0azhHUmp1NHhObW41UHpkWWpLb2xVL0o4VXZKNUZNeUdBamNubVFmQjRvTUM0MjdpZ1MzUlZPVUFSY3pESk9BRmUydXdTRFlGVVBNOFNGTjhMNFkwcFMvb3luUDBpUkl5aXR4ZzhIdXZyakZXZGFMYTRkUDJvMDBEd1NISm9QLzdSWVBKOXBvOExWcnQycnlQTTJMY3o4S01DdWpjMzkzK2tBem9Ld3F5aUFmc3BSaU5vaVRXK3o3K1JEbkdSN0UyZnV1Y1NGNU1NaFN2eGpFU1JIUUtnOHVKNnFzS1BNQTg4dXhWOHhZR2VWcEZVWS9sMzFlL08zY3pQS1VCMlVVVk1WZ01sYTAwWG5tTmMzM09QY0h3OEc3b0tUdk9tUU14VkdlMTFCTW8yQXdrczhqTEUyemN5OFBXSXI5eTFuc0U4eHdRbnQzZWtzNk1GNUY5anF1M3lzNkc5ZURkTkJEOUNUMjhtT2FjaU9kUUNOTnBBdlVTTi8rTHdBQS8vOFpyL0YvWWhRQUFBPT0=" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2021-06-24T09:50:08Z", + "labels": { + "modifiedAt": "1624529703", + "name": "coredns", + "owner": "helm", + "status": "superseded", + "version": "1" + }, + "name": "sh.helm.release.v1.coredns.v1", + "namespace": "kube-system", + "resourceVersion": "562542", + "selfLink": "/api/v1/namespaces/kube-system/secrets/sh.helm.release.v1.coredns.v1", + "uid": "a635e9ea-f793-4369-918a-7d644e85988b" + }, + "type": "helm.sh/release.v1" + }, + { + "apiVersion": "v1", + "data": { + "release": "SDRzSUFBQUFBQUFDLzZSWWEzT2JPcmYrS3g3TzE4VGxFdExZTSs4SFEyT1FZNU50SjBhZzNUMGRTUkRBbG9CeXNZTjcrdC9QQ0lndmJkTjk5cnNuazdHdTY2Ym5XVnJpbTVSaUhrcGppV1pGR0tTbGRDVWw2VXNtamI5SkwwbFJWbCtDTUdkWkV3YlNXRkpsVmJtV2I2L1ZtMmQ1Tk5ibHNmeHhlS2VvdC9MZDZPNGprcTRraHQvZG9LaGo3WGFzNk1NYjVWYlhkVVcvRXh1Q2tJVlZ1N1R0bExSSThpckpVbWtzcmZPb3dFRTRvQm5QeFNMcFNpb3JYTldsTkphT0tyNWZTVFRHUlNYTTVXR0ZBMXhoMGI3MDZacG02VXNTU1ZmU0xpektUcnc4VklieVQwb25BenRrZk5ES0hMeGt4ZUNoSm1HUmhsVW9Bb1B6eEQwSzJLbnRTTzZlaTVTdXBLckpoV3FjNXl5aHVKWDcvVXBpR2QxSzQ3Um03RXFxUXA0ekxFU08venlhZWh6ODhDVU9XUjRXNWJES21UQ3dkVWtLdFhMM0VPWFQwRFppd3BkUllNVU1XSHBNNERvaXF0LzNuY3ozWnZJOHlyN09OV1ZrcHRYSCtaT3hSUkRsaEs4andKMGQ1ZTZXcEl2S1YwYzE0aXlkODFaR0FreGw5TExNUDRhTkhDSEw1YjdubG9FNXFaZHFITk4wV1QvemFZV2VKclhMcDJVQTNVTzN6LzBqNEc1RE9kdWlKK01PMktzbWdQb0dmUElQd041SGdUM0xpZXNvaUtNOE5DZkovQWxFTDU3OEVHcGxCU3kzUnFZeGVsbG1ENzF2bnloMzQ4QmFSMzVuY3h6QVZ4bFlTQ0hXcXc3c3FlTEQxeHh4eHBCcHhOU2VSS1MxNmViQlZkZENsMEs0MCszM2xwR3pXVVIrYS9zaThybTc4VDMzZ0o2TUExRVZCdTRyeGVkdVE3Z3JJMi9SeXpFNGhtNko3RVhrZXpNR3JOZWNRQ1lqS0dKOUorS2IwMmFTK2VtMmkvWDk2bkhkR0xVUGxaeHdHbEhOWUg2enJjMHQ0OENlTVdLNU1WWFhiN0kzUk5WbEg3S2FOc1lHVzlNbU1OdTlERmhNQm5hUUUyc2YrWHdkQlo3VCtkY1ljZWY3L3VqblF6SzZPQ01NZFFiTTJZWm9NNFlzL1RDSHpvNXdsS05HNXdGOExidDlJSnA3Unl6a0tEay93emEyclIzUEdtSTBuZVhJV3AvT3lOU2hEMThWNUMxcWxMb2xzZjcxbVpjaUppZjVxeTRHbjdJUnNGWU04YWxDN0dVMDMvWTQzbmI0bmpNVUU5dGx0TkhmdDlWaUhGak9qcVNyR0VQOWNKUnQ2azhJdmpMZmM5aWJ2RFAvZnA0ekRabW1idTAzRTNYUmRQNWhxRHdIRUhIc1JSRkk1QVRZeDNneVlqdnNyTCtubk5XQjVVY2djUS96Si9jQWtrbTk1bTZKNFBTQW52VEh6cWJldGk1ZUN1R0xGcSt0YnM2cXRlWnl4TmtOTUVFRkV1TjA1bERmWHVqbXkvOGZuem9PdjJHeHl4L2lERlcySTBtTE5ZV3E3aFpZTXgzWXEwemd0Y2RvNmNNWkkrWS93bDYvOXdKM3Y0ekxqN25scmUrZWJMc1RYS0xXYSt5cjZ3Z2taUUxNMlF0SS9uMnVJYXBTRWZVbUl0WTBRZkQxWUNiWjdyUmVZSkhWNkdtUytPcW9RWEJWMDBZUkhPWVkwdnB0RDBnbWxkaURMYmNrVC9vQm02UE85MDlaRkdwbGhLRytJYmE3ZlU5T2Z5NEpNRy9lYlAzYlBWUVZXSEprb3MwVzdkbllpL1A5SFE5TS9WTjNCcnBCYlFNaWIzYkFjRlMzMk9IVFBUWDFJb0F6UnJuT0FrdmtBYmFidDV4eWNxTGUzQUs3K3ZpK2pIMUVQWGNYWEhEdkxiNjV5TXMxMXR3RWViTWFlU3ZCMlp3MG84cUhlb3hVZHp1SE0vMHhNVDZHelFVdm5wRTNVekc4NE5JUjMyMi95UjdXUjkvQmYzVnV2NDZkOGp1N1c1NTI5aG81NFU0WndOVjd1Tzg0blV4K0gyZVYxVlJieFlRNzdCZHg2UExEYjJLdzFHYk05MWFzNTJsL2g0eDQzejhjNDJoTk43NDZVa2phMzErZXcvNWhySnFBc3cxYXY4bFpQZmIrVlQ5ZzdYaEgvTHhuV2Z0SGV5L3FrZTV1TitPL2krbmxQWlpzeisrQ282OUw2R3lJNXRhQjJaL0IrL241VFBmczFENi9FMU9IMFJUbHZ1b2F2dXJzQXFqTGZZMzBLN3hmNXVYb1AvK1J2bC85b3FickNsQ084MkhEejRvNnJMSWFmY3FpWlYrTVBjUHBYZ0FIdStkazlGOU5yckRBbW01OWJ4VS9SbG5VRjM2MzRzTHJDN29IMEJjVVZETkVzcndGbGloeTFoWFYyQ0d3M01wTUprZlN0RExNU1lSZ2RaanphZVY3K1k3d3FVWWJmVU5VZWVlcm80cGFveHBCWFg1TWpPUEJDQm5BTklwSGUxSDVuckYvVEl3VzRITUJubjErc3UrK3RZdGorQ3BBZnRmdG05U1BtL1VCMk9XRDBOMzlHK0lpMzlIVy91TllodUMwREt6b2ZPeUNTTUJ5eXNCelpPU0JtbGlqalEvM0VZWTNsUTlYVzVyb01VMUY4Y1Qyemx2N1F1Y2tBcmF4RTRXZUtCUVFkQlRLMXovTXUzdXFyUm9FcDFWdis1c2QzSWV2WldERkRkSGNGSnRHVGhJbFJ0YXFtZk5wUTYzN0NIc1R0VytmMno4NmwwUHQyWTVBVjhhV3E5Qm1jdnY0Zks4czl0bEpqNFYyTkExaXlwZlJQSm5za0xmYXpMVVpvK3FvREJLOUplbTVQRitkYnJDMWpoYUh5Ym5Pa3Fpai9ZVmU3cFpFblc3UHg0ZzFpcEUxaTRrMXJYMjFqNE10OTc4dGFSb2Y2dWxsOGEvRWhFOVRCRmN2QWhjdCtEdHN0QWxOOUg4OGErVE5HcUxORGtmZGxoUDdhc3pBcDhYSnhndS8yNlQ3bGFpc0JpYUlRRExabnBIL2VLRjQ2cXFteXFoQm5yTWp0dUROb2lYcWozNmNTQ3RJK3RlVnRNT3NGbSt3YnhMSEtZN0M0SXQ0L283Ly9PdnFmT0JMRVpZWkUrOUdNZlg5U2lwcEhITDg5cFo3U2RqbE8yNFloNHduVVpvVjRZbm5vREdXdnJlU3hlVkNHME1talpFalZkOVJVZkNyTVNPSmtRU1FsY2hpTldxTXZRK2R3b2NCbzgzTkEyaU1OYmJZQWRpT1FtMWpSOU9WYUdjSXZwYkFDa3FpZ29qQXFleXJJcG5Tc250OFRHWHNJUVpzSXc2czZITGVtdFpva20yQXBUT2tUdVgyVWpmamZjdFhMeGFQRHcxN3F3eVk5L2s4TWY0Zy9GVUhJaGM4R2ZzM0g0QnRNSm9ZcFNnQzUxRldyNmJPeTFwYjdWb2VOY2F4dUhLM3pqT3dWcm5nOTV3SGVXRGVkYjhXU3drZk5XaVoxWDZhTi9OOTk0dGhVSXRjWXlaNmhocXhOazdQeGc0QnY5bVp5VUxreTRvSXU2MVo3S3VWUWsyanpUVjBuMzJkcDQ1R0o5blhPWi9GdU8ydnFxNC9hakNrRHc5cEcxUG9lN09jYU80QjNMUDdsYmQ0bUtkR1E5U2MrZHJ5WVM0ZU4vQitaeVpaSFZqS252TFJWelBSVmFvNk8yU3RkMTJpRjJqb3Z5ejhCQ0lweS9pd2pLWGZnRWxTaHUyZmRDVXBRM2tvRHhWSlNPUTRUVjdDc3BMRzB2WDE5ZWYwZndaUFdWM1FjRHk0L0pyeDRaM2I1WE82VGRKZ1BERGJ3UVhPUDZlbjd4Ymp3VTc1bkw1OUtSbC9UZ2NEZ2R1ajdMZUJNc2RDNDdZbTRYWFpsRlhJeFF6REpHUmx1MnN3Q0xmbEVITjh5Rks4TDRjMDR4OW94dk1zRGRQcVF0eGdzTDBycjNHZTkrTGE0YU4yTXl0Q3dhSHg0SCs3eGNPeHJnMitkZTFXVFZGa1JYbnF4eUZtVlh6cWI0OGZhQWFVMVdVVkZrT1dVY3dHU1hxTmc2QVk0aUxIZ3lTLzdScG5rZ2VEUEF2S1FaS1dJYTJMOEh5aXpzdXFDREUvSDN2QmpGVnhrZFZSL0d2WnA4WGZUODI4eUhoWXhXRmREc1lqUmRkT015OVpzY2RGTUJnT1BvUVYvZEFoWXlpTzhyU0dZaHFIQTAwK2piQXN5MCs5SW1RWkRzNW5jVUF3d3ludDNla3Q2Y0I0RWRuTHVQNm82R1JjRDlKQkQ5R2oyUE9QYWJkWDBoRTAwbGc2UTQzMC9mOENBQUQvL3pheFIzbGdGQUFB" + }, + "kind": "Secret", + "metadata": { + "creationTimestamp": "2021-06-24T12:36:16Z", + "labels": { + "modifiedAt": "1624538178", + "name": "coredns", + "owner": "helm", + "status": "deployed", + "version": "2" + }, + "name": "sh.helm.release.v1.coredns.v2", + "namespace": "kube-system", + "resourceVersion": "603757", + "selfLink": "/api/v1/namespaces/kube-system/secrets/sh.helm.release.v1.coredns.v6", + "uid": "b71dfb0d-8c4e-4592-8155-322bde8d402f" + }, + "type": "helm.sh/release.v1" + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "", + "selfLink": "" + } +} + "#; + let mut secrets = serde_json::from_str::>(json_content).unwrap(); + + // expired lock should be ok + let res = helm_get_secret_lock_name(&secrets, 300).unwrap(); + assert_eq!(res, "sh.helm.release.v1.coredns.v2".to_string()); + + // lock is not expired yet + let time_in_future = NaiveDateTime::from_timestamp(Utc::now().timestamp() + 30, 0); + let time_in_future_datetime_format: DateTime = DateTime::from_utc(time_in_future, Utc); + secrets.items[1].metadata.creation_timestamp = time_in_future_datetime_format.to_rfc3339(); + let res = helm_get_secret_lock_name(&secrets, 300); + assert_eq!( + res.unwrap_err().message, + "helm lock has not yet expired, please wait 330s before retrying".to_string() + ) + } +} diff --git a/src/cmd/kubectl.rs b/src/cmd/kubectl.rs index abe9e53e..8855bf18 100644 --- a/src/cmd/kubectl.rs +++ b/src/cmd/kubectl.rs @@ -275,6 +275,22 @@ where } } +pub fn kubectl_exec_get_secrets

( + kubernetes_config: P, + namespace: &str, + selector: &str, + envs: Vec<(&str, &str)>, +) -> Result, SimpleError> +where + P: AsRef, +{ + kubectl_exec::>( + vec!["get", "secrets", "-o", "json", "-n", namespace, "-l", selector], + kubernetes_config, + envs, + ) +} + pub fn kubectl_exec_is_pod_ready

( kubernetes_config: P, namespace: &str,