fix: avoid leaking details to legacy errors (#673)

Ticket: ENG-1156
This commit is contained in:
BenjaminCh
2022-03-30 14:23:09 +02:00
committed by GitHub
parent 9f472f67f5
commit 7fdfc0218c
20 changed files with 306 additions and 170 deletions

View File

@@ -429,28 +429,23 @@ fn get_managed_mongodb_version(requested_version: String) -> Result<String, Comm
#[cfg(test)]
mod tests_mongodb {
use crate::cloud_provider::aws::databases::mongodb::get_mongodb_version;
use crate::errors::ErrorMessageVerbosity;
#[test]
fn check_mongodb_version() {
// managed version
assert_eq!(get_mongodb_version("4".to_string(), true).unwrap(), "4.0.0");
assert_eq!(get_mongodb_version("4.0".to_string(), true).unwrap(), "4.0.0");
assert_eq!(
get_mongodb_version("4.4".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"DocumentDB 4.4 version is not supported"
);
assert!(get_mongodb_version("4.4".to_string(), true)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("DocumentDB 4.4 version is not supported"));
// self-hosted version
assert_eq!(get_mongodb_version("4".to_string(), false).unwrap(), "4.4.4");
assert_eq!(get_mongodb_version("4.2".to_string(), false).unwrap(), "4.2.12");
assert_eq!(
get_mongodb_version("3.4".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"MongoDB 3.4 version is not supported"
);
assert!(get_mongodb_version("3.4".to_string(), false)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("MongoDB 3.4 version is not supported"));
}
}

View File

@@ -449,6 +449,7 @@ fn get_managed_mysql_version(requested_version: String) -> Result<String, Comman
#[cfg(test)]
mod tests_mysql {
use crate::cloud_provider::aws::databases::mysql::get_mysql_version;
use crate::errors::ErrorMessageVerbosity;
#[test]
fn check_mysql_version() {
@@ -456,23 +457,17 @@ mod tests_mysql {
assert_eq!(get_mysql_version("8".to_string(), true).unwrap(), "8.0.26");
assert_eq!(get_mysql_version("8.0".to_string(), true).unwrap(), "8.0.26");
assert_eq!(get_mysql_version("8.0.16".to_string(), true).unwrap(), "8.0.16");
assert_eq!(
get_mysql_version("8.0.18".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"RDS MySQL 8.0.18 version is not supported"
);
assert!(get_mysql_version("8.0.18".to_string(), true)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("RDS MySQL 8.0.18 version is not supported"));
// self-hosted version
assert_eq!(get_mysql_version("5".to_string(), false).unwrap(), "5.7.34");
assert_eq!(get_mysql_version("5.7".to_string(), false).unwrap(), "5.7.34");
assert_eq!(get_mysql_version("5.7.31".to_string(), false).unwrap(), "5.7.31");
assert_eq!(
get_mysql_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"MySQL 1.0 version is not supported"
);
assert!(get_mysql_version("1.0".to_string(), false)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("MySQL 1.0 version is not supported"));
}
}

View File

@@ -441,36 +441,28 @@ fn get_managed_postgres_version(requested_version: String) -> Result<String, Com
#[cfg(test)]
mod tests_postgres {
use crate::cloud_provider::aws::databases::postgresql::get_postgres_version;
use crate::errors::ErrorMessageVerbosity;
#[test]
fn check_postgres_version() {
// managed version
assert_eq!(get_postgres_version("12".to_string(), true).unwrap(), "12.8");
assert_eq!(get_postgres_version("12.3".to_string(), true).unwrap(), "12.3");
assert_eq!(
get_postgres_version("12.3.0".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"Postgresql 12.3.0 version is not supported"
);
assert_eq!(
get_postgres_version("11.3".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"Postgresql 11.3 version is not supported"
);
assert!(get_postgres_version("12.3.0".to_string(), true)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Postgresql 12.3.0 version is not supported"));
assert!(get_postgres_version("11.3".to_string(), true)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Postgresql 11.3 version is not supported"));
// self-hosted version
assert_eq!(get_postgres_version("12".to_string(), false).unwrap(), "12.8.0");
assert_eq!(get_postgres_version("12.8".to_string(), false).unwrap(), "12.8.0");
assert_eq!(get_postgres_version("12.3.0".to_string(), false).unwrap(), "12.3.0");
assert_eq!(
get_postgres_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"Postgresql 1.0 version is not supported"
);
assert!(get_postgres_version("1.0".to_string(), false)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Postgresql 1.0 version is not supported"));
}
}

View File

@@ -438,29 +438,24 @@ fn get_managed_redis_version(requested_version: String) -> Result<String, Comman
#[cfg(test)]
mod tests {
use crate::cloud_provider::aws::databases::redis::get_redis_version;
use crate::errors::ErrorMessageVerbosity;
#[test]
fn check_redis_version() {
// managed version
assert_eq!(get_redis_version("6".to_string(), true).unwrap(), "6.x");
assert_eq!(get_redis_version("5".to_string(), true).unwrap(), "5.0.6");
assert_eq!(
get_redis_version("1.0".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"Elasticache 1.0 version is not supported"
);
assert!(get_redis_version("1.0".to_string(), true)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Elasticache 1.0 version is not supported"));
// self-hosted version
assert_eq!(get_redis_version("6".to_string(), false).unwrap(), "6.0.9");
assert_eq!(get_redis_version("6.0".to_string(), false).unwrap(), "6.0.9");
assert_eq!(
get_redis_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"Redis 1.0 version is not supported"
);
assert!(get_redis_version("1.0".to_string(), false)
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Redis 1.0 version is not supported"));
}
}

View File

@@ -28,6 +28,7 @@ pub fn aws_final_snapshot_name(database_name: &str) -> String {
mod tests_aws_databases_parameters {
use crate::cloud_provider::aws::databases::utilities::get_parameter_group_from_version;
use crate::cloud_provider::utilities::VersionsNumber;
use crate::errors::ErrorMessageVerbosity;
use crate::io_models::DatabaseKind;
use std::str::FromStr;
@@ -49,9 +50,9 @@ mod tests_aws_databases_parameters {
VersionsNumber::from_str("8").expect("error while trying to get version from str"),
DatabaseKind::Mysql,
);
assert_eq!(
mysql_parameter_group.unwrap_err().message(),
"Can't determine the minor version, to select parameter group for Mysql version 8"
);
assert!(mysql_parameter_group
.unwrap_err()
.message(ErrorMessageVerbosity::FullDetails)
.contains("Can't determine the minor version, to select parameter group for Mysql version 8"));
}
}

View File

@@ -35,7 +35,7 @@ use crate::cmd::terraform::{terraform_exec, terraform_init_validate_plan_apply,
use crate::deletion_utilities::{get_firsts_namespaces_to_delete, get_qovery_managed_namespaces};
use crate::dns_provider;
use crate::dns_provider::DnsProvider;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::Stage::Infrastructure;
use crate::events::{EngineEvent, EnvironmentStep, EventDetails, EventMessage, InfrastructureStep, Stage, Transmitter};
use crate::io_models::{
@@ -763,7 +763,10 @@ impl EKS {
.log(EngineEvent::Info(event_details, EventMessage::new(ok_line, None))),
Err(err) => self.logger().log(EngineEvent::Warning(
event_details,
EventMessage::new("Error trying to get kubernetes events".to_string(), Some(err.message())),
EventMessage::new(
"Error trying to get kubernetes events".to_string(),
Some(err.message(ErrorMessageVerbosity::FullDetails)),
),
)),
};
@@ -1011,7 +1014,7 @@ impl EKS {
let safe_message = "Skipping Kubernetes uninstall because it can't be reached.";
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new(safe_message.to_string(), Some(e.message())),
EventMessage::new(safe_message.to_string(), Some(e.message(ErrorMessageVerbosity::FullDetails))),
));
skip_kubernetes_step = true;
@@ -1084,7 +1087,7 @@ impl EKS {
)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
@@ -1104,7 +1107,7 @@ impl EKS {
);
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
EventMessage::new(message_safe, Some(e.message(ErrorMessageVerbosity::FullDetails))),
));
}
}
@@ -1182,7 +1185,7 @@ impl EKS {
EventMessage::new_from_safe(format!("Namespace {} is fully deleted", qovery_namespace)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!("Can't delete namespace {}.", qovery_namespace)),

View File

@@ -88,7 +88,7 @@ pub fn get_do_kubeconfig_by_cluster_name(token: &str, cluster_name: &str) -> Res
Ok(clusters) => Ok(clusters),
Err(e) => Err(CommandError::new_from_safe_message(e.to_string())),
},
Err(e) => Err(CommandError::new_from_safe_message(e.message())),
Err(e) => Err(e),
};
let clusters_copy = clusters.expect("Unable to list clusters").kubernetes_clusters;
@@ -108,7 +108,7 @@ pub fn get_do_kubeconfig_by_cluster_name(token: &str, cluster_name: &str) -> Res
}
Ok(Some(kubeconfig))
}
Err(e) => Err(CommandError::new_from_safe_message(e.message())),
Err(e) => Err(e),
}
}
None => Ok(None),

View File

@@ -34,7 +34,7 @@ use crate::cmd::kubectl::{
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;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::Stage::Infrastructure;
use crate::events::{
EngineEvent, EnvironmentStep, EventDetails, EventMessage, GeneralStep, InfrastructureStep, Stage, Transmitter,
@@ -692,10 +692,7 @@ impl DOKS {
let safe_message = "Load balancer IP wasn't able to be retrieved from UUID on DigitalOcean API and it's required for TLS setup";
return Err(EngineError::new_k8s_loadbalancer_configuration_issue(
event_details.clone(),
CommandError::new(
format!("{}, error: {}.", safe_message, e.message(),),
Some(safe_message.to_string()),
),
CommandError::new(e.message(ErrorMessageVerbosity::FullDetails), Some(safe_message.to_string())),
));
}
};
@@ -747,7 +744,10 @@ impl DOKS {
.log(EngineEvent::Warning(event_details, EventMessage::new(ok_line, None))),
Err(err) => self.logger().log(EngineEvent::Warning(
event_details,
EventMessage::new("Error trying to get kubernetes events".to_string(), Some(err.message())),
EventMessage::new(
"Error trying to get kubernetes events".to_string(),
Some(err.message(ErrorMessageVerbosity::FullDetails)),
),
)),
};
@@ -906,7 +906,7 @@ impl DOKS {
)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
@@ -926,7 +926,7 @@ impl DOKS {
);
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
EventMessage::new(message_safe, Some(e.message(ErrorMessageVerbosity::FullDetails))),
));
}
}
@@ -1001,7 +1001,7 @@ impl DOKS {
EventMessage::new_from_safe(format!("Namespace {} is fully deleted", qovery_namespace)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!("Can't delete namespace {}.", qovery_namespace)),
@@ -1209,12 +1209,7 @@ impl Kubernetes for DOKS {
}
Some(content) => content,
},
Err(e) => {
return Err(EngineError::new_cannot_retrieve_cluster_config_file(
event_details,
CommandError::new(e.message(), Some(e.message())),
))
}
Err(e) => return Err(EngineError::new_cannot_retrieve_cluster_config_file(event_details, e)),
};
let workspace_directory = crate::fs::workspace_directory(
@@ -1263,7 +1258,10 @@ impl Kubernetes for DOKS {
match result {
Err(e) => Err(EngineError::new_cannot_retrieve_cluster_config_file(
event_details,
CommandError::new(e.message(), Some(e.message())),
CommandError::new(
e.message(ErrorMessageVerbosity::FullDetails),
Some(e.message(ErrorMessageVerbosity::SafeOnly)),
),
)),
Ok((file_path, file)) => Ok((file_path, file)),
}

View File

@@ -7,7 +7,7 @@ use crate::cmd::kubectl::{
kubectl_exec_rollout_restart_deployment, kubectl_exec_with_output,
};
use crate::cmd::structs::HelmHistoryRow;
use crate::errors::CommandError;
use crate::errors::{CommandError, ErrorMessageVerbosity};
use crate::utilities::calculate_hash;
use semver::Version;
use std::collections::HashMap;
@@ -205,7 +205,7 @@ pub trait HelmChart: Send {
let payload = match self.exec(kubernetes_config, envs, payload.clone()) {
Ok(payload) => payload,
Err(e) => {
error!("Error while deploying chart: {}", e.message());
error!("Error while deploying chart: {}", e.message(ErrorMessageVerbosity::FullDetails));
self.on_deploy_failure(kubernetes_config, envs, payload)?;
return Err(e);
}
@@ -502,7 +502,10 @@ impl HelmChart for CoreDNSConfigChart {
Err(e) => return Err(e),
};
if let Err(e) = self.exec(kubernetes_config, envs, None) {
error!("Error while deploying chart: {:?}", e.message());
error!(
"Error while deploying chart: {:?}",
e.message(ErrorMessageVerbosity::FullDetails)
);
self.on_deploy_failure(kubernetes_config, envs, None)?;
return Err(e);
};

View File

@@ -27,7 +27,7 @@ use crate::cmd::kubectl::{
};
use crate::cmd::structs::KubernetesNodeCondition;
use crate::dns_provider::DnsProvider;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::Stage::Infrastructure;
use crate::events::{EngineEvent, EventDetails, EventMessage, GeneralStep, InfrastructureStep, Stage, Transmitter};
use crate::fs::workspace_directory;
@@ -183,10 +183,10 @@ pub trait Kubernetes: Listen {
Err(err) => {
let error = EngineError::new_cannot_get_cluster_nodes(
self.get_event_details(stage),
CommandError::new_from_safe_message(format!(
"Error while trying to get cluster nodes, error: {}",
err.message()
)),
CommandError::new(
err.message(ErrorMessageVerbosity::FullDetails),
Some("Error while trying to get cluster nodes.".to_string()),
),
);
self.logger().log(EngineEvent::Error(error.clone(), None));
@@ -267,7 +267,12 @@ pub trait Kubernetes: Listen {
{
let kubeconfig = match self.get_kubeconfig_file() {
Ok((path, _)) => path,
Err(e) => return Err(CommandError::new(e.message(), None)),
Err(e) => {
return Err(CommandError::new(
e.message(ErrorMessageVerbosity::FullDetails),
Some(e.message(ErrorMessageVerbosity::SafeOnly)),
))
}
};
send_progress_on_long_task(self, Action::Create, || {
@@ -786,12 +791,8 @@ where
logger.log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new(
format!(
"Encountering issues while trying to get objects kind {}: {:?}",
object,
e.message()
),
None,
format!("Encountering issues while trying to get objects kind {}", object,),
Some(e.message(ErrorMessageVerbosity::FullDetails)),
),
));
continue;

View File

@@ -19,7 +19,7 @@ use crate::cmd::kubectl::{kubectl_exec_api_custom_metrics, kubectl_exec_get_all_
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;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::Stage::Infrastructure;
use crate::events::{EngineEvent, EnvironmentStep, EventDetails, EventMessage, InfrastructureStep, Stage, Transmitter};
use crate::io_models::{
@@ -995,7 +995,10 @@ impl Kapsule {
.log(EngineEvent::Info(event_details, EventMessage::new_from_safe(ok_line))),
Err(err) => self.logger().log(EngineEvent::Warning(
event_details,
EventMessage::new("Error trying to get kubernetes events".to_string(), Some(err.message())),
EventMessage::new(
"Error trying to get kubernetes events".to_string(),
Some(err.message(ErrorMessageVerbosity::FullDetails)),
),
)),
};
@@ -1135,7 +1138,7 @@ impl Kapsule {
Err(e) => {
let safe_message = format!("Error while looking at the API metric value {}", metric_name);
OperationResult::Retry(
EngineError::new_cannot_get_k8s_api_custom_metrics(event_details.clone(), CommandError::new(format!("{}, error: {}", safe_message, e.message()), Some(safe_message))))
EngineError::new_cannot_get_k8s_api_custom_metrics(event_details.clone(), CommandError::new(e.message(ErrorMessageVerbosity::FullDetails), Some(safe_message))))
}
};
});
@@ -1306,7 +1309,7 @@ impl Kapsule {
)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
@@ -1326,7 +1329,7 @@ impl Kapsule {
);
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new(message_safe, Some(e.message())),
EventMessage::new(message_safe, Some(e.message(ErrorMessageVerbosity::FullDetails))),
));
}
}
@@ -1401,7 +1404,7 @@ impl Kapsule {
EventMessage::new_from_safe(format!("Namespace {} is fully deleted", qovery_namespace)),
)),
Err(e) => {
if !(e.message().contains("not found")) {
if !(e.message(ErrorMessageVerbosity::FullDetails).contains("not found")) {
self.logger().log(EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!("Can't delete namespace {}.", qovery_namespace)),

View File

@@ -18,7 +18,7 @@ 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};
use crate::cmd::structs::LabelsContent;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::{EngineEvent, EnvironmentStep, EventDetails, EventMessage, Stage, ToTransmitter};
use crate::io_models::ProgressLevel::Info;
use crate::io_models::{
@@ -1056,7 +1056,10 @@ where
Err(EngineError::new_k8s_service_issue(
event_details,
CommandError::new(err.message(), Some("Error with Kubernetes service".to_string())),
CommandError::new(
err.message(ErrorMessageVerbosity::FullDetails),
Some("Error with Kubernetes service".to_string()),
),
))
}
_ => {

View File

@@ -8,7 +8,7 @@ use crate::cmd::command::QoveryCommand;
use crate::cmd::helm::HelmCommand::{LIST, ROLLBACK, STATUS, UNINSTALL, UPGRADE};
use crate::cmd::helm::HelmError::{CannotRollback, CmdError, InvalidKubeConfig, ReleaseDoesNotExist};
use crate::cmd::structs::{HelmChart, HelmListItem};
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::events::EventDetails;
use semver::Version;
use serde_derive::Deserialize;
@@ -137,7 +137,7 @@ impl Helm {
) {
Err(_) if stderr.contains("release: not found") => Err(ReleaseDoesNotExist(chart.name.clone())),
Err(err) => {
stderr.push_str(&err.message());
stderr.push_str(&err.message(ErrorMessageVerbosity::FullDetails));
let error = CommandError::new(stderr, err.message_safe());
Err(CmdError(chart.name.clone(), STATUS, error))
}
@@ -174,7 +174,7 @@ impl Helm {
let mut stderr = String::new();
match helm_exec_with_output(&args, &self.get_all_envs(envs), &mut |_| {}, &mut |line| stderr.push_str(&line)) {
Err(err) => {
stderr.push_str(&err.message());
stderr.push_str(&err.message(ErrorMessageVerbosity::FullDetails));
let error = CommandError::new(stderr, err.message_safe());
Err(CmdError(chart.name.clone(), ROLLBACK, error))
}
@@ -207,7 +207,7 @@ impl Helm {
let mut stderr = String::new();
match helm_exec_with_output(&args, &self.get_all_envs(envs), &mut |_| {}, &mut |line| stderr.push_str(&line)) {
Err(err) => {
stderr.push_str(&err.message());
stderr.push_str(&err.message(ErrorMessageVerbosity::FullDetails));
let error = CommandError::new(stderr, err.message_safe());
Err(CmdError(chart.name.clone(), UNINSTALL, error))
}
@@ -483,7 +483,7 @@ impl Helm {
// Try do define/specify a bit more the message
let stderr_msg: String = error_message.into_iter().collect();
let stderr_msg = format!("{}: {}", stderr_msg, err.message());
let stderr_msg = format!("{}: {}", stderr_msg, err.message(ErrorMessageVerbosity::FullDetails));
let error = if stderr_msg.contains("another operation (install/upgrade/rollback) is in progress") {
HelmError::ReleaseLocked(chart.name.clone())
} else if stderr_msg.contains("has been rolled back") {

View File

@@ -14,7 +14,7 @@ use crate::cmd::structs::{
};
use crate::constants::KUBECONFIG;
use crate::error::{SimpleError, SimpleErrorKind};
use crate::errors::CommandError;
use crate::errors::{CommandError, ErrorMessageVerbosity};
pub enum ScalingKind {
Deployment,
@@ -839,7 +839,7 @@ where
match result {
Ok(_) => Ok(()),
Err(e) => {
let lower_case_message = e.message().to_lowercase();
let lower_case_message = e.message(ErrorMessageVerbosity::FullDetails).to_lowercase();
if lower_case_message.contains("no resources found") || lower_case_message.ends_with(" deleted") {
return Ok(());
}
@@ -1158,7 +1158,7 @@ where
&mut |_| {},
) {
Ok(_) => Ok(pod_to_be_deleted),
Err(e) => Err(CommandError::new(e.message(), None)),
Err(e) => Err(e),
}
}

View File

@@ -4,7 +4,7 @@ use retry::OperationResult;
use crate::cmd::command::QoveryCommand;
use crate::constants::TF_PLUGIN_CACHE_DIR;
use crate::errors::CommandError;
use crate::errors::{CommandError, ErrorMessageVerbosity};
use rand::Rng;
use retry::Error::Operation;
use std::{env, fs, thread, time};
@@ -14,8 +14,12 @@ fn manage_common_issues(terraform_provider_lock: &str, err: &CommandError) -> Re
// in order to avoid lock errors on parallel run, let's sleep a bit
// https://github.com/hashicorp/terraform/issues/28041
if err.message().contains("Failed to install provider from shared cache")
|| err.message().contains("Failed to install provider")
if err
.message(ErrorMessageVerbosity::FullDetails)
.contains("Failed to install provider from shared cache")
|| err
.message(ErrorMessageVerbosity::FullDetails)
.contains("Failed to install provider")
{
let sleep_time_int = rand::thread_rng().gen_range(20..45);
let sleep_time = time::Duration::from_secs(sleep_time_int);
@@ -33,7 +37,10 @@ fn manage_common_issues(terraform_provider_lock: &str, err: &CommandError) -> Re
)),
)),
};
} else if err.message().contains("Plugin reinitialization required") {
} else if err
.message(ErrorMessageVerbosity::FullDetails)
.contains("Plugin reinitialization required")
{
// terraform init is required
return Ok(());
}

View File

@@ -6,14 +6,14 @@ use serde_derive::{Deserialize, Serialize};
#[serde(rename_all = "lowercase")]
pub struct CommandError {
message: String,
message_unsafe: String,
full_details: String,
}
impl From<errors::CommandError> for CommandError {
fn from(error: errors::CommandError) -> Self {
CommandError {
message: error.message_safe.unwrap_or_default(),
message_unsafe: error.message_raw,
full_details: error.full_details,
}
}
}

View File

@@ -16,19 +16,28 @@ use std::fmt::{Display, Formatter};
use thiserror::Error;
use url::Url;
/// ErrorMessageVerbosity: represents command error message's verbosity from minimal to full verbosity.
pub enum ErrorMessageVerbosity {
SafeOnly,
FullDetailsWithoutEnvVars,
FullDetails,
}
/// CommandError: command error, mostly returned by third party tools.
#[derive(Clone, Debug, Error, PartialEq)]
pub struct CommandError {
/// message: full error message, can contains unsafe text such as passwords and tokens.
message_raw: String,
/// full_details: full error message, can contains unsafe text such as passwords and tokens.
full_details: String,
/// message_safe: error message omitting displaying any protected data such as passwords and tokens.
message_safe: Option<String>,
/// env_vars: environments variables including touchy data such as secret keys.
env_vars: Option<Vec<(String, String)>>,
}
impl CommandError {
/// Returns CommandError message_raw. May contains unsafe text such as passwords and tokens.
pub fn message_raw(&self) -> String {
self.message_raw.to_string()
self.full_details.to_string()
}
/// Returns CommandError message_safe omitting all unsafe text such as passwords and tokens.
@@ -36,17 +45,41 @@ impl CommandError {
self.message_safe.clone()
}
/// Returns error all message (safe + unsafe).
pub fn message(&self) -> String {
// TODO(benjaminch): To be revamped, not sure how we should deal with safe and unsafe messages.
if let Some(msg) = &self.message_safe {
// TODO(benjaminch): Handle raw / safe as for event message
if self.message_raw != *msg {
return format!("{} {}", msg, self.message_raw);
}
}
/// Returns CommandError env_vars.
pub fn env_vars(&self) -> Option<Vec<(String, String)>> {
self.env_vars.clone()
}
self.message_raw.to_string()
/// Returns error message based on verbosity.
pub fn message(&self, message_verbosity: ErrorMessageVerbosity) -> String {
match message_verbosity {
ErrorMessageVerbosity::SafeOnly => match &self.message_safe {
None => "".to_string(),
Some(msg) => msg.to_string(),
},
ErrorMessageVerbosity::FullDetailsWithoutEnvVars => match &self.message_safe {
None => self.full_details.to_string(),
Some(safe) => format!("{} / Full details: {}", safe, self.full_details),
},
ErrorMessageVerbosity::FullDetails => match &self.message_safe {
None => self.full_details.to_string(),
Some(safe) => match &self.env_vars {
None => format!("{} / Full details: {}", safe, self.full_details),
Some(env_vars) => {
format!(
"{} / Full details: {} / Env vars: {}",
safe,
self.full_details,
env_vars
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<String>>()
.join(" "),
)
}
},
},
}
}
/// Creates a new CommandError from safe message. To be used when message is safe.
@@ -57,8 +90,22 @@ impl CommandError {
/// Creates a new CommandError having both a safe and an unsafe message.
pub fn new(message_raw: String, message_safe: Option<String>) -> Self {
CommandError {
message_raw,
full_details: message_raw,
message_safe,
env_vars: None,
}
}
/// Creates a new CommandError having a safe, an unsafe message and env vars.
pub fn new_with_env_vars(
message_raw: String,
message_safe: Option<String>,
env_vars: Option<Vec<(String, String)>>,
) -> Self {
CommandError {
full_details: message_raw,
message_safe,
env_vars,
}
}
@@ -68,8 +115,9 @@ impl CommandError {
safe_message: Option<String>,
) -> Self {
CommandError {
message_raw: legacy_command_error.to_string(),
full_details: legacy_command_error.to_string(),
message_safe: safe_message,
env_vars: None,
}
}
@@ -82,16 +130,7 @@ impl CommandError {
stdout: Option<String>,
stderr: Option<String>,
) -> Self {
let mut unsafe_message = format!(
"{}\ncommand: {} {}\nenv: {}",
message,
bin,
cmd_args.join(" "),
envs.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<String>>()
.join(" ")
);
let mut unsafe_message = format!("{}\ncommand: {} {}", message, bin, cmd_args.join(" "),);
if let Some(txt) = stdout {
unsafe_message = format!("{}\nSTDOUT {}", unsafe_message, txt);
@@ -100,13 +139,13 @@ impl CommandError {
unsafe_message = format!("{}\nSTDERR {}", unsafe_message, txt);
}
CommandError::new(unsafe_message, Some(message))
CommandError::new_with_env_vars(unsafe_message, Some(message), Some(envs))
}
}
impl Display for CommandError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.message().as_str())
f.write_str(self.message(ErrorMessageVerbosity::SafeOnly).as_str()) // By default, expose safe message only
}
}
@@ -356,9 +395,9 @@ impl EngineError {
}
/// Returns proper error message.
pub fn message(&self) -> String {
pub fn message(&self, message_verbosity: ErrorMessageVerbosity) -> String {
match &self.message {
Some(msg) => msg.message(),
Some(msg) => msg.message(message_verbosity),
None => self.qovery_log_message.to_string(),
}
}
@@ -445,7 +484,9 @@ impl EngineError {
EngineErrorCause::Internal,
EngineErrorScope::from(self.event_details.transmitter()),
self.event_details.execution_id().to_string(),
Some(self.message()),
// Note: Since legacy EngineError is read directly as is in the Core, not all details are exposed
// since it can lead to expose secrets, hence not exposing env vars which may contains secrets.
Some(self.message(ErrorMessageVerbosity::FullDetailsWithoutEnvVars)),
)
}

View File

@@ -7,7 +7,7 @@ pub mod io;
extern crate url;
use crate::cloud_provider::Kind;
use crate::errors::{CommandError, EngineError};
use crate::errors::{CommandError, EngineError, ErrorMessageVerbosity};
use crate::io_models::QoveryIdentifier;
use std::fmt::{Display, Formatter};
@@ -41,7 +41,7 @@ impl EngineEvent {
EngineEvent::Debug(_details, message) => message.message(message_verbosity),
EngineEvent::Info(_details, message) => message.message(message_verbosity),
EngineEvent::Warning(_details, message) => message.message(message_verbosity),
EngineEvent::Error(engine_error, _message) => engine_error.message(),
EngineEvent::Error(engine_error, _message) => engine_error.message(message_verbosity.into()),
}
}
}
@@ -49,9 +49,20 @@ impl EngineEvent {
/// EventMessageVerbosity: represents event message's verbosity from minimal to full verbosity.
pub enum EventMessageVerbosity {
SafeOnly,
FullDetailsWithoutEnvVars,
FullDetails,
}
impl From<EventMessageVerbosity> for ErrorMessageVerbosity {
fn from(verbosity: EventMessageVerbosity) -> Self {
match verbosity {
EventMessageVerbosity::SafeOnly => ErrorMessageVerbosity::SafeOnly,
EventMessageVerbosity::FullDetailsWithoutEnvVars => ErrorMessageVerbosity::FullDetailsWithoutEnvVars,
EventMessageVerbosity::FullDetails => ErrorMessageVerbosity::FullDetails,
}
}
}
#[derive(Debug, Clone)]
/// EventMessage: represents an event message.
pub struct EventMessage {
@@ -59,6 +70,8 @@ pub struct EventMessage {
safe_message: String,
// String containing full details including touchy data (passwords and tokens).
full_details: Option<String>,
// Environments variables including touchy data such as secret keys.
env_vars: Option<Vec<(String, String)>>,
}
impl EventMessage {
@@ -72,6 +85,26 @@ impl EventMessage {
EventMessage {
safe_message,
full_details,
env_vars: None,
}
}
/// Creates e new EventMessage with environment variables.
///
/// Arguments
///
/// * `safe_message`: Event safe message string (from which all unsafe text such as passwords and tokens has been removed).
/// * `full_details`: Event raw message string (which may include unsafe text such as passwords and tokens).
/// * `env_vars`: Event environment variables (which may contains unsafe text such as secrets keys).
pub fn new_with_env_vars(
safe_message: String,
full_details: Option<String>,
env_vars: Option<Vec<(String, String)>>,
) -> Self {
EventMessage {
safe_message,
full_details,
env_vars,
}
}
@@ -84,6 +117,7 @@ impl EventMessage {
EventMessage {
safe_message,
full_details: None,
env_vars: None,
}
}
@@ -95,17 +129,35 @@ impl EventMessage {
pub fn message(&self, message_verbosity: EventMessageVerbosity) -> String {
match message_verbosity {
EventMessageVerbosity::SafeOnly => self.safe_message.to_string(),
EventMessageVerbosity::FullDetails => match &self.full_details {
EventMessageVerbosity::FullDetailsWithoutEnvVars => match &self.full_details {
None => self.safe_message.to_string(),
Some(details) => format!("{} / Full details: {}", self.safe_message, details),
},
EventMessageVerbosity::FullDetails => match &self.full_details {
None => self.safe_message.to_string(),
Some(details) => match &self.env_vars {
None => format!("{} / Full details: {}", self.safe_message, details),
Some(env_vars) => {
format!(
"{} / Full details: {} / Env vars: {}",
self.safe_message,
details,
env_vars
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect::<Vec<String>>()
.join(" "),
)
}
},
},
}
}
}
impl From<CommandError> for EventMessage {
fn from(e: CommandError) -> Self {
EventMessage::new(e.message_raw(), e.message_safe())
EventMessage::new_with_env_vars(e.message_raw(), e.message_safe(), e.env_vars())
}
}
@@ -420,27 +472,61 @@ mod tests {
#[test]
fn test_event_message() {
// setup:
let test_cases: Vec<(String, Option<String>, EventMessageVerbosity, String)> = vec![
let test_cases: Vec<(
String,
Option<String>,
Option<Vec<(String, String)>>,
EventMessageVerbosity,
String,
)> = vec![
(
"safe".to_string(),
Some("raw".to_string()),
Some(vec![("env".to_string(), "value".to_string())]),
EventMessageVerbosity::SafeOnly,
"safe".to_string(),
),
("safe".to_string(), None, EventMessageVerbosity::SafeOnly, "safe".to_string()),
("safe".to_string(), None, EventMessageVerbosity::FullDetails, "safe".to_string()),
(
"safe".to_string(),
None,
None,
EventMessageVerbosity::SafeOnly,
"safe".to_string(),
),
(
"safe".to_string(),
None,
None,
EventMessageVerbosity::FullDetails,
"safe".to_string(),
),
(
"safe".to_string(),
Some("raw".to_string()),
None,
EventMessageVerbosity::FullDetails,
"safe / Full details: raw".to_string(),
),
(
"safe".to_string(),
Some("raw".to_string()),
Some(vec![("env".to_string(), "value".to_string())]),
EventMessageVerbosity::FullDetailsWithoutEnvVars,
"safe / Full details: raw".to_string(),
),
(
"safe".to_string(),
Some("raw".to_string()),
Some(vec![("env".to_string(), "value".to_string())]),
EventMessageVerbosity::FullDetails,
"safe / Full details: raw / Env vars: env=value".to_string(),
),
];
for tc in test_cases {
// execute:
let (safe_message, raw_message, verbosity, expected) = tc;
let event_message = EventMessage::new(safe_message, raw_message);
let (safe_message, raw_message, env_vars, verbosity, expected) = tc;
let event_message = EventMessage::new_with_env_vars(safe_message, raw_message, env_vars);
// validate:
assert_eq!(expected, event_message.message(verbosity));

View File

@@ -33,6 +33,7 @@ use crate::cloud_provider::CloudProvider;
use crate::cloud_provider::Kind as CPKind;
use crate::cmd::docker::Docker;
use crate::container_registry::ContainerRegistryInfo;
use crate::errors::ErrorMessageVerbosity;
use crate::logger::Logger;
use crate::models;
use crate::models::application::{ApplicationError, ApplicationService};
@@ -829,7 +830,13 @@ impl Database {
Some(db)
}
Err(e) => {
error!("{}", format!("error while parsing postgres version, error: {}", e.message()));
error!(
"{}",
format!(
"error while parsing postgres version, error: {}",
e.message(ErrorMessageVerbosity::FullDetails)
)
);
None
}
},
@@ -854,7 +861,13 @@ impl Database {
Some(db)
}
Err(e) => {
error!("{}", format!("error while parsing mysql version, error: {}", e.message()));
error!(
"{}",
format!(
"error while parsing mysql version, error: {}",
e.message(ErrorMessageVerbosity::FullDetails)
)
);
None
}
},

View File

@@ -539,7 +539,7 @@ where
cluster_name.clone().as_str(),
) {
Ok(kubeconfig) => kubeconfig,
Err(e) => return OperationResult::Retry(CommandError::new(e.message(), Some(e.message()))),
Err(e) => return OperationResult::Retry(e),
};
match kubeconfig {