Merge branch 'dev' into chore/upgrade_aws_charts

This commit is contained in:
MacLikorne
2022-02-21 09:22:11 +01:00
committed by GitHub
31 changed files with 1526 additions and 1354 deletions

View File

@@ -106,3 +106,6 @@ test-all-whole-enchilada = ["test-aws-whole-enchilada", "test-do-whole-enchilada
test-aws-all = ["test-aws-infra", "test-aws-managed-services", "test-aws-self-hosted", "test-aws-whole-enchilada"]
test-do-all = ["test-do-infra", "test-do-managed-services", "test-do-self-hosted", "test-do-whole-enchilada"]
test-scw-all = ["test-scw-infra", "test-scw-managed-services", "test-scw-self-hosted", "test-scw-whole-enchilada"]
# functionnal test with only a k8s cluster as a dependency
test-with-kube = []

View File

@@ -505,7 +505,7 @@ controller:
admissionWebhooks:
annotations: {}
enabled: true
enabled: false
failurePolicy: Fail
# timeoutSeconds: 10
port: 8443

View File

@@ -1342,7 +1342,7 @@ prometheusOperator:
## rules from making their way into prometheus and potentially preventing the container from starting
admissionWebhooks:
failurePolicy: Fail
enabled: true
enabled: false
## A PEM encoded CA bundle which will be used to validate the webhook's server certificate.
## If unspecified, system trust roots on the apiserver are used.
caBundle: ""
@@ -1377,7 +1377,7 @@ prometheusOperator:
# Use certmanager to generate webhook certs
certManager:
enabled: false
enabled: true
# issuerRef:
# name: "issuer"
# kind: "ClusterIssuer"

View File

@@ -1,9 +1,9 @@
apiVersion: v2
appVersion: 0.10.1
appVersion: 0.10.4
description: Automatically removes Cloud managed services and Kubernetes resources
based on tags with TTL
home: https://github.com/Qovery/pleco
icon: https://github.com/Qovery/pleco/raw/main/assets/pleco_logo.png
name: pleco
type: application
version: 0.10.1
version: 0.10.4

View File

@@ -148,8 +148,6 @@ spec:
{{ end }}
{{- end }}
env:
- name: "AWS_EXECUTION_ENV"
value: "pleco_{{ .Values.image.plecoImageTag }}_{{ .Values.environmentVariables.PLECO_IDENTIFIER }}"
{{ range $key, $value := .Values.environmentVariables -}}
- name: "{{ $key }}"
valueFrom:

View File

@@ -3,7 +3,7 @@ replicaCount: 1
image:
repository: qoveryrd/pleco
pullPolicy: IfNotPresent
plecoImageTag: "0.10.1"
plecoImageTag: "0.10.4"
cloudProvider: ""

View File

@@ -70,7 +70,7 @@ charts:
dest: services
no_sync: true
- name: pleco
version: 0.10.1
version: 0.10.4
repo_name: pleco
- name: do-k8s-token-rotate
version: 0.1.3

View File

@@ -1,7 +1,7 @@
use crate::cloud_provider::aws::kubernetes::{Options, VpcQoveryNetworkMode};
use crate::cloud_provider::helm::{
get_chart_for_shell_agent, get_engine_helm_action_from_location, ChartInfo, ChartPayload, ChartSetValue,
ChartValuesGenerated, CommonChart, CoreDNSConfigChart, HelmAction, HelmChart, HelmChartNamespaces,
ChartValuesGenerated, CommonChart, CoreDNSConfigChart, HelmChart, HelmChartNamespaces,
PrometheusOperatorConfigChart, ShellAgentContext,
};
use crate::cloud_provider::qovery::{get_qovery_app_version, EngineLocation, QoveryAgent, QoveryAppName, QoveryEngine};
@@ -459,6 +459,7 @@ pub fn aws_helm_charts(
},
};
/* Example to delete an old install
let old_prometheus_operator = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
name: "prometheus-operator".to_string(),
@@ -466,7 +467,7 @@ pub fn aws_helm_charts(
action: HelmAction::Destroy,
..Default::default()
},
};
};*/
let kube_prometheus_stack = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
@@ -705,7 +706,9 @@ datasources:
},
ChartSetValue {
key: "prometheus.servicemonitor.enabled".to_string(),
value: chart_config_prerequisites.ff_metrics_history_enabled.to_string(),
// Due to cycle, prometheus need tls certificate from cert manager, and enabling this will require
// prometheus to be already installed
value: "false".to_string(),
},
ChartSetValue {
key: "prometheus.servicemonitor.prometheusInstance".to_string(),
@@ -731,11 +734,11 @@ datasources:
// Webhooks resources limits
ChartSetValue {
key: "webhook.resources.limits.cpu".to_string(),
value: "20m".to_string(),
value: "200m".to_string(),
},
ChartSetValue {
key: "webhook.resources.requests.cpu".to_string(),
value: "20m".to_string(),
value: "50m".to_string(),
},
ChartSetValue {
key: "webhook.resources.limits.memory".to_string(),
@@ -1154,26 +1157,27 @@ datasources:
Box::new(q_storage_class),
Box::new(coredns_config),
Box::new(aws_vpc_cni_chart),
Box::new(old_prometheus_operator),
];
let mut level_2: Vec<Box<dyn HelmChart>> = vec![];
let level_2: Vec<Box<dyn HelmChart>> = vec![Box::new(cert_manager)];
let mut level_3: Vec<Box<dyn HelmChart>> = vec![
let mut level_3: Vec<Box<dyn HelmChart>> = vec![];
let mut level_4: Vec<Box<dyn HelmChart>> = vec![
Box::new(cluster_autoscaler),
Box::new(aws_iam_eks_user_mapper),
Box::new(aws_calico),
];
let mut level_4: Vec<Box<dyn HelmChart>> = vec![
let mut level_5: Vec<Box<dyn HelmChart>> = vec![
Box::new(metrics_server),
Box::new(aws_node_term_handler),
Box::new(external_dns),
];
let mut level_5: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress), Box::new(cert_manager)];
let mut level_6: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress)];
let mut level_6: Vec<Box<dyn HelmChart>> = vec![
let mut level_7: Vec<Box<dyn HelmChart>> = vec![
Box::new(cert_manager_config),
Box::new(qovery_agent),
Box::new(shell_agent),
@@ -1182,26 +1186,26 @@ datasources:
// observability
if chart_config_prerequisites.ff_metrics_history_enabled {
level_2.push(Box::new(kube_prometheus_stack));
level_4.push(Box::new(prometheus_adapter));
level_4.push(Box::new(kube_state_metrics));
level_3.push(Box::new(kube_prometheus_stack));
level_5.push(Box::new(prometheus_adapter));
level_5.push(Box::new(kube_state_metrics));
}
if chart_config_prerequisites.ff_log_history_enabled {
level_3.push(Box::new(promtail));
level_4.push(Box::new(loki));
level_4.push(Box::new(promtail));
level_5.push(Box::new(loki));
}
if chart_config_prerequisites.ff_metrics_history_enabled || chart_config_prerequisites.ff_log_history_enabled {
level_6.push(Box::new(grafana))
level_7.push(Box::new(grafana))
};
// pleco
if !chart_config_prerequisites.disable_pleco {
level_5.push(Box::new(pleco));
level_6.push(Box::new(pleco));
}
info!("charts configuration preparation finished");
Ok(vec![level_1, level_2, level_3, level_4, level_5, level_6])
Ok(vec![level_1, level_2, level_3, level_4, level_5, level_6, level_7])
}
// AWS CNI

View File

@@ -14,7 +14,7 @@ use crate::cloud_provider::aws::kubernetes::node::AwsInstancesType;
use crate::cloud_provider::aws::kubernetes::roles::get_default_roles_to_create;
use crate::cloud_provider::aws::regions::{AwsRegion, AwsZones};
use crate::cloud_provider::environment::Environment;
use crate::cloud_provider::helm::deploy_charts_levels;
use crate::cloud_provider::helm::{deploy_charts_levels, ChartInfo};
use crate::cloud_provider::kubernetes::{
is_kubernetes_upgrade_required, send_progress_on_long_task, uninstall_cert_manager, Kind, Kubernetes,
KubernetesNodesType, KubernetesUpgradeStatus, ProviderOptions,
@@ -24,11 +24,11 @@ use crate::cloud_provider::qovery::EngineLocation;
use crate::cloud_provider::utilities::print_action;
use crate::cloud_provider::{kubernetes, CloudProvider};
use crate::cmd;
use crate::cmd::helm::{to_engine_error, Helm};
use crate::cmd::kubectl::{
kubectl_exec_api_custom_metrics, kubectl_exec_get_all_namespaces, kubectl_exec_get_events,
kubectl_exec_scale_replicas, ScalingKind,
};
use crate::cmd::structs::HelmChart;
use crate::cmd::terraform::{terraform_exec, terraform_init_validate_plan_apply, terraform_init_validate_state_list};
use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_managed_namespaces};
use crate::dns_provider;
@@ -1247,15 +1247,14 @@ impl<'a> EKS<'a> {
);
// delete custom metrics api to avoid stale namespaces on deletion
let _ = cmd::helm::helm_uninstall_list(
let helm = Helm::new(
&kubernetes_config_file_path,
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);
&self.cloud_provider.credentials_environment_variables(),
)
.map_err(|e| to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_release_name("metrics-server", "kube-system");
helm.uninstall(&chart, &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
// required to avoid namespace stuck on deletion
uninstall_cert_manager(
@@ -1275,50 +1274,27 @@ impl<'a> EKS<'a> {
let qovery_namespaces = get_qovery_managed_namespaces();
for qovery_namespace in qovery_namespaces.iter() {
let charts_to_delete = cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
Some(qovery_namespace),
);
match charts_to_delete {
Ok(charts) => {
for chart in charts {
match cmd::helm::helm_exec_uninstall(
&kubernetes_config_file_path,
&chart.namespace,
&chart.name,
self.cloud_provider().credentials_environment_variables(),
) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`", chart.name);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
),
)
}
}
}
}
Err(e) => {
if !(e.message().contains("not found")) {
let charts_to_delete = helm
.list_release(Some(qovery_namespace), &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
for chart in charts_to_delete {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`: {}", &chart.name, e);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Can't delete the namespace {}",
qovery_namespace
)),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1373,18 +1349,11 @@ impl<'a> EKS<'a> {
),
);
match cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
None,
) {
match helm.list_release(None, &vec![]) {
Ok(helm_charts) => {
for chart in helm_charts {
match cmd::helm::helm_uninstall_list(
&kubernetes_config_file_path,
vec![chart.clone()],
self.cloud_provider().credentials_environment_variables(),
) {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
@@ -1393,12 +1362,12 @@ impl<'a> EKS<'a> {
),
),
Err(e) => {
let message_safe = format!("Error deleting chart `{}` deleted", chart.name);
let message_safe = format!("Error deleting chart `{}`: {}", chart.name, e);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, e.message),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1411,7 +1380,7 @@ impl<'a> EKS<'a> {
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe.to_string(), Some(e.message())),
EventMessage::new(message_safe.to_string(), Some(e.to_string())),
),
)
}

View File

@@ -1,5 +1,6 @@
use tera::Context as TeraContext;
use crate::cloud_provider::helm::ChartInfo;
use crate::cloud_provider::models::{CustomDomain, CustomDomainDataTemplate, Route, RouteDataTemplate};
use crate::cloud_provider::service::{
default_tera_context, delete_router, deploy_stateless_service_error, send_progress_on_long_task, Action, Create,
@@ -7,8 +8,9 @@ use crate::cloud_provider::service::{
};
use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::cmd::helm;
use crate::cmd::helm::{to_engine_error, Timeout};
use crate::error::{EngineError, EngineErrorScope};
use crate::errors::EngineError as NewEngineError;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::models::{Context, Listen, Listener, Listeners};
@@ -325,25 +327,26 @@ impl Create for Router {
}
// do exec helm upgrade and return the last deployment status
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
self.selector(),
workspace_dir.as_str(),
self.start_timeout(),
kubernetes.cloud_provider().credentials_environment_variables(),
self.service_type(),
let helm = helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
})?;
.map_err(|e| to_engine_error(&event_details, e).to_legacy_engine_error())?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
environment.namespace().to_string(),
600_i64,
match self.service_type() {
ServiceType::Database(_) => vec![format!("{}/q-values.yaml", &workspace_dir)],
_ => vec![],
},
false,
self.selector(),
);
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
return Err(self.engine_error(EngineErrorCause::Internal, "Router has failed to be deployed".into()));
}
Ok(())
helm.upgrade(&chart, &vec![])
.map_err(|e| NewEngineError::new_helm_error(event_details.clone(), e).to_legacy_engine_error())
}
fn on_create_check(&self) -> Result<(), EngineError> {

View File

@@ -1,8 +1,7 @@
use crate::cloud_provider::digitalocean::kubernetes::DoksOptions;
use crate::cloud_provider::helm::{
get_chart_for_shell_agent, get_engine_helm_action_from_location, ChartInfo, ChartSetValue, ChartValuesGenerated,
CommonChart, CoreDNSConfigChart, HelmAction, HelmChart, HelmChartNamespaces, PrometheusOperatorConfigChart,
ShellAgentContext,
CommonChart, CoreDNSConfigChart, HelmChart, HelmChartNamespaces, PrometheusOperatorConfigChart, ShellAgentContext,
};
use crate::cloud_provider::qovery::{get_qovery_app_version, EngineLocation, QoveryAgent, QoveryAppName, QoveryEngine};
use crate::errors::CommandError;
@@ -309,6 +308,7 @@ pub fn do_helm_charts(
},
};
/*
let old_prometheus_operator = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
name: "prometheus-operator".to_string(),
@@ -316,7 +316,7 @@ pub fn do_helm_charts(
action: HelmAction::Destroy,
..Default::default()
},
};
};*/
let kube_prometheus_stack = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
@@ -544,7 +544,9 @@ datasources:
},
ChartSetValue {
key: "prometheus.servicemonitor.enabled".to_string(),
value: chart_config_prerequisites.ff_metrics_history_enabled.to_string(),
// Due to cycle, prometheus need tls certificate from cert manager, and enabling this will require
// prometheus to be already installed
value: "false".to_string(),
},
ChartSetValue {
key: "prometheus.servicemonitor.prometheusInstance".to_string(),
@@ -570,11 +572,11 @@ datasources:
// Webhooks resources limits
ChartSetValue {
key: "webhook.resources.limits.cpu".to_string(),
value: "20m".to_string(),
value: "200m".to_string(),
},
ChartSetValue {
key: "webhook.resources.requests.cpu".to_string(),
value: "20m".to_string(),
value: "50m".to_string(),
},
ChartSetValue {
key: "webhook.resources.limits.memory".to_string(),
@@ -1027,19 +1029,15 @@ datasources:
};
// chart deployment order matters!!!
let level_1: Vec<Box<dyn HelmChart>> = vec![
Box::new(q_storage_class),
Box::new(coredns_config),
Box::new(old_prometheus_operator),
];
let level_1: Vec<Box<dyn HelmChart>> = vec![Box::new(q_storage_class), Box::new(coredns_config)];
let mut level_2: Vec<Box<dyn HelmChart>> = vec![Box::new(container_registry_secret)];
let mut level_2: Vec<Box<dyn HelmChart>> = vec![Box::new(container_registry_secret), Box::new(cert_manager)];
let mut level_3: Vec<Box<dyn HelmChart>> = vec![];
let mut level_4: Vec<Box<dyn HelmChart>> = vec![Box::new(metrics_server), Box::new(external_dns)];
let mut level_5: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress), Box::new(cert_manager)];
let mut level_5: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress)];
let mut level_6: Vec<Box<dyn HelmChart>> = vec![
Box::new(cert_manager_config),

View File

@@ -26,11 +26,10 @@ use crate::cloud_provider::models::NodeGroups;
use crate::cloud_provider::qovery::EngineLocation;
use crate::cloud_provider::utilities::{print_action, VersionsNumber};
use crate::cloud_provider::{kubernetes, CloudProvider};
use crate::cmd::helm::{helm_exec_upgrade_with_chart_info, helm_upgrade_diff_with_chart_info};
use crate::cmd::helm::{to_engine_error, Helm};
use crate::cmd::kubectl::{
do_kubectl_exec_get_loadbalancer_id, kubectl_exec_get_all_namespaces, kubectl_exec_get_events,
};
use crate::cmd::structs::HelmChart;
use crate::cmd::terraform::{terraform_exec, terraform_init_validate_plan_apply, terraform_init_validate_state_list};
use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_managed_namespaces};
use crate::dns_provider::DnsProvider;
@@ -816,18 +815,16 @@ impl<'a> DOKS<'a> {
..Default::default()
};
let _ = helm_upgrade_diff_with_chart_info(
&kubeconfig_path,
&credentials_environment_variables,
&load_balancer_dns_hostname,
);
helm_exec_upgrade_with_chart_info(
let helm = Helm::new(
&kubeconfig_path,
&self.cloud_provider.credentials_environment_variables(),
&load_balancer_dns_hostname,
)
.map_err(|e| EngineError::new_helm_charts_deploy_error(event_details.clone(), e))
.map_err(|e| EngineError::new_helm_error(event_details.clone(), e))?;
// This will ony print the diff on stdout
let _ = helm.upgrade_diff(&load_balancer_dns_hostname, &vec![]);
helm.upgrade(&load_balancer_dns_hostname, &vec![])
.map_err(|e| EngineError::new_helm_error(event_details.clone(), e))
}
fn create_error(&self) -> Result<(), EngineError> {
@@ -1096,15 +1093,14 @@ impl<'a> DOKS<'a> {
);
// delete custom metrics api to avoid stale namespaces on deletion
let _ = cmd::helm::helm_uninstall_list(
let helm = Helm::new(
&kubernetes_config_file_path,
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);
&self.cloud_provider.credentials_environment_variables(),
)
.map_err(|e| to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_release_name("metrics-server", "kube-system");
helm.uninstall(&chart, &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
// required to avoid namespace stuck on deletion
uninstall_cert_manager(
@@ -1124,50 +1120,27 @@ impl<'a> DOKS<'a> {
let qovery_namespaces = get_qovery_managed_namespaces();
for qovery_namespace in qovery_namespaces.iter() {
let charts_to_delete = cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
Some(qovery_namespace),
);
match charts_to_delete {
Ok(charts) => {
for chart in charts {
match cmd::helm::helm_exec_uninstall(
&kubernetes_config_file_path,
&chart.namespace,
&chart.name,
self.cloud_provider().credentials_environment_variables(),
) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`", chart.name);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
),
)
}
}
}
}
Err(e) => {
if !(e.message().contains("not found")) {
let charts_to_delete = helm
.list_release(Some(qovery_namespace), &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
for chart in charts_to_delete {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`", chart.name);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Can't delete the namespace {}",
qovery_namespace
)),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1222,18 +1195,11 @@ impl<'a> DOKS<'a> {
),
);
match cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
None,
) {
match helm.list_release(None, &vec![]) {
Ok(helm_charts) => {
for chart in helm_charts {
match cmd::helm::helm_uninstall_list(
&kubernetes_config_file_path,
vec![chart.clone()],
self.cloud_provider().credentials_environment_variables(),
) {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
@@ -1242,12 +1208,12 @@ impl<'a> DOKS<'a> {
),
),
Err(e) => {
let message_safe = format!("Error deleting chart `{}` deleted", chart.name);
let message_safe = format!("Error deleting chart `{}`: {}", chart.name, e);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, e.message),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1260,7 +1226,7 @@ impl<'a> DOKS<'a> {
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe.to_string(), Some(e.message())),
EventMessage::new(message_safe.to_string(), Some(e.to_string())),
),
)
}

View File

@@ -1,5 +1,6 @@
use tera::Context as TeraContext;
use crate::cloud_provider::helm::ChartInfo;
use crate::cloud_provider::models::{CustomDomain, CustomDomainDataTemplate, Route, RouteDataTemplate};
use crate::cloud_provider::service::{
default_tera_context, delete_router, deploy_stateless_service_error, send_progress_on_long_task, Action, Create,
@@ -7,6 +8,7 @@ use crate::cloud_provider::service::{
};
use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm;
use crate::cmd::helm::Timeout;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::errors::EngineError as NewEngineError;
@@ -345,25 +347,26 @@ impl Create for Router {
}
// do exec helm upgrade and return the last deployment status
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
self.selector(),
workspace_dir.as_str(),
self.start_timeout(),
kubernetes.cloud_provider().credentials_environment_variables(),
self.service_type(),
let helm = helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
})?;
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
environment.namespace().to_string(),
600_i64,
match self.service_type() {
ServiceType::Database(_) => vec![format!("{}/q-values.yaml", &workspace_dir)],
_ => vec![],
},
false,
self.selector(),
);
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
return Err(self.engine_error(EngineErrorCause::Internal, "Router has failed to be deployed".into()));
}
Ok(())
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())
}
fn on_create_check(&self) -> Result<(), EngineError> {

View File

@@ -1,10 +1,7 @@
use crate::cloud_provider::helm::HelmAction::Deploy;
use crate::cloud_provider::helm::HelmChartNamespaces::KubeSystem;
use crate::cloud_provider::qovery::{get_qovery_app_version, EngineLocation, QoveryAppName, QoveryShellAgent};
use crate::cmd::helm::{
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::helm::{to_command_error, Helm};
use crate::cmd::kubectl::{
kubectl_delete_crash_looping_pods, kubectl_exec_delete_crd, kubectl_exec_get_configmap, kubectl_exec_get_events,
kubectl_exec_rollout_restart_deployment, kubectl_exec_with_output,
@@ -20,7 +17,7 @@ use thread::spawn;
use tracing::{span, Level};
use uuid::Uuid;
#[derive(Clone)]
#[derive(Clone, PartialEq, Eq)]
pub enum HelmAction {
Deploy,
Destroy,
@@ -108,6 +105,15 @@ impl ChartInfo {
}
}
pub fn new_from_release_name(name: &str, custom_namespace: &str) -> ChartInfo {
ChartInfo {
name: name.to_string(),
namespace: HelmChartNamespaces::Custom,
custom_namespace: Some(custom_namespace.to_string()),
..Default::default()
}
}
pub fn get_namespace_string(&self) -> String {
match self.namespace {
HelmChartNamespaces::Custom => self
@@ -130,7 +136,7 @@ impl Default for ChartInfo {
atomic: true,
force_upgrade: false,
last_breaking_version_requiring_restart: None,
timeout_in_seconds: 300,
timeout_in_seconds: 600,
dry_run: false,
wait: true,
values: Vec::new(),
@@ -216,36 +222,22 @@ pub trait HelmChart: Send {
) -> Result<Option<ChartPayload>, CommandError> {
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();
let helm = Helm::new(kubernetes_config, &environment_variables).map_err(to_command_error)?;
match chart_info.action {
HelmAction::Deploy => {
if let Err(e) = helm_destroy_chart_if_breaking_changes_version_detected(
kubernetes_config,
&environment_variables,
chart_info,
) {
if let Err(e) = helm.uninstall_chart_if_breaking_version(chart_info, &vec![]) {
warn!(
"error while trying to destroy chart if breaking change is detected: {:?}",
e.message()
e.to_string()
);
}
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, chart_info)?
helm.upgrade(&chart_info, &vec![]).map_err(to_command_error)?;
}
HelmAction::Destroy => {
let chart_info = self.get_chart_info();
match is_chart_deployed(
kubernetes_config,
environment_variables.clone(),
Some(chart_info.get_namespace_string().as_str()),
chart_info.name.clone(),
) {
Ok(deployed) => {
if deployed {
helm_exec_uninstall_with_chart_info(kubernetes_config, &environment_variables, chart_info)?
}
}
Err(e) => return Err(e),
};
helm.uninstall(&chart_info, &vec![]).map_err(to_command_error)?;
}
HelmAction::Skip => {}
}
@@ -303,24 +295,31 @@ fn deploy_parallel_charts(
handles.push(handle);
}
let mut errors: Vec<Result<(), CommandError>> = vec![];
for handle in handles {
match handle.join() {
Ok(helm_run_ret) => {
if let Err(e) = helm_run_ret {
return Err(e);
errors.push(Err(e));
}
}
Err(e) => {
let safe_message = "Thread panicked during parallel charts deployments.";
return Err(CommandError::new(
let error = Err(CommandError::new(
format!("{}, error: {:?}", safe_message.to_string(), e),
Some(safe_message.to_string()),
));
errors.push(error);
}
}
}
Ok(())
if errors.is_empty() {
Ok(())
} else {
error!("Deployments of charts failed with: {:?}", errors);
errors.remove(0)
}
}
pub fn deploy_charts_levels(
@@ -330,24 +329,24 @@ pub fn deploy_charts_levels(
dry_run: bool,
) -> Result<(), CommandError> {
// first show diff
for level in &charts {
for chart in level {
let envs_ref: Vec<(&str, &str)> = envs.iter().map(|(x, y)| (x.as_str(), y.as_str())).collect();
let helm = Helm::new(&kubernetes_config, &envs_ref).map_err(to_command_error)?;
for level in charts {
// Show diff for all chart in this state
for chart in &level {
let chart_info = chart.get_chart_info();
match chart_info.action {
// don't do diff on destroy or skip
HelmAction::Deploy => {
let _ = helm_upgrade_diff_with_chart_info(&kubernetes_config, envs, chart.get_chart_info());
}
_ => {}
// don't do diff on destroy or skip
if chart_info.action == HelmAction::Deploy {
let _ = helm.upgrade_diff(chart_info, &vec![]);
}
}
}
// then apply
if dry_run {
return Ok(());
}
for level in charts.into_iter() {
// Skip actual deployment if dry run
if dry_run {
continue;
}
if let Err(e) = deploy_parallel_charts(&kubernetes_config, &envs, level) {
return Err(e);
}
@@ -591,47 +590,36 @@ impl HelmChart for PrometheusOperatorConfigChart {
) -> Result<Option<ChartPayload>, CommandError> {
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();
let helm = Helm::new(kubernetes_config, &environment_variables).map_err(to_command_error)?;
match chart_info.action {
HelmAction::Deploy => {
if let Err(e) = helm_destroy_chart_if_breaking_changes_version_detected(
kubernetes_config,
&environment_variables,
chart_info,
) {
if let Err(e) = helm.uninstall_chart_if_breaking_version(chart_info, &vec![]) {
warn!(
"error while trying to destroy chart if breaking change is detected: {}",
e.message()
e.to_string()
);
}
helm_exec_upgrade_with_chart_info(kubernetes_config, &environment_variables, chart_info)?
helm.upgrade(&chart_info, &vec![]).map_err(to_command_error)?;
}
HelmAction::Destroy => {
let chart_info = self.get_chart_info();
match is_chart_deployed(
kubernetes_config,
environment_variables.clone(),
Some(chart_info.get_namespace_string().as_str()),
chart_info.name.clone(),
) {
Ok(deployed) => {
if deployed {
let prometheus_crds = [
"prometheuses.monitoring.coreos.com",
"prometheusrules.monitoring.coreos.com",
"servicemonitors.monitoring.coreos.com",
"podmonitors.monitoring.coreos.com",
"alertmanagers.monitoring.coreos.com",
"thanosrulers.monitoring.coreos.com",
];
helm_exec_uninstall_with_chart_info(kubernetes_config, &environment_variables, chart_info)?;
for crd in &prometheus_crds {
kubectl_exec_delete_crd(kubernetes_config, crd, environment_variables.clone())?;
}
}
if helm.check_release_exist(&chart_info, &vec![]).is_ok() {
helm.uninstall(&chart_info, &vec![]).map_err(to_command_error)?;
let prometheus_crds = [
"prometheuses.monitoring.coreos.com",
"prometheusrules.monitoring.coreos.com",
"servicemonitors.monitoring.coreos.com",
"podmonitors.monitoring.coreos.com",
"alertmanagers.monitoring.coreos.com",
"thanosrulers.monitoring.coreos.com",
];
for crd in &prometheus_crds {
let _ = kubectl_exec_delete_crd(kubernetes_config, crd, environment_variables.clone());
}
Err(e) => return Err(e),
};
}
}
HelmAction::Skip => {}
}

View File

@@ -1,7 +1,6 @@
use crate::cloud_provider::helm::{
get_chart_for_shell_agent, get_engine_helm_action_from_location, ChartInfo, ChartSetValue, ChartValuesGenerated,
CommonChart, CoreDNSConfigChart, HelmAction, HelmChart, HelmChartNamespaces, PrometheusOperatorConfigChart,
ShellAgentContext,
CommonChart, CoreDNSConfigChart, HelmChart, HelmChartNamespaces, PrometheusOperatorConfigChart, ShellAgentContext,
};
use crate::cloud_provider::qovery::{get_qovery_app_version, EngineLocation, QoveryAgent, QoveryAppName, QoveryEngine};
use crate::cloud_provider::scaleway::application::{ScwRegion, ScwZone};
@@ -283,6 +282,7 @@ pub fn scw_helm_charts(
},
};
/* Example to delete an old chart
let old_prometheus_operator = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
name: "prometheus-operator".to_string(),
@@ -290,7 +290,7 @@ pub fn scw_helm_charts(
action: HelmAction::Destroy,
..Default::default()
},
};
};*/
let kube_prometheus_stack = PrometheusOperatorConfigChart {
chart_info: ChartInfo {
@@ -493,7 +493,9 @@ datasources:
},
ChartSetValue {
key: "prometheus.servicemonitor.enabled".to_string(),
value: chart_config_prerequisites.ff_metrics_history_enabled.to_string(),
// Due to cycle, prometheus need tls certificate from cert manager, and enabling this will require
// prometheus to be already installed
value: "false".to_string(),
},
ChartSetValue {
key: "prometheus.servicemonitor.prometheusInstance".to_string(),
@@ -519,11 +521,11 @@ datasources:
// Webhooks resources limits
ChartSetValue {
key: "webhook.resources.limits.cpu".to_string(),
value: "20m".to_string(),
value: "200m".to_string(),
},
ChartSetValue {
key: "webhook.resources.requests.cpu".to_string(),
value: "20m".to_string(),
value: "50m".to_string(),
},
ChartSetValue {
key: "webhook.resources.limits.memory".to_string(),
@@ -856,21 +858,19 @@ datasources:
};
// chart deployment order matters!!!
let level_1: Vec<Box<dyn HelmChart>> = vec![
Box::new(q_storage_class),
Box::new(coredns_config),
Box::new(old_prometheus_operator),
];
let level_1: Vec<Box<dyn HelmChart>> = vec![Box::new(q_storage_class), Box::new(coredns_config)];
let mut level_2: Vec<Box<dyn HelmChart>> = vec![];
let level_2: Vec<Box<dyn HelmChart>> = vec![Box::new(cert_manager)];
let mut level_3: Vec<Box<dyn HelmChart>> = vec![];
let mut level_4: Vec<Box<dyn HelmChart>> = vec![Box::new(external_dns)];
let mut level_4: Vec<Box<dyn HelmChart>> = vec![];
let mut level_5: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress), Box::new(cert_manager)];
let mut level_5: Vec<Box<dyn HelmChart>> = vec![Box::new(external_dns)];
let mut level_6: Vec<Box<dyn HelmChart>> = vec![
let mut level_6: Vec<Box<dyn HelmChart>> = vec![Box::new(nginx_ingress)];
let mut level_7: Vec<Box<dyn HelmChart>> = vec![
Box::new(cert_manager_config),
Box::new(qovery_agent),
Box::new(shell_agent),
@@ -879,24 +879,24 @@ datasources:
// // observability
if chart_config_prerequisites.ff_metrics_history_enabled {
level_2.push(Box::new(kube_prometheus_stack));
level_4.push(Box::new(prometheus_adapter));
level_4.push(Box::new(kube_state_metrics));
level_3.push(Box::new(kube_prometheus_stack));
level_5.push(Box::new(prometheus_adapter));
level_5.push(Box::new(kube_state_metrics));
}
if chart_config_prerequisites.ff_log_history_enabled {
level_3.push(Box::new(promtail));
level_4.push(Box::new(loki));
level_4.push(Box::new(promtail));
level_5.push(Box::new(loki));
}
if chart_config_prerequisites.ff_metrics_history_enabled || chart_config_prerequisites.ff_log_history_enabled {
level_6.push(Box::new(grafana))
level_7.push(Box::new(grafana))
};
// pleco
if !chart_config_prerequisites.disable_pleco {
level_5.push(Box::new(pleco));
level_6.push(Box::new(pleco));
}
info!("charts configuration preparation finished");
Ok(vec![level_1, level_2, level_3, level_4, level_5, level_6])
Ok(vec![level_1, level_2, level_3, level_4, level_5, level_6, level_7])
}

View File

@@ -3,7 +3,7 @@ pub mod node;
use crate::cloud_provider::aws::regions::AwsZones;
use crate::cloud_provider::environment::Environment;
use crate::cloud_provider::helm::deploy_charts_levels;
use crate::cloud_provider::helm::{deploy_charts_levels, ChartInfo};
use crate::cloud_provider::kubernetes::{
is_kubernetes_upgrade_required, send_progress_on_long_task, uninstall_cert_manager, Kind, Kubernetes,
KubernetesUpgradeStatus, ProviderOptions,
@@ -15,8 +15,8 @@ use crate::cloud_provider::scaleway::kubernetes::helm_charts::{scw_helm_charts,
use crate::cloud_provider::scaleway::kubernetes::node::{ScwInstancesType, ScwNodeGroup};
use crate::cloud_provider::utilities::print_action;
use crate::cloud_provider::{kubernetes, CloudProvider};
use crate::cmd::helm::{to_engine_error, Helm};
use crate::cmd::kubectl::{kubectl_exec_api_custom_metrics, kubectl_exec_get_all_namespaces, kubectl_exec_get_events};
use crate::cmd::structs::HelmChart;
use crate::cmd::terraform::{terraform_exec, terraform_init_validate_plan_apply, terraform_init_validate_state_list};
use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_managed_namespaces};
use crate::dns_provider::DnsProvider;
@@ -1497,15 +1497,14 @@ impl<'a> Kapsule<'a> {
);
// delete custom metrics api to avoid stale namespaces on deletion
let _ = cmd::helm::helm_uninstall_list(
let helm = Helm::new(
&kubernetes_config_file_path,
vec![HelmChart {
name: "metrics-server".to_string(),
namespace: "kube-system".to_string(),
version: None,
}],
self.cloud_provider().credentials_environment_variables(),
);
&self.cloud_provider.credentials_environment_variables(),
)
.map_err(|e| to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_release_name("metrics-server", "kube-system");
helm.uninstall(&chart, &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
// required to avoid namespace stuck on deletion
uninstall_cert_manager(
@@ -1525,50 +1524,27 @@ impl<'a> Kapsule<'a> {
let qovery_namespaces = get_qovery_managed_namespaces();
for qovery_namespace in qovery_namespaces.iter() {
let charts_to_delete = cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
Some(qovery_namespace),
);
match charts_to_delete {
Ok(charts) => {
for chart in charts {
match cmd::helm::helm_exec_uninstall(
&kubernetes_config_file_path,
&chart.namespace,
&chart.name,
self.cloud_provider().credentials_environment_variables(),
) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`", chart.name);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
),
)
}
}
}
}
Err(e) => {
if !(e.message().contains("not found")) {
let charts_to_delete = helm
.list_release(Some(qovery_namespace), &vec![])
.map_err(|e| to_engine_error(&event_details, e))?;
for chart in charts_to_delete {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!("Chart `{}` deleted", chart.name)),
),
),
Err(e) => {
let message_safe = format!("Can't delete chart `{}`", chart.name);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Can't delete the namespace {}",
qovery_namespace
)),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1623,18 +1599,11 @@ impl<'a> Kapsule<'a> {
),
);
match cmd::helm::helm_list(
&kubernetes_config_file_path,
self.cloud_provider().credentials_environment_variables(),
None,
) {
match helm.list_release(None, &vec![]) {
Ok(helm_charts) => {
for chart in helm_charts {
match cmd::helm::helm_uninstall_list(
&kubernetes_config_file_path,
vec![chart.clone()],
self.cloud_provider().credentials_environment_variables(),
) {
let chart_info = ChartInfo::new_from_release_name(&chart.name, &chart.namespace);
match helm.uninstall(&chart_info, &vec![]) {
Ok(_) => self.logger().log(
LogLevel::Info,
EngineEvent::Deleting(
@@ -1643,12 +1612,12 @@ impl<'a> Kapsule<'a> {
),
),
Err(e) => {
let message_safe = format!("Error deleting chart `{}` deleted", chart.name);
let message_safe = format!("Error deleting chart `{}`: {}", chart.name, e);
self.logger().log(
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe, e.message),
EventMessage::new(message_safe, Some(e.to_string())),
),
)
}
@@ -1661,7 +1630,7 @@ impl<'a> Kapsule<'a> {
LogLevel::Error,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(message_safe.to_string(), Some(e.message())),
EventMessage::new(message_safe.to_string(), Some(e.to_string())),
),
)
}

View File

@@ -1,5 +1,6 @@
use tera::Context as TeraContext;
use crate::cloud_provider::helm::ChartInfo;
use crate::cloud_provider::models::{CustomDomain, CustomDomainDataTemplate, Route, RouteDataTemplate};
use crate::cloud_provider::service::{
default_tera_context, delete_router, deploy_stateless_service_error, send_progress_on_long_task, Action, Create,
@@ -7,8 +8,9 @@ use crate::cloud_provider::service::{
};
use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm;
use crate::cmd::helm::Timeout;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::error::{EngineError, EngineErrorScope};
use crate::errors::EngineError as NewEngineError;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::models::{Context, Listen, Listener, Listeners};
@@ -293,25 +295,26 @@ impl Create for Router {
}
// do exec helm upgrade and return the last deployment status
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
self.selector(),
workspace_dir.as_str(),
self.start_timeout(),
kubernetes.cloud_provider().credentials_environment_variables(),
self.service_type(),
let helm = helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
})?;
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
environment.namespace().to_string(),
600_i64,
match self.service_type() {
ServiceType::Database(_) => vec![format!("{}/q-values.yaml", &workspace_dir)],
_ => vec![],
},
false,
self.selector(),
);
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
return Err(self.engine_error(EngineErrorCause::Internal, "Router has failed to be deployed".into()));
}
Ok(())
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())
}
fn on_create_check(&self) -> Result<(), EngineError> {

View File

@@ -8,9 +8,12 @@ use tera::Context as TeraContext;
use crate::build_platform::Image;
use crate::cloud_provider::environment::Environment;
use crate::cloud_provider::helm::ChartInfo;
use crate::cloud_provider::kubernetes::Kubernetes;
use crate::cloud_provider::utilities::check_domain_for;
use crate::cloud_provider::DeploymentTarget;
use crate::cmd;
use crate::cmd::helm;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl::ScalingKind::Statefulset;
use crate::cmd::kubectl::{kubectl_exec_delete_secret, kubectl_exec_scale_replicas_by_selector, ScalingKind};
@@ -365,30 +368,11 @@ pub fn deploy_user_stateless_service<T>(target: &DeploymentTarget, service: &T)
where
T: Service + Helm,
{
deploy_stateless_service(
target,
service,
service.engine_error(
EngineErrorCause::User(
"Your application has failed to start. \
Ensure you can run it without issues with `qovery run` and check its logs from the web interface or the CLI with `qovery log`. \
This issue often occurs due to ports misconfiguration. Make sure you exposed the correct port (using EXPOSE statement in Dockerfile or via Qovery configuration).",
),
format!(
"{} {} has failed to start ",
service.service_type().name(),
service.name_with_id()
),
),
)
deploy_stateless_service(target, service)
}
/// deploy a stateless service (app, router, database...) on Kubernetes
pub fn deploy_stateless_service<T>(
target: &DeploymentTarget,
service: &T,
thrown_error: EngineError,
) -> Result<(), EngineError>
pub fn deploy_stateless_service<T>(target: &DeploymentTarget, service: &T) -> Result<(), EngineError>
where
T: Service + Helm,
{
@@ -441,26 +425,26 @@ where
})?;
// do exec helm upgrade and return the last deployment status
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
service.selector(),
workspace_dir.as_str(),
service.start_timeout(),
kubernetes.cloud_provider().credentials_environment_variables(),
service.service_type(),
let helm = helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error())?;
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
environment.namespace().to_string(),
600_i64,
match service.service_type() {
ServiceType::Database(_) => vec![format!("{}/q-values.yaml", &workspace_dir)],
_ => vec![],
},
false,
service.selector(),
);
// check deployment status
if helm_history_row.is_none()
|| !helm_history_row
.expect("Error getting helm history row")
.is_successfully_deployed()
{
return Err(thrown_error);
}
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
crate::cmd::kubectl::kubectl_exec_is_pod_ready_with_retry(
kubernetes_config_file_path.as_str(),
@@ -482,48 +466,12 @@ where
}
/// do specific operations on a stateless service deployment error
pub fn deploy_stateless_service_error<T>(target: &DeploymentTarget, service: &T) -> Result<(), EngineError>
pub fn deploy_stateless_service_error<T>(_target: &DeploymentTarget, _service: &T) -> Result<(), EngineError>
where
T: Service + Helm,
{
let kubernetes = target.kubernetes;
let environment = target.environment;
let helm_release_name = service.helm_release_name();
let event_details = service.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
let kubernetes_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => return Err(e.to_legacy_engine_error()),
};
let history_rows = crate::cmd::helm::helm_exec_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_chart_history_error(
event_details.clone(),
helm_release_name.to_string(),
environment.namespace().to_string(),
e,
)
.to_legacy_engine_error()
})?;
if history_rows.len() == 1 {
crate::cmd::helm::helm_exec_uninstall(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name.as_str(),
kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_chart_uninstall_error(event_details.clone(), helm_release_name.to_string(), e)
.to_legacy_engine_error()
})?;
}
// Nothing to do as we sait --atomic on chart release that we do
// So helm rollback for us if a deployment fails
Ok(())
}
@@ -789,30 +737,26 @@ where
})?;
// do exec helm upgrade and return the last deployment status
let helm_history_row = crate::cmd::helm::helm_exec_with_upgrade_history(
kubernetes_config_file_path.to_string(),
environment.namespace(),
service.helm_release_name().as_str(),
service.selector(),
workspace_dir.to_string(),
service.start_timeout(),
kubernetes.cloud_provider().credentials_environment_variables(),
service.service_type(),
let helm = helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
})?;
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
let chart = ChartInfo::new_from_custom_namespace(
service.helm_release_name(),
workspace_dir.clone(),
environment.namespace().to_string(),
600_i64,
match service.service_type() {
ServiceType::Database(_) => vec![format!("{}/q-values.yaml", &workspace_dir)],
_ => vec![],
},
false,
service.selector(),
);
// check deployment status
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
return Err(service.engine_error(
EngineErrorCause::Internal,
format!(
"{} service fails to be deployed (before start)",
service.service_type().name()
),
));
}
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
// check app status
match crate::cmd::kubectl::kubectl_exec_is_pod_ready_with_retry(
@@ -1306,34 +1250,15 @@ pub fn helm_uninstall_release(
.get_kubeconfig_file_path()
.map_err(|e| e.to_legacy_engine_error())?;
let history_rows = crate::cmd::helm::helm_exec_history(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name,
let helm = cmd::helm::Helm::new(
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_k8s_history(event_details.clone(), environment.namespace().to_string(), e)
.to_legacy_engine_error()
})?;
.map_err(|e| NewEngineError::new_helm_error(event_details.clone(), e).to_legacy_engine_error())?;
// if there is no valid history - then delete the helm chart
let first_valid_history_row = history_rows.iter().find(|x| x.is_successfully_deployed());
if first_valid_history_row.is_some() {
crate::cmd::helm::helm_exec_uninstall(
kubernetes_config_file_path.as_str(),
environment.namespace(),
helm_release_name,
kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| {
NewEngineError::new_helm_chart_uninstall_error(event_details.clone(), helm_release_name.to_string(), e)
.to_legacy_engine_error()
})?;
}
Ok(())
let chart = ChartInfo::new_from_release_name(helm_release_name, environment.namespace());
helm.uninstall(&chart, &vec![])
.map_err(|e| NewEngineError::new_helm_error(event_details.clone(), e).to_legacy_engine_error())
}
/// This function call (start|pause|delete)_in_progress function every 10 seconds when a

View File

@@ -319,16 +319,23 @@ impl fmt::Display for VersionsNumber {
}
}
fn cloudflare_dns_resolver() -> Resolver {
fn google_dns_resolver() -> Resolver {
let mut resolver_options = ResolverOpts::default();
// We want to avoid cache and using host file of the host, as some provider force caching
// which lead to stale response
resolver_options.cache_size = 0;
resolver_options.use_hosts_file = false;
resolver_options.use_hosts_file = true;
//resolver_options.ip_strategy = LookupIpStrategy::Ipv4Only;
//let dns = IpAddr::V4(Ipv4Addr::new(192, 168, 1, 254));
//let resolver = ResolverConfig::from_parts(
// None,
// vec![],
// NameServerConfigGroup::from_ips_clear(&vec![dns], 53, true),
//);
Resolver::new(ResolverConfig::cloudflare(), resolver_options)
.expect("Invalid cloudflare DNS resolver configuration")
//Resolver::new(resolver, resolver_options).unwrap()
Resolver::new(ResolverConfig::google(), resolver_options).expect("Invalid google DNS resolver configuration")
}
fn get_cname_record_value(resolver: &Resolver, cname: &str) -> Option<String> {
@@ -352,7 +359,7 @@ pub fn check_cname_for(
cname_to_check: &str,
execution_id: &str,
) -> Result<String, String> {
let resolver = cloudflare_dns_resolver();
let resolver = google_dns_resolver();
let listener_helper = ListenersHelper::new(listeners);
let send_deployment_progress = |msg: &str| {
@@ -420,7 +427,7 @@ pub fn check_domain_for(
execution_id: &str,
context_id: &str,
) -> Result<(), EngineError> {
let resolver = cloudflare_dns_resolver();
let resolver = google_dns_resolver();
for domain in domains_to_check {
listener_helper.deployment_in_progress(ProgressInfo::new(
@@ -578,7 +585,7 @@ pub fn print_action(cloud_provider_name: &str, struct_name: &str, fn_name: &str,
mod tests {
use crate::cloud_provider::models::CpuLimits;
use crate::cloud_provider::utilities::{
cloudflare_dns_resolver, convert_k8s_cpu_value_to_f32, get_cname_record_value,
convert_k8s_cpu_value_to_f32, get_cname_record_value, google_dns_resolver,
validate_k8s_required_cpu_and_burstable, VersionsNumber,
};
use crate::error::StringError;
@@ -626,7 +633,7 @@ mod tests {
#[test]
pub fn test_cname_resolution() {
let resolver = cloudflare_dns_resolver();
let resolver = google_dns_resolver();
let cname = get_cname_record_value(&resolver, "ci-test-no-delete.qovery.io");
assert_eq!(cname, Some(String::from("qovery.io.")));

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,7 @@ pub mod io;
extern crate url;
use crate::cloud_provider::utilities::VersionsNumber;
use crate::cmd::helm::HelmError;
use crate::error::{EngineError as LegacyEngineError, EngineErrorCause, EngineErrorScope};
use crate::events::EventDetails;
use url::Url;
@@ -1503,6 +1504,29 @@ impl EngineError {
)
}
/// Creates new error from an Helm error
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `error`: Raw error message.
pub fn new_helm_error(event_details: EventDetails, error: HelmError) -> EngineError {
let cmd_error = match &error {
HelmError::CmdError(_, _, cmd_error) => Some(cmd_error.clone()),
_ => None,
};
EngineError::new(
event_details,
Tag::HelmChartUninstallError,
error.to_string(),
error.to_string(),
cmd_error,
None,
None,
)
}
/// Creates new error while uninstalling Helm chart.
///
/// Arguments:

View File

@@ -0,0 +1,23 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -0,0 +1,24 @@
apiVersion: v2
name: nginx
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View File

@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "toto.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "toto.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "toto.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "toto.labels" -}}
helm.sh/chart: {{ include "toto.chart" . }}
{{ include "toto.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "toto.selectorLabels" -}}
app.kubernetes.io/name: {{ include "toto.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "toto.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "toto.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,62 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "toto.fullname" . }}
labels:
{{- include "toto.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "toto.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "toto.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "toto.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
initialDelaySeconds: {{ .Values.initialDelaySeconds }}
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,28 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "toto.fullname" . }}
labels:
{{- include "toto.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "toto.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,61 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "toto.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "toto.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
{{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }}
service:
name: {{ $fullName }}
port:
number: {{ $svcPort }}
{{- else }}
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "toto.fullname" . }}
labels:
{{- include "toto.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "toto.selectorLabels" . | nindent 4 }}

View File

@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "toto.serviceAccountName" . }}
labels:
{{- include "toto.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "toto.fullname" . }}-test-connection"
labels:
{{- include "toto.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "toto.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -0,0 +1,83 @@
# Default values for toto.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
initialDelaySeconds: 5
image:
repository: nginx
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: ""
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}