feat: migrate env logging to new method

Ticket: ENG-1112
This commit is contained in:
Benjamin Chastanier
2022-02-10 13:55:40 +01:00
committed by Benjamin
parent eb561b8dad
commit 4be43c3bec
62 changed files with 3217 additions and 1442 deletions

View File

@@ -6,10 +6,12 @@ use git2::{Cred, CredentialType};
use sysinfo::{Disk, DiskExt, SystemExt};
use crate::build_platform::{docker, Build, BuildPlatform, BuildResult, CacheResult, Credentials, Image, Kind};
use crate::cmd::command::{CommandError, QoveryCommand};
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, SimpleError};
use crate::cmd::command::QoveryCommand;
use crate::errors::{CommandError, EngineError, Tag};
use crate::events::{EngineEvent, EventDetails, EventMessage, ToTransmitter, Transmitter};
use crate::fs::workspace_directory;
use crate::git;
use crate::logger::{LogLevel, Logger};
use crate::models::{
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
};
@@ -30,15 +32,17 @@ pub struct LocalDocker {
id: String,
name: String,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl LocalDocker {
pub fn new(context: Context, id: &str, name: &str) -> Self {
pub fn new(context: Context, id: &str, name: &str, logger: Box<dyn Logger>) -> Self {
LocalDocker {
context,
id: id.to_string(),
name: name.to_string(),
listeners: vec![],
logger,
}
}
@@ -64,9 +68,14 @@ impl LocalDocker {
match fs::read(dockerfile_path) {
Ok(bytes) => Ok(bytes),
Err(err) => {
let error_msg = format!("Can't read Dockerfile '{}'", dockerfile_path);
error!("{}, error: {:?}", error_msg, err);
Err(self.engine_error(EngineErrorCause::Internal, error_msg))
let engine_error = EngineError::new_docker_cannot_read_dockerfile(
self.get_event_details(),
dockerfile_path.to_string(),
CommandError::new(err.to_string(), None),
);
self.logger
.log(LogLevel::Error, EngineEvent::Error(engine_error.clone(), None));
Err(engine_error)
}
}
}
@@ -102,9 +111,14 @@ impl LocalDocker {
let env_var_args = match docker::match_used_env_var_args(env_var_args, dockerfile_content) {
Ok(env_var_args) => env_var_args,
Err(err) => {
let error_msg = format!("Can't extract env vars from Dockerfile '{}'", dockerfile_complete_path);
error!("{}, error: {:?}", error_msg, err);
return Err(self.engine_error(EngineErrorCause::Internal, error_msg));
let engine_error = EngineError::new_docker_cannot_extract_env_vars_from_dockerfile(
self.get_event_details(),
dockerfile_complete_path.to_string(),
CommandError::new(err.to_string(), None),
);
self.logger
.log(LogLevel::Error, EngineEvent::Error(engine_error.clone(), None));
return Err(engine_error);
}
};
@@ -130,7 +144,10 @@ impl LocalDocker {
let exit_status = cmd.exec_with_abort(
Duration::minutes(BUILD_DURATION_TIMEOUT_MIN),
|line| {
info!("{}", line);
self.logger.log(
LogLevel::Info,
EngineEvent::Info(self.get_event_details(), EventMessage::new_from_safe(line.to_string())),
);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
@@ -142,7 +159,10 @@ impl LocalDocker {
));
},
|line| {
error!("{}", line);
self.logger.log(
LogLevel::Warning,
EngineEvent::Warning(self.get_event_details(), EventMessage::new_from_safe(line.to_string())),
);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
@@ -158,18 +178,10 @@ impl LocalDocker {
match exit_status {
Ok(_) => Ok(BuildResult { build }),
Err(CommandError::Killed(msg)) => Err(
self.engine_error(EngineErrorCause::Canceled, msg)
),
Err(err) => Err(self.engine_error(
EngineErrorCause::User(
"It looks like there is something wrong in your Dockerfile. Try building the application locally with `docker build --no-cache`.",
),
format!(
"error while building container image {}. Error: {:?}",
self.name_with_id(),
err
),
Err(err) => Err(EngineError::new_docker_cannot_build_container_image(
self.get_event_details(),
self.name_with_id(),
CommandError::new(format!("{:?}", err), None),
)),
}
}
@@ -187,7 +199,8 @@ impl LocalDocker {
let args = self.context.docker_build_options();
let mut exit_status: Result<(), CommandError> = Ok(());
let mut exit_status: Result<(), CommandError> =
Err(CommandError::new_from_safe_message("No builder names".to_string()));
for builder_name in BUILDPACKS_BUILDERS.iter() {
let mut buildpacks_args = if !use_build_cache {
@@ -250,12 +263,13 @@ impl LocalDocker {
self.context.execution_id(),
));
let err = EngineError::new(
EngineErrorCause::Internal,
EngineErrorScope::Engine,
self.context.execution_id().to_string(),
Some(msg),
let err = EngineError::new_buildpack_invalid_language_format(
self.get_event_details(),
buildpacks_language.to_string(),
);
self.logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
return Err(err);
}
}
@@ -273,34 +287,45 @@ impl LocalDocker {
// buildpacks build
let mut cmd = QoveryCommand::new("pack", &buildpacks_args, &self.get_docker_host_envs());
exit_status = cmd.exec_with_abort(
Duration::minutes(BUILD_DURATION_TIMEOUT_MIN),
|line| {
info!("{}", line);
exit_status = cmd
.exec_with_abort(
Duration::minutes(BUILD_DURATION_TIMEOUT_MIN),
|line| {
self.logger.log(
LogLevel::Info,
EngineEvent::Info(self.get_event_details(), EventMessage::new_from_safe(line.to_string())),
);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
id: build.image.application_id.clone(),
},
ProgressLevel::Info,
Some(line),
self.context.execution_id(),
));
},
|line| {
error!("{}", line);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
id: build.image.application_id.clone(),
},
ProgressLevel::Info,
Some(line),
self.context.execution_id(),
));
},
|line| {
self.logger.log(
LogLevel::Warning,
EngineEvent::Warning(
self.get_event_details(),
EventMessage::new_from_safe(line.to_string()),
),
);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
id: build.image.application_id.clone(),
},
ProgressLevel::Warn,
Some(line),
self.context.execution_id(),
));
},
is_task_canceled,
);
lh.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application {
id: build.image.application_id.clone(),
},
ProgressLevel::Warn,
Some(line),
self.context.execution_id(),
));
},
is_task_canceled,
)
.map_err(|err| CommandError::new(format!("{:?}", err), None));
if exit_status.is_ok() {
// quit now if the builder successfully build the app
@@ -310,21 +335,18 @@ impl LocalDocker {
match exit_status {
Ok(_) => Ok(BuildResult { build }),
Err(CommandError::Killed(msg)) => Err(self.engine_error(EngineErrorCause::Canceled, msg)),
Err(err) => {
warn!("{:?}", err);
let error = EngineError::new_buildpack_cannot_build_container_image(
self.get_event_details(),
self.name_with_id(),
BUILDPACKS_BUILDERS.iter().map(|b| b.to_string()).collect(),
CommandError::new(format!("{:?}", err), None),
);
Err(self.engine_error(
EngineErrorCause::User(
"None builders supports Your application can't be built without providing a Dockerfile",
),
format!(
"Qovery can't build your container image {} with one of the following builders: {}. \
Please do provide a valid Dockerfile to build your application or contact the support.",
self.name_with_id(),
BUILDPACKS_BUILDERS.join(", ")
),
))
self.logger
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
Err(error)
}
}
}
@@ -335,7 +357,12 @@ impl LocalDocker {
self.context.execution_id(),
format!("build/{}", build.image.name.as_str()),
)
.map_err(|err| self.engine_error(EngineErrorCause::Internal, err.to_string()))
.map_err(|err| {
EngineError::new_cannot_get_workspace_directory(
self.get_event_details(),
CommandError::new(err.to_string(), None),
)
})
}
}
@@ -358,11 +385,17 @@ impl BuildPlatform for LocalDocker {
fn is_valid(&self) -> Result<(), EngineError> {
if !crate::cmd::command::does_binary_exist("docker") {
return Err(self.engine_error(EngineErrorCause::Internal, String::from("docker binary not found")));
return Err(EngineError::new_missing_required_binary(
self.get_event_details(),
"docker".to_string(),
));
}
if !crate::cmd::command::does_binary_exist("pack") {
return Err(self.engine_error(EngineErrorCause::Internal, String::from("pack binary not found")));
return Err(EngineError::new_missing_required_binary(
self.get_event_details(),
"pack".to_string(),
));
}
Ok(())
@@ -374,9 +407,9 @@ impl BuildPlatform for LocalDocker {
// Check if a local cache layers for the container image exists.
let repository_root_path = self.get_repository_build_root_path(&build)?;
let parent_build = build
.to_previous_build(repository_root_path)
.map_err(|err| self.engine_error(EngineErrorCause::Internal, err.to_string()))?;
let parent_build = build.to_previous_build(repository_root_path).map_err(|err| {
EngineError::new_builder_get_build_error(self.get_event_details(), build.image.commit_id.to_string(), err)
})?;
let parent_build = match parent_build {
Some(parent_build) => parent_build,
@@ -402,20 +435,32 @@ impl BuildPlatform for LocalDocker {
force_build: bool,
is_task_canceled: &dyn Fn() -> bool,
) -> Result<BuildResult, EngineError> {
info!("LocalDocker.build() called for {}", self.name());
let event_details = self.get_event_details();
self.logger.log(
LogLevel::Info,
EngineEvent::Info(
event_details.clone(),
EventMessage::new_from_safe("LocalDocker.build() called".to_string()),
),
);
if is_task_canceled() {
return Err(self.engine_error(
EngineErrorCause::Canceled,
"Notified to cancel current task".to_string(),
));
return Err(EngineError::new_task_cancellation_requested(event_details.clone()));
}
let listeners_helper = ListenersHelper::new(&self.listeners);
if !force_build && self.image_does_exist(&build.image)? {
info!(
"image {:?} found on repository, container build is not required",
build.image
self.logger.log(
LogLevel::Info,
EngineEvent::Info(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Image `{}` found on repository, container build is not required",
build.image.name_with_tag()
)),
),
);
return Ok(BuildResult { build });
@@ -423,9 +468,15 @@ impl BuildPlatform for LocalDocker {
let repository_root_path = self.get_repository_build_root_path(&build)?;
info!(
"cloning repository: {} to {}",
build.git_repository.url, repository_root_path
self.logger.log(
LogLevel::Info,
EngineEvent::Info(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Cloning repository: {} to {}",
build.git_repository.url, repository_root_path
)),
),
);
let get_credentials = |user: &str| {
@@ -456,10 +507,7 @@ impl BuildPlatform for LocalDocker {
// git clone
if is_task_canceled() {
return Err(self.engine_error(
EngineErrorCause::Canceled,
"Notified to cancel current task".to_string(),
));
return Err(EngineError::new_task_cancellation_requested(event_details.clone()));
}
if let Err(clone_error) = git::clone_at_commit(
&build.git_repository.url,
@@ -467,12 +515,16 @@ impl BuildPlatform for LocalDocker {
&repository_root_path,
&get_credentials,
) {
let message = format!(
"Error while cloning repository {}. Error: {:?}",
&build.git_repository.url, clone_error
let error = EngineError::new_builder_clone_repository_error(
self.get_event_details(),
build.git_repository.url.to_string(),
CommandError::new(clone_error.to_string(), None),
);
error!("{}", message);
return Err(self.engine_error(EngineErrorCause::Internal, message));
self.logger
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
let mut disable_build_cache = false;
@@ -491,7 +543,15 @@ impl BuildPlatform for LocalDocker {
// ensure docker_path is a mounted volume, otherwise ignore because it's not what Qovery does in production
// ex: this cause regular cleanup on CI, leading to random tests errors
match env::var_os("CI") {
Some(_) => info!("CI environment variable found, no docker prune will be made"),
Some(_) => self.logger.log(
LogLevel::Info,
EngineEvent::Info(
event_details.clone(),
EventMessage::new_from_safe(
"CI environment variable found, no docker prune will be made".to_string(),
),
),
),
None => {
// ensure there is enough disk space left before building a new image
let docker_path_string = "/var/lib/docker";
@@ -503,9 +563,20 @@ impl BuildPlatform for LocalDocker {
for disk in system.get_disks() {
if disk.get_mount_point() == docker_path {
match check_docker_space_usage_and_clean(disk, self.get_docker_host_envs()) {
Ok(msg) => info!("{:?}", msg),
Err(e) => error!("{:?}", e.message),
let event_details = self.get_event_details();
if let Err(e) = check_docker_space_usage_and_clean(
disk,
self.get_docker_host_envs(),
event_details.clone(),
&*self.logger(),
) {
self.logger.log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new(e.message_raw(), e.message_safe()),
),
);
}
break;
};
@@ -530,7 +601,6 @@ impl BuildPlatform for LocalDocker {
// If the dockerfile does not exist, abort
if !Path::new(dockerfile_absolute_path.as_str()).exists() {
warn!("Dockerfile not found under {}", dockerfile_absolute_path);
listeners_helper.error(ProgressInfo::new(
ProgressScope::Application {
id: build.image.application_id.clone(),
@@ -543,14 +613,13 @@ impl BuildPlatform for LocalDocker {
self.context.execution_id(),
));
return Err(self.engine_error(
EngineErrorCause::User("Dockerfile not found at location"),
format!(
"Your Dockerfile is not present at the specified location {}/{}",
build.git_repository.root_path.as_str(),
build.git_repository.dockerfile_path.unwrap_or_default().as_str()
),
));
let error =
EngineError::new_docker_cannot_find_dockerfile(self.get_event_details(), dockerfile_absolute_path);
self.logger
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
self.build_image_with_docker(
@@ -576,14 +645,14 @@ impl BuildPlatform for LocalDocker {
let msg = match &result {
Ok(_) => format!("✅ Container {} is built", self.name_with_id()),
Err(engine_err) if engine_err.is_cancel() => {
Err(engine_err) if engine_err.tag() == &Tag::TaskCancellationRequested => {
format!("🚫 Container {} build has been canceled", self.name_with_id())
}
Err(engine_err) => {
format!(
"❌ Container {} failed to be build: {}",
self.name_with_id(),
engine_err.message.as_ref().map(|x| x.as_str()).unwrap_or_default()
engine_err.message()
)
}
};
@@ -591,15 +660,27 @@ impl BuildPlatform for LocalDocker {
listeners_helper.deployment_in_progress(ProgressInfo::new(
ProgressScope::Application { id: app_id },
ProgressLevel::Info,
Some(msg),
Some(msg.to_string()),
self.context.execution_id(),
));
self.logger.log(
LogLevel::Info,
EngineEvent::Info(event_details.clone(), EventMessage::new_from_safe(msg.to_string())),
);
result
}
fn build_error(&self, build: Build) -> Result<BuildResult, EngineError> {
warn!("LocalDocker.build_error() called for {}", self.name());
let event_details = self.get_event_details();
self.logger.log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!("LocalDocker.build_error() called for {}", self.name())),
),
);
let listener_helper = ListenersHelper::new(&self.listeners);
@@ -615,8 +696,16 @@ impl BuildPlatform for LocalDocker {
self.context.execution_id(),
));
let err = EngineError::new_not_implemented_error(event_details);
self.logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
// FIXME
Err(self.engine_error(EngineErrorCause::Internal, message))
Err(err)
}
fn logger(&self) -> Box<dyn Logger> {
self.logger.clone()
}
}
@@ -630,38 +719,54 @@ impl Listen for LocalDocker {
}
}
impl ToTransmitter for LocalDocker {
fn to_transmitter(&self) -> Transmitter {
Transmitter::BuildPlatform(self.id().to_string(), self.name().to_string())
}
}
fn check_docker_space_usage_and_clean(
docker_path_size_info: &Disk,
envs: Vec<(&str, &str)>,
) -> Result<String, SimpleError> {
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), CommandError> {
let docker_max_disk_percentage_usage_before_purge = 60; // arbitrary percentage that should make the job anytime
let available_space = docker_path_size_info.get_available_space();
let docker_percentage_remaining = available_space * 100 / docker_path_size_info.get_total_space();
if docker_percentage_remaining < docker_max_disk_percentage_usage_before_purge || available_space == 0 {
warn!(
"Docker disk remaining ({}%) is lower than {}%, requesting cleaning (purge)",
docker_percentage_remaining, docker_max_disk_percentage_usage_before_purge
logger.log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Docker disk remaining ({}%) is lower than {}%, requesting cleaning (purge)",
docker_percentage_remaining, docker_max_disk_percentage_usage_before_purge
)),
),
);
return match docker_prune_images(envs) {
Err(e) => {
error!("error while purging docker images: {:?}", e.message);
Err(e)
}
_ => Ok("docker images have been purged".to_string()),
};
return docker_prune_images(envs);
};
Ok(format!(
"no need to purge old docker images, only {}% ({}/{}) disk used",
100 - docker_percentage_remaining,
docker_path_size_info.get_available_space(),
docker_path_size_info.get_total_space(),
))
logger.log(
LogLevel::Info,
EngineEvent::Info(
event_details.clone(),
EventMessage::new_from_safe(format!(
"No need to purge old docker images, only {}% ({}/{}) disk used",
100 - docker_percentage_remaining,
docker_path_size_info.get_available_space(),
docker_path_size_info.get_total_space(),
)),
),
);
Ok(())
}
fn docker_prune_images(envs: Vec<(&str, &str)>) -> Result<(), SimpleError> {
fn docker_prune_images(envs: Vec<(&str, &str)>) -> Result<(), CommandError> {
let all_prunes_commands = vec![
vec!["container", "prune", "-f"],
vec!["image", "prune", "-a", "-f"],
@@ -669,20 +774,19 @@ fn docker_prune_images(envs: Vec<(&str, &str)>) -> Result<(), SimpleError> {
vec!["volume", "prune", "-f"],
];
let mut errored_commands = vec![];
for prune in all_prunes_commands {
let mut cmd = QoveryCommand::new("docker", &prune, &envs);
match cmd.exec_with_timeout(
Duration::minutes(BUILD_DURATION_TIMEOUT_MIN),
|line| {
debug!("{}", line);
},
|line| {
debug!("{}", line);
},
) {
Ok(_) => {}
Err(e) => error!("error while puring {}. {:?}", prune[0], e),
};
if let Err(e) = cmd.exec_with_timeout(Duration::minutes(BUILD_DURATION_TIMEOUT_MIN), |_| {}, |_| {}) {
errored_commands.push(format!("{} {:?}", prune[0], e));
}
}
if errored_commands.len() > 0 {
return Err(CommandError::new(
errored_commands.join("/ "),
Some("Error while trying to prune images.".to_string()),
));
}
Ok(())

View File

@@ -1,18 +1,20 @@
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter};
use crate::git;
use crate::models::{Context, Listen};
use crate::logger::Logger;
use crate::models::{Context, Listen, QoveryIdentifier};
use crate::utilities::get_image_tag;
use git2::{Cred, CredentialType, Error};
use git2::{Cred, CredentialType};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::path::Path;
pub mod docker;
pub mod local_docker;
pub trait BuildPlatform: Listen {
pub trait BuildPlatform: ToTransmitter + Listen {
fn context(&self) -> &Context;
fn kind(&self) -> Kind;
fn id(&self) -> &str;
@@ -29,15 +31,17 @@ pub trait BuildPlatform: Listen {
is_task_canceled: &dyn Fn() -> bool,
) -> Result<BuildResult, EngineError>;
fn build_error(&self, build: Build) -> Result<BuildResult, EngineError>;
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::BuildPlatform(self.id().to_string(), self.name().to_string())
}
fn engine_error(&self, cause: EngineErrorCause, message: String) -> EngineError {
EngineError::new(
cause,
self.engine_error_scope(),
self.context().execution_id(),
Some(message),
fn logger(&self) -> Box<dyn Logger>;
fn get_event_details(&self) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
Stage::Environment(EnvironmentStep::Build),
self.to_transmitter(),
)
}
}
@@ -49,7 +53,7 @@ pub struct Build {
}
impl Build {
pub fn to_previous_build<P>(&self, clone_repo_into_dir: P) -> Result<Option<Build>, Error>
pub fn to_previous_build<P>(&self, clone_repo_into_dir: P) -> Result<Option<Build>, CommandError>
where
P: AsRef<Path>,
{
@@ -64,7 +68,8 @@ impl Build {
Cred::userpass_plaintext(creds.login.as_str(), creds.password.as_str()).unwrap(),
)],
},
)?;
)
.map_err(|err| CommandError::new(err.to_string(), Some("Cannot get parent commit ID.".to_string())))?;
let parent_commit_id = match parent_commit_id {
None => return Ok(None),

View File

@@ -1,6 +1,7 @@
use tera::Context as TeraContext;
use crate::build_platform::Image;
use crate::cloud_provider::kubernetes::validate_k8s_required_cpu_and_burstable;
use crate::cloud_provider::models::{
EnvironmentVariable, EnvironmentVariableDataTemplate, Storage, StorageDataTemplate,
};
@@ -9,13 +10,13 @@ use crate::cloud_provider::service::{
scale_down_application, send_progress_on_long_task, Action, Application as CApplication, Create, Delete, Helm,
Pause, Service, ServiceType, StatelessService,
};
use crate::cloud_provider::utilities::{print_action, sanitize_name, validate_k8s_required_cpu_and_burstable};
use crate::cloud_provider::utilities::{print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl::ScalingKind::{Deployment, Statefulset};
use crate::error::EngineErrorCause::Internal;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners, ListenersHelper, Port};
use ::function_name::named;
@@ -35,6 +36,7 @@ pub struct Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Application {
@@ -54,6 +56,7 @@ impl Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Application {
context,
@@ -71,6 +74,7 @@ impl Application {
storage,
environment_variables,
listeners,
logger,
}
}
@@ -192,6 +196,7 @@ impl Service for Application {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let mut context = default_tera_context(self, target.kubernetes, target.environment);
let commit_id = self.image().commit_id.as_str();
@@ -201,10 +206,18 @@ impl Service for Application {
Some(registry_url) => context.insert("image_name_with_tag", registry_url.as_str()),
None => {
let image_name_with_tag = self.image().name_with_tag();
warn!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
)),
),
);
context.insert("image_name_with_tag", image_name_with_tag.as_str());
}
}
@@ -237,17 +250,20 @@ impl Service for Application {
&self.id,
self.total_cpus(),
self.cpu_burst(),
event_details.clone(),
self.logger(),
) {
Ok(l) => l,
Err(e) => {
return Err(EngineError::new(
Internal,
EngineErrorScope::Application(self.id().to_string(), self.name().to_string()),
self.context.execution_id(),
Some(e.to_string()),
return Err(EngineError::new_k8s_validate_required_cpu_and_burstable_error(
event_details.clone(),
self.total_cpus(),
self.cpu_burst(),
e,
));
}
};
context.insert("cpu_burst", &cpu_limits.cpu_limit);
let storage = self
@@ -286,23 +302,26 @@ impl Service for Application {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("appId={}", self.id))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Application(self.id().to_string(), self.name().to_string())
fn selector(&self) -> Option<String> {
Some(format!("appId={}", self.id))
}
}
impl Create for Application {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_user_stateless_service(target, self)
@@ -315,11 +334,14 @@ impl Create for Application {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -331,11 +353,14 @@ impl Create for Application {
impl Pause for Application {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -354,11 +379,14 @@ impl Pause for Application {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -374,6 +402,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
@@ -393,6 +423,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {

View File

@@ -6,7 +6,7 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{
generate_supported_version, get_self_hosted_mongodb_version, get_supported_version_to_use, print_action,
@@ -14,8 +14,9 @@ use crate::cloud_provider::utilities::{
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -33,6 +34,7 @@ pub struct MongoDB {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MongoDB {
@@ -49,6 +51,7 @@ impl MongoDB {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
MongoDB {
context,
@@ -63,11 +66,21 @@ impl MongoDB {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<String, EngineError> {
check_service_version(get_mongodb_version(self.version(), is_managed_services), self)
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_mongodb_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -155,17 +168,14 @@ impl Service for MongoDB {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, target.kubernetes, target.environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -176,7 +186,9 @@ impl Service for MongoDB {
context.insert("namespace", environment.namespace());
let version = self.matching_correct_version(self.is_managed_service())?;
let version = self
.matching_correct_version(self.is_managed_service(), event_details.clone())?
.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -221,16 +233,12 @@ impl Service for MongoDB {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -287,24 +295,35 @@ impl Create for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), &*self.logger)
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -313,11 +332,14 @@ impl Create for MongoDB {
impl Pause for MongoDB {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -331,11 +353,14 @@ impl Pause for MongoDB {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -351,10 +376,12 @@ impl Delete for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -364,11 +391,14 @@ impl Delete for MongoDB {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -384,7 +414,7 @@ impl Listen for MongoDB {
}
}
fn get_mongodb_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn get_mongodb_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
get_managed_mongodb_version(requested_version)
} else {
@@ -392,7 +422,7 @@ fn get_mongodb_version(requested_version: String, is_managed_service: bool) -> R
}
}
fn get_managed_mongodb_version(requested_version: String) -> Result<String, StringError> {
fn get_managed_mongodb_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_mongodb_versions = HashMap::new();
// v3.6.0
@@ -410,6 +440,7 @@ fn get_managed_mongodb_version(requested_version: String) -> Result<String, Stri
mod tests_mongodb {
use crate::cloud_provider::aws::databases::mongodb::{get_mongodb_version, MongoDB};
use crate::cloud_provider::service::{Action, DatabaseOptions, Service};
use crate::logger::StdIoLogger;
use crate::models::{Context, DatabaseMode};
#[test]
@@ -418,14 +449,20 @@ mod tests_mongodb {
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().as_str(),
get_mongodb_version("4.4".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"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().as_str(),
get_mongodb_version("3.4".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"MongoDB 3.4 version is not supported"
);
}
@@ -470,6 +507,7 @@ mod tests_mongodb {
publicly_accessible: false,
},
vec![],
Box::new(StdIoLogger::new()),
);
assert_eq!(database.sanitized_name(), db_expected_name);
}

View File

@@ -6,7 +6,7 @@ use crate::cloud_provider::aws::databases::utilities::{aws_final_snapshot_name,
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{
generate_supported_version, get_self_hosted_mysql_version, get_supported_version_to_use, managed_db_name_sanitizer,
@@ -15,8 +15,9 @@ use crate::cloud_provider::utilities::{
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, DatabaseKind, Listen, Listener, Listeners};
use ::function_name::named;
@@ -34,6 +35,7 @@ pub struct MySQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MySQL {
@@ -50,6 +52,7 @@ impl MySQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -64,11 +67,21 @@ impl MySQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<String, EngineError> {
check_service_version(get_mysql_version(self.version(), is_managed_services), self)
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_mysql_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -161,17 +174,13 @@ impl Service for MySQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -182,21 +191,23 @@ impl Service for MySQL {
context.insert("namespace", environment.namespace());
let version = &self.matching_correct_version(self.is_managed_service())?;
context.insert("version", &version);
let version = &self.matching_correct_version(self.is_managed_service(), event_details.clone())?;
context.insert("version", &version.matched_version());
if self.is_managed_service() {
let parameter_group_family = match get_parameter_group_from_version(&version, DatabaseKind::Mysql) {
Ok(v) => v,
Err(e) => {
return Err(EngineError {
cause: EngineErrorCause::Internal,
scope: EngineErrorScope::Engine,
execution_id: (&self.context.execution_id()).to_string(),
message: Some(e),
})
}
};
let parameter_group_family =
match get_parameter_group_from_version(version.matched_version(), DatabaseKind::Mysql) {
Ok(v) => v,
Err(e) => {
return Err(EngineError::new_terraform_unsupported_context_parameter_value(
event_details.clone(),
"MySQL".to_string(),
"parameter_group_family".to_string(),
version.matched_version().to_string(),
Some(e),
))
}
};
context.insert("parameter_group_family", &parameter_group_family);
};
@@ -242,16 +253,12 @@ impl Service for MySQL {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -298,24 +305,35 @@ impl Create for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -325,11 +343,14 @@ impl Create for MySQL {
impl Pause for MySQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -343,11 +364,14 @@ impl Pause for MySQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -362,10 +386,12 @@ impl Delete for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -375,11 +401,14 @@ impl Delete for MySQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -395,7 +424,7 @@ impl Listen for MySQL {
}
}
fn get_mysql_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn get_mysql_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
get_managed_mysql_version(requested_version)
} else {
@@ -403,7 +432,7 @@ fn get_mysql_version(requested_version: String, is_managed_service: bool) -> Res
}
}
fn get_managed_mysql_version(requested_version: String) -> Result<String, StringError> {
fn get_managed_mysql_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_mysql_versions = HashMap::new();
// https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_MySQL.html#MySQL.Concepts.VersionMgmt
@@ -432,6 +461,7 @@ fn get_managed_mysql_version(requested_version: String) -> Result<String, String
mod tests_mysql {
use crate::cloud_provider::aws::databases::mysql::{get_mysql_version, MySQL};
use crate::cloud_provider::service::{Action, DatabaseOptions, Service};
use crate::logger::StdIoLogger;
use crate::models::{Context, DatabaseMode};
#[test]
@@ -441,7 +471,10 @@ mod tests_mysql {
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().as_str(),
get_mysql_version("8.0.18".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"RDS MySQL 8.0.18 version is not supported"
);
// self-hosted version
@@ -449,7 +482,10 @@ mod tests_mysql {
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().as_str(),
get_mysql_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"MySQL 1.0 version is not supported"
);
}
@@ -494,6 +530,7 @@ mod tests_mysql {
publicly_accessible: false,
},
vec![],
Box::new(StdIoLogger::new()),
);
assert_eq!(database.sanitized_name(), db_expected_name);
}

View File

@@ -6,7 +6,7 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{
generate_supported_version, get_self_hosted_postgres_version, get_supported_version_to_use,
@@ -15,8 +15,9 @@ use crate::cloud_provider::utilities::{
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -34,6 +35,7 @@ pub struct PostgreSQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl PostgreSQL {
@@ -50,6 +52,7 @@ impl PostgreSQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
PostgreSQL {
context,
@@ -64,11 +67,21 @@ impl PostgreSQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<String, EngineError> {
check_service_version(get_postgres_version(self.version(), is_managed_services), self)
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_postgres_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -161,17 +174,13 @@ impl Service for PostgreSQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -182,7 +191,9 @@ impl Service for PostgreSQL {
context.insert("namespace", environment.namespace());
let version = self.matching_correct_version(self.is_managed_service())?;
let version = self
.matching_correct_version(self.is_managed_service(), event_details.clone())?
.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -229,16 +240,12 @@ impl Service for PostgreSQL {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -285,24 +292,35 @@ impl Create for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -312,11 +330,14 @@ impl Create for PostgreSQL {
impl Pause for PostgreSQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -330,11 +351,14 @@ impl Pause for PostgreSQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -350,10 +374,12 @@ impl Delete for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -363,11 +389,14 @@ impl Delete for PostgreSQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -384,7 +413,7 @@ impl Listen for PostgreSQL {
}
}
fn get_postgres_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn get_postgres_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
get_managed_postgres_version(requested_version)
} else {
@@ -392,7 +421,7 @@ fn get_postgres_version(requested_version: String, is_managed_service: bool) ->
}
}
fn get_managed_postgres_version(requested_version: String) -> Result<String, StringError> {
fn get_managed_postgres_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_postgres_versions = HashMap::new();
// https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts
@@ -423,6 +452,7 @@ fn get_managed_postgres_version(requested_version: String) -> Result<String, Str
mod tests_postgres {
use crate::cloud_provider::aws::databases::postgresql::{get_postgres_version, PostgreSQL};
use crate::cloud_provider::service::{Action, DatabaseOptions, Service};
use crate::logger::StdIoLogger;
use crate::models::{Context, DatabaseMode};
#[test]
@@ -431,11 +461,17 @@ mod tests_postgres {
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().as_str(),
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().as_str(),
get_postgres_version("11.3".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"Postgresql 11.3 version is not supported"
);
// self-hosted version
@@ -443,7 +479,10 @@ mod tests_postgres {
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().as_str(),
get_postgres_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"Postgresql 1.0 version is not supported"
);
}
@@ -488,6 +527,7 @@ mod tests_postgres {
publicly_accessible: false,
},
vec![],
Box::new(StdIoLogger::new()),
);
assert_eq!(database.sanitized_name(), db_expected_name);
}

View File

@@ -6,14 +6,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_redis_version, get_supported_version_to_use, print_action};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -31,6 +32,7 @@ pub struct Redis {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Redis {
@@ -47,6 +49,7 @@ impl Redis {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -61,11 +64,21 @@ impl Redis {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<String, EngineError> {
check_service_version(get_redis_version(self.version(), is_managed_services), self)
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_redis_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -164,17 +177,14 @@ impl Service for Redis {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -183,23 +193,29 @@ impl Service for Redis {
kubernetes.cloud_provider().credentials_environment_variables(),
);
let version = self.matching_correct_version(self.is_managed_service())?;
let version = self
.matching_correct_version(self.is_managed_service(), event_details.clone())?
.matched_version()
.to_string();
let parameter_group_name = if version.starts_with("5.") {
"default.redis5.0"
} else if version.starts_with("6.") {
"default.redis6.x"
} else {
return Err(self.engine_error(
EngineErrorCause::Internal,
"Elasticache parameter group name unknown".to_string(),
return Err(EngineError::new_terraform_unsupported_context_parameter_value(
event_details.clone(),
"Elasicache".to_string(),
"database_elasticache_parameter_group_name".to_string(),
format!("default.redis{}", version),
None,
));
};
context.insert("database_elasticache_parameter_group_name", parameter_group_name);
context.insert("namespace", environment.namespace());
context.insert("version", &version);
context.insert("version", version.as_str());
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
context.insert(k, v);
@@ -241,16 +257,12 @@ impl Service for Redis {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -297,24 +309,35 @@ impl Create for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -323,11 +346,14 @@ impl Create for Redis {
impl Pause for Redis {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -341,11 +367,14 @@ impl Pause for Redis {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -361,10 +390,12 @@ impl Delete for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -374,11 +405,14 @@ impl Delete for Redis {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -394,7 +428,7 @@ impl Listen for Redis {
}
}
fn get_redis_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn get_redis_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
get_managed_redis_version(requested_version)
} else {
@@ -402,7 +436,7 @@ fn get_redis_version(requested_version: String, is_managed_service: bool) -> Res
}
}
fn get_managed_redis_version(requested_version: String) -> Result<String, StringError> {
fn get_managed_redis_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_redis_versions = HashMap::with_capacity(2);
// https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/supported-engine-versions.html
@@ -416,6 +450,7 @@ fn get_managed_redis_version(requested_version: String) -> Result<String, String
mod tests {
use crate::cloud_provider::aws::databases::redis::{get_redis_version, Redis};
use crate::cloud_provider::service::{Action, DatabaseOptions, Service};
use crate::logger::StdIoLogger;
use crate::models::{Context, DatabaseMode};
#[test]
@@ -424,7 +459,10 @@ mod tests {
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().as_str(),
get_redis_version("1.0".to_string(), true)
.unwrap_err()
.message()
.as_str(),
"Elasticache 1.0 version is not supported"
);
@@ -432,7 +470,10 @@ mod tests {
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().as_str(),
get_redis_version("1.0".to_string(), false)
.unwrap_err()
.message()
.as_str(),
"Redis 1.0 version is not supported"
);
}
@@ -477,6 +518,7 @@ mod tests {
publicly_accessible: false,
},
vec![],
Box::new(StdIoLogger::new()),
);
assert_eq!(database.sanitized_name(), db_expected_name);
}

View File

@@ -1,28 +1,20 @@
use crate::cloud_provider::utilities::VersionsNumber;
use crate::error::StringError;
use crate::errors::CommandError;
use crate::models::DatabaseKind;
use std::str::FromStr;
pub fn get_parameter_group_from_version(version: &str, database_kind: DatabaseKind) -> Result<String, StringError> {
let version_number = match VersionsNumber::from_str(version) {
Ok(v) => {
if v.minor.is_none() {
return Err(format!(
"Can't determine the minor version, to select parameter group for {:?} version {}",
database_kind, version
));
};
v
}
Err(e) => return Err(e),
pub fn get_parameter_group_from_version(
version: VersionsNumber,
database_kind: DatabaseKind,
) -> Result<String, CommandError> {
if version.minor.is_none() {
return Err(CommandError::new_from_safe_message(format!(
"Can't determine the minor version, to select parameter group for {:?} version {}",
database_kind, version
)));
};
match database_kind {
DatabaseKind::Mysql => Ok(format!(
"mysql{}.{}",
version_number.major,
version_number.minor.unwrap()
)),
DatabaseKind::Mysql => Ok(format!("mysql{}.{}", version.major, version.minor.unwrap())),
_ => Ok("".to_string()),
}
}
@@ -35,19 +27,30 @@ pub fn aws_final_snapshot_name(database_name: &str) -> String {
#[cfg(test)]
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::models::DatabaseKind;
use std::str::FromStr;
#[test]
fn check_rds_mysql_parameter_groups() {
let mysql_parameter_group = get_parameter_group_from_version("5.7.0", DatabaseKind::Mysql);
let mysql_parameter_group = get_parameter_group_from_version(
VersionsNumber::from_str("5.7.0").expect("error while trying to get version from str"),
DatabaseKind::Mysql,
);
assert_eq!(mysql_parameter_group.unwrap(), "mysql5.7");
let mysql_parameter_group = get_parameter_group_from_version("8.0", DatabaseKind::Mysql);
let mysql_parameter_group = get_parameter_group_from_version(
VersionsNumber::from_str("8.0").expect("error while trying to get version from str"),
DatabaseKind::Mysql,
);
assert_eq!(mysql_parameter_group.unwrap(), "mysql8.0");
let mysql_parameter_group = get_parameter_group_from_version("8", DatabaseKind::Mysql);
let mysql_parameter_group = get_parameter_group_from_version(
VersionsNumber::from_str("8").expect("error while trying to get version from str"),
DatabaseKind::Mysql,
);
assert_eq!(
mysql_parameter_group.unwrap_err(),
mysql_parameter_group.unwrap_err().message(),
"Can't determine the minor version, to select parameter group for Mysql version 8"
);
}

View File

@@ -185,7 +185,7 @@ impl<'a> EKS<'a> {
e,
);
logger.log(LogLevel::Error, EngineEvent::Error(err.clone()));
logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
return Err(err);
}
@@ -465,10 +465,13 @@ impl<'a> EKS<'a> {
Some(secret_id) => context.insert("vault_secret_id", secret_id.to_str().unwrap()),
None => self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
)),
EngineEvent::Error(
EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
),
None,
),
),
}
}
@@ -618,7 +621,7 @@ impl<'a> EKS<'a> {
)
}
Err(e) => {
self.logger().log(LogLevel::Error, EngineEvent::Error(e));
self.logger().log(LogLevel::Error, EngineEvent::Error(e, None));
self.logger().log(
LogLevel::Info,
EngineEvent::Deploying(
@@ -653,11 +656,10 @@ impl<'a> EKS<'a> {
),
Err(e) => self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_cannot_get_or_create_iam_role(
event_details.clone(),
role.role_name,
e,
)),
EngineEvent::Error(
EngineError::new_cannot_get_or_create_iam_role(event_details.clone(), role.role_name, e),
None,
),
),
}
}
@@ -736,10 +738,10 @@ impl<'a> EKS<'a> {
}
Err(e) => self.logger().log(
LogLevel::Warning,
EngineEvent::Error(EngineError::new_terraform_state_does_not_exist(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_state_does_not_exist(event_details.clone(), e),
None,
),
),
};
@@ -941,7 +943,8 @@ impl<'a> EKS<'a> {
}
Err(e) => {
let error = EngineError::new_terraform_state_does_not_exist(event_details.clone(), e);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
};
@@ -1149,10 +1152,10 @@ impl<'a> EKS<'a> {
// An issue occurred during the apply before destroy of Terraform, it may be expected if you're resuming a destroy
self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_terraform_error_while_executing_pipeline(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_error_while_executing_pipeline(event_details.clone(), e),
None,
),
);
};
@@ -1509,22 +1512,28 @@ impl<'a> Kubernetes for EKS<'a> {
#[named]
fn on_create(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create())
}
#[named]
fn on_create_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create_error())
}
@@ -1688,7 +1697,7 @@ impl<'a> Kubernetes for EKS<'a> {
self.cloud_provider().credentials_environment_variables(),
Stage::Infrastructure(InfrastructureStep::Upgrade),
) {
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone()));
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone(), None));
return Err(e);
}
@@ -1798,88 +1807,112 @@ impl<'a> Kubernetes for EKS<'a> {
#[named]
fn on_upgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade())
}
#[named]
fn on_upgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade_error())
}
#[named]
fn on_downgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade())
}
#[named]
fn on_downgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade_error())
}
#[named]
fn on_pause(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause())
}
#[named]
fn on_pause_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause_error())
}
#[named]
fn on_delete(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete())
}
#[named]
fn on_delete_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete_error())
}
@@ -1892,8 +1925,10 @@ impl<'a> Kubernetes for EKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment(self, environment, event_details)
kubernetes::deploy_environment(self, environment, event_details, self.logger())
}
#[named]
@@ -1904,8 +1939,10 @@ impl<'a> Kubernetes for EKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment_error(self, environment, event_details)
kubernetes::deploy_environment_error(self, environment, event_details, self.logger())
}
#[named]
@@ -1916,17 +1953,22 @@ impl<'a> Kubernetes for EKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::pause_environment(self, environment, event_details)
kubernetes::pause_environment(self, environment, event_details, self.logger())
}
#[named]
fn pause_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}
@@ -1939,17 +1981,22 @@ impl<'a> Kubernetes for EKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::delete_environment(self, environment, event_details)
kubernetes::delete_environment(self, environment, event_details, self.logger())
}
#[named]
fn delete_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}

View File

@@ -5,10 +5,11 @@ use rusoto_credential::StaticProvider;
use rusoto_sts::{GetCallerIdentityRequest, Sts, StsClient};
use uuid::Uuid;
use crate::cloud_provider::{CloudProvider, EngineError, Kind, TerraformStateCredentials};
use crate::cloud_provider::{CloudProvider, Kind, TerraformStateCredentials};
use crate::constants::{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY};
use crate::error::EngineErrorCause;
use crate::models::{Context, Listen, Listener, Listeners};
use crate::errors::EngineError;
use crate::events::{EventDetails, GeneralStep, Stage, ToTransmitter, Transmitter};
use crate::models::{Context, Listen, Listener, Listeners, QoveryIdentifier};
use crate::runtime::block_on;
pub mod application;
@@ -108,18 +109,15 @@ impl CloudProvider for AWS {
}
fn is_valid(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::General(GeneralStep::RetrieveClusterConfig));
let client = StsClient::new_with_client(self.client(), Region::default());
let s = block_on(client.get_caller_identity(GetCallerIdentityRequest::default()));
match s {
Ok(_x) => Ok(()),
Err(_) => {
return Err(self.engine_error(
EngineErrorCause::User(
"Your AWS account seems to be no longer valid (bad Credentials). \
Please contact your Organization administrator to fix or change the Credentials.",
),
format!("failed to login to AWS {}", self.name_with_id()),
return Err(EngineError::new_client_invalid_cloud_provider_credentials(
event_details,
));
}
}
@@ -150,6 +148,19 @@ impl CloudProvider for AWS {
fn as_any(&self) -> &dyn Any {
self
}
fn get_event_details(&self, stage: Stage) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
stage,
self.to_transmitter(),
)
}
}
impl Listen for AWS {
@@ -161,3 +172,9 @@ impl Listen for AWS {
self.listeners.push(listener);
}
}
impl ToTransmitter for AWS {
fn to_transmitter(&self) -> Transmitter {
Transmitter::CloudProvider(self.id.to_string(), self.name.to_string())
}
}

View File

@@ -10,9 +10,9 @@ use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_n
use crate::cloud_provider::DeploymentTarget;
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::errors::EngineError;
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -26,6 +26,7 @@ pub struct Router {
sticky_sessions_enabled: bool,
routes: Vec<Route>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Router {
@@ -39,6 +40,7 @@ impl Router {
routes: Vec<Route>,
sticky_sessions_enabled: bool,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Router {
context,
@@ -50,6 +52,7 @@ impl Router {
sticky_sessions_enabled,
routes,
listeners,
logger,
}
}
@@ -124,6 +127,7 @@ impl Service for Router {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
@@ -177,10 +181,7 @@ impl Service for Router {
context.insert("nginx_limit_cpu", "200m");
context.insert("nginx_limit_memory", "128Mi");
let kubernetes_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => return Err(e.to_legacy_engine_error()),
};
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path()?;
// Default domain
match crate::cmd::kubectl::kubectl_exec_get_external_ingress_hostname(
@@ -192,12 +193,28 @@ impl Service for Router {
Ok(external_ingress_hostname_default) => match external_ingress_hostname_default {
Some(hostname) => context.insert("external_ingress_hostname_default", hostname.as_str()),
None => {
warn!("unable to get external_ingress_hostname_default - what's wrong? This must never happened");
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(
"Error while trying to get Load Balancer hostname from Kubernetes cluster".to_string(),
),
),
);
}
},
_ => {
// FIXME really?
warn!("can't fetch kubernetes config file - what's wrong? This must never happened");
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe("Can't fetch external ingress hostname.".to_string()),
),
);
}
}
@@ -224,12 +241,12 @@ impl Service for Router {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("routerId={}", self.id))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Router(self.id().to_string(), self.name().to_string())
fn selector(&self) -> Option<String> {
Some(format!("routerId={}", self.id))
}
}
@@ -292,22 +309,21 @@ impl ToTransmitter for Router {
impl Create for Router {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
let kubernetes = target.kubernetes;
let environment = target.environment;
let workspace_dir = self.workspace_directory();
let helm_release_name = self.helm_release_name();
let kubernetes_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(p) => p,
Err(e) => return Err(e.to_legacy_engine_error()),
};
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path()?;
// respect order - getting the context here and not before is mandatory
// the nginx-ingress must be available to get the external dns target if necessary
@@ -317,13 +333,12 @@ impl Create for Router {
if let Err(e) =
crate::template::generate_and_copy_all_files_into_dir(from_dir.as_str(), workspace_dir.as_str(), context)
{
return Err(NewEngineError::new_cannot_copy_files_from_one_directory_to_another(
return Err(EngineError::new_cannot_copy_files_from_one_directory_to_another(
event_details.clone(),
from_dir.to_string(),
workspace_dir.to_string(),
e,
)
.to_legacy_engine_error());
));
}
// do exec helm upgrade and return the last deployment status
@@ -331,7 +346,8 @@ impl Create for Router {
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| to_engine_error(&event_details, e).to_legacy_engine_error())?;
.map_err(|e| to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
@@ -346,12 +362,14 @@ impl Create for Router {
);
helm.upgrade(&chart, &vec![])
.map_err(|e| NewEngineError::new_helm_error(event_details.clone(), e).to_legacy_engine_error())
.map_err(|e| EngineError::new_helm_error(event_details.clone(), e))
}
fn on_create_check(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
// check non custom domains
self.check_domains()?;
self.check_domains(event_details.clone(), self.logger())?;
// Wait/Check that custom domain is a CNAME targeting qovery
for domain_to_check in self.custom_domains.iter() {
@@ -365,9 +383,19 @@ impl Create for Router {
continue
}
Ok(err) | Err(err) => {
warn!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN: {}",
domain_to_check.domain, err
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new(
format!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN.",
domain_to_check.domain,
),
Some(err.to_string()),
),
),
);
}
}
@@ -378,11 +406,14 @@ impl Create for Router {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -392,8 +423,17 @@ impl Create for Router {
}
impl Pause for Router {
#[named]
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
info!("AWS.router.on_pause() called for {}, doing nothing", self.name());
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -403,11 +443,14 @@ impl Pause for Router {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -422,6 +465,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, false, event_details)
}
@@ -438,6 +483,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, true, event_details)
}

View File

@@ -1,6 +1,7 @@
use tera::Context as TeraContext;
use crate::build_platform::Image;
use crate::cloud_provider::kubernetes::validate_k8s_required_cpu_and_burstable;
use crate::cloud_provider::models::{
EnvironmentVariable, EnvironmentVariableDataTemplate, Storage, StorageDataTemplate,
};
@@ -9,14 +10,13 @@ use crate::cloud_provider::service::{
scale_down_application, send_progress_on_long_task, Action, Create, Delete, Helm, Pause, Service, ServiceType,
StatelessService,
};
use crate::cloud_provider::utilities::{print_action, sanitize_name, validate_k8s_required_cpu_and_burstable};
use crate::cloud_provider::utilities::{print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl::ScalingKind::{Deployment, Statefulset};
use crate::error::EngineErrorCause::Internal;
use crate::error::{EngineError, EngineErrorScope};
use crate::errors::CommandError;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners, ListenersHelper, Port};
use ::function_name::named;
use std::fmt;
@@ -38,6 +38,7 @@ pub struct Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Application {
@@ -57,6 +58,7 @@ impl Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Application {
context,
@@ -74,6 +76,7 @@ impl Application {
storage,
environment_variables,
listeners,
logger,
}
}
@@ -195,6 +198,7 @@ impl Service for Application {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
@@ -206,10 +210,18 @@ impl Service for Application {
Some(registry_url) => context.insert("image_name_with_tag", registry_url.as_str()),
None => {
let image_name_with_tag = self.image.name_with_tag();
warn!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
)),
),
);
context.insert("image_name_with_tag", image_name_with_tag.as_str());
}
}
@@ -220,14 +232,16 @@ impl Service for Application {
&self.id,
self.total_cpus(),
self.cpu_burst(),
event_details.clone(),
self.logger(),
) {
Ok(l) => l,
Err(e) => {
return Err(EngineError::new(
Internal,
EngineErrorScope::Application(self.id().to_string(), self.name().to_string()),
self.context.execution_id(),
Some(e.to_string()),
return Err(EngineError::new_k8s_validate_required_cpu_and_burstable_error(
event_details.clone(),
self.total_cpus(),
self.cpu_burst(),
e,
));
}
};
@@ -288,23 +302,26 @@ impl Service for Application {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("appId={}", self.id))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Application(self.id().to_string(), self.name().to_string())
fn selector(&self) -> Option<String> {
Some(format!("appId={}", self.id))
}
}
impl Create for Application {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -318,11 +335,14 @@ impl Create for Application {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -334,11 +354,14 @@ impl Create for Application {
impl Pause for Application {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -357,11 +380,14 @@ impl Pause for Application {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -377,6 +403,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
@@ -396,6 +424,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_mongodb_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct MongoDB {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MongoDB {
@@ -44,6 +46,7 @@ impl MongoDB {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
MongoDB {
context,
@@ -58,11 +61,17 @@ impl MongoDB {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_mongodb_version(self.version().clone()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_mongodb_version(self.version().clone()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,17 +161,13 @@ impl Service for MongoDB {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -173,7 +178,7 @@ impl Service for MongoDB {
context.insert("namespace", environment.namespace());
let version = self.matching_correct_version()?;
let version = self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -218,12 +223,8 @@ impl Service for MongoDB {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -270,10 +271,12 @@ impl Create for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -283,11 +286,14 @@ impl Create for MongoDB {
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -297,11 +303,14 @@ impl Create for MongoDB {
impl Pause for MongoDB {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -315,11 +324,14 @@ impl Pause for MongoDB {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -335,10 +347,12 @@ impl Delete for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -348,11 +362,14 @@ impl Delete for MongoDB {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_mysql_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct MySQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MySQL {
@@ -44,6 +46,7 @@ impl MySQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -58,11 +61,17 @@ impl MySQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_mysql_version(self.version()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_mysql_version(self.version()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,17 +161,13 @@ impl Service for MySQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -173,7 +178,7 @@ impl Service for MySQL {
context.insert("namespace", environment.namespace());
let version = &self.matching_correct_version()?;
let version = &self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -214,16 +219,12 @@ impl Service for MySQL {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -270,12 +271,14 @@ impl Create for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Create,
Box::new(|| deploy_stateful_service(target, self, event_details.clone())),
Box::new(|| deploy_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -286,11 +289,14 @@ impl Create for MySQL {
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -300,11 +306,14 @@ impl Create for MySQL {
impl Pause for MySQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -318,11 +327,14 @@ impl Pause for MySQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -338,12 +350,14 @@ impl Delete for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Delete,
Box::new(|| delete_stateful_service(target, self, event_details.clone())),
Box::new(|| delete_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -353,11 +367,14 @@ impl Delete for MySQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_postgres_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct PostgreSQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl PostgreSQL {
@@ -44,6 +46,7 @@ impl PostgreSQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
PostgreSQL {
context,
@@ -58,11 +61,17 @@ impl PostgreSQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_postgres_version(self.version()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_postgres_version(self.version()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,17 +161,13 @@ impl Service for PostgreSQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -173,7 +178,7 @@ impl Service for PostgreSQL {
context.insert("namespace", environment.namespace());
let version = self.matching_correct_version()?;
let version = self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -220,12 +225,8 @@ impl Service for PostgreSQL {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -272,12 +273,14 @@ impl Create for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Create,
Box::new(|| deploy_stateful_service(target, self, event_details.clone())),
Box::new(|| deploy_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -287,11 +290,14 @@ impl Create for PostgreSQL {
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -301,11 +307,14 @@ impl Create for PostgreSQL {
impl Pause for PostgreSQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -319,11 +328,14 @@ impl Pause for PostgreSQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -339,12 +351,14 @@ impl Delete for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Delete,
Box::new(|| delete_stateful_service(target, self, event_details.clone())),
Box::new(|| delete_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -354,11 +368,14 @@ impl Delete for PostgreSQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_redis_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct Redis {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Redis {
@@ -44,6 +46,7 @@ impl Redis {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -58,11 +61,17 @@ impl Redis {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_redis_version(self.version()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_redis_version(self.version()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,17 +161,13 @@ impl Service for Redis {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -171,7 +176,7 @@ impl Service for Redis {
kubernetes.cloud_provider().credentials_environment_variables(),
);
let version = self.matching_correct_version()?;
let version = self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("namespace", environment.namespace());
context.insert("version", &version);
@@ -217,12 +222,8 @@ impl Service for Redis {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -269,12 +270,14 @@ impl Create for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Create,
Box::new(|| deploy_stateful_service(target, self, event_details.clone())),
Box::new(|| deploy_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -285,11 +288,14 @@ impl Create for Redis {
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -298,11 +304,14 @@ impl Create for Redis {
impl Pause for Redis {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -316,11 +325,14 @@ impl Pause for Redis {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -335,12 +347,14 @@ impl Delete for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(
self,
crate::cloud_provider::service::Action::Pause,
Box::new(|| delete_stateful_service(target, self, event_details.clone())),
Box::new(|| delete_stateful_service(target, self, event_details.clone(), self.logger())),
)
}
@@ -350,11 +364,14 @@ impl Delete for Redis {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -63,12 +63,10 @@ fn get_do_kubernetes_latest_slug_version(
doks_versions: &Vec<KubernetesVersion>,
wished_version: &str,
) -> Result<Option<String>, CommandError> {
let wished_k8s_version =
VersionsNumber::from_str(wished_version).map_err(|e| CommandError::new_from_safe_message(e.to_string()))?;
let wished_k8s_version = VersionsNumber::from_str(wished_version)?;
for kubernetes_doks_version in doks_versions {
let current_k8s_version = VersionsNumber::from_str(kubernetes_doks_version.kubernetes_version.as_str())
.map_err(|e| CommandError::new_from_safe_message(e.to_string()))?;
let current_k8s_version = VersionsNumber::from_str(kubernetes_doks_version.kubernetes_version.as_str())?;
if current_k8s_version.major == wished_k8s_version.major
&& current_k8s_version.minor == wished_k8s_version.minor
{

View File

@@ -133,7 +133,7 @@ impl<'a> DOKS<'a> {
e,
);
logger.log(LogLevel::Error, EngineEvent::Error(err.clone()));
logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
return Err(err);
}
@@ -368,10 +368,13 @@ impl<'a> DOKS<'a> {
Some(secret_id) => context.insert("vault_secret_id", secret_id.to_str().unwrap()),
None => self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
)),
EngineEvent::Error(
EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
),
None,
),
),
}
}
@@ -399,7 +402,9 @@ impl<'a> DOKS<'a> {
None => Err(EngineError::new_unsupported_version_error(
event_details.clone(),
self.kind().to_string(),
VersionsNumber::from_str(&self.version).expect("cannot parse version"),
VersionsNumber::from_str(&self.version)
.expect("cannot parse version")
.to_string(),
)),
Some(v) => Ok(v),
},
@@ -503,7 +508,7 @@ impl<'a> DOKS<'a> {
)
}
Err(e) => {
self.logger().log(LogLevel::Error, EngineEvent::Error(e));
self.logger().log(LogLevel::Error, EngineEvent::Error(e, None));
self.logger().log(
LogLevel::Info,
EngineEvent::Deploying(
@@ -598,10 +603,10 @@ impl<'a> DOKS<'a> {
}
Err(e) => self.logger().log(
LogLevel::Warning,
EngineEvent::Error(EngineError::new_terraform_state_does_not_exist(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_state_does_not_exist(event_details.clone(), e),
None,
),
),
};
@@ -619,7 +624,8 @@ impl<'a> DOKS<'a> {
self.kubeconfig_bucket_name(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -630,7 +636,8 @@ impl<'a> DOKS<'a> {
self.logs_bucket_name(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -661,7 +668,8 @@ impl<'a> DOKS<'a> {
kubeconfig_name.to_string(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -782,11 +790,7 @@ impl<'a> DOKS<'a> {
return Err(EngineError::new_k8s_loadbalancer_configuration_issue(
event_details.clone(),
CommandError::new(
format!(
"{}, error: {}.",
safe_message.to_string(),
e.message.unwrap_or("No error message".to_string())
),
format!("{}, error: {}.", safe_message.to_string(), e.message(),),
Some(safe_message.to_string()),
),
));
@@ -995,10 +999,10 @@ impl<'a> DOKS<'a> {
// An issue occurred during the apply before destroy of Terraform, it may be expected if you're resuming a destroy
self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_terraform_error_while_executing_pipeline(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_error_while_executing_pipeline(event_details.clone(), e),
None,
),
);
};
@@ -1355,22 +1359,28 @@ impl<'a> Kubernetes for DOKS<'a> {
#[named]
fn on_create(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create())
}
#[named]
fn on_create_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create_error())
}
@@ -1407,7 +1417,7 @@ impl<'a> Kubernetes for DOKS<'a> {
self.cloud_provider().credentials_environment_variables(),
event_details.stage().clone(),
) {
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone()));
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone(), None));
return Err(e);
}
@@ -1436,7 +1446,9 @@ impl<'a> Kubernetes for DOKS<'a> {
return Err(EngineError::new_unsupported_version_error(
event_details.clone(),
self.kind().to_string(),
VersionsNumber::from_str(&self.version).expect("cannot parse version"),
VersionsNumber::from_str(&self.version)
.expect("cannot parse version")
.to_string(),
))
}
Some(v) => v,
@@ -1528,88 +1540,112 @@ impl<'a> Kubernetes for DOKS<'a> {
#[named]
fn on_upgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade())
}
#[named]
fn on_upgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade_error())
}
#[named]
fn on_downgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade())
}
#[named]
fn on_downgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade_error())
}
#[named]
fn on_pause(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause())
}
#[named]
fn on_pause_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause_error())
}
#[named]
fn on_delete(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete())
}
#[named]
fn on_delete_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete_error())
}
@@ -1622,8 +1658,10 @@ impl<'a> Kubernetes for DOKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment(self, environment, event_details)
kubernetes::deploy_environment(self, environment, event_details, self.logger())
}
#[named]
@@ -1634,8 +1672,10 @@ impl<'a> Kubernetes for DOKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment_error(self, environment, event_details)
kubernetes::deploy_environment_error(self, environment, event_details, self.logger())
}
#[named]
@@ -1646,17 +1686,22 @@ impl<'a> Kubernetes for DOKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::pause_environment(self, environment, event_details)
kubernetes::pause_environment(self, environment, event_details, self.logger())
}
#[named]
fn pause_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}
@@ -1669,17 +1714,22 @@ impl<'a> Kubernetes for DOKS<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::delete_environment(self, environment, event_details)
kubernetes::delete_environment(self, environment, event_details, self.logger())
}
#[named]
fn delete_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}

View File

@@ -7,8 +7,9 @@ use uuid::Uuid;
use crate::cloud_provider::{CloudProvider, Kind, TerraformStateCredentials};
use crate::constants::DIGITAL_OCEAN_TOKEN;
use crate::error::{EngineError, EngineErrorCause};
use crate::models::{Context, Listen, Listener, Listeners};
use crate::errors::EngineError;
use crate::events::{EventDetails, GeneralStep, Stage, ToTransmitter, Transmitter};
use crate::models::{Context, Listen, Listener, Listeners, QoveryIdentifier};
pub mod application;
pub mod databases;
@@ -100,16 +101,13 @@ impl CloudProvider for DO {
}
fn is_valid(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::General(GeneralStep::RetrieveClusterConfig));
let client = DigitalOcean::new(&self.token);
match client {
Ok(_x) => Ok(()),
Err(_) => {
return Err(self.engine_error(
EngineErrorCause::User(
"Your DigitalOcean account seems to be no longer valid (bad Credentials). \
Please contact your Organization administrator to fix or change the Credentials.",
),
format!("failed to login to Digital Ocean {}", self.name_with_id()),
return Err(EngineError::new_client_invalid_cloud_provider_credentials(
event_details,
));
}
}
@@ -134,6 +132,19 @@ impl CloudProvider for DO {
fn as_any(&self) -> &dyn Any {
self
}
fn get_event_details(&self, stage: Stage) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
stage,
self.to_transmitter(),
)
}
}
impl Listen for DO {
@@ -145,3 +156,9 @@ impl Listen for DO {
self.listeners.push(listener);
}
}
impl ToTransmitter for DO {
fn to_transmitter(&self) -> Transmitter {
Transmitter::CloudProvider(self.id.to_string(), self.name.to_string())
}
}

View File

@@ -3,35 +3,34 @@ extern crate serde_json;
use reqwest::StatusCode;
use crate::cloud_provider::digitalocean::models::load_balancers::LoadBalancer;
use crate::error::{SimpleError, SimpleErrorKind};
use crate::errors::CommandError;
use crate::utilities::get_header_with_bearer;
use std::net::Ipv4Addr;
use std::str::FromStr;
pub const DO_LOAD_BALANCER_API_PATH: &str = "https://api.digitalocean.com/v2/load_balancers";
pub fn get_ip_from_do_load_balancer_api_output(json_content: &str) -> Result<Ipv4Addr, SimpleError> {
pub fn get_ip_from_do_load_balancer_api_output(json_content: &str) -> Result<Ipv4Addr, CommandError> {
let res_load_balancer = serde_json::from_str::<LoadBalancer>(json_content);
match res_load_balancer {
Ok(lb) => match Ipv4Addr::from_str(&lb.load_balancer.ip) {
Ok(ip) => Ok(ip),
Err(e) => Err(SimpleError::new(
SimpleErrorKind::Other,
Err(e) => Err(CommandError::new(
e.to_string(),
Some(format!(
"Info returned from DO API is not a valid IP, received '{:?}' instead. {:?}",
&lb.load_balancer.ip, e
"Info returned from DO API is not a valid IP, received '{:?}' instead.",
&lb.load_balancer.ip,
)),
)),
},
Err(_) => Err(SimpleError::new(
SimpleErrorKind::Other,
Some("Error While trying to deserialize json received from Digital Ocean Load Balancer API".to_string()),
Err(_) => Err(CommandError::new_from_safe_message(
"Error While trying to deserialize json received from Digital Ocean Load Balancer API".to_string(),
)),
}
}
pub fn do_get_load_balancer_ip(token: &str, load_balancer_id: &str) -> Result<Ipv4Addr, SimpleError> {
pub fn do_get_load_balancer_ip(token: &str, load_balancer_id: &str) -> Result<Ipv4Addr, CommandError> {
let headers = get_header_with_bearer(token);
let url = format!("{}/{}", DO_LOAD_BALANCER_API_PATH, load_balancer_id);
let res = reqwest::blocking::Client::new().get(&url).headers(headers).send();
@@ -42,17 +41,16 @@ pub fn do_get_load_balancer_ip(token: &str, load_balancer_id: &str) -> Result<Ip
let content = response.text().unwrap();
get_ip_from_do_load_balancer_api_output(content.as_str())
}
_ => Err(SimpleError::new(
SimpleErrorKind::Other,
_ => Err(CommandError::new(
format!("{:?}", response),
Some(
format!("Unknown status code received from Digital Ocean Kubernetes API while retrieving load balancer information. {:?}", response),
"Unknown status code received from Digital Ocean Kubernetes API while retrieving load balancer information.".to_string(),
),
)),
},
Err(_) => {
Err(SimpleError::new(
SimpleErrorKind::Other,
Some("Unable to get a response from Digital Ocean Load Balancer API"),
Err(CommandError::new_from_safe_message(
"Unable to get a response from Digital Ocean Load Balancer API".to_string(),
))
}
};

View File

@@ -4,15 +4,15 @@ 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,
Delete, Helm, Pause, Service, ServiceType, StatelessService,
Delete, Helm, Pause, Router as RRouter, Service, ServiceType, StatelessService,
};
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;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -26,6 +26,7 @@ pub struct Router {
sticky_sessions_enabled: bool,
routes: Vec<Route>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Router {
@@ -39,6 +40,7 @@ impl Router {
routes: Vec<Route>,
sticky_sessions_enabled: bool,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Router {
context,
@@ -50,6 +52,7 @@ impl Router {
sticky_sessions_enabled,
routes,
listeners,
logger,
}
}
@@ -124,6 +127,7 @@ impl Service for Router {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
@@ -187,34 +191,44 @@ impl Service for Router {
context.insert("nginx_requests_memory", "128Mi");
context.insert("nginx_limit_cpu", "200m");
context.insert("nginx_limit_memory", "128Mi");
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path();
match kubernetes_config_file_path {
Ok(kubernetes_config_file_path_string) => {
// Default domain
let external_ingress_hostname_default = crate::cmd::kubectl::kubectl_exec_get_external_ingress_hostname(
kubernetes_config_file_path_string.as_str(),
"nginx-ingress",
"nginx-ingress-ingress-nginx-controller",
kubernetes.cloud_provider().credentials_environment_variables(),
);
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path()?;
match external_ingress_hostname_default {
Ok(external_ingress_hostname_default) => match external_ingress_hostname_default {
Some(hostname) => context.insert("external_ingress_hostname_default", hostname.as_str()),
None => {
return Err(self.engine_error(
EngineErrorCause::Internal,
"Error while trying to get Load Balancer hostname from Kubernetes cluster".into(),
));
}
},
_ => {
error!("can't fetch external ingress hostname");
}
// Default domain
let external_ingress_hostname_default = crate::cmd::kubectl::kubectl_exec_get_external_ingress_hostname(
kubernetes_config_file_path,
"nginx-ingress",
"nginx-ingress-ingress-nginx-controller",
kubernetes.cloud_provider().credentials_environment_variables(),
);
match external_ingress_hostname_default {
Ok(external_ingress_hostname_default) => match external_ingress_hostname_default {
Some(hostname) => context.insert("external_ingress_hostname_default", hostname.as_str()),
None => {
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(
"Error while trying to get Load Balancer hostname from Kubernetes cluster".to_string(),
),
),
);
}
},
_ => {
// FIXME really?
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe("Can't fetch external ingress hostname.".to_string()),
),
);
}
Err(_) => error!("can't fetch kubernetes config file - what's wrong? This must never happened"),
}
let router_default_domain_hash = crate::crypto::to_sha1_truncate_16(self.default_domain.as_str());
@@ -244,8 +258,8 @@ impl Service for Router {
Some(format!("routerId={}", self.id))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Router(self.id().to_string(), self.name().to_string())
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -311,23 +325,22 @@ impl ToTransmitter for Router {
impl Create for Router {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
let kubernetes = target.kubernetes;
let environment = target.environment;
let workspace_dir = self.workspace_directory();
let helm_release_name = self.helm_release_name();
let kubernetes_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(p) => p,
Err(e) => return Err(e.to_legacy_engine_error()),
};
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path()?;
// respect order - getting the context here and not before is mandatory
// the nginx-ingress must be available to get the external dns target if necessary
@@ -337,13 +350,12 @@ impl Create for Router {
if let Err(e) =
crate::template::generate_and_copy_all_files_into_dir(from_dir.as_str(), workspace_dir.as_str(), context)
{
return Err(NewEngineError::new_cannot_copy_files_from_one_directory_to_another(
return Err(EngineError::new_cannot_copy_files_from_one_directory_to_another(
event_details.clone(),
from_dir.to_string(),
workspace_dir.to_string(),
e,
)
.to_legacy_engine_error());
));
}
// do exec helm upgrade and return the last deployment status
@@ -351,7 +363,7 @@ impl Create for Router {
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
.map_err(|e| helm::to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
@@ -366,14 +378,14 @@ impl Create for Router {
);
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())
.map_err(|e| helm::to_engine_error(&event_details, e))
}
fn on_create_check(&self) -> Result<(), EngineError> {
use crate::cloud_provider::service::Router;
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
// check non custom domains
self.check_domains()?;
self.check_domains(event_details.clone(), self.logger())?;
// Wait/Check that custom domain is a CNAME targeting qovery
for domain_to_check in self.custom_domains.iter() {
@@ -387,9 +399,19 @@ impl Create for Router {
continue;
}
Ok(err) | Err(err) => {
warn!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN: {}",
domain_to_check.domain, err
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new(
format!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN.",
domain_to_check.domain,
),
Some(err.to_string()),
),
),
);
}
}
@@ -400,11 +422,14 @@ impl Create for Router {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -416,11 +441,14 @@ impl Create for Router {
impl Pause for Router {
#[named]
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -431,11 +459,14 @@ impl Pause for Router {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -450,6 +481,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, false, event_details)
}
@@ -466,6 +499,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, true, event_details)
}

View File

@@ -1,5 +1,5 @@
use crate::cloud_provider::service::{Action, StatefulService, StatelessService};
use crate::error::EngineError;
use crate::errors::EngineError;
use crate::unit_conversion::cpu_string_to_float;
pub struct Environment {

View File

@@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize};
use crate::cloud_provider::aws::regions::AwsZones;
use crate::cloud_provider::environment::Environment;
use crate::cloud_provider::models::NodeGroups;
use crate::cloud_provider::models::{CpuLimits, NodeGroups};
use crate::cloud_provider::service::CheckAction;
use crate::cloud_provider::utilities::VersionsNumber;
use crate::cloud_provider::{service, CloudProvider, DeploymentTarget};
@@ -96,8 +96,20 @@ pub trait Kubernetes: Listen {
if Path::new(&local_kubeconfig_generated).exists() {
match File::open(&local_kubeconfig_generated) {
Ok(_) => Some(local_kubeconfig_generated),
Err(_) => {
debug!("couldn't open {} file", &local_kubeconfig_generated);
Err(err) => {
self.logger().log(
LogLevel::Debug,
EngineEvent::Debug(
self.get_event_details(stage.clone()),
EventMessage::new(
err.to_string(),
Some(
format!("Error, couldn't open {} file", &local_kubeconfig_generated,)
.to_string(),
),
),
),
);
None
}
}
@@ -124,7 +136,7 @@ pub trait Kubernetes: Listen {
Ok((path, file)) => (path, file),
Err(err) => {
let error = EngineError::new_cannot_retrieve_cluster_config_file(
self.get_event_details(stage),
self.get_event_details(stage.clone()),
CommandError::new_from_safe_message(
format!(
"Error getting file from store, error: {}",
@@ -133,7 +145,8 @@ pub trait Kubernetes: Listen {
.to_string(),
),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
}
@@ -144,12 +157,13 @@ pub trait Kubernetes: Listen {
Ok(metadata) => metadata,
Err(err) => {
let error = EngineError::new_cannot_retrieve_cluster_config_file(
self.get_event_details(stage),
self.get_event_details(stage.clone()),
CommandError::new_from_safe_message(
format!("Error getting file metadata, error: {}", err.to_string(),).to_string(),
),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
};
@@ -158,13 +172,14 @@ pub trait Kubernetes: Listen {
permissions.set_mode(0o400);
if let Err(err) = std::fs::set_permissions(string_path.as_str(), permissions) {
let error = EngineError::new_cannot_retrieve_cluster_config_file(
self.get_event_details(stage),
self.get_event_details(stage.clone()),
CommandError::new_from_safe_message(format!(
"Error setting file permissions, error: {}",
err.to_string(),
)),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -193,7 +208,8 @@ pub trait Kubernetes: Listen {
),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -273,6 +289,7 @@ pub trait Kubernetes: Listen {
Ok((path, _)) => path,
Err(e) => return Err(CommandError::new(e.message(), None)),
};
send_progress_on_long_task(self, Action::Create, || {
check_workers_status(&kubeconfig, self.cloud_provider().credentials_environment_variables())
})
@@ -403,6 +420,7 @@ pub fn deploy_environment(
kubernetes: &dyn Kubernetes,
environment: &Environment,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), EngineError> {
let listeners_helper = ListenersHelper::new(kubernetes.listeners());
@@ -421,11 +439,6 @@ pub fn deploy_environment(
},
};
// Resources check before deploy
// TODO(ENG-1066): we should check if env can be deployed or not based on required resources and available ones.
// This check is not trivial since auto-scaler comes to play and resources distribution on nodes might not behave necesarly the way we think.
// do not deploy if there is not enough resources
// create all stateful services (database)
for service in &environment.stateful_services {
let _ = service::check_kubernetes_service_error(
@@ -433,6 +446,7 @@ pub fn deploy_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"deployment",
@@ -445,6 +459,7 @@ pub fn deploy_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"check deployment",
@@ -468,6 +483,7 @@ pub fn deploy_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"deployment",
@@ -485,6 +501,7 @@ pub fn deploy_environment(
kubernetes,
service,
event_details.clone(),
logger.clone(),
&stateless_deployment_target,
&listeners_helper,
"check deployment",
@@ -501,6 +518,7 @@ pub fn deploy_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"check deployment",
@@ -516,6 +534,7 @@ pub fn deploy_environment_error(
kubernetes: &dyn Kubernetes,
environment: &Environment,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), EngineError> {
let listeners_helper = ListenersHelper::new(kubernetes.listeners());
@@ -540,6 +559,7 @@ pub fn deploy_environment_error(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"revert deployment",
@@ -563,6 +583,7 @@ pub fn deploy_environment_error(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"revert deployment",
@@ -578,6 +599,7 @@ pub fn pause_environment(
kubernetes: &dyn Kubernetes,
environment: &Environment,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), EngineError> {
let listeners_helper = ListenersHelper::new(kubernetes.listeners());
@@ -599,6 +621,7 @@ pub fn pause_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"pause",
@@ -616,6 +639,7 @@ pub fn pause_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"pause",
@@ -632,6 +656,7 @@ pub fn pause_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"check pause",
@@ -649,6 +674,7 @@ pub fn pause_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"check pause",
@@ -664,6 +690,7 @@ pub fn delete_environment(
kubernetes: &dyn Kubernetes,
environment: &Environment,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), EngineError> {
let listeners_helper = ListenersHelper::new(kubernetes.listeners());
@@ -685,6 +712,7 @@ pub fn delete_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"delete",
@@ -702,6 +730,7 @@ pub fn delete_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"delete",
@@ -718,6 +747,7 @@ pub fn delete_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateless_deployment_target,
&listeners_helper,
"delete check",
@@ -735,6 +765,7 @@ pub fn delete_environment(
kubernetes,
service,
event_details.clone(),
logger,
&stateful_deployment_target,
&listeners_helper,
"delete check",
@@ -780,8 +811,12 @@ where
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new(
format!("Encountering issues while trying to get objects kind {}.", object,),
Some(e.message()),
format!(
"Encountering issues while trying to get objects kind {}: {:?}",
object,
e.message()
),
None,
),
),
);
@@ -798,10 +833,7 @@ where
LogLevel::Warning,
EngineEvent::Deleting(
event_details.clone(),
EventMessage::new_from_safe(format!(
"Failed to delete all {} objects, retrying...",
object,
)),
EventMessage::new(format!("Failed to delete all {} objects, retrying...", object,), None),
),
);
OperationResult::Retry(e)
@@ -1043,7 +1075,7 @@ fn check_kubernetes_upgrade_status(
return Err(EngineError::new_cannot_determine_k8s_requested_upgrade_version(
event_details.clone(),
requested_version.to_string(),
Some(CommandError::new_from_safe_message(e)),
Some(e),
));
}
};
@@ -1256,35 +1288,222 @@ impl NodeGroups {
}
}
/// TODO(benjaminch): to be refactored with similar function in services.rs
/// This function call (start|pause|delete)_in_progress function every 10 seconds when a
/// long blocking task is running.
pub fn send_progress_on_long_task<K, R, F>(kubernetes: &K, action: Action, long_task: F) -> R
where
K: Kubernetes + Listen,
F: Fn() -> R,
{
let waiting_message = match action {
Action::Create => Some(format!(
"Infrastructure '{}' deployment is in progress...",
kubernetes.name_with_id()
)),
Action::Pause => Some(format!(
"Infrastructure '{}' pause is in progress...",
kubernetes.name_with_id()
)),
Action::Delete => Some(format!(
"Infrastructure '{}' deletion is in progress...",
kubernetes.name_with_id()
)),
Action::Nothing => None,
};
send_progress_on_long_task_with_message(kubernetes, waiting_message, action, long_task)
}
/// TODO(benjaminch): to be refactored with similar function in services.rs
/// This function call (start|pause|delete)_in_progress function every 10 seconds when a
/// long blocking task is running.
pub fn send_progress_on_long_task_with_message<K, R, F>(
kubernetes: &K,
waiting_message: Option<String>,
action: Action,
long_task: F,
) -> R
where
K: Kubernetes + Listen,
F: Fn() -> R,
{
let listeners = std::clone::Clone::clone(kubernetes.listeners());
let logger = kubernetes.logger().clone_dyn();
let event_details = kubernetes
.get_event_details(Stage::Infrastructure(InfrastructureStep::Create))
.clone();
let progress_info = ProgressInfo::new(
ProgressScope::Infrastructure {
execution_id: kubernetes.context().execution_id().to_string(),
},
Info,
waiting_message.clone(),
kubernetes.context().execution_id(),
);
let (tx, rx) = mpsc::channel();
// monitor thread to notify user while the blocking task is executed
let _ = std::thread::Builder::new()
.name("task-monitor".to_string())
.spawn(move || {
// stop the thread when the blocking task is done
let listeners_helper = ListenersHelper::new(&listeners);
let action = action;
let progress_info = progress_info;
let waiting_message = waiting_message.unwrap_or("no message ...".to_string());
loop {
// do notify users here
let progress_info = std::clone::Clone::clone(&progress_info);
let event_details = std::clone::Clone::clone(&event_details);
let event_message = EventMessage::new_from_safe(waiting_message.to_string());
match action {
Action::Create => {
listeners_helper.deployment_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Deploying(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Create),
),
event_message,
),
);
}
Action::Pause => {
listeners_helper.pause_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Pausing(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Pause),
),
event_message,
),
);
}
Action::Delete => {
listeners_helper.delete_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Deleting(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Delete),
),
event_message,
),
);
}
Action::Nothing => {} // should not happens
};
thread::sleep(Duration::from_secs(10));
// watch for thread termination
match rx.try_recv() {
Ok(_) | Err(TryRecvError::Disconnected) => break,
Err(TryRecvError::Empty) => {}
}
}
});
let blocking_task_result = long_task();
let _ = tx.send(());
blocking_task_result
}
pub fn validate_k8s_required_cpu_and_burstable(
listener_helper: &ListenersHelper,
execution_id: &str,
context_id: &str,
total_cpu: String,
cpu_burst: String,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<CpuLimits, CommandError> {
let total_cpu_float = convert_k8s_cpu_value_to_f32(total_cpu.clone())?;
let cpu_burst_float = convert_k8s_cpu_value_to_f32(cpu_burst.clone())?;
let mut set_cpu_burst = cpu_burst.clone();
if cpu_burst_float < total_cpu_float {
let message = format!(
"CPU burst value '{}' was lower than the desired total of CPUs {}, using burstable value.",
cpu_burst, total_cpu,
);
listener_helper.error(ProgressInfo::new(
ProgressScope::Environment {
id: execution_id.to_string(),
},
ProgressLevel::Warn,
Some(message.to_string()),
context_id,
));
logger.log(
LogLevel::Warning,
EngineEvent::Warning(event_details, EventMessage::new_from_safe(message)),
);
set_cpu_burst = total_cpu.clone();
}
Ok(CpuLimits {
cpu_limit: set_cpu_burst,
cpu_request: total_cpu,
})
}
pub fn convert_k8s_cpu_value_to_f32(value: String) -> Result<f32, CommandError> {
if value.ends_with('m') {
let mut value_number_string = value;
value_number_string.pop();
return match value_number_string.parse::<f32>() {
Ok(n) => {
Ok(n * 0.001) // return in milli cpu the value
}
Err(e) => Err(CommandError::new(
e.to_string(),
Some(format!(
"Error while trying to parse `{}` to float 32.",
value_number_string.as_str()
)),
)),
};
}
match value.parse::<f32>() {
Ok(n) => Ok(n),
Err(e) => Err(CommandError::new(
e.to_string(),
Some(format!("Error while trying to parse `{}` to float 32.", value.as_str())),
)),
}
}
#[cfg(test)]
mod tests {
use crate::cloud_provider::Kind::Aws;
use std::str::FromStr;
use crate::cloud_provider::kubernetes::{
check_kubernetes_upgrade_status, compare_kubernetes_cluster_versions_for_upgrade, KubernetesNodesType,
check_kubernetes_upgrade_status, compare_kubernetes_cluster_versions_for_upgrade, convert_k8s_cpu_value_to_f32,
validate_k8s_required_cpu_and_burstable, KubernetesNodesType,
};
use crate::cloud_provider::models::CpuLimits;
use crate::cloud_provider::utilities::VersionsNumber;
use crate::cmd::structs::{KubernetesList, KubernetesNode, KubernetesVersion};
use crate::events::{EngineEvent, EventDetails, InfrastructureStep, Stage, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::QoveryIdentifier;
#[derive(Clone)]
struct FakeLogger {}
impl FakeLogger {
pub fn new() -> Self {
FakeLogger {}
}
}
impl Logger for FakeLogger {
fn log(&self, _log_level: LogLevel, _event: EngineEvent) {}
fn clone_dyn(&self) -> Box<dyn Logger> {
Box::new(self.clone())
}
}
use crate::events::{EventDetails, InfrastructureStep, Stage, Transmitter};
use crate::logger::StdIoLogger;
use crate::models::{ListenersHelper, QoveryIdentifier};
#[test]
pub fn check_kubernetes_upgrade_method() {
@@ -1299,7 +1518,7 @@ mod tests {
Stage::Infrastructure(InfrastructureStep::Upgrade),
Transmitter::Kubernetes(QoveryIdentifier::new_random().to_string(), "test".to_string()),
);
let logger = FakeLogger::new();
let logger = StdIoLogger::new();
// test full cluster upgrade (masters + workers)
let result = check_kubernetes_upgrade_status(
@@ -1851,136 +2070,72 @@ mod tests {
}
}
}
}
/// TODO(benjaminch): to be refactored with similar function in services.rs
/// This function call (start|pause|delete)_in_progress function every 10 seconds when a
/// long blocking task is running.
pub fn send_progress_on_long_task<K, R, F>(kubernetes: &K, action: Action, long_task: F) -> R
where
K: Kubernetes + Listen,
F: Fn() -> R,
{
let waiting_message = match action {
Action::Create => Some(format!(
"Infrastructure '{}' deployment is in progress...",
kubernetes.name_with_id()
)),
Action::Pause => Some(format!(
"Infrastructure '{}' pause is in progress...",
kubernetes.name_with_id()
)),
Action::Delete => Some(format!(
"Infrastructure '{}' deletion is in progress...",
kubernetes.name_with_id()
)),
Action::Nothing => None,
};
#[test]
pub fn test_k8s_milli_cpu_convert() {
let milli_cpu = "250m".to_string();
let int_cpu = "2".to_string();
send_progress_on_long_task_with_message(kubernetes, waiting_message, action, long_task)
}
assert_eq!(convert_k8s_cpu_value_to_f32(milli_cpu).unwrap(), 0.25 as f32);
assert_eq!(convert_k8s_cpu_value_to_f32(int_cpu).unwrap(), 2 as f32);
}
/// TODO(benjaminch): to be refactored with similar function in services.rs
/// This function call (start|pause|delete)_in_progress function every 10 seconds when a
/// long blocking task is running.
pub fn send_progress_on_long_task_with_message<K, R, F>(
kubernetes: &K,
waiting_message: Option<String>,
action: Action,
long_task: F,
) -> R
where
K: Kubernetes + Listen,
F: Fn() -> R,
{
let listeners = std::clone::Clone::clone(kubernetes.listeners());
let logger = kubernetes.logger().clone_dyn();
let event_details = kubernetes
.get_event_details(Stage::Infrastructure(InfrastructureStep::Create))
.clone(); // TODO(benjaminch): change the way event details is built, it's very dirty to make it mut and change the stage :(
#[test]
pub fn test_cpu_set() {
let v = vec![];
let listener_helper = ListenersHelper::new(&v);
let logger = StdIoLogger::new();
let execution_id = "execution_id";
let context_id = "context_id";
let organization_id = "organization_id";
let cluster_id = "cluster_id";
let progress_info = ProgressInfo::new(
ProgressScope::Infrastructure {
execution_id: kubernetes.context().execution_id().to_string(),
},
Info,
waiting_message.clone(),
kubernetes.context().execution_id(),
);
let event_details = EventDetails::new(
Some(Aws),
QoveryIdentifier::new(organization_id.to_string()),
QoveryIdentifier::new(cluster_id.to_string()),
QoveryIdentifier::new(execution_id.to_string()),
Some("region_fake".to_string()),
Stage::Infrastructure(InfrastructureStep::LoadConfiguration),
Transmitter::Kubernetes(cluster_id.to_string(), format!("{}-name", cluster_id)),
);
let (tx, rx) = mpsc::channel();
// monitor thread to notify user while the blocking task is executed
let _ = std::thread::Builder::new()
.name("task-monitor".to_string())
.spawn(move || {
// stop the thread when the blocking task is done
let listeners_helper = ListenersHelper::new(&listeners);
let action = action;
let progress_info = progress_info;
let waiting_message = waiting_message.unwrap_or("no message ...".to_string());
loop {
// do notify users here
let progress_info = std::clone::Clone::clone(&progress_info);
let event_details = std::clone::Clone::clone(&event_details);
let event_message = EventMessage::new_from_safe(waiting_message.to_string());
match action {
Action::Create => {
listeners_helper.deployment_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Deploying(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Create),
),
event_message,
),
);
}
Action::Pause => {
listeners_helper.pause_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Pausing(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Pause),
),
event_message,
),
);
}
Action::Delete => {
listeners_helper.delete_in_progress(progress_info);
logger.log(
LogLevel::Info,
EngineEvent::Deleting(
EventDetails::clone_changing_stage(
event_details,
Stage::Infrastructure(InfrastructureStep::Delete),
),
event_message,
),
);
}
Action::Nothing => {} // should not happens
};
thread::sleep(Duration::from_secs(10));
// watch for thread termination
match rx.try_recv() {
Ok(_) | Err(TryRecvError::Disconnected) => break,
Err(TryRecvError::Empty) => {}
}
let mut total_cpu = "0.25".to_string();
let mut cpu_burst = "1".to_string();
assert_eq!(
validate_k8s_required_cpu_and_burstable(
&listener_helper,
execution_id,
context_id,
total_cpu,
cpu_burst,
event_details.clone(),
&logger
)
.unwrap(),
CpuLimits {
cpu_request: "0.25".to_string(),
cpu_limit: "1".to_string()
}
});
);
let blocking_task_result = long_task();
let _ = tx.send(());
blocking_task_result
total_cpu = "1".to_string();
cpu_burst = "0.5".to_string();
assert_eq!(
validate_k8s_required_cpu_and_burstable(
&listener_helper,
execution_id,
context_id,
total_cpu,
cpu_burst,
event_details.clone(),
&logger
)
.unwrap(),
CpuLimits {
cpu_request: "1".to_string(),
cpu_limit: "1".to_string()
}
);
}
}

View File

@@ -5,7 +5,8 @@ use serde::{Deserialize, Serialize};
use crate::cloud_provider::environment::Environment;
use crate::cloud_provider::kubernetes::Kubernetes;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::errors::EngineError;
use crate::events::{EventDetails, Stage, ToTransmitter};
use crate::models::{Context, Listen};
pub mod aws;
@@ -21,7 +22,7 @@ pub mod scaleway;
pub mod service;
pub mod utilities;
pub trait CloudProvider: Listen {
pub trait CloudProvider: Listen + ToTransmitter {
fn context(&self) -> &Context;
fn kind(&self) -> Kind;
fn id(&self) -> &str;
@@ -41,18 +42,8 @@ pub trait CloudProvider: Listen {
/// environment variables to inject to generate Terraform files from templates
fn tera_context_environment_variables(&self) -> Vec<(&str, &str)>;
fn terraform_state_credentials(&self) -> &TerraformStateCredentials;
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::CloudProvider(self.id().to_string(), self.name().to_string())
}
fn engine_error(&self, cause: EngineErrorCause, message: String) -> EngineError {
EngineError::new(
cause,
self.engine_error_scope(),
self.context().execution_id(),
Some(message),
)
}
fn as_any(&self) -> &dyn Any;
fn get_event_details(&self, stage: Stage) -> EventDetails;
}
#[derive(Serialize, Deserialize, Clone, Debug, Hash, PartialEq, Eq)]

View File

@@ -4,6 +4,7 @@ use std::str::FromStr;
use tera::Context as TeraContext;
use crate::build_platform::Image;
use crate::cloud_provider::kubernetes::validate_k8s_required_cpu_and_burstable;
use crate::cloud_provider::models::{
EnvironmentVariable, EnvironmentVariableDataTemplate, Storage, StorageDataTemplate,
};
@@ -12,14 +13,13 @@ use crate::cloud_provider::service::{
scale_down_application, send_progress_on_long_task, Action, Application as CApplication, Create, Delete, Helm,
Pause, Service, ServiceType, StatelessService,
};
use crate::cloud_provider::utilities::{print_action, sanitize_name, validate_k8s_required_cpu_and_burstable};
use crate::cloud_provider::utilities::{print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl::ScalingKind::{Deployment, Statefulset};
use crate::error::EngineErrorCause::Internal;
use crate::error::{EngineError, EngineErrorScope};
use crate::errors::CommandError;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners, ListenersHelper, Port};
use ::function_name::named;
@@ -39,6 +39,7 @@ pub struct Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Application {
@@ -58,7 +59,8 @@ impl Application {
storage: Vec<Storage<StorageType>>,
environment_variables: Vec<EnvironmentVariable>,
listeners: Listeners,
) -> Application {
logger: Box<dyn Logger>,
) -> Self {
Application {
context,
id: id.to_string(),
@@ -75,6 +77,7 @@ impl Application {
storage,
environment_variables,
listeners,
logger,
}
}
@@ -196,6 +199,7 @@ impl Service for Application {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
@@ -210,9 +214,15 @@ impl Service for Application {
),
None => {
let image_name_with_tag = self.image().name_with_tag();
warn!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new_from_safe(format!(
"there is no registry url, use image name with tag with the default container registry: {}",
image_name_with_tag.as_str()
)),
),
);
context.insert("image_name_with_tag", image_name_with_tag.as_str());
}
@@ -246,14 +256,16 @@ impl Service for Application {
&self.id,
self.total_cpus(),
self.cpu_burst(),
event_details.clone(),
self.logger(),
) {
Ok(l) => l,
Err(e) => {
return Err(EngineError::new(
Internal,
EngineErrorScope::Application(self.id().to_string(), self.name().to_string()),
self.context.execution_id(),
Some(e.to_string()),
return Err(EngineError::new_k8s_validate_required_cpu_and_burstable_error(
event_details.clone(),
self.total_cpus(),
self.cpu_burst(),
e,
));
}
};
@@ -310,19 +322,22 @@ impl Service for Application {
Some(format!("appId={}", self.id))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Application(self.id().to_string(), self.name().to_string())
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
impl Create for Application {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -336,11 +351,14 @@ impl Create for Application {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -352,11 +370,14 @@ impl Create for Application {
impl Pause for Application {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -375,11 +396,14 @@ impl Pause for Application {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -395,6 +419,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
@@ -414,6 +440,8 @@ impl Delete for Application {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_mongodb_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct MongoDB {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MongoDB {
@@ -44,6 +46,7 @@ impl MongoDB {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
MongoDB {
context,
@@ -58,11 +61,17 @@ impl MongoDB {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_mongodb_version(self.version()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_mongodb_version(self.version()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,18 +161,15 @@ impl Service for MongoDB {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -174,7 +180,7 @@ impl Service for MongoDB {
context.insert("namespace", environment.namespace());
let version = self.matching_correct_version()?;
let version = self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("version", &version);
for (k, v) in kubernetes.cloud_provider().tera_context_environment_variables() {
@@ -215,16 +221,12 @@ impl Service for MongoDB {
Ok(context)
}
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
fn logger(&self) -> &dyn Logger {
&*self.logger
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn selector(&self) -> Option<String> {
Some(format!("app={}", self.sanitized_name()))
}
}
@@ -271,24 +273,35 @@ impl Create for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -297,11 +310,14 @@ impl Create for MongoDB {
impl Pause for MongoDB {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -315,11 +331,14 @@ impl Pause for MongoDB {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -335,10 +354,12 @@ impl Delete for MongoDB {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -348,11 +369,14 @@ impl Delete for MongoDB {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -3,7 +3,7 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{
get_self_hosted_mysql_version, get_supported_version_to_use, print_action, sanitize_name, VersionsNumber,
@@ -11,13 +11,13 @@ use crate::cloud_provider::utilities::{
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
use std::collections::HashMap;
use std::str::FromStr;
pub struct MySQL {
context: Context,
@@ -32,6 +32,7 @@ pub struct MySQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl MySQL {
@@ -48,6 +49,7 @@ impl MySQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -62,21 +64,24 @@ impl MySQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<VersionsNumber, EngineError> {
let version = check_service_version(Self::pick_mysql_version(self.version(), is_managed_services), self)?;
match VersionsNumber::from_str(version.as_str()) {
Ok(res) => Ok(res),
Err(e) => Err(self.engine_error(
EngineErrorCause::Internal,
format!("cannot parse database version, err: {}", e),
)),
}
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
Self::pick_mysql_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn pick_mysql_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn pick_mysql_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
Self::pick_managed_mysql_version(requested_version)
} else {
@@ -84,7 +89,7 @@ impl MySQL {
}
}
fn pick_managed_mysql_version(requested_version: String) -> Result<String, StringError> {
fn pick_managed_mysql_version(requested_version: String) -> Result<String, CommandError> {
// Scaleway supported MySQL versions
// https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines
let mut supported_mysql_versions = HashMap::new();
@@ -183,18 +188,14 @@ impl Service for MySQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -205,7 +206,9 @@ impl Service for MySQL {
context.insert("namespace", environment.namespace());
let version = &self.matching_correct_version(self.is_managed_service())?;
let version = &self
.matching_correct_version(self.is_managed_service(), event_details.clone())?
.matched_version();
context.insert("version_major", &version.to_major_version_string());
context.insert("version", &version.to_string()); // Scaleway needs to have major version only
@@ -256,12 +259,8 @@ impl Service for MySQL {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -308,24 +307,35 @@ impl Create for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -335,11 +345,14 @@ impl Create for MySQL {
impl Pause for MySQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -353,11 +366,14 @@ impl Pause for MySQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -373,10 +389,12 @@ impl Delete for MySQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -386,11 +404,14 @@ impl Delete for MySQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -3,7 +3,7 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{
get_self_hosted_postgres_version, get_supported_version_to_use, print_action, sanitize_name, VersionsNumber,
@@ -11,13 +11,13 @@ use crate::cloud_provider::utilities::{
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, StringError};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::{CommandError, EngineError};
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
use std::collections::HashMap;
use std::str::FromStr;
pub struct PostgreSQL {
context: Context,
@@ -32,6 +32,7 @@ pub struct PostgreSQL {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl PostgreSQL {
@@ -48,6 +49,7 @@ impl PostgreSQL {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -62,21 +64,24 @@ impl PostgreSQL {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self, is_managed_services: bool) -> Result<VersionsNumber, EngineError> {
let version = check_service_version(Self::pick_postgres_version(self.version(), is_managed_services), self)?;
match VersionsNumber::from_str(version.as_str()) {
Ok(res) => Ok(res),
Err(e) => Err(self.engine_error(
EngineErrorCause::Internal,
format!("cannot parse database version, err: {}", e),
)),
}
fn matching_correct_version(
&self,
is_managed_services: bool,
event_details: EventDetails,
) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
Self::pick_postgres_version(self.version(), is_managed_services),
self,
event_details,
self.logger(),
)
}
fn pick_postgres_version(requested_version: String, is_managed_service: bool) -> Result<String, StringError> {
fn pick_postgres_version(requested_version: String, is_managed_service: bool) -> Result<String, CommandError> {
if is_managed_service {
Self::pick_managed_postgres_version(requested_version)
} else {
@@ -84,7 +89,7 @@ impl PostgreSQL {
}
}
fn pick_managed_postgres_version(requested_version: String) -> Result<String, StringError> {
fn pick_managed_postgres_version(requested_version: String) -> Result<String, CommandError> {
// Scaleway supported postgres versions
// https://api.scaleway.com/rdb/v1/regions/fr-par/database-engines
let mut supported_postgres_versions = HashMap::new();
@@ -192,18 +197,14 @@ impl Service for PostgreSQL {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -214,7 +215,9 @@ impl Service for PostgreSQL {
context.insert("namespace", environment.namespace());
let version = &self.matching_correct_version(self.is_managed_service())?;
let version = &self
.matching_correct_version(self.is_managed_service(), event_details.clone())?
.matched_version();
context.insert("version_major", &version.to_major_version_string());
context.insert("version", &version.to_string()); // Scaleway needs to have major version only
@@ -265,12 +268,8 @@ impl Service for PostgreSQL {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -317,24 +316,35 @@ impl Create for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -344,11 +354,14 @@ impl Create for PostgreSQL {
impl Pause for PostgreSQL {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -362,11 +375,14 @@ impl Pause for PostgreSQL {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
@@ -382,10 +398,12 @@ impl Delete for PostgreSQL {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -395,11 +413,14 @@ impl Delete for PostgreSQL {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -3,14 +3,15 @@ use tera::Context as TeraContext;
use crate::cloud_provider::service::{
check_service_version, default_tera_context, delete_stateful_service, deploy_stateful_service, get_tfstate_name,
get_tfstate_suffix, scale_down_database, send_progress_on_long_task, Action, Create, Database, DatabaseOptions,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, StatefulService, Terraform,
DatabaseType, Delete, Helm, Pause, Service, ServiceType, ServiceVersionCheckResult, StatefulService, Terraform,
};
use crate::cloud_provider::utilities::{get_self_hosted_redis_version, print_action, sanitize_name};
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm::Timeout;
use crate::cmd::kubectl;
use crate::error::{EngineError, EngineErrorScope};
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter, Transmitter};
use crate::logger::Logger;
use crate::models::DatabaseMode::MANAGED;
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -28,6 +29,7 @@ pub struct Redis {
database_instance_type: String,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Redis {
@@ -44,6 +46,7 @@ impl Redis {
database_instance_type: &str,
options: DatabaseOptions,
listeners: Listeners,
logger: Box<dyn Logger>,
) -> Self {
Self {
context,
@@ -58,11 +61,17 @@ impl Redis {
database_instance_type: database_instance_type.to_string(),
options,
listeners,
logger,
}
}
fn matching_correct_version(&self) -> Result<String, EngineError> {
check_service_version(get_self_hosted_redis_version(self.version()), self)
fn matching_correct_version(&self, event_details: EventDetails) -> Result<ServiceVersionCheckResult, EngineError> {
check_service_version(
get_self_hosted_redis_version(self.version()),
self,
event_details,
self.logger(),
)
}
fn cloud_provider_name(&self) -> &str {
@@ -152,18 +161,14 @@ impl Service for Redis {
}
fn tera_context(&self, target: &DeploymentTarget) -> Result<TeraContext, EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::LoadConfiguration));
let kubernetes = target.kubernetes;
let environment = target.environment;
let mut context = default_tera_context(self, kubernetes, environment);
// we need the kubernetes config file to store tfstates file in kube secrets
let kube_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(path) => path,
Err(e) => {
return Err(e.to_legacy_engine_error());
}
};
let kube_config_file_path = kubernetes.get_kubeconfig_file_path()?;
context.insert("kubeconfig_path", &kube_config_file_path);
kubectl::kubectl_exec_create_namespace_without_labels(
@@ -172,7 +177,7 @@ impl Service for Redis {
kubernetes.cloud_provider().credentials_environment_variables(),
);
let version = self.matching_correct_version()?;
let version = self.matching_correct_version(event_details.clone())?.matched_version();
context.insert("namespace", environment.namespace());
context.insert("version", &version);
@@ -218,12 +223,8 @@ impl Service for Redis {
Some(format!("app={}", self.sanitized_name()))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Database(
self.id().to_string(),
self.service_type().name().to_string(),
self.name().to_string(),
)
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -270,24 +271,35 @@ impl Create for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
deploy_stateful_service(target, self, event_details.clone())
deploy_stateful_service(target, self, event_details.clone(), self.logger())
})
}
fn on_create_check(&self) -> Result<(), EngineError> {
self.check_domains(self.listeners.clone(), vec![self.fqdn.as_str()])
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
self.check_domains(
self.listeners.clone(),
vec![self.fqdn.as_str()],
event_details,
self.logger(),
)
}
#[named]
fn on_create_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -296,11 +308,14 @@ impl Create for Redis {
impl Pause for Redis {
#[named]
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
@@ -314,11 +329,14 @@ impl Pause for Redis {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -333,10 +351,12 @@ impl Delete for Redis {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
delete_stateful_service(target, self, event_details.clone())
delete_stateful_service(target, self, event_details.clone(), self.logger())
})
}
@@ -346,11 +366,14 @@ impl Delete for Redis {
#[named]
fn on_delete_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}

View File

@@ -171,7 +171,7 @@ impl<'a> Kapsule<'a> {
e,
);
logger.log(LogLevel::Error, EngineEvent::Error(err.clone()));
logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
return Err(err);
}
@@ -395,14 +395,17 @@ impl<'a> Kapsule<'a> {
self.logger.log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_missing_workers_group_info_error(
event_details,
CommandError::new_from_safe_message(format!(
"Missing node pool info {} for cluster {}",
name,
self.context.cluster_id()
)),
)),
EngineEvent::Error(
EngineError::new_missing_workers_group_info_error(
event_details,
CommandError::new_from_safe_message(format!(
"Missing node pool info {} for cluster {}",
name,
self.context.cluster_id()
)),
),
None,
),
);
if item.is_none() {
@@ -550,10 +553,13 @@ impl<'a> Kapsule<'a> {
Some(secret_id) => context.insert("vault_secret_id", secret_id.to_str().unwrap()),
None => self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
)),
EngineEvent::Error(
EngineError::new_missing_required_env_variable(
event_details.clone(),
"VAULT_SECRET_ID".to_string(),
),
None,
),
),
}
}
@@ -635,7 +641,7 @@ impl<'a> Kapsule<'a> {
)
}
Err(e) => {
self.logger().log(LogLevel::Error, EngineEvent::Error(e));
self.logger().log(LogLevel::Error, EngineEvent::Error(e, None));
self.logger().log(
LogLevel::Info,
EngineEvent::Deploying(
@@ -726,10 +732,10 @@ impl<'a> Kapsule<'a> {
}
Err(e) => self.logger().log(
LogLevel::Warning,
EngineEvent::Error(EngineError::new_terraform_state_does_not_exist(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_state_does_not_exist(event_details.clone(), e),
None,
),
),
};
@@ -751,7 +757,8 @@ impl<'a> Kapsule<'a> {
self.kubeconfig_bucket_name(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -762,7 +769,8 @@ impl<'a> Kapsule<'a> {
self.logs_bucket_name(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -793,7 +801,8 @@ impl<'a> Kapsule<'a> {
kubeconfig_name.to_string(),
CommandError::new(e.message.unwrap_or("No error message".to_string()), None),
);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
@@ -912,14 +921,14 @@ impl<'a> Kapsule<'a> {
Some(c),
);
self.logger
.log(LogLevel::Error, EngineEvent::Error(current_error.clone()));
.log(LogLevel::Error, EngineEvent::Error(current_error.clone(), None));
OperationResult::Retry(current_error)
}
ScwNodeGroupErrors::ClusterDoesNotExists(c) => {
let current_error =
EngineError::new_no_cluster_found_error(event_details.clone(), c);
self.logger
.log(LogLevel::Error, EngineEvent::Error(current_error.clone()));
.log(LogLevel::Error, EngineEvent::Error(current_error.clone(), None));
OperationResult::Retry(current_error)
}
ScwNodeGroupErrors::MultipleClusterFound => {
@@ -943,7 +952,7 @@ impl<'a> Kapsule<'a> {
Some(c),
);
self.logger
.log(LogLevel::Error, EngineEvent::Error(current_error.clone()));
.log(LogLevel::Error, EngineEvent::Error(current_error.clone(), None));
OperationResult::Retry(current_error)
}
}
@@ -1191,7 +1200,8 @@ impl<'a> Kapsule<'a> {
}
Err(e) => {
let error = EngineError::new_terraform_state_does_not_exist(event_details.clone(), e);
self.logger().log(LogLevel::Error, EngineEvent::Error(error.clone()));
self.logger()
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
return Err(error);
}
};
@@ -1399,10 +1409,10 @@ impl<'a> Kapsule<'a> {
// An issue occurred during the apply before destroy of Terraform, it may be expected if you're resuming a destroy
self.logger().log(
LogLevel::Error,
EngineEvent::Error(EngineError::new_terraform_error_while_executing_pipeline(
event_details.clone(),
e,
)),
EngineEvent::Error(
EngineError::new_terraform_error_while_executing_pipeline(event_details.clone(), e),
None,
),
);
};
@@ -1759,22 +1769,28 @@ impl<'a> Kubernetes for Kapsule<'a> {
#[named]
fn on_create(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create())
}
#[named]
fn on_create_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Create));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.create_error())
}
@@ -1811,7 +1827,7 @@ impl<'a> Kubernetes for Kapsule<'a> {
self.cloud_provider().credentials_environment_variables(),
Stage::Infrastructure(InfrastructureStep::Upgrade),
) {
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone()));
self.logger().log(LogLevel::Error, EngineEvent::Error(e.clone(), None));
return Err(e);
}
@@ -1911,88 +1927,112 @@ impl<'a> Kubernetes for Kapsule<'a> {
#[named]
fn on_upgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade())
}
#[named]
fn on_upgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Upgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.upgrade_error())
}
#[named]
fn on_downgrade(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade())
}
#[named]
fn on_downgrade_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Downgrade));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Create, || self.downgrade_error())
}
#[named]
fn on_pause(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause())
}
#[named]
fn on_pause_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Pause, || self.pause_error())
}
#[named]
fn on_delete(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete())
}
#[named]
fn on_delete_error(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Infrastructure(InfrastructureStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
send_progress_on_long_task(self, Action::Delete, || self.delete_error())
}
@@ -2005,8 +2045,10 @@ impl<'a> Kubernetes for Kapsule<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment(self, environment, event_details)
kubernetes::deploy_environment(self, environment, event_details, self.logger())
}
#[named]
@@ -2017,8 +2059,10 @@ impl<'a> Kubernetes for Kapsule<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::deploy_environment_error(self, environment, event_details)
kubernetes::deploy_environment_error(self, environment, event_details, self.logger())
}
#[named]
@@ -2029,17 +2073,22 @@ impl<'a> Kubernetes for Kapsule<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::pause_environment(self, environment, event_details)
kubernetes::pause_environment(self, environment, event_details, self.logger())
}
#[named]
fn pause_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}
@@ -2052,17 +2101,22 @@ impl<'a> Kubernetes for Kapsule<'a> {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
kubernetes::delete_environment(self, environment, event_details)
kubernetes::delete_environment(self, environment, event_details, self.logger())
}
#[named]
fn delete_environment_error(&self, _environment: &Environment) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
Ok(())
}

View File

@@ -3,7 +3,8 @@ use uuid::Uuid;
use crate::cloud_provider::{CloudProvider, EngineError, Kind, TerraformStateCredentials};
use crate::constants::{SCALEWAY_ACCESS_KEY, SCALEWAY_DEFAULT_PROJECT_ID, SCALEWAY_SECRET_KEY};
use crate::models::{Context, Listen, Listener, Listeners};
use crate::events::{EventDetails, Stage, ToTransmitter, Transmitter};
use crate::models::{Context, Listen, Listener, Listeners, QoveryIdentifier};
pub mod application;
pub mod databases;
@@ -119,6 +120,19 @@ impl CloudProvider for Scaleway {
fn as_any(&self) -> &dyn Any {
self
}
fn get_event_details(&self, stage: Stage) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
stage,
self.to_transmitter(),
)
}
}
impl Listen for Scaleway {
@@ -130,3 +144,9 @@ impl Listen for Scaleway {
self.listeners.push(listener);
}
}
impl ToTransmitter for Scaleway {
fn to_transmitter(&self) -> Transmitter {
Transmitter::CloudProvider(self.id.to_string(), self.name.to_string())
}
}

View File

@@ -10,9 +10,9 @@ use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_n
use crate::cloud_provider::DeploymentTarget;
use crate::cmd::helm;
use crate::cmd::helm::Timeout;
use crate::error::{EngineError, EngineErrorScope};
use crate::errors::EngineError as NewEngineError;
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
use crate::errors::EngineError;
use crate::events::{EngineEvent, EnvironmentStep, EventMessage, Stage, ToTransmitter, Transmitter};
use crate::logger::{LogLevel, Logger};
use crate::models::{Context, Listen, Listener, Listeners};
use ::function_name::named;
@@ -26,6 +26,7 @@ pub struct Router {
sticky_sessions_enabled: bool,
routes: Vec<Route>,
listeners: Listeners,
logger: Box<dyn Logger>,
}
impl Router {
@@ -39,7 +40,8 @@ impl Router {
routes: Vec<Route>,
sticky_sessions_enabled: bool,
listeners: Listeners,
) -> Router {
logger: Box<dyn Logger>,
) -> Self {
Router {
context,
id: id.to_string(),
@@ -50,6 +52,7 @@ impl Router {
sticky_sessions_enabled,
routes,
listeners,
logger,
}
}
@@ -195,8 +198,8 @@ impl Service for Router {
Some(format!("routerId={}", self.id))
}
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::Router(self.id().to_string(), self.name().to_string())
fn logger(&self) -> &dyn Logger {
&*self.logger
}
}
@@ -259,23 +262,22 @@ impl ToTransmitter for Router {
impl Create for Router {
#[named]
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
let kubernetes = target.kubernetes;
let environment = target.environment;
let workspace_dir = self.workspace_directory();
let helm_release_name = self.helm_release_name();
let kubernetes_config_file_path = match kubernetes.get_kubeconfig_file_path() {
Ok(p) => p,
Err(e) => return Err(e.to_legacy_engine_error()),
};
let kubernetes_config_file_path = kubernetes.get_kubeconfig_file_path()?;
// respect order - getting the context here and not before is mandatory
// the nginx-ingress must be available to get the external dns target if necessary
@@ -285,13 +287,12 @@ impl Create for Router {
if let Err(e) =
crate::template::generate_and_copy_all_files_into_dir(from_dir.as_str(), workspace_dir.as_str(), context)
{
return Err(NewEngineError::new_cannot_copy_files_from_one_directory_to_another(
return Err(EngineError::new_cannot_copy_files_from_one_directory_to_another(
event_details.clone(),
from_dir.to_string(),
workspace_dir.to_string(),
e,
)
.to_legacy_engine_error());
));
}
// do exec helm upgrade and return the last deployment status
@@ -299,7 +300,8 @@ impl Create for Router {
&kubernetes_config_file_path,
&kubernetes.cloud_provider().credentials_environment_variables(),
)
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())?;
.map_err(|e| helm::to_engine_error(&event_details, e))?;
let chart = ChartInfo::new_from_custom_namespace(
helm_release_name,
workspace_dir.clone(),
@@ -314,12 +316,14 @@ impl Create for Router {
);
helm.upgrade(&chart, &vec![])
.map_err(|e| helm::to_engine_error(&event_details, e).to_legacy_engine_error())
.map_err(|e| helm::to_engine_error(&event_details, e))
}
fn on_create_check(&self) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
// check non custom domains
self.check_domains()?;
self.check_domains(event_details.clone(), self.logger())?;
// Wait/Check that custom domain is a CNAME targeting qovery
for domain_to_check in self.custom_domains.iter() {
@@ -333,9 +337,19 @@ impl Create for Router {
continue
}
Ok(err) | Err(err) => {
warn!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN: {}",
domain_to_check.domain, err
// TODO(benjaminch): Handle better this one via a proper error eventually
self.logger().log(
LogLevel::Warning,
EngineEvent::Warning(
event_details.clone(),
EventMessage::new(
format!(
"Invalid CNAME for {}. Might not be an issue if user is using a CDN.",
domain_to_check.domain,
),
Some(err.to_string()),
),
),
);
}
}
@@ -346,11 +360,14 @@ impl Create for Router {
#[named]
fn on_create_error(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
@@ -362,11 +379,14 @@ impl Create for Router {
impl Pause for Router {
#[named]
fn on_pause(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -377,11 +397,14 @@ impl Pause for Router {
#[named]
fn on_pause_error(&self, _target: &DeploymentTarget) -> Result<(), EngineError> {
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
print_action(
self.cloud_provider_name(),
self.struct_name(),
function_name!(),
self.name(),
event_details,
self.logger(),
);
Ok(())
}
@@ -396,6 +419,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, false, event_details)
}
@@ -412,6 +437,8 @@ impl Delete for Router {
self.struct_name(),
function_name!(),
self.name(),
event_details.clone(),
self.logger(),
);
delete_router(target, self, true, event_details)
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,8 @@
use std::collections::HashMap;
use crate::cloud_provider::models::CpuLimits;
use crate::error::{EngineError, StringError};
use crate::errors::{CommandError, EngineError};
use crate::events::{EngineEvent, EventDetails, EventMessage};
use crate::logger::{LogLevel, Logger};
use crate::models::{Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope};
use chrono::Duration;
use core::option::Option::{None, Some};
@@ -11,13 +12,12 @@ use retry::delay::Fixed;
use retry::OperationResult;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::num::ParseFloatError;
use std::str::FromStr;
use trust_dns_resolver::config::*;
use trust_dns_resolver::proto::rr::{RData, RecordType};
use trust_dns_resolver::Resolver;
pub fn get_self_hosted_postgres_version(requested_version: String) -> Result<String, StringError> {
pub fn get_self_hosted_postgres_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_postgres_versions = HashMap::new();
// https://hub.docker.com/r/bitnami/postgresql/tags?page=1&ordering=last_updated
@@ -41,7 +41,7 @@ pub fn get_self_hosted_postgres_version(requested_version: String) -> Result<Str
get_supported_version_to_use("Postgresql", supported_postgres_versions, requested_version)
}
pub fn get_self_hosted_mysql_version(requested_version: String) -> Result<String, StringError> {
pub fn get_self_hosted_mysql_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_mysql_versions = HashMap::new();
// https://hub.docker.com/r/bitnami/mysql/tags?page=1&ordering=last_updated
@@ -56,7 +56,7 @@ pub fn get_self_hosted_mysql_version(requested_version: String) -> Result<String
get_supported_version_to_use("MySQL", supported_mysql_versions, requested_version)
}
pub fn get_self_hosted_mongodb_version(requested_version: String) -> Result<String, StringError> {
pub fn get_self_hosted_mongodb_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_mongodb_versions = HashMap::new();
// https://hub.docker.com/r/bitnami/mongodb/tags?page=1&ordering=last_updated
@@ -80,7 +80,7 @@ pub fn get_self_hosted_mongodb_version(requested_version: String) -> Result<Stri
get_supported_version_to_use("MongoDB", supported_mongodb_versions, requested_version)
}
pub fn get_self_hosted_redis_version(requested_version: String) -> Result<String, StringError> {
pub fn get_self_hosted_redis_version(requested_version: String) -> Result<String, CommandError> {
let mut supported_redis_versions = HashMap::with_capacity(4);
// https://hub.docker.com/r/bitnami/redis/tags?page=1&ordering=last_updated
@@ -96,11 +96,8 @@ pub fn get_supported_version_to_use(
database_name: &str,
all_supported_versions: HashMap<String, String>,
version_to_check: String,
) -> Result<String, StringError> {
let version = match VersionsNumber::from_str(version_to_check.as_str()) {
Ok(version) => version,
Err(e) => return Err(e),
};
) -> Result<String, CommandError> {
let version = VersionsNumber::from_str(version_to_check.as_str())?;
// if a patch version is required
if version.patch.is_some() {
@@ -112,10 +109,10 @@ pub fn get_supported_version_to_use(
)) {
Some(version) => Ok(version.to_string()),
None => {
return Err(format!(
return Err(CommandError::new_from_safe_message(format!(
"{} {} version is not supported",
database_name, version_to_check
));
)));
}
};
}
@@ -125,10 +122,10 @@ pub fn get_supported_version_to_use(
return match all_supported_versions.get(&format!("{}.{}", version.major, version.minor.unwrap())) {
Some(version) => Ok(version.to_string()),
None => {
return Err(format!(
return Err(CommandError::new_from_safe_message(format!(
"{} {} version is not supported",
database_name, version_to_check
));
)));
}
};
};
@@ -137,10 +134,10 @@ pub fn get_supported_version_to_use(
match all_supported_versions.get(&version.major) {
Some(version) => Ok(version.to_string()),
None => {
return Err(format!(
return Err(CommandError::new_from_safe_message(format!(
"{} {} version is not supported",
database_name, version_to_check
));
)));
}
}
}
@@ -275,11 +272,13 @@ impl VersionsNumber {
}
impl FromStr for VersionsNumber {
type Err = StringError;
type Err = CommandError;
fn from_str(version: &str) -> Result<Self, Self::Err> {
if version.trim() == "" {
return Err(StringError::from("version cannot be empty"));
return Err(CommandError::new_from_safe_message(
"version cannot be empty".to_string(),
));
}
let mut version_split = version.splitn(4, '.').map(|v| v.trim());
@@ -290,10 +289,10 @@ impl FromStr for VersionsNumber {
major.replace("v", "")
}
None => {
return Err(format!(
return Err(CommandError::new_from_safe_message(format!(
"please check the version you've sent ({}), it can't be checked",
version
))
)))
}
};
@@ -438,19 +437,23 @@ pub fn check_domain_for(
domains_to_check: Vec<&str>,
execution_id: &str,
context_id: &str,
event_details: EventDetails,
logger: &dyn Logger,
) -> Result<(), EngineError> {
let resolvers = dns_resolvers();
for domain in domains_to_check {
let message = format!(
"Let's check domain resolution for '{}'. Please wait, it can take some time...",
domain
);
listener_helper.deployment_in_progress(ProgressInfo::new(
ProgressScope::Environment {
id: execution_id.to_string(),
},
ProgressLevel::Info,
Some(format!(
"Let's check domain resolution for '{}'. Please wait, it can take some time...",
domain
)),
Some(message.to_string()),
execution_id,
));
@@ -460,13 +463,22 @@ pub fn check_domain_for(
ix += 1;
resolver
};
logger.log(
LogLevel::Info,
EngineEvent::Info(event_details.clone(), EventMessage::new_from_safe(message.to_string())),
);
let fixed_iterable = Fixed::from_millis(3000).take(100);
let check_result = retry::retry(fixed_iterable, || match next_resolver().lookup_ip(domain) {
Ok(lookup_ip) => OperationResult::Ok(lookup_ip),
Err(err) => {
let x = format!("Domain resolution check for '{}' is still in progress...", domain);
info!("{}", x);
logger.log(
LogLevel::Info,
EngineEvent::Info(event_details.clone(), EventMessage::new_from_safe(x.to_string())),
);
listener_helper.deployment_in_progress(ProgressInfo::new(
ProgressScope::Environment {
@@ -485,7 +497,10 @@ pub fn check_domain_for(
Ok(_) => {
let x = format!("Domain {} is ready! ⚡️", domain);
info!("{}", x);
logger.log(
LogLevel::Info,
EngineEvent::Info(event_details.clone(), EventMessage::new_from_safe(message.to_string())),
);
listener_helper.deployment_in_progress(ProgressInfo::new(
ProgressScope::Environment {
@@ -503,7 +518,10 @@ pub fn check_domain_for(
domain
);
warn!("{}", message);
logger.log(
LogLevel::Warning,
EngineEvent::Warning(event_details.clone(), EventMessage::new_from_safe(message.to_string())),
);
listener_helper.deployment_in_progress(ProgressInfo::new(
ProgressScope::Environment {
@@ -533,122 +551,36 @@ pub fn managed_db_name_sanitizer(max_size: usize, prefix: &str, name: &str) -> S
new_name
}
pub fn convert_k8s_cpu_value_to_f32(value: String) -> Result<f32, ParseFloatError> {
if value.ends_with('m') {
let mut value_number_string = value;
value_number_string.pop();
return match value_number_string.parse::<f32>() {
Ok(n) => {
Ok(n * 0.001) // return in milli cpu the value
}
Err(e) => Err(e),
};
}
match value.parse::<f32>() {
Ok(n) => Ok(n),
Err(e) => Err(e),
}
}
pub fn validate_k8s_required_cpu_and_burstable(
listener_helper: &ListenersHelper,
execution_id: &str,
context_id: &str,
total_cpu: String,
cpu_burst: String,
) -> Result<CpuLimits, ParseFloatError> {
let total_cpu_float = convert_k8s_cpu_value_to_f32(total_cpu.clone())?;
let cpu_burst_float = convert_k8s_cpu_value_to_f32(cpu_burst.clone())?;
let mut set_cpu_burst = cpu_burst.clone();
if cpu_burst_float < total_cpu_float {
let message = format!(
"CPU burst value '{}' was lower than the desired total of CPUs {}, using burstable value. Please ensure your configuration is valid",
cpu_burst,
total_cpu,
);
warn!("{}", message);
listener_helper.error(ProgressInfo::new(
ProgressScope::Environment {
id: execution_id.to_string(),
},
ProgressLevel::Warn,
Some(message),
context_id,
));
set_cpu_burst = total_cpu.clone();
}
Ok(CpuLimits {
cpu_limit: set_cpu_burst,
cpu_request: total_cpu,
})
}
pub fn print_action(cloud_provider_name: &str, struct_name: &str, fn_name: &str, item_name: &str) {
pub fn print_action(
cloud_provider_name: &str,
struct_name: &str,
fn_name: &str,
item_name: &str,
event_details: EventDetails,
logger: &dyn Logger,
) {
let msg = format!(
"{}.{}.{} called for {}",
cloud_provider_name, struct_name, fn_name, item_name
);
match fn_name.contains("error") {
true => warn!("{}", msg),
false => info!("{}", msg),
true => logger.log(
LogLevel::Warning,
EngineEvent::Warning(event_details, EventMessage::new_from_safe(msg)),
),
false => logger.log(
LogLevel::Info,
EngineEvent::Info(event_details, EventMessage::new_from_safe(msg)),
),
}
}
#[cfg(test)]
mod tests {
use crate::cloud_provider::models::CpuLimits;
use crate::cloud_provider::utilities::{
convert_k8s_cpu_value_to_f32, dns_resolvers, get_cname_record_value, validate_k8s_required_cpu_and_burstable,
VersionsNumber,
};
use crate::error::StringError;
use crate::models::ListenersHelper;
use crate::cloud_provider::utilities::{dns_resolvers, get_cname_record_value, VersionsNumber};
use crate::errors::CommandError;
use std::str::FromStr;
#[test]
pub fn test_k8s_milli_cpu_convert() {
let milli_cpu = "250m".to_string();
let int_cpu = "2".to_string();
assert_eq!(convert_k8s_cpu_value_to_f32(milli_cpu).unwrap(), 0.25 as f32);
assert_eq!(convert_k8s_cpu_value_to_f32(int_cpu).unwrap(), 2 as f32);
}
#[test]
pub fn test_cpu_set() {
let v = vec![];
let listener_helper = ListenersHelper::new(&v);
let execution_id = "execution_id";
let context_id = "context_id";
let mut total_cpu = "0.25".to_string();
let mut cpu_burst = "1".to_string();
assert_eq!(
validate_k8s_required_cpu_and_burstable(&listener_helper, execution_id, context_id, total_cpu, cpu_burst)
.unwrap(),
CpuLimits {
cpu_request: "0.25".to_string(),
cpu_limit: "1".to_string()
}
);
total_cpu = "1".to_string();
cpu_burst = "0.5".to_string();
assert_eq!(
validate_k8s_required_cpu_and_burstable(&listener_helper, execution_id, context_id, total_cpu, cpu_burst)
.unwrap(),
CpuLimits {
cpu_request: "1".to_string(),
cpu_limit: "1".to_string()
}
);
}
#[test]
pub fn test_cname_resolution() {
let resolvers = dns_resolvers();
@@ -662,24 +594,30 @@ mod tests {
// setup:
struct TestCase<'a> {
input: &'a str,
expected_output: Result<VersionsNumber, StringError>,
expected_output: Result<VersionsNumber, CommandError>,
description: &'a str,
}
let test_cases = vec![
TestCase {
input: "",
expected_output: Err(StringError::from("version cannot be empty")),
expected_output: Err(CommandError::new_from_safe_message(
"version cannot be empty".to_string(),
)),
description: "empty version str",
},
TestCase {
input: " ",
expected_output: Err(StringError::from("version cannot be empty")),
expected_output: Err(CommandError::new_from_safe_message(
"version cannot be empty".to_string(),
)),
description: "version a tab str",
},
TestCase {
input: " ",
expected_output: Err(StringError::from("version cannot be empty")),
expected_output: Err(CommandError::new_from_safe_message(
"version cannot be empty".to_string(),
)),
description: "version as a space str",
},
TestCase {

View File

@@ -7,6 +7,8 @@ use crate::cmd::command::QoveryCommand;
use crate::container_registry::docker::{docker_pull_image, docker_tag_and_push_image};
use crate::container_registry::{ContainerRegistry, EngineError, Kind, PullResult, PushResult};
use crate::error::EngineErrorCause;
use crate::errors::EngineError as NewEngineError;
use crate::events::{ToTransmitter, Transmitter};
use crate::models::{
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
};
@@ -72,6 +74,12 @@ impl DockerHub {
}
}
impl ToTransmitter for DockerHub {
fn to_transmitter(&self) -> Transmitter {
Transmitter::ContainerRegistry(self.id().to_string(), self.name().to_string())
}
}
impl ContainerRegistry for DockerHub {
fn context(&self) -> &Context {
&self.context
@@ -89,16 +97,7 @@ impl ContainerRegistry for DockerHub {
self.name.as_str()
}
fn is_valid(&self) -> Result<(), EngineError> {
// check the version of docker and print it as info
let mut output_from_cmd = String::new();
let mut cmd = QoveryCommand::new("docker", &vec!["--version"], &vec![]);
let _ = cmd.exec_with_output(
|r_out| output_from_cmd.push_str(&r_out),
|r_err| error!("Error executing docker command {}", r_err),
);
info!("Using Docker: {}", output_from_cmd);
fn is_valid(&self) -> Result<(), NewEngineError> {
Ok(())
}

View File

@@ -8,6 +8,8 @@ use crate::cmd::command::QoveryCommand;
use crate::container_registry::docker::{docker_pull_image, docker_tag_and_push_image};
use crate::container_registry::{ContainerRegistry, EngineError, Kind, PullResult, PushResult};
use crate::error::{cast_simple_error_to_engine_error, EngineErrorCause, SimpleError, SimpleErrorKind};
use crate::errors::EngineError as NewEngineError;
use crate::events::{ToTransmitter, Transmitter};
use crate::models::{
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
};
@@ -228,6 +230,12 @@ impl DOCR {
}
}
impl ToTransmitter for DOCR {
fn to_transmitter(&self) -> Transmitter {
Transmitter::ContainerRegistry(self.id().to_string(), self.name().to_string())
}
}
impl ContainerRegistry for DOCR {
fn context(&self) -> &Context {
&self.context
@@ -245,7 +253,7 @@ impl ContainerRegistry for DOCR {
self.name.as_str()
}
fn is_valid(&self) -> Result<(), EngineError> {
fn is_valid(&self) -> Result<(), NewEngineError> {
Ok(())
}

View File

@@ -13,6 +13,8 @@ use crate::cmd::command::QoveryCommand;
use crate::container_registry::docker::{docker_pull_image, docker_tag_and_push_image};
use crate::container_registry::{ContainerRegistry, Kind, PullResult, PushResult};
use crate::error::{EngineError, EngineErrorCause};
use crate::errors::EngineError as NewEngineError;
use crate::events::{ToTransmitter, Transmitter};
use crate::models::{
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
};
@@ -359,6 +361,12 @@ impl ECR {
}
}
impl ToTransmitter for ECR {
fn to_transmitter(&self) -> Transmitter {
Transmitter::ContainerRegistry(self.id().to_string(), self.name().to_string())
}
}
impl ContainerRegistry for ECR {
fn context(&self) -> &Context {
&self.context
@@ -376,18 +384,14 @@ impl ContainerRegistry for ECR {
self.name.as_str()
}
fn is_valid(&self) -> Result<(), EngineError> {
fn is_valid(&self) -> Result<(), NewEngineError> {
let client = StsClient::new_with_client(self.client(), Region::default());
let s = block_on(client.get_caller_identity(GetCallerIdentityRequest::default()));
match s {
Ok(_) => Ok(()),
Err(_) => Err(self.engine_error(
EngineErrorCause::User(
"Your ECR account seems to be no longer valid (bad Credentials). \
Please contact your Organization administrator to fix or change the Credentials.",
),
format!("bad ECR credentials for {}", self.name_with_id()),
Err(_) => Err(NewEngineError::new_client_invalid_cloud_provider_credentials(
self.get_event_details(),
)),
}
}

View File

@@ -2,7 +2,9 @@ use serde::{Deserialize, Serialize};
use crate::build_platform::Image;
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::models::{Context, Listen};
use crate::errors::EngineError as NewEngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter};
use crate::models::{Context, Listen, QoveryIdentifier};
pub mod docker;
pub mod docker_hub;
@@ -10,7 +12,7 @@ pub mod docr;
pub mod ecr;
pub mod scaleway_container_registry;
pub trait ContainerRegistry: Listen {
pub trait ContainerRegistry: Listen + ToTransmitter {
fn context(&self) -> &Context;
fn kind(&self) -> Kind;
fn id(&self) -> &str;
@@ -18,7 +20,7 @@ pub trait ContainerRegistry: Listen {
fn name_with_id(&self) -> String {
format!("{} ({})", self.name(), self.id())
}
fn is_valid(&self) -> Result<(), EngineError>;
fn is_valid(&self) -> Result<(), NewEngineError>;
fn on_create(&self) -> Result<(), EngineError>;
fn on_create_error(&self) -> Result<(), EngineError>;
fn on_delete(&self) -> Result<(), EngineError>;
@@ -38,6 +40,18 @@ pub trait ContainerRegistry: Listen {
Some(message),
)
}
fn get_event_details(&self) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
Stage::Environment(EnvironmentStep::Build),
self.to_transmitter(),
)
}
}
pub struct PushResult {

View File

@@ -9,6 +9,8 @@ use crate::container_registry::docker::{
};
use crate::container_registry::{ContainerRegistry, Kind, PullResult, PushResult};
use crate::error::{EngineError, EngineErrorCause};
use crate::errors::EngineError as NewEngineError;
use crate::events::{ToTransmitter, Transmitter};
use crate::models::{
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
};
@@ -341,6 +343,12 @@ impl ScalewayCR {
}
}
impl ToTransmitter for ScalewayCR {
fn to_transmitter(&self) -> Transmitter {
Transmitter::ContainerRegistry(self.id().to_string(), self.name().to_string())
}
}
impl ContainerRegistry for ScalewayCR {
fn context(&self) -> &Context {
&self.context
@@ -358,7 +366,7 @@ impl ContainerRegistry for ScalewayCR {
self.name.as_str()
}
fn is_valid(&self) -> Result<(), EngineError> {
fn is_valid(&self) -> Result<(), NewEngineError> {
Ok(())
}

View File

@@ -1,7 +1,8 @@
use std::net::Ipv4Addr;
use crate::dns_provider::{DnsProvider, Kind};
use crate::error::{EngineError, EngineErrorCause};
use crate::errors::EngineError;
use crate::events::{ToTransmitter, Transmitter};
use crate::models::{Context, Domain};
pub struct Cloudflare {
@@ -72,15 +73,17 @@ impl DnsProvider for Cloudflare {
fn is_valid(&self) -> Result<(), EngineError> {
if self.cloudflare_api_token.is_empty() || self.cloudflare_email.is_empty() {
Err(self.engine_error(
EngineErrorCause::User(
"Your Cloudflare account seems to be no longer valid (bad Credentials). \
Please contact your Organization administrator to fix or change the Credentials.",
),
format!("bad Cloudflare credentials for {}", self.name_with_id()),
Err(EngineError::new_client_invalid_cloud_provider_credentials(
self.get_event_details(),
))
} else {
Ok(())
}
}
}
impl ToTransmitter for Cloudflare {
fn to_transmitter(&self) -> Transmitter {
Transmitter::DnsProvider(self.id().to_string(), self.name().to_string())
}
}

View File

@@ -3,11 +3,13 @@ use std::net::Ipv4Addr;
use serde::{Deserialize, Serialize};
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
use crate::models::{Context, Domain};
use crate::errors::EngineError as NewEngineError;
use crate::events::{EnvironmentStep, EventDetails, Stage, ToTransmitter};
use crate::models::{Context, Domain, QoveryIdentifier};
pub mod cloudflare;
pub trait DnsProvider {
pub trait DnsProvider: ToTransmitter {
fn context(&self) -> &Context;
fn provider_name(&self) -> &str;
fn kind(&self) -> Kind;
@@ -20,7 +22,7 @@ pub trait DnsProvider {
fn token(&self) -> &str;
fn domain(&self) -> &Domain;
fn resolvers(&self) -> Vec<Ipv4Addr>;
fn is_valid(&self) -> Result<(), EngineError>;
fn is_valid(&self) -> Result<(), NewEngineError>;
fn engine_error_scope(&self) -> EngineErrorScope {
EngineErrorScope::DnsProvider(self.id().to_string(), self.name().to_string())
}
@@ -32,6 +34,18 @@ pub trait DnsProvider {
Some(message),
)
}
fn get_event_details(&self) -> EventDetails {
let context = self.context();
EventDetails::new(
None,
QoveryIdentifier::from(context.organization_id().to_string()),
QoveryIdentifier::from(context.cluster_id().to_string()),
QoveryIdentifier::from(context.execution_id().to_string()),
None,
Stage::Environment(EnvironmentStep::Deploy),
self.to_transmitter(),
)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]

View File

@@ -4,7 +4,7 @@ use crate::build_platform::BuildPlatform;
use crate::cloud_provider::CloudProvider;
use crate::container_registry::ContainerRegistry;
use crate::dns_provider::DnsProvider;
use crate::error::EngineError;
use crate::errors::EngineError;
use crate::logger::Logger;
use crate::models::Context;
use crate::session::Session;

View File

@@ -6,6 +6,7 @@ pub type Id = String;
pub type Name = String;
#[derive(Debug)]
#[deprecated(note = "errors.EngineError to be used instead")]
pub struct EngineError {
pub cause: EngineErrorCause,
pub scope: EngineErrorScope,
@@ -72,6 +73,7 @@ pub enum EngineErrorCause {
}
#[derive(Debug)]
#[deprecated(note = "errors.CommandError to be used instead")]
pub struct SimpleError {
pub kind: SimpleErrorKind,
pub message: Option<String>,

View File

@@ -79,6 +79,25 @@ pub enum Tag {
NoClusterFound,
OnlyOneClusterExpected,
CloudProviderApiMissingInfo,
K8sValidateRequiredCPUandBurstableError,
TerraformContextUnsupportedParameterValue,
ClientServiceFailedToStart,
ClientServiceFailedToDeployBeforeStart,
DatabaseFailedToStartAfterSeveralRetries,
RouterFailedToDeploy,
CloudProviderClientInvalidCredentials,
VersionNumberParsingError,
NotImplementedError,
BuilderDockerCannotFindAnyDockerfile,
BuilderDockerCannotReadDockerfile,
BuilderDockerCannotExtractEnvVarsFromDockerfile,
BuilderDockerCannotBuildContainerImage,
BuilderBuildpackInvalidLanguageFormat,
BuilderBuildpackCannotBuildContainerImage,
BuilderGetBuildError,
BuilderCloningRepositoryError,
DockerPushImageError,
DockerPullImageError,
}
impl From<errors::Tag> for Tag {
@@ -145,6 +164,28 @@ impl From<errors::Tag> for Tag {
errors::Tag::NoClusterFound => Tag::NoClusterFound,
errors::Tag::OnlyOneClusterExpected => Tag::OnlyOneClusterExpected,
errors::Tag::CloudProviderApiMissingInfo => Tag::CloudProviderApiMissingInfo,
errors::Tag::K8sValidateRequiredCPUandBurstableError => Tag::K8sValidateRequiredCPUandBurstableError,
errors::Tag::TerraformContextUnsupportedParameterValue => Tag::TerraformContextUnsupportedParameterValue,
errors::Tag::ClientServiceFailedToStart => Tag::ClientServiceFailedToStart,
errors::Tag::ClientServiceFailedToDeployBeforeStart => Tag::ClientServiceFailedToDeployBeforeStart,
errors::Tag::DatabaseFailedToStartAfterSeveralRetries => Tag::DatabaseFailedToStartAfterSeveralRetries,
errors::Tag::RouterFailedToDeploy => Tag::RouterFailedToDeploy,
errors::Tag::CloudProviderClientInvalidCredentials => Tag::CloudProviderClientInvalidCredentials,
errors::Tag::VersionNumberParsingError => Tag::VersionNumberParsingError,
errors::Tag::NotImplementedError => Tag::NotImplementedError,
errors::Tag::TaskCancellationRequested => Tag::CannotPauseClusterTasksAreRunning,
errors::Tag::BuilderDockerCannotFindAnyDockerfile => Tag::BuilderDockerCannotFindAnyDockerfile,
errors::Tag::BuilderDockerCannotReadDockerfile => Tag::BuilderDockerCannotReadDockerfile,
errors::Tag::BuilderDockerCannotExtractEnvVarsFromDockerfile => {
Tag::BuilderDockerCannotExtractEnvVarsFromDockerfile
}
errors::Tag::BuilderDockerCannotBuildContainerImage => Tag::BuilderDockerCannotBuildContainerImage,
errors::Tag::BuilderBuildpackInvalidLanguageFormat => Tag::BuilderBuildpackInvalidLanguageFormat,
errors::Tag::BuilderBuildpackCannotBuildContainerImage => Tag::BuilderBuildpackCannotBuildContainerImage,
errors::Tag::BuilderGetBuildError => Tag::BuilderGetBuildError,
errors::Tag::BuilderCloningRepositoryError => Tag::BuilderCloningRepositoryError,
errors::Tag::DockerPushImageError => Tag::DockerPushImageError,
errors::Tag::DockerPullImageError => Tag::DockerPullImageError,
}
}
}

View File

@@ -5,7 +5,8 @@ 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 crate::events::{EventDetails, GeneralStep, Stage, Transmitter};
use crate::models::QoveryIdentifier;
use url::Url;
/// CommandError: command error, mostly returned by third party tools.
@@ -32,7 +33,10 @@ impl CommandError {
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 {
return format!("{} {}", msg, self.message_raw);
// TODO(benjaminch): Handle raw / safe as for event message
if self.message_raw != *msg {
return format!("{} {}", msg, self.message_raw);
}
}
self.message_raw.to_string()
@@ -82,7 +86,7 @@ impl CommandError {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
/// Tag: unique identifier for an error.
pub enum Tag {
/// Unknown: unknown error.
@@ -155,6 +159,8 @@ pub enum Tag {
K8sNodeIsNotReadyWithTheRequestedVersion,
/// K8sNodeIsNotReady: represents an error where the given node is not ready.
K8sNodeIsNotReady,
/// K8sValidateRequiredCPUandBurstableError: represents an error validating required CPU and burstable.
K8sValidateRequiredCPUandBurstableError,
/// CannotFindRequiredBinary: represents an error where a required binary is not found on the system.
CannotFindRequiredBinary,
/// SubnetsCountShouldBeEven: represents an error where subnets count should be even to have as many public than private subnets.
@@ -173,6 +179,8 @@ pub enum Tag {
TerraformErrorWhileExecutingPipeline,
/// TerraformErrorWhileExecutingDestroyPipeline: represents an error while executing Terraform destroying pipeline.
TerraformErrorWhileExecutingDestroyPipeline,
/// TerraformContextUnsupportedParameterValue: represents an error while trying to render terraform context because of unsupported parameter value.
TerraformContextUnsupportedParameterValue,
/// HelmChartsSetupError: represents an error while trying to setup helm charts.
HelmChartsSetupError,
/// HelmChartsDeployError: represents an error while trying to deploy helm charts.
@@ -197,8 +205,44 @@ pub enum Tag {
ObjectStorageCannotCreateBucket,
/// ObjectStorageCannotPutFileIntoBucket: represents an error while trying to put a file into an object storage bucket.
ObjectStorageCannotPutFileIntoBucket,
/// ClientServiceFailedToStart: represent an error while trying to start a client's service.
ClientServiceFailedToStart,
/// ClientServiceFailedToDeployBeforeStart: represents an error while trying to deploy a client's service before start.
ClientServiceFailedToDeployBeforeStart,
/// DatabaseFailedToStartAfterSeveralRetries: represents an error while trying to start a database after several retries.
DatabaseFailedToStartAfterSeveralRetries,
/// RouterFailedToDeploy: represents an error while trying to deploy a router.
RouterFailedToDeploy,
/// CloudProviderClientInvalidCredentials: represents an error where client credentials for a cloud providers appear to be invalid.
CloudProviderClientInvalidCredentials,
/// CloudProviderApiMissingInfo: represents an error while expecting mandatory info
CloudProviderApiMissingInfo,
/// ServiceInvalidVersionNumberError: represents an error where the version number is not valid.
VersionNumberParsingError,
/// NotImplementedError: represents an error where feature / code has not been implemented yet.
NotImplementedError,
/// TaskCancellationRequested: represents an error where current task cancellation has been requested.
TaskCancellationRequested,
/// BuilderDockerCannotFindAnyDockerfile: represents an error when trying to get a Dockerfile.
BuilderDockerCannotFindAnyDockerfile,
/// BuilderDockerCannotReadDockerfile: represents an error while trying to read Dockerfile.
BuilderDockerCannotReadDockerfile,
/// BuilderDockerCannotExtractEnvVarsFromDockerfile: represents an error while trying to extract ENV vars from Dockerfile.
BuilderDockerCannotExtractEnvVarsFromDockerfile,
/// BuilderDockerCannotBuildContainerImage: represents an error while trying to build Docker container image.
BuilderDockerCannotBuildContainerImage,
/// BuilderBuildpackInvalidLanguageFormat: represents an error where buildback requested language has wrong format.
BuilderBuildpackInvalidLanguageFormat,
/// BuilderBuildpackCannotBuildContainerImage: represents an error while trying to build container image with Buildpack.
BuilderBuildpackCannotBuildContainerImage,
/// BuilderGetBuildError: represents an error when builder is trying to get parent build.
BuilderGetBuildError,
/// BuilderCloningRepositoryError: represents an error when builder is trying to clone a git repository.
BuilderCloningRepositoryError,
/// DockerPushImageError: represents an error when trying to push a docker image.
DockerPushImageError,
/// DockerPullImageError: represents an error when trying to pull a docker image.
DockerPullImageError,
}
#[derive(Clone, Debug)]
@@ -291,6 +335,40 @@ impl EngineError {
}
}
/// Creates new engine error from legacy engine error easing migration.
pub fn new_from_legacy_engine_error(e: LegacyEngineError) -> Self {
let message = e.message.unwrap_or("".to_string());
EngineError {
tag: Tag::Unknown,
event_details: EventDetails::new(
None,
QoveryIdentifier::new("".to_string()),
QoveryIdentifier::new("".to_string()),
QoveryIdentifier::new(e.execution_id.to_string()),
None,
Stage::General(GeneralStep::UnderMigration),
match e.scope {
EngineErrorScope::Engine => Transmitter::Kubernetes("".to_string(), "".to_string()),
EngineErrorScope::BuildPlatform(id, name) => Transmitter::BuildPlatform(id, name),
EngineErrorScope::ContainerRegistry(id, name) => Transmitter::ContainerRegistry(id, name),
EngineErrorScope::CloudProvider(id, name) => Transmitter::CloudProvider(id, name),
EngineErrorScope::Kubernetes(id, name) => Transmitter::Kubernetes(id, name),
EngineErrorScope::DnsProvider(id, name) => Transmitter::DnsProvider(id, name),
EngineErrorScope::ObjectStorage(id, name) => Transmitter::ObjectStorage(id, name),
EngineErrorScope::Environment(id, name) => Transmitter::Environment(id, name),
EngineErrorScope::Database(id, db_type, name) => Transmitter::Database(id, db_type, name),
EngineErrorScope::Application(id, name) => Transmitter::Application(id, name),
EngineErrorScope::Router(id, name) => Transmitter::Router(id, name),
},
),
qovery_log_message: message.to_string(),
user_log_message: message.to_string(),
message: None,
link: None,
hint_message: None,
}
}
/// Converts to legacy engine error easing migration.
pub fn to_legacy_engine_error(self) -> LegacyEngineError {
LegacyEngineError::new(
@@ -382,8 +460,6 @@ impl EngineError {
/// Missing API info from the Cloud provider itself
///
///
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
@@ -1228,6 +1304,36 @@ impl EngineError {
)
}
/// Creates new error for kubernetes validate required CPU and burstable.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `total_cpus_raw`: Total CPUs raw format.
/// * `cpu_burst_raw`: CPU burst raw.
/// * `raw_error`: Raw error message.
pub fn new_k8s_validate_required_cpu_and_burstable_error(
event_details: EventDetails,
total_cpus_raw: String,
cpu_burst_raw: String,
raw_error: CommandError,
) -> EngineError {
let message = format!(
"Error while trying to validate required CPU ({}) and burstable ({}).",
total_cpus_raw, cpu_burst_raw
);
EngineError::new(
event_details,
Tag::K8sValidateRequiredCPUandBurstableError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
Some("Please ensure your configuration is valid.".to_string()),
)
}
/// Creates new error for kubernetes not being able to get crash looping pods.
///
/// Arguments:
@@ -1444,6 +1550,38 @@ impl EngineError {
)
}
/// Creates new error representing an unsupported value for Terraform context parameter.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `service_type`: Service type.
/// * `parameter_name`: Context parameter name.
/// * `parameter_value`: Context parameter value.
/// * `raw_error`: Raw error message.
pub fn new_terraform_unsupported_context_parameter_value(
event_details: EventDetails,
service_type: String,
parameter_name: String,
parameter_value: String,
raw_error: Option<CommandError>,
) -> EngineError {
let message = format!(
"{} value `{}` not supported for parameter `{}`",
service_type, parameter_value, parameter_name,
);
EngineError::new(
event_details,
Tag::TerraformContextUnsupportedParameterValue,
message.to_string(),
message.to_string(),
raw_error,
None,
None,
)
}
/// Creates new error while setup Helm charts to deploy.
///
/// Arguments:
@@ -1637,11 +1775,11 @@ impl EngineError {
///
/// * `event_details`: Error linked event details.
/// * `product_name`: Product name for which version is not supported.
/// * `raw_error`: Raw error message.
/// * `version`: unsupported version raw string.
pub fn new_unsupported_version_error(
event_details: EventDetails,
product_name: String,
version: VersionsNumber,
version: String,
) -> EngineError {
let message = format!(
"Error, version `{}` is not supported for `{}`.",
@@ -1739,6 +1877,158 @@ impl EngineError {
)
}
/// Creates new error while trying to start a client service.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `service_id`: Client service ID.
/// * `service_name`: Client service name.
pub fn new_client_service_failed_to_start_error(
event_details: EventDetails,
service_id: String,
service_name: String,
) -> EngineError {
// TODO(benjaminch): Service should probably passed otherwise, either inside event_details or via a new dedicated struct.
let message = format!("Service `{}` (id `{}`) failed to start. ", service_name, service_id);
EngineError::new(
event_details,
Tag::ClientServiceFailedToStart,
message.to_string(),
message.to_string(),
None,
None,
Some("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).".to_string()),
)
}
/// Creates new error while trying to deploy a client service before start.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `service_id`: Client service ID.
/// * `service_name`: Client service name.
pub fn new_client_service_failed_to_deploy_before_start_error(
event_details: EventDetails,
service_id: String,
service_name: String,
) -> EngineError {
// TODO(benjaminch): Service should probably passed otherwise, either inside event_details or via a new dedicated struct.
let message = format!(
"Service `{}` (id `{}`) failed to deploy (before start).",
service_name, service_id
);
EngineError::new(
event_details,
Tag::ClientServiceFailedToDeployBeforeStart,
message.to_string(),
message.to_string(),
None,
None,
None,
)
}
/// Creates new error while trying to start a client service before start.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `service_id`: Client service ID.
/// * `service_type`: Client service type.
/// * `raw_error`: Raw error message.
pub fn new_database_failed_to_start_after_several_retries(
event_details: EventDetails,
service_id: String,
service_type: String,
raw_error: Option<CommandError>,
) -> EngineError {
let message = format!(
"Database `{}` (id `{}`) failed to start after several retries.",
service_type, service_id
);
EngineError::new(
event_details,
Tag::DatabaseFailedToStartAfterSeveralRetries,
message.to_string(),
message.to_string(),
raw_error,
None,
None,
)
}
/// Creates new error while trying to deploy a router.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
pub fn new_router_failed_to_deploy(event_details: EventDetails) -> EngineError {
let message = "Router has failed to be deployed.";
EngineError::new(
event_details,
Tag::RouterFailedToDeploy,
message.to_string(),
message.to_string(),
None,
None,
None,
)
}
/// Creates new error when trying to connect to user's account with its credentials.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
pub fn new_client_invalid_cloud_provider_credentials(event_details: EventDetails) -> EngineError {
let message = "Your cloud provider account seems to be no longer valid (bad credentials).";
EngineError::new(
event_details,
Tag::CloudProviderClientInvalidCredentials,
message.to_string(),
message.to_string(),
None,
None,
Some("Please contact your Organization administrator to fix or change the Credentials.".to_string()),
)
}
/// Creates new error when trying to parse a version number.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `raw_version_number`: Raw version number string.
/// * `raw_error`: Raw error message.
pub fn new_version_number_parsing_error(
event_details: EventDetails,
raw_version_number: String,
raw_error: CommandError,
) -> EngineError {
let message = format!(
"Error while trying to parse `{}` to a version number.",
raw_version_number
);
EngineError::new(
event_details,
Tag::VersionNumberParsingError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error while trying to get cluster.
///
/// Arguments:
@@ -1801,4 +2091,303 @@ impl EngineError {
Some("Please contact Qovery support for investigation.".to_string()),
)
}
/// Current task cancellation has been requested.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
pub fn new_task_cancellation_requested(event_details: EventDetails) -> EngineError {
let message = "Task cancellation has been requested.";
EngineError::new(
event_details,
Tag::TaskCancellationRequested,
message.to_string(),
message.to_string(),
None,
None,
None,
)
}
/// Creates new error when trying to get Dockerfile.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `location_path`: Dockerfile location path.
pub fn new_docker_cannot_find_dockerfile(event_details: EventDetails, location_path: String) -> EngineError {
let message = format!("Dockerfile not found at location `{}`.", location_path);
EngineError::new(
event_details,
Tag::BuilderDockerCannotFindAnyDockerfile,
message.to_string(),
message.to_string(),
None,
None,
Some("Your Dockerfile is not present at the specified location, check your settings.".to_string()),
)
}
/// Creates new error buildpack invalid language format.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `requested_language`: Requested language.
pub fn new_buildpack_invalid_language_format(
event_details: EventDetails,
requested_language: String,
) -> EngineError {
let message = format!(
"Cannot build: Invalid buildpacks language format: `{}`.",
requested_language
);
EngineError::new(
event_details,
Tag::BuilderBuildpackInvalidLanguageFormat,
message.to_string(),
message.to_string(),
None,
None,
Some("Expected format `builder[@version]`.".to_string()),
)
}
/// Creates new error when trying to build container.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `container_image_name`: Container image name.
/// * `raw_error`: Raw error message.
pub fn new_buildpack_cannot_build_container_image(
event_details: EventDetails,
container_image_name: String,
builders: Vec<String>,
raw_error: CommandError,
) -> EngineError {
let message = "Cannot find a builder to build application.";
EngineError::new(
event_details,
Tag::BuilderBuildpackCannotBuildContainerImage,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
Some(format!(
"Qovery can't build your container image {} with one of the following builders: {}. Please do provide a valid Dockerfile to build your application or contact the support.",
container_image_name,
builders.join(", ")
),),
)
}
/// Creates new error when trying to get build.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `commit_id`: Commit ID of build to be retrieved.
/// * `raw_error`: Raw error message.
pub fn new_builder_get_build_error(
event_details: EventDetails,
commit_id: String,
raw_error: CommandError,
) -> EngineError {
let message = format!("Error while trying to get build with commit ID: `{}`.", commit_id);
EngineError::new(
event_details,
Tag::BuilderGetBuildError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when builder is trying to clone a git repository.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `repository_url`: Repository URL.
/// * `raw_error`: Raw error message.
pub fn new_builder_clone_repository_error(
event_details: EventDetails,
repository_url: String,
raw_error: CommandError,
) -> EngineError {
let message = format!("Error while cloning repository `{}`.", repository_url);
EngineError::new(
event_details,
Tag::BuilderCloningRepositoryError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when something went wrong because it's not implemented.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
pub fn new_not_implemented_error(event_details: EventDetails) -> EngineError {
let message = "Error, something went wrong because it's not implemented.";
EngineError::new(
event_details,
Tag::NotImplementedError,
message.to_string(),
message.to_string(),
None,
None,
None,
)
}
/// Creates new error when trying to push a Docker image.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `image_name`: Docker image name.
/// * `repository_url`: Repository URL.
/// * `raw_error`: Raw error message.
pub fn new_docker_push_image_error(
event_details: EventDetails,
image_name: String,
repository_url: String,
raw_error: CommandError,
) -> EngineError {
let message = format!(
"Error, trying to push Docker image `{}` to repository `{}`.",
image_name, repository_url
);
EngineError::new(
event_details,
Tag::DockerPushImageError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when trying to pull a Docker image.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `image_name`: Docker image name.
/// * `repository_url`: Repository URL.
/// * `raw_error`: Raw error message.
pub fn new_docker_pull_image_error(
event_details: EventDetails,
image_name: String,
repository_url: String,
raw_error: CommandError,
) -> EngineError {
let message = format!(
"Error, trying to pull Docker image `{}` from repository `{}`.",
image_name, repository_url
);
EngineError::new(
event_details,
Tag::DockerPullImageError,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when trying to read Dockerfile content.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `dockerfile_path`: Dockerfile path.
/// * `raw_error`: Raw error message.
pub fn new_docker_cannot_read_dockerfile(
event_details: EventDetails,
dockerfile_path: String,
raw_error: CommandError,
) -> EngineError {
let message = format!("Can't read Dockerfile `{}`.", dockerfile_path);
EngineError::new(
event_details,
Tag::BuilderDockerCannotReadDockerfile,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when trying to extract env vars from Dockerfile.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `dockerfile_path`: Dockerfile path.
/// * `raw_error`: Raw error message.
pub fn new_docker_cannot_extract_env_vars_from_dockerfile(
event_details: EventDetails,
dockerfile_path: String,
raw_error: CommandError,
) -> EngineError {
let message = format!("Can't extract ENV vars from Dockerfile `{}`.", dockerfile_path);
EngineError::new(
event_details,
Tag::BuilderDockerCannotExtractEnvVarsFromDockerfile,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
None,
)
}
/// Creates new error when trying to build Docker container.
///
/// Arguments:
///
/// * `event_details`: Error linked event details.
/// * `container_image_name`: Container image name.
/// * `raw_error`: Raw error message.
pub fn new_docker_cannot_build_container_image(
event_details: EventDetails,
container_image_name: String,
raw_error: CommandError,
) -> EngineError {
let message = format!("Error while building container image `{}`.", container_image_name);
EngineError::new(
event_details,
Tag::BuilderDockerCannotBuildContainerImage,
message.to_string(),
message.to_string(),
Some(raw_error),
None,
Some("It looks like there is something wrong in your Dockerfile. Try building the application locally with `docker build --no-cache`.".to_string()),
)
}
}

View File

@@ -23,6 +23,7 @@ pub enum EngineEvent {
},
Error {
error: EngineError,
message: Option<EventMessage>,
},
#[deprecated(note = "event status is carried by EventDetails directly")]
Waiting {
@@ -76,8 +77,12 @@ impl From<events::EngineEvent> for EngineEvent {
details: EventDetails::from(d),
message: EventMessage::from(m),
},
events::EngineEvent::Error(e) => EngineEvent::Error {
events::EngineEvent::Error(e, m) => EngineEvent::Error {
error: EngineError::from(e),
message: match m {
Some(msg) => Some(EventMessage::from(msg)),
None => None,
},
},
events::EngineEvent::Waiting(d, m) => EngineEvent::Waiting {
details: EventDetails::from(d),
@@ -151,6 +156,7 @@ pub enum GeneralStep {
RetrieveClusterConfig,
RetrieveClusterResources,
ValidateSystemRequirements,
UnderMigration,
}
impl From<events::GeneralStep> for GeneralStep {
@@ -159,6 +165,7 @@ impl From<events::GeneralStep> for GeneralStep {
events::GeneralStep::RetrieveClusterConfig => GeneralStep::RetrieveClusterConfig,
events::GeneralStep::RetrieveClusterResources => GeneralStep::RetrieveClusterResources,
events::GeneralStep::ValidateSystemRequirements => GeneralStep::ValidateSystemRequirements,
events::GeneralStep::UnderMigration => GeneralStep::UnderMigration,
}
}
}

View File

@@ -5,7 +5,7 @@ pub mod io;
extern crate url;
use crate::cloud_provider::Kind;
use crate::errors::EngineError;
use crate::errors::{CommandError, EngineError};
use crate::models::QoveryIdentifier;
use std::fmt::{Display, Formatter};
@@ -19,7 +19,7 @@ pub enum EngineEvent {
/// Warning: represents a warning message event.
Warning(EventDetails, EventMessage),
/// Error: represents an error event.
Error(EngineError),
Error(EngineError, Option<EventMessage>),
/// Waiting: represents an engine waiting event.
///
/// Engine is waiting for a task to be done.
@@ -52,7 +52,7 @@ impl EngineEvent {
EngineEvent::Debug(details, _message) => details,
EngineEvent::Info(details, _message) => details,
EngineEvent::Warning(details, _message) => details,
EngineEvent::Error(engine_error) => engine_error.event_details(),
EngineEvent::Error(engine_error, _message) => engine_error.event_details(),
EngineEvent::Waiting(details, _message) => details,
EngineEvent::Deploying(details, _message) => details,
EngineEvent::Pausing(details, _message) => details,
@@ -69,7 +69,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) => engine_error.message(),
EngineEvent::Error(engine_error, _message) => engine_error.message(),
EngineEvent::Waiting(_details, message) => message.message(message_verbosity),
EngineEvent::Deploying(_details, message) => message.message(message_verbosity),
EngineEvent::Pausing(_details, message) => message.message(message_verbosity),
@@ -142,6 +142,12 @@ impl EventMessage {
}
}
impl From<CommandError> for EventMessage {
fn from(e: CommandError) -> Self {
EventMessage::new(e.message_raw(), e.message_safe())
}
}
impl Display for EventMessage {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(self.message(EventMessageVerbosity::SafeOnly).as_str()) // By default, expose only the safe message.
@@ -193,6 +199,8 @@ pub enum GeneralStep {
RetrieveClusterConfig,
/// RetrieveClusterResources: retrieving cluster resources
RetrieveClusterResources,
/// UnderMigration: error migration hasn't been completed yet.
UnderMigration,
}
impl Display for GeneralStep {
@@ -204,6 +212,7 @@ impl Display for GeneralStep {
GeneralStep::RetrieveClusterConfig => "retrieve-cluster-config",
GeneralStep::RetrieveClusterResources => "retrieve-cluster-resources",
GeneralStep::ValidateSystemRequirements => "validate-system-requirements",
GeneralStep::UnderMigration => "under-migration",
}
)
}

View File

@@ -1,10 +1,11 @@
#![allow(deprecated)]
extern crate tera;
#[macro_use]
extern crate tracing;
extern crate trust_dns_resolver;
pub mod build_platform;
#[allow(deprecated)]
pub mod cloud_provider;
pub mod cmd;
pub mod constants;

View File

@@ -116,25 +116,28 @@ mod tests {
let test_cases = vec![
TestCase {
log_level: LogLevel::Error,
event: EngineEvent::Error(EngineError::new_unknown(
EventDetails::new(
Some(Kind::Scw),
orga_id.clone(),
cluster_id.clone(),
execution_id.clone(),
Some(ScwRegion::Paris.as_str().to_string()),
Stage::Infrastructure(InfrastructureStep::Create),
Transmitter::Kubernetes(cluster_id.to_string(), cluster_name.to_string()),
event: EngineEvent::Error(
EngineError::new_unknown(
EventDetails::new(
Some(Kind::Scw),
orga_id.clone(),
cluster_id.clone(),
execution_id.clone(),
Some(ScwRegion::Paris.as_str().to_string()),
Stage::Infrastructure(InfrastructureStep::Create),
Transmitter::Kubernetes(cluster_id.to_string(), cluster_name.to_string()),
),
qovery_message.to_string(),
user_message.to_string(),
Some(errors::CommandError::new(
safe_message.to_string(),
Some(raw_message.to_string()),
)),
Some(link.clone()),
Some(hint.to_string()),
),
qovery_message.to_string(),
user_message.to_string(),
Some(errors::CommandError::new(
safe_message.to_string(),
Some(raw_message.to_string()),
)),
Some(link.clone()),
Some(hint.to_string()),
)),
None,
),
description: "Error event",
},
TestCase {

View File

@@ -23,6 +23,7 @@ use crate::cloud_provider::utilities::VersionsNumber;
use crate::cloud_provider::CloudProvider;
use crate::cloud_provider::Kind as CPKind;
use crate::git;
use crate::logger::Logger;
use crate::utilities::get_image_tag;
#[derive(Clone, Debug)]
@@ -100,13 +101,14 @@ impl Environment {
context: &Context,
built_applications: &Vec<Box<dyn crate::cloud_provider::service::Application>>,
cloud_provider: &dyn CloudProvider,
logger: Box<dyn Logger>,
) -> crate::cloud_provider::environment::Environment {
let applications = self
.applications
.iter()
.map(|x| match built_applications.iter().find(|y| x.id.as_str() == y.id()) {
Some(app) => x.to_stateless_service(context, app.image().clone(), cloud_provider),
_ => x.to_stateless_service(context, x.to_image(), cloud_provider),
Some(app) => x.to_stateless_service(context, app.image().clone(), cloud_provider, logger.clone()),
_ => x.to_stateless_service(context, x.to_image(), cloud_provider, logger.clone()),
})
.filter(|x| x.is_some())
.map(|x| x.unwrap())
@@ -115,7 +117,7 @@ impl Environment {
let routers = self
.routers
.iter()
.map(|x| x.to_stateless_service(context, cloud_provider))
.map(|x| x.to_stateless_service(context, cloud_provider, logger.clone()))
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.collect::<Vec<_>>();
@@ -129,7 +131,7 @@ impl Environment {
let databases = self
.databases
.iter()
.map(|x| x.to_stateful_service(context, cloud_provider))
.map(|x| x.to_stateful_service(context, cloud_provider, logger.clone()))
.filter(|x| x.is_some())
.map(|x| x.unwrap())
.collect::<Vec<_>>();
@@ -221,6 +223,7 @@ impl Application {
context: &Context,
image: &Image,
cloud_provider: &dyn CloudProvider,
logger: Box<dyn Logger>,
) -> Option<Box<(dyn crate::cloud_provider::service::Application)>> {
let environment_variables = to_environment_variable(&self.environment_vars);
let listeners = cloud_provider.listeners().clone();
@@ -242,6 +245,7 @@ impl Application {
self.storage.iter().map(|s| s.to_aws_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger,
))),
CPKind::Do => Some(Box::new(
crate::cloud_provider::digitalocean::application::Application::new(
@@ -260,6 +264,7 @@ impl Application {
self.storage.iter().map(|s| s.to_do_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger,
),
)),
CPKind::Scw => Some(Box::new(
@@ -279,6 +284,7 @@ impl Application {
self.storage.iter().map(|s| s.to_scw_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger,
),
)),
}
@@ -289,6 +295,7 @@ impl Application {
context: &Context,
image: Image,
cloud_provider: &dyn CloudProvider,
logger: Box<dyn Logger>,
) -> Option<Box<dyn StatelessService>> {
let environment_variables = to_environment_variable(&self.environment_vars);
let listeners = cloud_provider.listeners().clone();
@@ -310,6 +317,7 @@ impl Application {
self.storage.iter().map(|s| s.to_aws_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger.clone(),
))),
CPKind::Do => Some(Box::new(
crate::cloud_provider::digitalocean::application::Application::new(
@@ -328,6 +336,7 @@ impl Application {
self.storage.iter().map(|s| s.to_do_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger.clone(),
),
)),
CPKind::Scw => Some(Box::new(
@@ -347,6 +356,7 @@ impl Application {
self.storage.iter().map(|s| s.to_scw_storage()).collect::<Vec<_>>(),
environment_variables,
listeners,
logger.clone(),
),
)),
}
@@ -583,6 +593,7 @@ impl Router {
&self,
context: &Context,
cloud_provider: &dyn CloudProvider,
logger: Box<dyn Logger>,
) -> Option<Box<dyn StatelessService>> {
let custom_domains = self
.custom_domains
@@ -616,6 +627,7 @@ impl Router {
routes,
self.sticky_sessions_enabled,
listeners,
logger,
));
Some(router)
}
@@ -631,6 +643,7 @@ impl Router {
routes,
self.sticky_sessions_enabled,
listeners,
logger,
));
Some(router)
}
@@ -645,6 +658,7 @@ impl Router {
routes,
self.sticky_sessions_enabled,
listeners,
logger,
));
Some(router)
}
@@ -701,6 +715,7 @@ impl Database {
&self,
context: &Context,
cloud_provider: &dyn CloudProvider,
logger: Box<dyn Logger>,
) -> Option<Box<dyn StatefulService>> {
let database_options = DatabaseOptions {
mode: self.mode.clone(),
@@ -734,6 +749,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -752,6 +768,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -770,6 +787,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -788,6 +806,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -809,6 +828,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
),
);
@@ -829,6 +849,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -848,6 +869,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -867,6 +889,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)
@@ -889,12 +912,16 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger.clone(),
));
Some(db)
}
Err(e) => {
error!("{}", format!("error while parsing postgres version, error: {}", e));
error!(
"{}",
format!("error while parsing postgres version, error: {}", e.message())
);
None
}
},
@@ -914,12 +941,16 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger.clone(),
));
Some(db)
}
Err(e) => {
error!("{}", format!("error while parsing mysql version, error: {}", e));
error!(
"{}",
format!("error while parsing mysql version, error: {}", e.message())
);
None
}
},
@@ -938,6 +969,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger.clone(),
));
Some(db)
@@ -957,6 +989,7 @@ impl Database {
self.database_instance_type.as_str(),
database_options,
listeners,
logger,
));
Some(db)

View File

@@ -6,8 +6,9 @@ use crate::cloud_provider::kubernetes::Kubernetes;
use crate::cloud_provider::service::{Application, Service};
use crate::container_registry::PushResult;
use crate::engine::Engine;
use crate::error::EngineError;
use crate::errors::EngineError as NewEngineError;
use crate::errors::{EngineError, Tag};
use crate::events::{EngineEvent, EventMessage};
use crate::logger::{LogLevel, Logger};
use crate::models::{
Action, Environment, EnvironmentAction, EnvironmentError, ListenersHelper, ProgressInfo, ProgressLevel,
ProgressScope,
@@ -28,7 +29,7 @@ impl<'a> Transaction<'a> {
}
}
pub fn create_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), NewEngineError> {
pub fn create_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::CreateKubernetes(kubernetes));
@@ -38,7 +39,7 @@ impl<'a> Transaction<'a> {
}
}
pub fn pause_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), NewEngineError> {
pub fn pause_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::PauseKubernetes(kubernetes));
@@ -48,7 +49,7 @@ impl<'a> Transaction<'a> {
}
}
pub fn delete_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), NewEngineError> {
pub fn delete_kubernetes(&mut self, kubernetes: &'a dyn Kubernetes) -> Result<(), EngineError> {
match kubernetes.is_valid() {
Ok(_) => {
self.steps.push(Step::DeleteKubernetes(kubernetes));
@@ -151,17 +152,12 @@ impl<'a> Transaction<'a> {
container_registry.kind()
))
);
return Err(err);
return Err(EngineError::new_from_legacy_engine_error(err));
}
};
}
Err(err) => {
warn!(
"load build app {} cache error: {}",
app.name.as_str(),
err.message.clone().unwrap_or("<no message>".to_string())
);
warn!("load build app {} cache error: {}", app.name.as_str(), err.message());
return Err(err);
}
};
@@ -173,6 +169,7 @@ impl<'a> Transaction<'a> {
&self,
environment: &Environment,
option: &DeploymentOption,
logger: Box<dyn Logger>,
) -> Result<Vec<Box<dyn Application>>, EngineError> {
// do the same for applications
let apps_to_build = environment
@@ -219,6 +216,7 @@ impl<'a> Transaction<'a> {
self.engine.context(),
&build_result.build.image,
self.engine.cloud_provider(),
logger.clone(),
) {
applications.push(app)
}
@@ -252,7 +250,7 @@ impl<'a> Transaction<'a> {
Ok(tuple) => results.push(tuple),
Err(err) => {
error!("error pushing docker image {:?}", err);
return Err(err);
return Err(EngineError::new_from_legacy_engine_error(err));
}
}
}
@@ -260,10 +258,14 @@ impl<'a> Transaction<'a> {
Ok(results)
}
fn check_environment(&self, environment: &crate::cloud_provider::environment::Environment) -> TransactionResult {
fn check_environment(
&self,
environment: &crate::cloud_provider::environment::Environment,
logger: Box<dyn Logger>,
) -> TransactionResult {
if let Err(engine_error) = environment.is_valid() {
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_error);
return match self.rollback() {
return match self.rollback(logger) {
Ok(_) => TransactionResult::Rollback(engine_error),
Err(err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", err);
@@ -275,25 +277,25 @@ impl<'a> Transaction<'a> {
TransactionResult::Ok
}
pub fn rollback(&self) -> Result<(), RollbackError> {
pub fn rollback(&self, logger: Box<dyn Logger>) -> Result<(), RollbackError> {
for step in self.executed_steps.iter() {
match step {
Step::CreateKubernetes(kubernetes) => {
// revert kubernetes creation
if let Err(err) = kubernetes.on_create_error() {
return Err(RollbackError::CommitError(err.to_legacy_engine_error()));
return Err(RollbackError::CommitError(err));
};
}
Step::DeleteKubernetes(kubernetes) => {
// revert kubernetes deletion
if let Err(err) = kubernetes.on_delete_error() {
return Err(RollbackError::CommitError(err.to_legacy_engine_error()));
return Err(RollbackError::CommitError(err));
};
}
Step::PauseKubernetes(kubernetes) => {
// revert pause
if let Err(err) = kubernetes.on_pause_error() {
return Err(RollbackError::CommitError(err.to_legacy_engine_error()));
return Err(RollbackError::CommitError(err));
};
}
Step::BuildEnvironment(_environment_action, _option) => {
@@ -301,13 +303,13 @@ impl<'a> Transaction<'a> {
}
Step::DeployEnvironment(kubernetes, environment_action) => {
// revert environment deployment
self.rollback_environment(*kubernetes, *environment_action)?;
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
}
Step::PauseEnvironment(kubernetes, environment_action) => {
self.rollback_environment(*kubernetes, *environment_action)?;
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
}
Step::DeleteEnvironment(kubernetes, environment_action) => {
self.rollback_environment(*kubernetes, *environment_action)?;
self.rollback_environment(*kubernetes, *environment_action, logger.clone())?;
}
}
}
@@ -321,21 +323,29 @@ impl<'a> Transaction<'a> {
&self,
kubernetes: &dyn Kubernetes,
environment_action: &EnvironmentAction,
logger: Box<dyn Logger>,
) -> Result<(), RollbackError> {
let qe_environment = |environment: &Environment| {
let mut _applications = Vec::with_capacity(environment.applications.len());
for application in environment.applications.iter() {
let build = application.to_build();
if let Some(x) =
application.to_application(self.engine.context(), &build.image, self.engine.cloud_provider())
{
if let Some(x) = application.to_application(
self.engine.context(),
&build.image,
self.engine.cloud_provider(),
logger.clone(),
) {
_applications.push(x)
}
}
let qe_environment =
environment.to_qe_environment(self.engine.context(), &_applications, self.engine.cloud_provider());
let qe_environment = environment.to_qe_environment(
self.engine.context(),
&_applications,
self.engine.cloud_provider(),
logger.clone(),
);
qe_environment
};
@@ -354,7 +364,7 @@ impl<'a> Transaction<'a> {
let _ = match action {
Ok(_) => {}
Err(err) => return Err(RollbackError::CommitError(err.to_legacy_engine_error())),
Err(err) => return Err(RollbackError::CommitError(err)),
};
Err(RollbackError::NoFailoverEnvironment)
@@ -362,7 +372,7 @@ impl<'a> Transaction<'a> {
}
}
pub fn commit(mut self) -> TransactionResult {
pub fn commit(mut self, logger: Box<dyn Logger>) -> TransactionResult {
let mut applications_by_environment: HashMap<&Environment, Vec<Box<dyn Application>>> = HashMap::new();
for step in self.steps.iter() {
@@ -375,7 +385,8 @@ impl<'a> Transaction<'a> {
match self.commit_infrastructure(
*kubernetes,
Action::Create,
kubernetes.on_create().map_err(|e| e.to_legacy_engine_error()),
kubernetes.on_create(),
logger.clone(),
) {
TransactionResult::Ok => {}
err => {
@@ -389,7 +400,8 @@ impl<'a> Transaction<'a> {
match self.commit_infrastructure(
*kubernetes,
Action::Delete,
kubernetes.on_delete().map_err(|e| e.to_legacy_engine_error()),
kubernetes.on_delete(),
logger.clone(),
) {
TransactionResult::Ok => {}
err => {
@@ -400,11 +412,8 @@ impl<'a> Transaction<'a> {
}
Step::PauseKubernetes(kubernetes) => {
// pause kubernetes
match self.commit_infrastructure(
*kubernetes,
Action::Pause,
kubernetes.on_pause().map_err(|e| e.to_legacy_engine_error()),
) {
match self.commit_infrastructure(*kubernetes, Action::Pause, kubernetes.on_pause(), logger.clone())
{
TransactionResult::Ok => {}
err => {
error!("Error while pausing infrastructure: {:?}", err);
@@ -418,11 +427,21 @@ impl<'a> Transaction<'a> {
EnvironmentAction::Environment(te) => te,
};
let applications_builds = match self.build_applications(target_environment, option) {
let applications_builds = match self.build_applications(target_environment, option, logger.clone())
{
Ok(apps) => apps,
Err(engine_err) => {
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_err);
return if engine_err.is_cancel() {
logger.log(
LogLevel::Error,
EngineEvent::Error(
engine_err.clone(),
Some(EventMessage::new_from_safe(
"ROLLBACK STARTED! an error occurred".to_string(),
)),
),
);
return if engine_err.tag() == &Tag::TaskCancellationRequested {
TransactionResult::Canceled
} else {
TransactionResult::Rollback(engine_err)
@@ -442,7 +461,7 @@ impl<'a> Transaction<'a> {
}
Err(engine_err) => {
warn!("ROLLBACK STARTED! an error occurred {:?}", engine_err);
return match self.rollback() {
return match self.rollback(logger.clone()) {
Ok(_) => TransactionResult::Rollback(engine_err),
Err(err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", err);
@@ -460,11 +479,8 @@ impl<'a> Transaction<'a> {
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| {
kubernetes
.deploy_environment(qe_env)
.map_err(|e| e.to_legacy_engine_error())
},
|qe_env| kubernetes.deploy_environment(qe_env),
logger.clone(),
) {
TransactionResult::Ok => {}
err => {
@@ -479,11 +495,8 @@ impl<'a> Transaction<'a> {
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| {
kubernetes
.pause_environment(qe_env)
.map_err(|e| e.to_legacy_engine_error())
},
|qe_env| kubernetes.pause_environment(qe_env),
logger.clone(),
) {
TransactionResult::Ok => {}
err => {
@@ -498,11 +511,8 @@ impl<'a> Transaction<'a> {
*kubernetes,
*environment_action,
&applications_by_environment,
|qe_env| {
kubernetes
.delete_environment(qe_env)
.map_err(|e| e.to_legacy_engine_error())
},
|qe_env| kubernetes.delete_environment(qe_env),
logger.clone(),
) {
TransactionResult::Ok => {}
err => {
@@ -522,6 +532,7 @@ impl<'a> Transaction<'a> {
kubernetes: &dyn Kubernetes,
action: Action,
result: Result<(), EngineError>,
logger: Box<dyn Logger>,
) -> TransactionResult {
// send back the right progress status
fn send_progress(lh: &ListenersHelper, action: Action, execution_id: &str, is_error: bool) {
@@ -563,7 +574,7 @@ impl<'a> Transaction<'a> {
match result {
Err(err) => {
warn!("infrastructure ROLLBACK STARTED! an error occurred {:?}", err);
match self.rollback() {
match self.rollback(logger) {
Ok(_) => {
// an error occurred on infrastructure deployment BUT rolledback is OK
send_progress(&lh, action, execution_id, true);
@@ -591,6 +602,7 @@ impl<'a> Transaction<'a> {
environment_action: &EnvironmentAction,
applications_by_environment: &HashMap<&Environment, Vec<Box<dyn Application>>>,
action_fn: F,
logger: Box<dyn Logger>,
) -> TransactionResult
where
F: Fn(&crate::cloud_provider::environment::Environment) -> Result<(), EngineError>,
@@ -609,9 +621,10 @@ impl<'a> Transaction<'a> {
self.engine.context(),
built_applications,
kubernetes.cloud_provider(),
logger.clone(),
);
let _ = match self.check_environment(&qe_environment) {
let _ = match self.check_environment(&qe_environment, logger.clone()) {
TransactionResult::Ok => {}
err => return err, // which it means that an error occurred
};
@@ -661,7 +674,7 @@ impl<'a> Transaction<'a> {
let _ = match action_fn(&qe_environment) {
Err(err) => {
let rollback_result = match self.rollback() {
let rollback_result = match self.rollback(logger) {
Ok(_) => TransactionResult::Rollback(err),
Err(rollback_err) => {
error!("ROLLBACK FAILED! fatal error: {:?}", rollback_err);

View File

@@ -66,7 +66,7 @@ impl Cluster<AWS, Options> for AWS {
let container_registry = Box::new(container_registry_ecr(context));
// use LocalDocker
let build_platform = Box::new(build_platform_local_docker(context));
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use AWS
let cloud_provider = AWS::cloud_provider(context);

View File

@@ -120,7 +120,7 @@ impl Infrastructure for Environment {
},
);
tx.commit()
tx.commit(logger.clone())
}
fn pause_environment(
@@ -151,7 +151,7 @@ impl Infrastructure for Environment {
let _ = tx.pause_environment(k.as_ref(), &environment_action);
tx.commit()
tx.commit(logger.clone())
}
fn delete_environment(
@@ -182,7 +182,7 @@ impl Infrastructure for Environment {
let _ = tx.delete_environment(k.as_ref(), &environment_action);
tx.commit()
tx.commit(logger.clone())
}
}
@@ -990,6 +990,7 @@ pub fn test_db(
let context_for_delete = context.clone_not_same_execution_id();
let app_id = generate_id();
let database_username = "superuser".to_string();
let database_password = generate_password(true);
let db_kind_str = db_kind.name().to_string();
@@ -1384,7 +1385,7 @@ pub fn cluster_test(
if let Err(err) = deploy_tx.create_kubernetes(kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(deploy_tx.commit(), TransactionResult::Ok));
assert!(matches!(deploy_tx.commit(logger.clone()), TransactionResult::Ok));
// Deploy env if any
if let Some(env) = environment_to_deploy {
@@ -1395,7 +1396,7 @@ pub fn cluster_test(
panic!("{:?}", err)
}
assert!(matches!(deploy_env_tx.commit(), TransactionResult::Ok));
assert!(matches!(deploy_env_tx.commit(logger.clone()), TransactionResult::Ok));
}
if let Err(err) = metrics_server_test(
@@ -1417,14 +1418,14 @@ pub fn cluster_test(
if let Err(err) = pause_tx.pause_kubernetes(kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(pause_tx.commit(), TransactionResult::Ok));
assert!(matches!(pause_tx.commit(logger.clone()), TransactionResult::Ok));
// Resume
if let Err(err) = resume_tx.create_kubernetes(kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(resume_tx.commit(), TransactionResult::Ok));
assert!(matches!(resume_tx.commit(logger.clone()), TransactionResult::Ok));
if let Err(err) = metrics_server_test(
kubernetes
@@ -1458,7 +1459,7 @@ pub fn cluster_test(
if let Err(err) = upgrade_tx.create_kubernetes(upgraded_kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(upgrade_tx.commit(), TransactionResult::Ok));
assert!(matches!(upgrade_tx.commit(logger.clone()), TransactionResult::Ok));
if let Err(err) = metrics_server_test(
upgraded_kubernetes
@@ -1477,7 +1478,7 @@ pub fn cluster_test(
if let Err(err) = delete_tx.delete_kubernetes(upgraded_kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(delete_tx.commit(), TransactionResult::Ok));
assert!(matches!(delete_tx.commit(logger.clone()), TransactionResult::Ok));
return test_name.to_string();
}
@@ -1491,14 +1492,14 @@ pub fn cluster_test(
if let Err(err) = destroy_env_tx.delete_environment(kubernetes.as_ref(), env) {
panic!("{:?}", err)
}
assert!(matches!(destroy_env_tx.commit(), TransactionResult::Ok));
assert!(matches!(destroy_env_tx.commit(logger.clone()), TransactionResult::Ok));
}
// Delete
if let Err(err) = delete_tx.delete_kubernetes(kubernetes.as_ref()) {
panic!("{:?}", err)
}
assert!(matches!(delete_tx.commit(), TransactionResult::Ok));
assert!(matches!(delete_tx.commit(logger.clone()), TransactionResult::Ok));
test_name.to_string()
}

View File

@@ -43,7 +43,7 @@ impl Cluster<DO, DoksOptions> for DO {
// use DigitalOcean Container Registry
let container_registry = Box::new(container_registry_digital_ocean(context));
// use LocalDocker
let build_platform = Box::new(build_platform_local_docker(context));
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use Digital Ocean
let cloud_provider = DO::cloud_provider(context);

View File

@@ -1,3 +1,5 @@
#![allow(deprecated)]
#[macro_use]
extern crate maplit;

View File

@@ -63,7 +63,7 @@ impl Cluster<Scaleway, KapsuleOptions> for Scaleway {
let container_registry = Box::new(container_registry_scw(context));
// use LocalDocker
let build_platform = Box::new(build_platform_local_docker(context));
let build_platform = Box::new(build_platform_local_docker(context, logger.clone()));
// use Scaleway
let cloud_provider = Scaleway::cloud_provider(context);

View File

@@ -362,8 +362,8 @@ impl FuncTestsSecrets {
}
}
pub fn build_platform_local_docker(context: &Context) -> LocalDocker {
LocalDocker::new(context.clone(), "oxqlm3r99vwcmvuj", "qovery-local-docker")
pub fn build_platform_local_docker(context: &Context, logger: Box<dyn Logger>) -> LocalDocker {
LocalDocker::new(context.clone(), "oxqlm3r99vwcmvuj", "qovery-local-docker", logger)
}
pub fn init() -> Instant {

View File

@@ -105,7 +105,7 @@ fn test_build_cache() {
);
let ecr = container_registry_ecr(&context);
let local_docker = build_platform_local_docker(&context);
let local_docker = build_platform_local_docker(&context, logger());
let app = environment.applications.first().unwrap();
let image = app.to_image();

View File

@@ -108,7 +108,7 @@ fn test_build_cache() {
);
let docr = container_registry_digital_ocean(&context);
let local_docker = build_platform_local_docker(&context);
let local_docker = build_platform_local_docker(&context, logger());
let app = environment.applications.first().unwrap();
let image = app.to_image();

View File

@@ -66,7 +66,7 @@ fn create_digitalocean_kubernetes_doks_test_cluster() {
if let Err(err) = tx.create_kubernetes(&kubernetes) {
panic!("{:?}", err)
}
let ret = tx.commit();
let ret = tx.commit(logger.clone());
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()
@@ -129,7 +129,7 @@ fn destroy_digitalocean_kubernetes_doks_test_cluster() {
if let Err(err) = tx.delete_kubernetes(&kubernetes) {
panic!("{:?}", err)
}
let ret = tx.commit();
let ret = tx.commit(logger.clone());
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()

View File

@@ -112,7 +112,7 @@ fn test_build_cache() {
);
let scr = container_registry_scw(&context);
let local_docker = build_platform_local_docker(&context);
let local_docker = build_platform_local_docker(&context, logger());
let app = environment.applications.first().unwrap();
let image = app.to_image();

View File

@@ -66,7 +66,7 @@ fn create_scaleway_kubernetes_kapsule_test_cluster() {
panic!("{:?}", err)
}
assert!(matches!(tx.commit(), TransactionResult::Ok));
assert!(matches!(tx.commit(logger.clone()), TransactionResult::Ok));
test_name.to_string()
});
@@ -127,7 +127,7 @@ fn destroy_scaleway_kubernetes_kapsule_test_cluster() {
if let Err(err) = tx.delete_kubernetes(&kubernetes) {
panic!("{:?}", err)
}
let ret = tx.commit();
let ret = tx.commit(logger.clone());
assert!(matches!(ret, TransactionResult::Ok));
test_name.to_string()