Feat: handle helm breaking changes upgrade (#414)

* feat: handle helm deploy version with breaking changes
* feat: set promtail breaking change version
This commit is contained in:
Benjamin
2021-10-01 14:19:10 +02:00
committed by GitHub
parent 871a1985dd
commit a4c594d038
11 changed files with 124 additions and 12 deletions

9
Cargo.lock generated
View File

@@ -2065,6 +2065,7 @@ dependencies = [
"rusoto_sts",
"rust-crypto",
"scaleway_api_rs",
"semver 1.0.4",
"serde",
"serde_derive",
"serde_json",
@@ -2720,7 +2721,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
"semver 0.9.0",
]
[[package]]
@@ -2799,6 +2800,12 @@ dependencies = [
"semver-parser",
]
[[package]]
name = "semver"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "semver-parser"
version = "0.7.0"

View File

@@ -18,6 +18,7 @@ rust-crypto = "0.2.36"
retry = "1.2.1"
trust-dns-resolver = "0.20.3"
rand = "0.8.3"
semver = "1.0.4"
gethostname = "0.2.1"
reqwest = { version = "0.11.3", features = ["blocking", "json"] }
futures = "0.3.15"

View File

@@ -6,6 +6,7 @@ use crate::cloud_provider::helm::{
use crate::cloud_provider::qovery::{get_qovery_app_version, QoveryAgent, QoveryAppName, QoveryEngine};
use crate::cmd::kubectl::{kubectl_exec_get_daemonset, kubectl_exec_with_output};
use crate::error::{SimpleError, SimpleErrorKind};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
@@ -365,6 +366,7 @@ pub fn aws_helm_charts(
let promtail = CommonChart {
chart_info: ChartInfo {
name: "promtail".to_string(),
last_breaking_version_requiring_restart: Some(Version::new(0, 24, 0)),
path: chart_path("common/charts/promtail"),
// because of priorityClassName, we need to add it to kube-system
namespace: HelmChartNamespaces::KubeSystem,

View File

@@ -1318,6 +1318,7 @@ impl<'a> Kubernetes for EKS<'a> {
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);

View File

@@ -5,6 +5,7 @@ use crate::cloud_provider::helm::{
};
use crate::cloud_provider::qovery::{get_qovery_app_version, QoveryAgent, QoveryAppName, QoveryEngine};
use crate::error::{SimpleError, SimpleErrorKind};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
@@ -206,6 +207,7 @@ pub fn do_helm_charts(
let promtail = CommonChart {
chart_info: ChartInfo {
name: "promtail".to_string(),
last_breaking_version_requiring_restart: Some(Version::new(0, 24, 0)),
path: chart_path("common/charts/promtail"),
// because of priorityClassName, we need to add it to kube-system
namespace: HelmChartNamespaces::KubeSystem,

View File

@@ -843,6 +843,7 @@ impl<'a> Kubernetes for DOKS<'a> {
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);

View File

@@ -1,8 +1,8 @@
use crate::cloud_provider::helm::HelmAction::Deploy;
use crate::cloud_provider::helm::HelmChartNamespaces::KubeSystem;
use crate::cmd::helm::{
helm_exec_uninstall_with_chart_info, helm_exec_upgrade_with_chart_info, helm_upgrade_diff_with_chart_info,
is_chart_deployed,
helm_destroy_chart_if_breaking_changes_version_detected, helm_exec_uninstall_with_chart_info,
helm_exec_upgrade_with_chart_info, helm_upgrade_diff_with_chart_info, is_chart_deployed,
};
use crate::cmd::kubectl::{
kubectl_exec_delete_crd, kubectl_exec_get_configmap, kubectl_exec_get_events,
@@ -11,6 +11,7 @@ use crate::cmd::kubectl::{
use crate::cmd::structs::HelmHistoryRow;
use crate::error::{SimpleError, SimpleErrorKind};
use crate::utilities::calculate_hash;
use semver::Version;
use std::collections::HashMap;
use std::path::Path;
use std::{fs, thread};
@@ -54,6 +55,7 @@ pub struct ChartInfo {
pub action: HelmAction,
pub atomic: bool,
pub force_upgrade: bool,
pub last_breaking_version_requiring_restart: Option<Version>,
pub timeout: String,
pub dry_run: bool,
pub wait: bool,
@@ -71,6 +73,7 @@ impl Default for ChartInfo {
action: Deploy,
atomic: true,
force_upgrade: false,
last_breaking_version_requiring_restart: None,
timeout: "180s".to_string(),
dry_run: false,
wait: true,
@@ -153,10 +156,22 @@ pub trait HelmChart: Send {
envs: &[(String, String)],
payload: Option<ChartPayload>,
) -> Result<Option<ChartPayload>, SimpleError> {
let environment_variables = envs.iter().map(|x| (x.0.as_str(), x.1.as_str())).collect();
match self.get_chart_info().action {
let environment_variables: Vec<(&str, &str)> = envs.iter().map(|x| (x.0.as_str(), x.1.as_str())).collect();
let chart_info = self.get_chart_info();
match chart_info.action {
HelmAction::Deploy => {
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, self.get_chart_info())?
if let Err(e) = helm_destroy_chart_if_breaking_changes_version_detected(
kubernetes_config,
&environment_variables,
chart_info,
) {
warn!(
"error while trying to destroy chart if breaking change is detected: {:?}",
e.message
);
}
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, chart_info)?
}
HelmAction::Destroy => {
let chart_info = self.get_chart_info();
@@ -508,10 +523,22 @@ impl HelmChart for PrometheusOperatorConfigChart {
envs: &[(String, String)],
payload: Option<ChartPayload>,
) -> Result<Option<ChartPayload>, SimpleError> {
let environment_variables = envs.iter().map(|x| (x.0.as_str(), x.1.as_str())).collect();
match self.get_chart_info().action {
let environment_variables: Vec<(&str, &str)> = envs.iter().map(|x| (x.0.as_str(), x.1.as_str())).collect();
let chart_info = self.get_chart_info();
match chart_info.action {
HelmAction::Deploy => {
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, self.get_chart_info())?
if let Err(e) = helm_destroy_chart_if_breaking_changes_version_detected(
kubernetes_config,
&environment_variables,
chart_info,
) {
warn!(
"error while trying to destroy chart if breaking change is detected: {:?}",
e.message
);
}
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, chart_info)?
}
HelmAction::Destroy => {
let chart_info = self.get_chart_info();

View File

@@ -6,6 +6,7 @@ use crate::cloud_provider::qovery::{get_qovery_app_version, QoveryAgent, QoveryA
use crate::cloud_provider::scaleway::application::Zone;
use crate::cloud_provider::scaleway::kubernetes::KapsuleOptions;
use crate::error::{SimpleError, SimpleErrorKind};
use semver::Version;
use serde::{Deserialize, Serialize};
use std::fs::File;
use std::io::BufReader;
@@ -191,6 +192,7 @@ pub fn scw_helm_charts(
let promtail = CommonChart {
chart_info: ChartInfo {
name: "promtail".to_string(),
last_breaking_version_requiring_restart: Some(Version::new(0, 24, 0)),
path: chart_path("common/charts/promtail"),
// because of priorityClassName, we need to add it to kube-system
namespace: HelmChartNamespaces::KubeSystem,

View File

@@ -764,6 +764,7 @@ impl<'a> Kubernetes for Kapsule<'a> {
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);

View File

@@ -14,7 +14,9 @@ use core::time;
use retry::delay::Fixed;
use retry::Error::Operation;
use retry::OperationResult;
use semver::Version;
use std::fs::File;
use std::str::FromStr;
use std::thread;
const HELM_DEFAULT_TIMEOUT_IN_SECONDS: u32 = 300;
@@ -66,6 +68,37 @@ where
.map(|helm_history_row| helm_history_row.clone()))
}
pub fn helm_destroy_chart_if_breaking_changes_version_detected(
kubernetes_config: &Path,
environment_variables: &Vec<(&str, &str)>,
chart_info: &ChartInfo,
) -> Result<(), SimpleError> {
// If there is a breaking version set for the current helm chart,
// then we compare this breaking version with the currently installed version if any.
// If current installed version is older than breaking change one, then we delete
// the chart before applying it.
if let Some(breaking_version) = &chart_info.last_breaking_version_requiring_restart {
let chart_namespace = get_chart_namespace(chart_info.namespace.clone());
if let Some(installed_version) = helm_get_chart_version(
kubernetes_config,
environment_variables.to_owned(),
Some(chart_namespace.as_str()),
chart_info.name.clone(),
) {
if installed_version.le(breaking_version) {
return helm_exec_uninstall(
kubernetes_config,
chart_namespace.as_str(),
chart_info.name.as_str(),
environment_variables.to_owned(),
);
}
}
}
Ok(())
}
pub fn helm_exec_upgrade_with_chart_info<P>(
kubernetes_config: P,
envs: &Vec<(&str, &str)>,
@@ -706,6 +739,29 @@ where
Ok(false)
}
pub fn helm_get_chart_version<P>(
kubernetes_config: P,
envs: Vec<(&str, &str)>,
namespace: Option<&str>,
chart_name: String,
) -> Option<Version>
where
P: AsRef<Path>,
{
match helm_list(kubernetes_config, envs, namespace) {
Ok(deployed_charts) => {
for chart in deployed_charts {
if chart.name == chart_name {
return chart.version;
}
}
None
}
Err(_) => None,
}
}
/// List deployed helm charts
///
/// # Arguments
@@ -754,7 +810,13 @@ where
match values {
Ok(all_helms) => {
for helm in all_helms {
helms_charts.push(HelmChart::new(helm.name, helm.namespace))
let raw_version = helm.chart.replace(format!("{}-", helm.name).as_str(), "");
let version = match Version::from_str(raw_version.as_str()) {
Ok(v) => Some(v),
Err(_) => None,
};
helms_charts.push(HelmChart::new(helm.name, helm.namespace, version))
}
}
Err(e) => {

View File

@@ -1,3 +1,4 @@
use semver::Version;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Hash)]
@@ -261,11 +262,16 @@ pub struct Helm {
pub struct HelmChart {
pub name: String,
pub namespace: String,
pub version: Option<Version>,
}
impl HelmChart {
pub fn new(name: String, namespace: String) -> HelmChart {
HelmChart { name, namespace }
pub fn new(name: String, namespace: String, version: Option<Version>) -> HelmChart {
HelmChart {
name,
namespace,
version,
}
}
}