mirror of
https://github.com/jlengrand/engine.git
synced 2026-03-10 08:11:21 +00:00
wip
This commit is contained in:
@@ -7,9 +7,11 @@ use sysinfo::{Disk, DiskExt, SystemExt};
|
||||
|
||||
use crate::build_platform::{docker, Build, BuildPlatform, BuildResult, CacheResult, Credentials, Image, Kind};
|
||||
use crate::cmd::utilities::QoveryCommand;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope, SimpleError, SimpleErrorKind};
|
||||
use crate::errors::{CommandError, EngineError};
|
||||
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,
|
||||
};
|
||||
@@ -25,20 +27,22 @@ const BUILDPACKS_BUILDERS: [&str; 1] = [
|
||||
];
|
||||
|
||||
/// use Docker in local
|
||||
pub struct LocalDocker {
|
||||
pub struct LocalDocker<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
name: String,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl LocalDocker {
|
||||
pub fn new(context: Context, id: &str, name: &str) -> Self {
|
||||
impl<'a> LocalDocker<'a> {
|
||||
pub fn new(context: Context, id: &str, name: &str, logger: &'a 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,9 +110,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));
|
||||
Err(engine_error)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,7 +143,10 @@ impl LocalDocker {
|
||||
let exit_status = cmd.exec_with_timeout(
|
||||
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 {
|
||||
@@ -141,7 +158,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 {
|
||||
@@ -156,15 +176,10 @@ impl LocalDocker {
|
||||
|
||||
match exit_status {
|
||||
Ok(_) => Ok(BuildResult { build }),
|
||||
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),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -181,8 +196,8 @@ impl LocalDocker {
|
||||
|
||||
let args = self.context.docker_build_options();
|
||||
|
||||
let mut exit_status: Result<(), SimpleError> =
|
||||
Err(SimpleError::new(SimpleErrorKind::Other, Some("no builder names")));
|
||||
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 {
|
||||
@@ -245,12 +260,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);
|
||||
}
|
||||
}
|
||||
@@ -272,7 +288,10 @@ impl LocalDocker {
|
||||
.exec_with_timeout(
|
||||
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 {
|
||||
@@ -284,7 +303,13 @@ 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 {
|
||||
@@ -296,7 +321,7 @@ impl LocalDocker {
|
||||
));
|
||||
},
|
||||
)
|
||||
.map_err(|err| SimpleError::new(SimpleErrorKind::Other, Some(format!("{:?}", err))));
|
||||
.map_err(|err| CommandError::new(format!("{:?}", err), None));
|
||||
|
||||
if exit_status.is_ok() {
|
||||
// quit now if the builder successfully build the app
|
||||
@@ -307,19 +332,17 @@ impl LocalDocker {
|
||||
match exit_status {
|
||||
Ok(_) => Ok(BuildResult { build }),
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +353,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),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,11 +381,17 @@ impl BuildPlatform for LocalDocker {
|
||||
|
||||
fn is_valid(&self) -> Result<(), EngineError> {
|
||||
if !crate::cmd::utilities::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::utilities::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(())
|
||||
@@ -369,9 +403,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,
|
||||
@@ -449,8 +483,17 @@ impl BuildPlatform for LocalDocker {
|
||||
"Error while cloning repository {}. Error: {:?}",
|
||||
&build.git_repository.url, clone_error
|
||||
);
|
||||
error!("{}", message);
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
|
||||
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),
|
||||
);
|
||||
|
||||
self.logger
|
||||
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
let mut disable_build_cache = false;
|
||||
@@ -481,9 +524,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;
|
||||
};
|
||||
@@ -508,7 +562,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(),
|
||||
@@ -521,14 +574,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(
|
||||
@@ -561,7 +613,14 @@ impl BuildPlatform for LocalDocker {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -578,7 +637,11 @@ impl BuildPlatform for LocalDocker {
|
||||
));
|
||||
|
||||
// FIXME
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message))
|
||||
Err(EngineError::new_not_implemented_error(event_details))
|
||||
}
|
||||
|
||||
fn logger(&self) -> &dyn Logger {
|
||||
self.logger
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,38 +655,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"],
|
||||
@@ -631,20 +710,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(())
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
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 std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
@@ -12,7 +14,7 @@ use std::path::Path;
|
||||
pub mod docker;
|
||||
pub mod local_docker;
|
||||
|
||||
pub trait BuildPlatform: Listen {
|
||||
pub trait BuildPlatform: ToTransmitter {
|
||||
fn context(&self) -> &Context;
|
||||
fn kind(&self) -> Kind;
|
||||
fn id(&self) -> &str;
|
||||
@@ -24,15 +26,17 @@ pub trait BuildPlatform: Listen {
|
||||
fn has_cache(&self, build: &Build) -> Result<CacheResult, EngineError>;
|
||||
fn build(&self, build: Build, force_build: 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) -> &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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -44,7 +48,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>,
|
||||
{
|
||||
@@ -59,7 +63,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),
|
||||
|
||||
@@ -91,7 +91,7 @@ impl<'a> Application<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> crate::cloud_provider::service::Application<'a> for Application<'a> {
|
||||
impl<'a> crate::cloud_provider::service::Application for Application<'a> {
|
||||
fn image(&self) -> &Image {
|
||||
&self.image
|
||||
}
|
||||
@@ -123,7 +123,7 @@ impl<'a> Helm for Application<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> StatelessService<'a> for Application<'a> {}
|
||||
impl<'a> StatelessService for Application<'a> {}
|
||||
|
||||
impl<'a> ToTransmitter for Application<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
@@ -303,7 +303,7 @@ impl<'a> Service for Application<'a> {
|
||||
}
|
||||
|
||||
fn logger(&self) -> &dyn Logger {
|
||||
todo!()
|
||||
self.logger
|
||||
}
|
||||
|
||||
fn selector(&self) -> Option<String> {
|
||||
|
||||
@@ -185,7 +185,9 @@ impl<'a> Service for MongoDB<'a> {
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
let version = self.matching_correct_version(self.is_managed_service(), event_details.clone())?;
|
||||
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() {
|
||||
@@ -367,12 +369,12 @@ impl<'a> Delete for MongoDB<'a> {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details,
|
||||
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(), self.logger)
|
||||
delete_stateful_service(target, self, event_details, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -92,13 +92,13 @@ impl<'a> MySQL<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for MySQL {
|
||||
impl<'a> StatefulService for MySQL<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for MySQL {
|
||||
impl<'a> ToTransmitter for MySQL<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -108,7 +108,7 @@ impl ToTransmitter for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for MySQL {
|
||||
impl<'a> Service for MySQL<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -190,7 +190,9 @@ impl Service for MySQL {
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
let version = &self.matching_correct_version(self.is_managed_service(), event_details.clone())?;
|
||||
let version = &self
|
||||
.matching_correct_version(self.is_managed_service(), event_details.clone())?
|
||||
.matched_version();
|
||||
context.insert("version", &version);
|
||||
|
||||
if self.is_managed_service() {
|
||||
@@ -261,9 +263,9 @@ impl Service for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MySQL {}
|
||||
impl<'a> Database for MySQL<'a> {}
|
||||
|
||||
impl Helm for MySQL {
|
||||
impl<'a> Helm for MySQL<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -285,7 +287,7 @@ impl Helm for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for MySQL {
|
||||
impl<'a> Terraform for MySQL<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/aws/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -295,7 +297,7 @@ impl Terraform for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for MySQL {
|
||||
impl<'a> Create for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -304,12 +306,12 @@ impl Create for MySQL {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details,
|
||||
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(), self.logger)
|
||||
deploy_stateful_service(target, self, event_details, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -333,7 +335,7 @@ impl Create for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for MySQL {
|
||||
impl<'a> Pause for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
|
||||
@@ -370,7 +372,7 @@ impl Pause for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for MySQL {
|
||||
impl<'a> Delete for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -384,7 +386,7 @@ impl Delete for MySQL {
|
||||
);
|
||||
|
||||
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
|
||||
delete_stateful_service(target, self, event_details, self.logger)
|
||||
delete_stateful_service(target, self, event_details, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -407,7 +409,7 @@ impl Delete for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for MySQL {
|
||||
impl<'a> Listen for MySQL<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -190,7 +190,9 @@ impl<'a> Service for PostgreSQL<'a> {
|
||||
|
||||
context.insert("namespace", environment.namespace());
|
||||
|
||||
let version = self.matching_correct_version(self.is_managed_service(), event_details.clone())?;
|
||||
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() {
|
||||
@@ -294,7 +296,7 @@ impl<'a> Create for PostgreSQL<'a> {
|
||||
);
|
||||
|
||||
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
|
||||
deploy_stateful_service(target, self, event_details, self.logger)
|
||||
deploy_stateful_service(target, self, event_details, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -370,7 +372,7 @@ impl<'a> Delete for PostgreSQL<'a> {
|
||||
);
|
||||
|
||||
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Delete, || {
|
||||
delete_stateful_service(target, self, event_details, self.logger)
|
||||
delete_stateful_service(target, self, event_details, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -386,7 +388,7 @@ impl<'a> Delete for PostgreSQL<'a> {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details.clone(),
|
||||
event_details,
|
||||
self.logger(),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use crate::cloud_provider::utilities::VersionsNumber;
|
||||
use crate::error::StringError;
|
||||
use crate::errors::CommandError;
|
||||
use crate::models::DatabaseKind;
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -5,9 +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::models::{Context, Listen, Listener, Listeners};
|
||||
use crate::errors::EngineError;
|
||||
use crate::events::{EventDetails, GeneralStep, Stage};
|
||||
use crate::models::{Context, Listen, Listener, Listeners, QoveryIdentifier};
|
||||
use crate::runtime::block_on;
|
||||
|
||||
pub mod application;
|
||||
@@ -107,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,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -149,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 {
|
||||
|
||||
@@ -192,7 +192,15 @@ impl<'a> Service for Router<'a> {
|
||||
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("unable to get external_ingress_hostname_default - what's wrong? This must never happened".to_string())));
|
||||
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(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
@@ -202,9 +210,7 @@ impl<'a> Service for Router<'a> {
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(
|
||||
"can't fetch kubernetes config file - what's wrong? This must never happened".to_string(),
|
||||
),
|
||||
EventMessage::new_from_safe("Can't fetch external ingress hostname.".to_string()),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -14,13 +15,14 @@ use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::cmd::helm::Timeout;
|
||||
use crate::cmd::kubectl::ScalingKind::{Deployment, Statefulset};
|
||||
use crate::errors::{CommandError, EngineError};
|
||||
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
|
||||
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;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub struct Application {
|
||||
pub struct Application<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -36,9 +38,10 @@ pub struct Application {
|
||||
storage: Vec<Storage<StorageType>>,
|
||||
environment_variables: Vec<EnvironmentVariable>,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
impl<'a> Application<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -55,6 +58,7 @@ impl Application {
|
||||
storage: Vec<Storage<StorageType>>,
|
||||
environment_variables: Vec<EnvironmentVariable>,
|
||||
listeners: Listeners,
|
||||
logger: &dyn Logger,
|
||||
) -> Self {
|
||||
Application {
|
||||
context,
|
||||
@@ -72,6 +76,7 @@ impl Application {
|
||||
storage,
|
||||
environment_variables,
|
||||
listeners,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +93,7 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Application for Application {
|
||||
impl<'a> crate::cloud_provider::service::Application for Application<'a> {
|
||||
fn image(&self) -> &Image {
|
||||
&self.image
|
||||
}
|
||||
@@ -98,7 +103,7 @@ impl crate::cloud_provider::service::Application for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl Helm for Application {
|
||||
impl<'a> Helm for Application<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -120,15 +125,15 @@ impl Helm for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatelessService for Application {}
|
||||
impl<'a> StatelessService for Application<'a> {}
|
||||
|
||||
impl ToTransmitter for Application {
|
||||
impl<'a> ToTransmitter for Application<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Application(self.id().to_string(), self.name().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Application {
|
||||
impl<'a> Service for Application<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -193,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);
|
||||
@@ -204,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());
|
||||
}
|
||||
}
|
||||
@@ -218,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,
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -286,12 +302,16 @@ impl Service for Application {
|
||||
Ok(context)
|
||||
}
|
||||
|
||||
fn logger(&self) -> &dyn Logger {
|
||||
self.logger
|
||||
}
|
||||
|
||||
fn selector(&self) -> Option<String> {
|
||||
Some(format!("appId={}", self.id))
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for Application {
|
||||
impl<'a> Create for Application<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -300,10 +320,12 @@ impl Create 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::Create, || {
|
||||
deploy_user_stateless_service(target, self, event_details.clone(), self.logger())
|
||||
deploy_user_stateless_service(target, self, event_details)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -319,15 +341,17 @@ impl Create for Application {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details,
|
||||
self.logger(),
|
||||
);
|
||||
|
||||
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Create, || {
|
||||
deploy_stateless_service_error(target, self, event_details.clone(), self.logger())
|
||||
deploy_stateless_service_error(target, self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Application {
|
||||
impl<'a> Pause for Application<'a> {
|
||||
#[named]
|
||||
fn on_pause(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Pause));
|
||||
@@ -336,6 +360,8 @@ impl Pause for Application {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details,
|
||||
self.logger(),
|
||||
);
|
||||
|
||||
send_progress_on_long_task(self, crate::cloud_provider::service::Action::Pause, || {
|
||||
@@ -354,18 +380,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Application {
|
||||
impl<'a> Delete for Application<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -374,10 +403,12 @@ 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, || {
|
||||
delete_stateless_service(target, self, false, event_details.clone())
|
||||
delete_stateless_service(target, self, false, event_details)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -393,15 +424,17 @@ 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, || {
|
||||
delete_stateless_service(target, self, true, event_details.clone())
|
||||
delete_stateless_service(target, self, true, event_details)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Application {
|
||||
impl<'a> Listen for Application<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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;
|
||||
|
||||
pub struct MongoDB {
|
||||
pub struct MongoDB<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct MongoDB {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl MongoDB {
|
||||
impl<'a> MongoDB<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl MongoDB {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
MongoDB {
|
||||
context,
|
||||
@@ -58,11 +61,16 @@ 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,
|
||||
)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +82,13 @@ impl MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for MongoDB {
|
||||
impl<'a> StatefulService for MongoDB<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for MongoDB {
|
||||
impl<'a> ToTransmitter for MongoDB<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +98,7 @@ impl ToTransmitter for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for MongoDB {
|
||||
impl<'a> Service for MongoDB<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,17 +160,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 +177,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,18 +222,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MongoDB {}
|
||||
impl<'a> Database for MongoDB<'a> {}
|
||||
|
||||
impl Helm for MongoDB {
|
||||
impl<'a> Helm for MongoDB<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -251,7 +251,7 @@ impl Helm for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for MongoDB {
|
||||
impl<'a> Terraform for MongoDB<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/digitalocean/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -261,7 +261,7 @@ impl Terraform for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for MongoDB {
|
||||
impl<'a> Create for MongoDB<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -270,10 +270,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -283,25 +285,31 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for MongoDB {
|
||||
impl<'a> Pause for MongoDB<'a> {
|
||||
#[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,18 +323,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for MongoDB {
|
||||
impl<'a> Delete for MongoDB<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -335,10 +346,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -348,18 +361,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for MongoDB {
|
||||
impl<'a> Listen for MongoDB<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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;
|
||||
|
||||
pub struct MySQL {
|
||||
pub struct MySQL<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct MySQL {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl MySQL {
|
||||
impl<'a> MySQL<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl MySQL {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
@@ -58,11 +61,12 @@ 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)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +78,13 @@ impl MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for MySQL {
|
||||
impl<'a> StatefulService for MySQL<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for MySQL {
|
||||
impl<'a> ToTransmitter for MySQL<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +94,7 @@ impl ToTransmitter for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for MySQL {
|
||||
impl<'a> Service for MySQL<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,17 +156,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 +173,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() {
|
||||
@@ -217,19 +217,11 @@ impl Service for MySQL {
|
||||
fn selector(&self) -> Option<String> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MySQL {}
|
||||
impl<'a> Database for MySQL<'a> {}
|
||||
|
||||
impl Helm for MySQL {
|
||||
impl<'a> Helm for MySQL<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -251,7 +243,7 @@ impl Helm for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for MySQL {
|
||||
impl<'a> Terraform for MySQL<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/digitalocean/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -261,7 +253,7 @@ impl Terraform for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for MySQL {
|
||||
impl<'a> Create for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -270,12 +262,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -286,25 +280,31 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for MySQL {
|
||||
impl<'a> Pause for MySQL<'a> {
|
||||
#[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,18 +318,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for MySQL {
|
||||
impl<'a> Delete for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -338,12 +341,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -353,17 +358,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for MySQL {
|
||||
impl<'a> Listen for MySQL<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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;
|
||||
|
||||
pub struct PostgreSQL {
|
||||
pub struct PostgreSQL<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct PostgreSQL {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl PostgreSQL {
|
||||
impl<'a> PostgreSQL<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl PostgreSQL {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &dyn Logger,
|
||||
) -> Self {
|
||||
PostgreSQL {
|
||||
context,
|
||||
@@ -58,11 +61,12 @@ 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)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +78,13 @@ impl PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for PostgreSQL {
|
||||
impl<'a> StatefulService for PostgreSQL<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for PostgreSQL {
|
||||
impl<'a> ToTransmitter for PostgreSQL<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +94,7 @@ impl ToTransmitter for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for PostgreSQL {
|
||||
impl<'a> Service for PostgreSQL<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,17 +156,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 +173,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,18 +220,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for PostgreSQL {}
|
||||
impl<'a> Database for PostgreSQL<'a> {}
|
||||
|
||||
impl Helm for PostgreSQL {
|
||||
impl<'a> Helm for PostgreSQL<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -253,7 +249,7 @@ impl Helm for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for PostgreSQL {
|
||||
impl<'a> Terraform for PostgreSQL<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/digitalocean/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -263,7 +259,7 @@ impl Terraform for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for PostgreSQL {
|
||||
impl<'a> Create for PostgreSQL<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -272,12 +268,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -287,25 +285,31 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for PostgreSQL {
|
||||
impl<'a> Pause for PostgreSQL<'a> {
|
||||
#[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,18 +323,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for PostgreSQL {
|
||||
impl<'a> Delete for PostgreSQL<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -339,12 +346,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -354,18 +363,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for PostgreSQL {
|
||||
impl<'a> Listen for PostgreSQL<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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;
|
||||
|
||||
pub struct Redis {
|
||||
pub struct Redis<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct Redis {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Redis {
|
||||
impl<'a> Redis<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl Redis {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
@@ -58,11 +61,12 @@ 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)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +78,13 @@ impl Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for Redis {
|
||||
impl<'a> StatefulService for Redis<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for Redis {
|
||||
impl<'a> ToTransmitter for Redis<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +94,7 @@ impl ToTransmitter for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Redis {
|
||||
impl<'a> Service for Redis<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,17 +156,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 +171,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,18 +217,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for Redis {}
|
||||
impl<'a> Database for Redis<'a> {}
|
||||
|
||||
impl Helm for Redis {
|
||||
impl<'a> Helm for Redis<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -250,7 +246,7 @@ impl Helm for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for Redis {
|
||||
impl<'a> Terraform for Redis<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/digitalocean/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -260,7 +256,7 @@ impl Terraform for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for Redis {
|
||||
impl<'a> Create for Redis<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -269,12 +265,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -285,24 +283,30 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Redis {
|
||||
impl<'a> Pause for Redis<'a> {
|
||||
#[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,17 +320,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Redis {
|
||||
impl<'a> Delete for Redis<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -335,12 +342,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, self.logger())),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -350,17 +359,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Redis {
|
||||
impl<'a> Listen for Redis<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
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 {
|
||||
|
||||
@@ -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(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,18 +3,18 @@ use tera::Context as TeraContext;
|
||||
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::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;
|
||||
|
||||
pub struct Router {
|
||||
pub struct Router<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -24,9 +24,10 @@ pub struct Router {
|
||||
sticky_sessions_enabled: bool,
|
||||
routes: Vec<Route>,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
impl<'a> Router<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -37,6 +38,7 @@ impl Router {
|
||||
routes: Vec<Route>,
|
||||
sticky_sessions_enabled: bool,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Router {
|
||||
context,
|
||||
@@ -48,6 +50,7 @@ impl Router {
|
||||
sticky_sessions_enabled,
|
||||
routes,
|
||||
listeners,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Router {
|
||||
impl<'a> Service for Router<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -122,6 +125,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);
|
||||
@@ -185,34 +189,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());
|
||||
@@ -242,12 +256,12 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Router for Router {
|
||||
impl<'a> crate::cloud_provider::service::Router for Router<'a> {
|
||||
fn domains(&self) -> Vec<&str> {
|
||||
let mut _domains = vec![self.default_domain.as_str()];
|
||||
|
||||
@@ -263,7 +277,7 @@ impl crate::cloud_provider::service::Router for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Helm for Router {
|
||||
impl<'a> Helm for Router<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -288,7 +302,7 @@ impl Helm for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Router {
|
||||
impl<'a> Listen for Router<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
@@ -298,34 +312,33 @@ impl Listen for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatelessService for Router {}
|
||||
impl<'a> StatelessService for Router<'a> {}
|
||||
|
||||
impl ToTransmitter for Router {
|
||||
impl<'a> ToTransmitter for Router<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Router(self.id().to_string(), self.name().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for Router {
|
||||
impl<'a> Create for Router<'a> {
|
||||
#[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
|
||||
@@ -335,13 +348,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
|
||||
@@ -355,19 +367,17 @@ impl Create for Router {
|
||||
kubernetes.cloud_provider().credentials_environment_variables(),
|
||||
self.service_type(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
|
||||
})?;
|
||||
.map_err(|e| EngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error())?;
|
||||
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, "Router has failed to be deployed".into()));
|
||||
return Err(EngineError::new_router_failed_to_deploy(event_details.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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()?;
|
||||
@@ -384,9 +394,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()),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -397,11 +417,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, || {
|
||||
@@ -410,14 +433,17 @@ impl Create for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Router {
|
||||
impl<'a> Pause for Router<'a> {
|
||||
#[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(())
|
||||
}
|
||||
@@ -428,17 +454,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Router {
|
||||
impl<'a> Delete for Router<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -447,6 +476,8 @@ impl Delete for Router {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
);
|
||||
delete_router(target, self, false, event_details)
|
||||
}
|
||||
@@ -463,6 +494,8 @@ impl Delete for Router {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
);
|
||||
delete_router(target, self, true, event_details)
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
use crate::models::{Context, Listen};
|
||||
|
||||
pub mod aws;
|
||||
@@ -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)]
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -16,14 +17,13 @@ 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;
|
||||
|
||||
pub struct Application {
|
||||
pub struct Application<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -39,9 +39,10 @@ pub struct Application {
|
||||
storage: Vec<Storage<StorageType>>,
|
||||
environment_variables: Vec<EnvironmentVariable>,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Application {
|
||||
impl<'a> Application<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -58,7 +59,8 @@ impl Application {
|
||||
storage: Vec<Storage<StorageType>>,
|
||||
environment_variables: Vec<EnvironmentVariable>,
|
||||
listeners: Listeners,
|
||||
) -> Application {
|
||||
logger: &dyn Logger,
|
||||
) -> Self {
|
||||
Application {
|
||||
context,
|
||||
id: id.to_string(),
|
||||
@@ -75,6 +77,7 @@ impl Application {
|
||||
storage,
|
||||
environment_variables,
|
||||
listeners,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +94,7 @@ impl Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Application for Application {
|
||||
impl<'a> crate::cloud_provider::service::Application for Application<'a> {
|
||||
fn image(&self) -> &Image {
|
||||
&self.image
|
||||
}
|
||||
@@ -101,7 +104,7 @@ impl crate::cloud_provider::service::Application for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl Helm for Application {
|
||||
impl<'a> Helm for Application<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -123,15 +126,15 @@ impl Helm for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatelessService for Application {}
|
||||
impl<'a> StatelessService for Application<'a> {}
|
||||
|
||||
impl ToTransmitter for Application {
|
||||
impl<'a> ToTransmitter for Application<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Application(self.id().to_string(), self.name().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Application {
|
||||
impl<'a> Service for Application<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -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,23 +322,26 @@ 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 {
|
||||
impl<'a> Create for Application<'a> {
|
||||
#[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)
|
||||
deploy_user_stateless_service(target, self, event_details)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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, || {
|
||||
@@ -349,14 +367,17 @@ impl Create for Application {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Application {
|
||||
impl<'a> Pause for Application<'a> {
|
||||
#[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,18 +396,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Application {
|
||||
impl<'a> Delete for Application<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -395,10 +419,12 @@ 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, || {
|
||||
delete_stateless_service(target, self, false, event_details.clone())
|
||||
delete_stateless_service(target, self, false, event_details)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -414,15 +440,17 @@ 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, || {
|
||||
delete_stateless_service(target, self, true, event_details.clone())
|
||||
delete_stateless_service(target, self, true, event_details)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Application {
|
||||
impl<'a> Listen for Application<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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::errors::EngineError;
|
||||
use crate::events::{EnvironmentStep, Stage, ToTransmitter, Transmitter};
|
||||
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;
|
||||
|
||||
pub struct MongoDB {
|
||||
pub struct MongoDB<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct MongoDB {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl MongoDB {
|
||||
impl<'a> MongoDB<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl MongoDB {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
MongoDB {
|
||||
context,
|
||||
@@ -58,11 +61,12 @@ 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)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +78,13 @@ impl MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for MongoDB {
|
||||
impl<'a> StatefulService for MongoDB<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for MongoDB {
|
||||
impl<'a> ToTransmitter for MongoDB<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +94,7 @@ impl ToTransmitter for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for MongoDB {
|
||||
impl<'a> Service for MongoDB<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,6 +156,7 @@ 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;
|
||||
|
||||
@@ -170,7 +175,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() {
|
||||
@@ -211,14 +216,18 @@ impl Service for MongoDB {
|
||||
Ok(context)
|
||||
}
|
||||
|
||||
fn logger(&self) -> &dyn Logger {
|
||||
self.logger
|
||||
}
|
||||
|
||||
fn selector(&self) -> Option<String> {
|
||||
Some(format!("app={}", self.sanitized_name()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MongoDB {}
|
||||
impl<'a> Database for MongoDB<'a> {}
|
||||
|
||||
impl Helm for MongoDB {
|
||||
impl<'a> Helm for MongoDB<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -240,7 +249,7 @@ impl Helm for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for MongoDB {
|
||||
impl<'a> Terraform for MongoDB<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/scaleway/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -250,7 +259,7 @@ impl Terraform for MongoDB {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for MongoDB {
|
||||
impl<'a> Create for MongoDB<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -259,10 +268,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -272,24 +283,30 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for MongoDB {
|
||||
impl<'a> Pause for MongoDB<'a> {
|
||||
#[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, || {
|
||||
@@ -303,18 +320,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for MongoDB {
|
||||
impl<'a> Delete for MongoDB<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -323,10 +343,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -336,17 +358,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for MongoDB {
|
||||
impl<'a> Listen for MongoDB<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -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,15 +11,16 @@ 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 {
|
||||
pub struct MySQL<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -32,9 +33,10 @@ pub struct MySQL {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl MySQL {
|
||||
impl<'a> MySQL<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -48,6 +50,7 @@ impl MySQL {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &dyn Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
@@ -62,21 +65,23 @@ 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,
|
||||
)
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -105,13 +110,13 @@ impl MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for MySQL {
|
||||
impl<'a> StatefulService for MySQL<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for MySQL {
|
||||
impl<'a> ToTransmitter for MySQL<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -121,7 +126,7 @@ impl ToTransmitter for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for MySQL {
|
||||
impl<'a> Service for MySQL<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -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,18 +259,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for MySQL {}
|
||||
impl<'a> Database for MySQL<'a> {}
|
||||
|
||||
impl Helm for MySQL {
|
||||
impl<'a> Helm for MySQL<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -289,7 +288,7 @@ impl Helm for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for MySQL {
|
||||
impl<'a> Terraform for MySQL<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/scaleway/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -299,7 +298,7 @@ impl Terraform for MySQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for MySQL {
|
||||
impl<'a> Create for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -308,10 +307,12 @@ 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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -321,25 +322,31 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for MySQL {
|
||||
impl<'a> Pause for MySQL<'a> {
|
||||
#[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,18 +360,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for MySQL {
|
||||
impl<'a> Delete for MySQL<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -373,10 +383,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -386,17 +398,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for MySQL {
|
||||
impl<'a> Listen for MySQL<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -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,15 +11,16 @@ 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 {
|
||||
pub struct PostgreSQL<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -32,9 +33,10 @@ pub struct PostgreSQL {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl PostgreSQL {
|
||||
impl<'a> PostgreSQL<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -48,6 +50,7 @@ impl PostgreSQL {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
@@ -62,21 +65,23 @@ 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,
|
||||
)
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -114,13 +119,13 @@ impl PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for PostgreSQL {
|
||||
impl<'a> StatefulService for PostgreSQL<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for PostgreSQL {
|
||||
impl<'a> ToTransmitter for PostgreSQL<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -130,7 +135,7 @@ impl ToTransmitter for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for PostgreSQL {
|
||||
impl<'a> Service for PostgreSQL<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -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,18 +268,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for PostgreSQL {}
|
||||
impl<'a> Database for PostgreSQL<'a> {}
|
||||
|
||||
impl Helm for PostgreSQL {
|
||||
impl<'a> Helm for PostgreSQL<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -298,7 +297,7 @@ impl Helm for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for PostgreSQL {
|
||||
impl<'a> Terraform for PostgreSQL<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/scaleway/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -308,7 +307,7 @@ impl Terraform for PostgreSQL {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for PostgreSQL {
|
||||
impl<'a> Create for PostgreSQL<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -317,10 +316,12 @@ 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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -330,25 +331,31 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for PostgreSQL {
|
||||
impl<'a> Pause for PostgreSQL<'a> {
|
||||
#[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,18 +369,21 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for PostgreSQL {
|
||||
impl<'a> Delete for PostgreSQL<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -382,10 +392,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -395,17 +407,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for PostgreSQL {
|
||||
impl<'a> Listen for PostgreSQL<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -3,19 +3,20 @@ 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;
|
||||
|
||||
pub struct Redis {
|
||||
pub struct Redis<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -28,9 +29,10 @@ pub struct Redis {
|
||||
database_instance_type: String,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Redis {
|
||||
impl<'a> Redis<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -44,6 +46,7 @@ impl Redis {
|
||||
database_instance_type: &str,
|
||||
options: DatabaseOptions,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
@@ -58,11 +61,12 @@ 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)
|
||||
}
|
||||
|
||||
fn cloud_provider_name(&self) -> &str {
|
||||
@@ -74,13 +78,13 @@ impl Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatefulService for Redis {
|
||||
impl<'a> StatefulService for Redis<'a> {
|
||||
fn is_managed_service(&self) -> bool {
|
||||
self.options.mode == MANAGED
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTransmitter for Redis {
|
||||
impl<'a> ToTransmitter for Redis<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Database(
|
||||
self.id().to_string(),
|
||||
@@ -90,7 +94,7 @@ impl ToTransmitter for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Redis {
|
||||
impl<'a> Service for Redis<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -152,18 +156,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 +172,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,18 +218,14 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for Redis {}
|
||||
impl<'a> Database for Redis<'a> {}
|
||||
|
||||
impl Helm for Redis {
|
||||
impl<'a> Helm for Redis<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -251,7 +247,7 @@ impl Helm for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Terraform for Redis {
|
||||
impl<'a> Terraform for Redis<'a> {
|
||||
fn terraform_common_resource_dir_path(&self) -> String {
|
||||
format!("{}/scaleway/services/common", self.context.lib_root_dir())
|
||||
}
|
||||
@@ -261,7 +257,7 @@ impl Terraform for Redis {
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for Redis {
|
||||
impl<'a> Create for Redis<'a> {
|
||||
#[named]
|
||||
fn on_create(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Deploy));
|
||||
@@ -270,10 +266,12 @@ 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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -283,24 +281,30 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Redis {
|
||||
impl<'a> Pause for Redis<'a> {
|
||||
#[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,17 +318,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Redis {
|
||||
impl<'a> Delete for Redis<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -333,10 +340,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, self.logger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -346,17 +355,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Redis {
|
||||
impl<'a> Listen for Redis<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
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 {
|
||||
|
||||
@@ -8,13 +8,13 @@ use crate::cloud_provider::service::{
|
||||
use crate::cloud_provider::utilities::{check_cname_for, print_action, sanitize_name};
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::cmd::helm::Timeout;
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::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;
|
||||
|
||||
pub struct Router {
|
||||
pub struct Router<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
action: Action,
|
||||
@@ -24,9 +24,10 @@ pub struct Router {
|
||||
sticky_sessions_enabled: bool,
|
||||
routes: Vec<Route>,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl Router {
|
||||
impl<'a> Router<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -37,7 +38,8 @@ impl Router {
|
||||
routes: Vec<Route>,
|
||||
sticky_sessions_enabled: bool,
|
||||
listeners: Listeners,
|
||||
) -> Router {
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
Router {
|
||||
context,
|
||||
id: id.to_string(),
|
||||
@@ -48,6 +50,7 @@ impl Router {
|
||||
sticky_sessions_enabled,
|
||||
routes,
|
||||
listeners,
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +63,7 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Service for Router {
|
||||
impl<'a> Service for Router<'a> {
|
||||
fn context(&self) -> &Context {
|
||||
&self.context
|
||||
}
|
||||
@@ -122,6 +125,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;
|
||||
|
||||
@@ -193,12 +197,12 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::cloud_provider::service::Router for Router {
|
||||
impl<'a> crate::cloud_provider::service::Router for Router<'a> {
|
||||
fn domains(&self) -> Vec<&str> {
|
||||
let mut _domains = vec![self.default_domain.as_str()];
|
||||
|
||||
@@ -214,7 +218,7 @@ impl crate::cloud_provider::service::Router for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Helm for Router {
|
||||
impl<'a> Helm for Router<'a> {
|
||||
fn helm_selector(&self) -> Option<String> {
|
||||
self.selector()
|
||||
}
|
||||
@@ -236,7 +240,7 @@ impl Helm for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Listen for Router {
|
||||
impl<'a> Listen for Router<'a> {
|
||||
fn listeners(&self) -> &Listeners {
|
||||
&self.listeners
|
||||
}
|
||||
@@ -246,34 +250,33 @@ impl Listen for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl StatelessService for Router {}
|
||||
impl<'a> StatelessService for Router<'a> {}
|
||||
|
||||
impl ToTransmitter for Router {
|
||||
impl<'a> ToTransmitter for Router<'a> {
|
||||
fn to_transmitter(&self) -> Transmitter {
|
||||
Transmitter::Router(self.id().to_string(), self.name().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl Create for Router {
|
||||
impl<'a> Create for Router<'a> {
|
||||
#[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
|
||||
@@ -283,13 +286,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
|
||||
@@ -303,18 +305,18 @@ impl Create for Router {
|
||||
kubernetes.cloud_provider().credentials_environment_variables(),
|
||||
self.service_type(),
|
||||
)
|
||||
.map_err(|e| {
|
||||
NewEngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error()
|
||||
})?;
|
||||
.map_err(|e| EngineError::new_helm_charts_upgrade_error(event_details.clone(), e).to_legacy_engine_error())?;
|
||||
|
||||
if helm_history_row.is_none() || !helm_history_row.unwrap().is_successfully_deployed() {
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, "Router has failed to be deployed".into()));
|
||||
return Err(EngineError::new_router_failed_to_deploy(event_details.clone()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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()?;
|
||||
|
||||
@@ -330,9 +332,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()),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -343,11 +355,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, || {
|
||||
@@ -356,14 +371,17 @@ impl Create for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pause for Router {
|
||||
impl<'a> Pause for Router<'a> {
|
||||
#[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(())
|
||||
}
|
||||
@@ -374,17 +392,20 @@ 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(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Delete for Router {
|
||||
impl<'a> Delete for Router<'a> {
|
||||
#[named]
|
||||
fn on_delete(&self, target: &DeploymentTarget) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details(Stage::Environment(EnvironmentStep::Delete));
|
||||
@@ -393,6 +414,8 @@ impl Delete for Router {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
);
|
||||
delete_router(target, self, false, event_details)
|
||||
}
|
||||
@@ -409,6 +432,8 @@ impl Delete for Router {
|
||||
self.struct_name(),
|
||||
function_name!(),
|
||||
self.name(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
);
|
||||
delete_router(target, self, true, event_details)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ use tera::Context as TeraContext;
|
||||
use crate::build_platform::Image;
|
||||
use crate::cloud_provider::environment::Environment;
|
||||
use crate::cloud_provider::kubernetes::Kubernetes;
|
||||
use crate::cloud_provider::utilities::{check_domain_for, VersionsNumber};
|
||||
use crate::cloud_provider::utilities::check_domain_for;
|
||||
use crate::cloud_provider::DeploymentTarget;
|
||||
use crate::cmd::helm::Timeout;
|
||||
use crate::cmd::kubectl::ScalingKind::Statefulset;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::cloud_provider::models::CpuLimits;
|
||||
use crate::error::{EngineError, StringError};
|
||||
use crate::errors::CommandError;
|
||||
use crate::events::{EngineEvent, EventDetails, EventMessage};
|
||||
@@ -14,7 +13,6 @@ 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};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::cmd;
|
||||
use crate::cmd::utilities::QoveryCommand;
|
||||
use crate::container_registry::Kind;
|
||||
use crate::error::{SimpleError, SimpleErrorKind};
|
||||
use crate::errors::CommandError;
|
||||
use crate::events::{EngineEvent, EventDetails, EventMessage};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use chrono::Duration;
|
||||
use retry::delay::Fibonacci;
|
||||
use retry::Error::Operation;
|
||||
@@ -38,7 +40,9 @@ pub fn docker_manifest_inspect(
|
||||
image_name: String,
|
||||
image_tag: String,
|
||||
registry_url: String,
|
||||
) -> Option<DockerImageManifest> {
|
||||
event_details: EventDetails,
|
||||
logger: &dyn Logger,
|
||||
) -> Result<DockerImageManifest, CommandError> {
|
||||
let image_with_tag = format!("{}:{}", image_name, image_tag);
|
||||
let registry_provider = match container_registry_kind {
|
||||
Kind::DockerHub => "DockerHub",
|
||||
@@ -62,26 +66,44 @@ pub fn docker_manifest_inspect(
|
||||
Ok(_) => {
|
||||
let joined = raw_output.join("");
|
||||
match serde_json::from_str(&joined) {
|
||||
Ok(extracted_manifest) => Some(extracted_manifest),
|
||||
Ok(extracted_manifest) => Ok(extracted_manifest),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"error while trying to deserialize manifest image manifest for image {} in {} ({}): {:?}",
|
||||
image_with_tag, registry_provider, registry_url, e,
|
||||
let error = CommandError::new(
|
||||
e.to_string(),
|
||||
Some(format!(
|
||||
"Error while trying to deserialize manifest image manifest for image {} in {} ({}).",
|
||||
image_with_tag, registry_provider, registry_url,
|
||||
)),
|
||||
);
|
||||
None
|
||||
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(event_details.clone(), EventMessage::from(error.clone())),
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"error while trying to inspect image manifest for image {} in {} ({}), command `{}`: {:?}",
|
||||
image_with_tag,
|
||||
registry_provider,
|
||||
registry_url,
|
||||
cmd::utilities::command_to_string(binary, &args, &envs),
|
||||
e,
|
||||
let error = CommandError::new(
|
||||
format!(
|
||||
"Command `{}`: {:?}",
|
||||
cmd::utilities::command_to_string(binary, &args, &envs),
|
||||
e
|
||||
),
|
||||
Some(format!(
|
||||
"Error while trying to inspect image manifest for image {} in {} ({}).",
|
||||
image_with_tag, registry_provider, registry_url,
|
||||
)),
|
||||
);
|
||||
None
|
||||
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(event_details.clone(), EventMessage::from(error.clone())),
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -92,7 +114,9 @@ pub fn docker_login(
|
||||
registry_login: String,
|
||||
registry_pass: String,
|
||||
registry_url: String,
|
||||
) -> Result<(), SimpleError> {
|
||||
event_details: EventDetails,
|
||||
logger: &dyn Logger,
|
||||
) -> Result<(), CommandError> {
|
||||
let registry_provider = match container_registry_kind {
|
||||
Kind::DockerHub => "DockerHub",
|
||||
Kind::Ecr => "AWS ECR",
|
||||
@@ -114,16 +138,24 @@ pub fn docker_login(
|
||||
match cmd.exec() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
let error_message = format!(
|
||||
"error while trying to login to registry {} {}, command `{}`: {:?}",
|
||||
registry_provider,
|
||||
registry_url,
|
||||
cmd::utilities::command_to_string(binary, &args, &docker_envs),
|
||||
e,
|
||||
let err = CommandError::new(
|
||||
format!(
|
||||
"Command `{}`: {:?}",
|
||||
cmd::utilities::command_to_string(binary, &args, &docker_envs),
|
||||
e,
|
||||
),
|
||||
Some(format!(
|
||||
"Error while trying to login to registry {} {}.",
|
||||
registry_provider, registry_url,
|
||||
)),
|
||||
);
|
||||
error!("{}", error_message);
|
||||
|
||||
Err(SimpleError::new(SimpleErrorKind::Other, Some(error_message)))
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(event_details.clone(), EventMessage::from(err.clone())),
|
||||
);
|
||||
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +166,9 @@ pub fn docker_tag_and_push_image(
|
||||
image_name: String,
|
||||
image_tag: String,
|
||||
dest: String,
|
||||
) -> Result<(), SimpleError> {
|
||||
event_details: EventDetails,
|
||||
logger: &dyn Logger,
|
||||
) -> Result<(), CommandError> {
|
||||
let image_with_tag = format!("{}:{}", image_name, image_tag);
|
||||
let registry_provider = match container_registry_kind {
|
||||
Kind::DockerHub => "DockerHub",
|
||||
@@ -143,19 +177,37 @@ pub fn docker_tag_and_push_image(
|
||||
Kind::ScalewayCr => "Scaleway Registry",
|
||||
};
|
||||
|
||||
let mut cmd = QoveryCommand::new("docker", &vec!["tag", &image_with_tag, dest.as_str()], &docker_envs);
|
||||
let binary = "docker";
|
||||
let args = vec!["tag", &image_with_tag, dest.as_str()];
|
||||
let mut cmd = QoveryCommand::new(binary, &args, &docker_envs);
|
||||
match retry::retry(Fibonacci::from_millis(3000).take(5), || match cmd.exec() {
|
||||
Ok(_) => OperationResult::Ok(()),
|
||||
Err(e) => {
|
||||
info!("failed to tag image {}, retrying...", image_with_tag);
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new(
|
||||
format!(
|
||||
"Command `{}`: {:?}",
|
||||
cmd::utilities::command_to_string(binary, &args, &docker_envs),
|
||||
e
|
||||
),
|
||||
Some(format!("Failed to tag image {}, retrying...", image_with_tag)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
OperationResult::Retry(e)
|
||||
}
|
||||
}) {
|
||||
Err(Operation { error, .. }) => {
|
||||
return Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(format!("failed to tag image {}: {:?}", image_with_tag, error)),
|
||||
))
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(event_details.clone(), EventMessage::from(error.clone())),
|
||||
);
|
||||
|
||||
Err(error)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -169,24 +221,39 @@ pub fn docker_tag_and_push_image(
|
||||
) {
|
||||
Ok(_) => OperationResult::Ok(()),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"failed to push image {} on {}, {:?} retrying...",
|
||||
image_with_tag, registry_provider, e
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new(
|
||||
format!(
|
||||
"Failed to push image {} on {}, retrying...",
|
||||
image_with_tag, registry_provider
|
||||
),
|
||||
Some(format!("{:?}", e)),
|
||||
),
|
||||
),
|
||||
);
|
||||
OperationResult::Retry(e)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Err(Operation { error, .. }) => Err(SimpleError::new(SimpleErrorKind::Other, Some(error.to_string()))),
|
||||
Err(e) => Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Err(Operation { error, .. }) => Err(CommandError::new(error.to_string(), None)),
|
||||
Err(e) => Err(CommandError::new(
|
||||
format!("{:?}", e),
|
||||
Some(format!(
|
||||
"unknown error while trying to push image {} to {}. {:?}",
|
||||
image_with_tag, registry_provider, e
|
||||
"Unknown error while trying to push image {} to {}.",
|
||||
image_with_tag, registry_provider,
|
||||
)),
|
||||
)),
|
||||
_ => {
|
||||
info!("image {} has successfully been pushed", image_with_tag);
|
||||
logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("image {} has successfully been pushed", image_with_tag)),
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -196,7 +263,9 @@ pub fn docker_pull_image(
|
||||
container_registry_kind: Kind,
|
||||
docker_envs: Vec<(&str, &str)>,
|
||||
dest: String,
|
||||
) -> Result<(), SimpleError> {
|
||||
event_details: EventDetails,
|
||||
logger: &dyn Logger,
|
||||
) -> Result<(), CommandError> {
|
||||
let registry_provider = match container_registry_kind {
|
||||
Kind::DockerHub => "DockerHub",
|
||||
Kind::Ecr => "AWS ECR",
|
||||
@@ -213,31 +282,45 @@ pub fn docker_pull_image(
|
||||
) {
|
||||
Ok(_) => OperationResult::Ok(()),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
"failed to pull image from {} registry {}, {:?} retrying...",
|
||||
registry_provider,
|
||||
dest.as_str(),
|
||||
e,
|
||||
logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new(
|
||||
format!(
|
||||
"failed to pull image from {} registry {}, retrying...",
|
||||
registry_provider,
|
||||
dest.as_str(),
|
||||
),
|
||||
Some(format!("{:?}", e)),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
OperationResult::Retry(e)
|
||||
}
|
||||
}
|
||||
}) {
|
||||
Err(Operation { error, .. }) => Err(SimpleError::new(SimpleErrorKind::Other, Some(error.to_string()))),
|
||||
Err(e) => Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Err(Operation { error, .. }) => Err(CommandError::new(error.to_string(), None)),
|
||||
Err(e) => Err(CommandError::new(
|
||||
format!("{:?}", e),
|
||||
Some(format!(
|
||||
"unknown error while trying to pull image {} from {} registry. {:?}",
|
||||
"Unknown error while trying to pull image {} from {} registry.",
|
||||
dest.as_str(),
|
||||
registry_provider,
|
||||
e,
|
||||
)),
|
||||
)),
|
||||
_ => {
|
||||
info!(
|
||||
"image {} has successfully been pulled from {} registry",
|
||||
dest.as_str(),
|
||||
registry_provider,
|
||||
logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!(
|
||||
"Image {} has successfully been pulled from {} registry",
|
||||
dest.as_str(),
|
||||
registry_provider,
|
||||
)),
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,22 +6,25 @@ use crate::build_platform::Image;
|
||||
use crate::cmd::utilities::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::CommandError;
|
||||
use crate::events::{EngineEvent, EventMessage};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use crate::models::{
|
||||
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
|
||||
pub struct DockerHub {
|
||||
pub struct DockerHub<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
name: String,
|
||||
login: String,
|
||||
password: String,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl DockerHub {
|
||||
pub fn new(context: Context, id: &str, name: &str, login: &str, password: &str) -> Self {
|
||||
impl<'a> DockerHub<'a> {
|
||||
pub fn new(context: Context, id: &str, name: &str, login: &str, password: &str, logger: &'a dyn Logger) -> Self {
|
||||
DockerHub {
|
||||
context,
|
||||
id: id.to_string(),
|
||||
@@ -29,10 +32,13 @@ impl DockerHub {
|
||||
login: login.to_string(),
|
||||
password: password.to_string(),
|
||||
listeners: vec![],
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_docker_login(&self) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let envs = match self.context.docker_tcp_socket() {
|
||||
Some(tcp_socket) => vec![("DOCKER_HOST", tcp_socket.as_str())],
|
||||
None => vec![],
|
||||
@@ -46,27 +52,25 @@ impl DockerHub {
|
||||
|
||||
match cmd.exec() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"Your DockerHub 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 DockerHub {}", self.name_with_id()),
|
||||
Err(_) => Err(EngineError::new_client_invalid_cloud_provider_credentials(
|
||||
event_details,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn pull_image(&self, dest: String, image: &Image) -> Result<PullResult, EngineError> {
|
||||
match docker_pull_image(self.kind(), vec![], dest.clone()) {
|
||||
let event_details = self.get_event_details();
|
||||
match docker_pull_image(self.kind(), vec![], dest.clone(), event_details.clone(), self.logger) {
|
||||
Ok(_) => {
|
||||
let mut image = image.clone();
|
||||
image.registry_url = Some(dest);
|
||||
Ok(PullResult::Some(image))
|
||||
}
|
||||
Err(e) => Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker pull".to_string()),
|
||||
Err(e) => Err(EngineError::new_docker_pull_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -90,15 +94,6 @@ impl ContainerRegistry for DockerHub {
|
||||
}
|
||||
|
||||
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);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -119,6 +114,7 @@ impl ContainerRegistry for DockerHub {
|
||||
}
|
||||
|
||||
fn does_image_exists(&self, image: &Image) -> bool {
|
||||
let event_details = self.get_event_details();
|
||||
use reqwest::blocking::Client;
|
||||
let client = Client::new();
|
||||
let path = format!(
|
||||
@@ -133,13 +129,27 @@ impl ContainerRegistry for DockerHub {
|
||||
match res {
|
||||
Ok(out) => matches!(out.status(), StatusCode::OK),
|
||||
Err(e) => {
|
||||
error!("While trying to retrieve if DockerHub repository exist {:?}", e);
|
||||
self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
EngineError::new_container_registry_repository_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new(
|
||||
e.to_string(),
|
||||
Some("Error while trying to retrieve if DockerHub repository exist.".to_string()),
|
||||
)),
|
||||
),
|
||||
None,
|
||||
),
|
||||
);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pull(&self, image: &Image) -> Result<PullResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
if !self.does_image_exists(image) {
|
||||
@@ -148,7 +158,14 @@ impl ContainerRegistry for DockerHub {
|
||||
image,
|
||||
self.name()
|
||||
);
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -163,7 +180,14 @@ impl ContainerRegistry for DockerHub {
|
||||
}
|
||||
|
||||
let info_message = format!("pull image {:?} from DockerHub {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -183,6 +207,8 @@ impl ContainerRegistry for DockerHub {
|
||||
}
|
||||
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let _ = self.exec_docker_login()?;
|
||||
|
||||
let dest = format!("{}/{}", self.login.as_str(), image.name_with_tag().as_str());
|
||||
@@ -196,7 +222,13 @@ impl ContainerRegistry for DockerHub {
|
||||
self.name()
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -219,6 +251,14 @@ impl ContainerRegistry for DockerHub {
|
||||
self.name()
|
||||
);
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
id: image.application_id.clone(),
|
||||
@@ -228,16 +268,25 @@ impl ContainerRegistry for DockerHub {
|
||||
self.context.execution_id(),
|
||||
));
|
||||
|
||||
match docker_tag_and_push_image(self.kind(), vec![], image.name.clone(), image.tag.clone(), dest.clone()) {
|
||||
match docker_tag_and_push_image(
|
||||
self.kind(),
|
||||
vec![],
|
||||
image.name.clone(),
|
||||
image.tag.clone(),
|
||||
dest.clone(),
|
||||
event_details.clone(),
|
||||
self.logger,
|
||||
) {
|
||||
Ok(_) => {
|
||||
let mut image = image.clone();
|
||||
image.registry_url = Some(dest);
|
||||
Ok(PushResult { image })
|
||||
}
|
||||
Err(e) => Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker push".to_string()),
|
||||
Err(e) => Err(EngineError::new_docker_push_image_error(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use crate::build_platform::Image;
|
||||
use crate::cmd::utilities::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::CommandError;
|
||||
use crate::events::{EngineEvent, EventDetails, EventMessage};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use crate::models::{
|
||||
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
@@ -22,40 +24,42 @@ const CR_CLUSTER_API_PATH: &str = "https://api.digitalocean.com/v2/kubernetes/re
|
||||
// TODO : use --output json
|
||||
// see https://www.digitalocean.com/community/tutorials/how-to-use-doctl-the-official-digitalocean-command-line-client
|
||||
|
||||
pub struct DOCR {
|
||||
pub struct DOCR<'a> {
|
||||
pub context: Context,
|
||||
pub name: String,
|
||||
pub api_key: String,
|
||||
pub id: String,
|
||||
pub listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl DOCR {
|
||||
pub fn new(context: Context, id: &str, name: &str, api_key: &str) -> Self {
|
||||
impl<'a> DOCR<'a> {
|
||||
pub fn new(context: Context, id: &str, name: &str, api_key: &str, logger: &dyn Logger) -> Self {
|
||||
DOCR {
|
||||
context,
|
||||
name: name.into(),
|
||||
api_key: api_key.into(),
|
||||
id: id.into(),
|
||||
listeners: vec![],
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_registry_name(&self, image: &Image) -> Result<String, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let registry_name = match image.registry_name.as_ref() {
|
||||
// DOCR does not support upper cases
|
||||
Some(registry_name) => registry_name.to_lowercase(),
|
||||
None => cast_simple_error_to_engine_error(
|
||||
self.engine_error_scope(),
|
||||
self.context().execution_id(),
|
||||
get_current_registry_name(self.api_key.as_str()),
|
||||
)?,
|
||||
None => get_current_registry_name(self.api_key.as_str(), event_details)?,
|
||||
};
|
||||
|
||||
Ok(registry_name)
|
||||
}
|
||||
|
||||
fn create_repository(&self, image: &Image) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let registry_name = match image.registry_name.as_ref() {
|
||||
// DOCR does not support upper cases
|
||||
Some(registry_name) => registry_name.to_lowercase(),
|
||||
@@ -83,46 +87,71 @@ impl DOCR {
|
||||
StatusCode::OK => Ok(()),
|
||||
StatusCode::CREATED => Ok(()),
|
||||
status => {
|
||||
warn!("status from DO registry API {}", status);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"Bad status code : {} returned by the DO registry API for creating DO CR {}",
|
||||
return Err(EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
self.name_with_id(),
|
||||
registry_name.to_string(),
|
||||
CommandError::new_from_safe_message(format!(
|
||||
"Bad status code: `{}` returned by the DO registry API for creating DOCR `{}`.",
|
||||
status,
|
||||
registry_name.as_str(),
|
||||
),
|
||||
)),
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!("failed to create DOCR repository {} : {:?}", registry_name.as_str(), e,),
|
||||
return Err(EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
self.name_with_id(),
|
||||
registry_name.to_string(),
|
||||
CommandError::new(
|
||||
e.to_string(),
|
||||
Some(format!(
|
||||
"Failed to create DOCR repository `{}`.",
|
||||
registry_name.as_str(),
|
||||
)),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!("Unable to initialize DO Registry {} : {:?}", registry_name.as_str(), e,),
|
||||
return Err(EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
self.name_with_id(),
|
||||
registry_name.to_string(),
|
||||
CommandError::new(
|
||||
e.to_string(),
|
||||
Some(format!(
|
||||
"Failed to create DOCR repository `{}`.",
|
||||
registry_name.as_str(),
|
||||
)),
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_image(&self, registry_name: String, dest: String, image: &Image) -> Result<PushResult, EngineError> {
|
||||
let _ =
|
||||
match docker_tag_and_push_image(self.kind(), vec![], image.name.clone(), image.tag.clone(), dest.clone()) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker push".to_string()),
|
||||
));
|
||||
}
|
||||
};
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_tag_and_push_image(
|
||||
self.kind(),
|
||||
vec![],
|
||||
image.name.clone(),
|
||||
image.tag.clone(),
|
||||
dest.clone(),
|
||||
event_details.clone(),
|
||||
self.logger,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => Err(EngineError::new_docker_push_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
|
||||
let mut image = image.clone();
|
||||
image.registry_name = Some(registry_name.clone());
|
||||
@@ -134,15 +163,23 @@ impl DOCR {
|
||||
match self.does_image_exists(&image) {
|
||||
true => OperationResult::Ok(&image),
|
||||
false => {
|
||||
warn!("image is not yet available on Digital Ocean Registry, retrying in a few seconds...");
|
||||
self.logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe(
|
||||
"Image is not yet available on DOCR, retrying in a few seconds...".to_string(),
|
||||
),
|
||||
),
|
||||
);
|
||||
OperationResult::Retry(())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let image_not_reachable = Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"image has been pushed on Digital Ocean Registry but is not yet available after 2min. Please try to redeploy in a few minutes".to_string(),
|
||||
let image_not_reachable = Err(EngineError::new_container_registry_image_unreachable_after_push(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
));
|
||||
match result {
|
||||
Ok(_) => Ok(PushResult { image }),
|
||||
@@ -161,6 +198,8 @@ impl DOCR {
|
||||
}
|
||||
|
||||
pub fn delete_repository(&self) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let headers = utilities::get_header_with_bearer(&self.api_key);
|
||||
let res = reqwest::blocking::Client::new()
|
||||
.delete(CR_API_PATH)
|
||||
@@ -171,26 +210,32 @@ impl DOCR {
|
||||
Ok(out) => match out.status() {
|
||||
StatusCode::NO_CONTENT => Ok(()),
|
||||
status => {
|
||||
warn!("delete status from DO registry API {}", status);
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"Bad status code : {} returned by the DO registry API for deleting DOCR repository",
|
||||
return Err(EngineError::new_container_registry_delete_repository_error(
|
||||
event_details.clone(),
|
||||
"default".to_string(), // DO has only one repository
|
||||
Some(CommandError::new_from_safe_message(format!(
|
||||
"Bad status code: `{}` returned by the DO registry API for deleting DOCR.",
|
||||
status,
|
||||
),
|
||||
))),
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!("No response from the Digital Ocean API : {:?}", e),
|
||||
return Err(EngineError::new_container_registry_delete_repository_error(
|
||||
event_details.clone(),
|
||||
"default".to_string(), // DO has only one repository
|
||||
Some(CommandError::new(
|
||||
e.to_string(),
|
||||
Some("No response from the Digital Ocean API.".to_string()),
|
||||
)),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_docr_login(&self) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let mut cmd = QoveryCommand::new(
|
||||
"doctl",
|
||||
&vec!["registry", "login", self.name.as_str(), "-t", self.api_key.as_str()],
|
||||
@@ -199,18 +244,16 @@ impl DOCR {
|
||||
|
||||
match cmd.exec() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"Your DOCR 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 DOCR {}", self.name_with_id()),
|
||||
Err(_) => Err(EngineError::new_client_invalid_cloud_provider_credentials(
|
||||
event_details,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn pull_image(&self, registry_name: String, dest: String, image: &Image) -> Result<PullResult, EngineError> {
|
||||
match docker_pull_image(self.kind(), vec![], dest.clone()) {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_pull_image(self.kind(), vec![], dest.clone(), event_details.clone(), self.logger()) {
|
||||
Ok(_) => {
|
||||
let mut image = image.clone();
|
||||
image.registry_name = Some(registry_name.clone());
|
||||
@@ -219,10 +262,11 @@ impl DOCR {
|
||||
image.registry_url = Some(dest);
|
||||
Ok(PullResult::Some(image))
|
||||
}
|
||||
Err(e) => Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker pull".to_string()),
|
||||
Err(e) => Err(EngineError::new_docker_pull_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -266,10 +310,12 @@ impl ContainerRegistry for DOCR {
|
||||
}
|
||||
|
||||
fn does_image_exists(&self, image: &Image) -> bool {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
let registry_name = match self.get_registry_name(image) {
|
||||
Ok(registry_name) => registry_name,
|
||||
Err(err) => {
|
||||
warn!("{:?}", err);
|
||||
self.logger.log(LogLevel::Error, EngineEvent::Error(err, None));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -290,18 +336,38 @@ impl ContainerRegistry for DOCR {
|
||||
Ok(output) => match output.status() {
|
||||
StatusCode::OK => output.text(),
|
||||
_ => {
|
||||
error!(
|
||||
"While tyring to get all tags for image: {}, maybe this image not exist !",
|
||||
&image.name
|
||||
self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
EngineError::new_container_registry_image_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new_from_safe_message(format!(
|
||||
"While tyring to get all tags for image: `{}`, maybe this image not exist !",
|
||||
image.name.to_string()
|
||||
))),
|
||||
),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
error!(
|
||||
"While trying to communicate with DigitalOcean API to retrieve all tags for image {}",
|
||||
&image.name
|
||||
self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
EngineError::new_container_registry_image_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new_from_safe_message(format!(
|
||||
"While trying to communicate with DigitalOcean API to retrieve all tags for image `{}`.",
|
||||
image.name.to_string()
|
||||
))),
|
||||
),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
return false;
|
||||
@@ -322,9 +388,22 @@ impl ContainerRegistry for DOCR {
|
||||
false
|
||||
}
|
||||
Err(_) => {
|
||||
error!(
|
||||
"Unable to deserialize tags from DigitalOcean API for image {}",
|
||||
&image.tag
|
||||
self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
EngineError::new_container_registry_image_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new(
|
||||
out.to_string(),
|
||||
Some(format!(
|
||||
"Unable to deserialize tags from DigitalOcean API for image {}",
|
||||
&image.tag.to_string(),
|
||||
)),
|
||||
)),
|
||||
),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
false
|
||||
@@ -332,9 +411,19 @@ impl ContainerRegistry for DOCR {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
error!(
|
||||
"while retrieving tags for image {} Unable to get output from DigitalOcean API",
|
||||
&image.name
|
||||
self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
EngineError::new_container_registry_image_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new_from_safe_message(format!(
|
||||
"While retrieving tags for image `{}` Unable to get output from DigitalOcean API.",
|
||||
image.name.to_string()
|
||||
))),
|
||||
),
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
false
|
||||
@@ -343,11 +432,19 @@ impl ContainerRegistry for DOCR {
|
||||
}
|
||||
|
||||
fn pull(&self, image: &Image) -> Result<PullResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
if !self.does_image_exists(image) {
|
||||
let info_message = format!("image {:?} does not exist in DOCR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -362,7 +459,14 @@ impl ContainerRegistry for DOCR {
|
||||
}
|
||||
|
||||
let info_message = format!("pull image {:?} from DOCR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -389,11 +493,27 @@ impl ContainerRegistry for DOCR {
|
||||
|
||||
// https://www.digitalocean.com/docs/images/container-registry/how-to/use-registry-docker-kubernetes/
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let registry_name = self.get_registry_name(image)?;
|
||||
|
||||
match self.create_repository(image) {
|
||||
Ok(_) => info!("DOCR {} has been created", registry_name.as_str()),
|
||||
Err(_) => warn!("DOCR {} already exists", registry_name.as_str()),
|
||||
Ok(_) => self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("DOCR {} has been created", registry_name.as_str())),
|
||||
),
|
||||
),
|
||||
Err(e) => self.logger.log(
|
||||
LogLevel::Error,
|
||||
EngineEvent::Error(
|
||||
e.clone(),
|
||||
Some(EventMessage::new_from_safe(format!(
|
||||
"DOCR {} already exists",
|
||||
registry_name.as_str()
|
||||
))),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
let _ = self.exec_docr_login()?;
|
||||
@@ -414,7 +534,13 @@ impl ContainerRegistry for DOCR {
|
||||
registry_name.as_str()
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -439,6 +565,14 @@ impl ContainerRegistry for DOCR {
|
||||
image, registry_name
|
||||
);
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
id: image.application_id.clone(),
|
||||
@@ -466,7 +600,7 @@ impl Listen for DOCR {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscribe_kube_cluster_to_container_registry(api_key: &str, cluster_uuid: &str) -> Result<(), SimpleError> {
|
||||
pub fn subscribe_kube_cluster_to_container_registry(api_key: &str, cluster_uuid: &str) -> Result<(), CommandError> {
|
||||
let headers = utilities::get_header_with_bearer(api_key);
|
||||
let cluster_ids = DoApiSubscribeToKubeCluster {
|
||||
cluster_uuids: vec![cluster_uuid.to_string()],
|
||||
@@ -484,31 +618,25 @@ pub fn subscribe_kube_cluster_to_container_registry(api_key: &str, cluster_uuid:
|
||||
match res {
|
||||
Ok(output) => match output.status() {
|
||||
StatusCode::NO_CONTENT => Ok(()),
|
||||
status => {
|
||||
warn!("status from DO registry API {}", status);
|
||||
Err(SimpleError::new(SimpleErrorKind::Other, Some("Incorrect Status received from Digital Ocean when tyring to subscribe repository to cluster")))
|
||||
}
|
||||
status => Err(CommandError::new_from_safe_message(
|
||||
"Incorrect Status received from Digital Ocean when tyring to subscribe repository to cluster"
|
||||
.to_string(),
|
||||
)),
|
||||
},
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("Unable to call Digital Ocean when tyring to subscribe repository to cluster"),
|
||||
))
|
||||
}
|
||||
Err(e) => Err(CommandError::new(
|
||||
e.to_string(),
|
||||
Some("Unable to call Digital Ocean when tyring to subscribe repository to cluster".to_string()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("Unable to Serialize digital ocean cluster uuids"),
|
||||
))
|
||||
}
|
||||
Err(e) => Err(CommandError::new(
|
||||
e.to_string(),
|
||||
Some("Unable to Serialize digital ocean cluster uuids".to_string()),
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_current_registry_name(api_key: &str) -> Result<String, SimpleError> {
|
||||
pub fn get_current_registry_name(api_key: &str, event_details: EventDetails) -> Result<String, EngineError> {
|
||||
let headers = utilities::get_header_with_bearer(api_key);
|
||||
let res = reqwest::blocking::Client::new()
|
||||
.get(CR_API_PATH)
|
||||
@@ -523,28 +651,41 @@ pub fn get_current_registry_name(api_key: &str) -> Result<String, SimpleError> {
|
||||
|
||||
match res_registry {
|
||||
Ok(registry) => Ok(registry.registry.name),
|
||||
Err(err) => Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some(format!(
|
||||
"An error occurred while deserializing JSON coming from Digital Ocean API: error: {:?}",
|
||||
err
|
||||
Err(err) => Err(EngineError::new_container_registry_repository_doesnt_exist(
|
||||
event_details.clone(),
|
||||
"default".to_string(), // DO has only one repository
|
||||
Some(CommandError::new(
|
||||
err.to_string(),
|
||||
Some(
|
||||
"An error occurred while deserializing JSON coming from Digital Ocean API.".to_string(),
|
||||
),
|
||||
)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
status => {
|
||||
warn!("status from Digital Ocean Registry API {}", status);
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("Incorrect Status received from Digital Ocean when tyring to get container registry"),
|
||||
Err(EngineError::new_container_registry_repository_doesnt_exist(
|
||||
event_details.clone(),
|
||||
"default".to_string(), // DO has only one repository
|
||||
Some(CommandError::new(
|
||||
format!("Status: {}", status),
|
||||
Some(
|
||||
"Incorrect Status received from Digital Ocean when tyring to get container registry."
|
||||
.to_string(),
|
||||
),
|
||||
)),
|
||||
))
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("{:?}", e);
|
||||
Err(SimpleError::new(
|
||||
SimpleErrorKind::Other,
|
||||
Some("Unable to call Digital Ocean when tyring to fetch the container registry name"),
|
||||
Err(EngineError::new_container_registry_repository_doesnt_exist(
|
||||
event_details.clone(),
|
||||
"default".to_string(), // DO has only one repository
|
||||
Some(CommandError::new(
|
||||
e.to_string(),
|
||||
Some("Unable to call Digital Ocean when tyring to fetch the container registry name.".to_string()),
|
||||
)),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,7 +12,9 @@ use crate::build_platform::Image;
|
||||
use crate::cmd::utilities::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::{CommandError, EngineError};
|
||||
use crate::events::{EngineEvent, EventMessage};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use crate::models::{
|
||||
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
@@ -22,7 +24,7 @@ use retry::Error::Operation;
|
||||
use retry::OperationResult;
|
||||
use serde_json::json;
|
||||
|
||||
pub struct ECR {
|
||||
pub struct ECR<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
name: String,
|
||||
@@ -30,9 +32,10 @@ pub struct ECR {
|
||||
secret_access_key: String,
|
||||
region: Region,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl ECR {
|
||||
impl<'a> ECR<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -40,6 +43,7 @@ impl ECR {
|
||||
access_key_id: &str,
|
||||
secret_access_key: &str,
|
||||
region: &str,
|
||||
logger: &'a dyn Logger,
|
||||
) -> Self {
|
||||
ECR {
|
||||
context,
|
||||
@@ -49,6 +53,7 @@ impl ECR {
|
||||
secret_access_key: secret_access_key.to_string(),
|
||||
region: Region::from_str(region).unwrap(),
|
||||
listeners: vec![],
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +120,7 @@ impl ECR {
|
||||
fn push_image(&self, dest: String, image: &Image) -> Result<PushResult, EngineError> {
|
||||
// READ https://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-push-ecr-image.html
|
||||
// docker tag e9ae3c220b23 aws_account_id.dkr.ecr.region.amazonaws.com/my-web-app
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_tag_and_push_image(
|
||||
self.kind(),
|
||||
@@ -122,16 +128,19 @@ impl ECR {
|
||||
image.name.clone(),
|
||||
image.tag.clone(),
|
||||
dest.clone(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
) {
|
||||
Ok(_) => {
|
||||
let mut image = image.clone();
|
||||
image.registry_url = Some(dest);
|
||||
Ok(PushResult { image })
|
||||
}
|
||||
Err(e) => Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker push".to_string()),
|
||||
Err(e) => Err(EngineError::new_docker_push_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -139,24 +148,40 @@ impl ECR {
|
||||
fn pull_image(&self, dest: String, image: &Image) -> Result<PullResult, EngineError> {
|
||||
// READ https://docs.aws.amazon.com/AmazonECR/latest/userguide/docker-pull-ecr-image.html
|
||||
// docker pull aws_account_id.dkr.ecr.us-west-2.amazonaws.com/amazonlinux:latest
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_pull_image(self.kind(), self.docker_envs(), dest.clone()) {
|
||||
match docker_pull_image(
|
||||
self.kind(),
|
||||
self.docker_envs(),
|
||||
dest.clone(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
) {
|
||||
Ok(_) => {
|
||||
let mut image = image.clone();
|
||||
image.registry_url = Some(dest);
|
||||
Ok(PullResult::Some(image))
|
||||
}
|
||||
Err(e) => Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker pull".to_string()),
|
||||
Err(e) => Err(EngineError::new_docker_pull_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_repository(&self, image: &Image) -> Result<Repository, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let repository_name = image.name.as_str();
|
||||
info!("creating ECR repository {}", &repository_name);
|
||||
|
||||
self.logger().log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("Creating ECR repository {}", &repository_name)),
|
||||
),
|
||||
);
|
||||
|
||||
let mut repo_creation_counter = 0;
|
||||
let container_registry_request = DescribeRepositoriesRequest {
|
||||
@@ -176,7 +201,13 @@ impl ECR {
|
||||
.describe_repositories(container_registry_request.clone()),
|
||||
) {
|
||||
Ok(x) => {
|
||||
debug!("created {:?} repository", x);
|
||||
self.logger().log(
|
||||
LogLevel::Debug,
|
||||
EngineEvent::Debug(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("Created {:?} repository", x)),
|
||||
),
|
||||
);
|
||||
OperationResult::Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -184,40 +215,78 @@ impl ECR {
|
||||
RusotoError::Service(s) => match s {
|
||||
DescribeRepositoriesError::RepositoryNotFound(_) => {
|
||||
if repo_creation_counter != 0 {
|
||||
warn!(
|
||||
"repository {} was not found, {}x retrying...",
|
||||
&repository_name, &repo_creation_counter
|
||||
self.logger().log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!(
|
||||
"Repository {} was not found, {}x retrying...",
|
||||
&repository_name, &repo_creation_counter
|
||||
)),
|
||||
),
|
||||
);
|
||||
}
|
||||
repo_creation_counter += 1;
|
||||
}
|
||||
_ => warn!("{:?}", s),
|
||||
_ => self.logger().log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new(
|
||||
"Error while trying to create repository.".to_string(),
|
||||
Some(format!("{:?}", s)),
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
_ => warn!("{:?}", e),
|
||||
_ => self.logger().log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
event_details.clone(),
|
||||
EventMessage::new(
|
||||
"Error while trying to create repository.".to_string(),
|
||||
Some(format!("{:?}", e)),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
|
||||
// TODO: This behavior is weird, returning an ok message saying repository has been created in an error ...
|
||||
let msg = match block_on(self.ecr_client().create_repository(crr.clone())) {
|
||||
Ok(_) => format!("repository {} created", &repository_name),
|
||||
Err(err) => format!(
|
||||
"can't create ECR repository {} for {}. {:?}",
|
||||
&repository_name,
|
||||
self.name_with_id(),
|
||||
err
|
||||
),
|
||||
Err(err) => format!("{:?}", err),
|
||||
};
|
||||
|
||||
OperationResult::Retry(Err(self.engine_error(EngineErrorCause::Internal, msg)))
|
||||
OperationResult::Retry(Err(EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
repository_name.to_string(),
|
||||
self.name_with_id(),
|
||||
CommandError::new(msg.to_string(), Some("Can't create ECR repository".to_string())),
|
||||
)))
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
match repo_created {
|
||||
Ok(_) => info!(
|
||||
"repository {} created after {} attempt(s)",
|
||||
&repository_name, repo_creation_counter
|
||||
Ok(_) => self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!(
|
||||
"repository {} created after {} attempt(s)",
|
||||
&repository_name, repo_creation_counter,
|
||||
)),
|
||||
),
|
||||
),
|
||||
Err(Operation { error, .. }) => return error,
|
||||
Err(retry::Error::Internal(e)) => return Err(self.engine_error(EngineErrorCause::Internal, e)),
|
||||
Err(retry::Error::Internal(e)) => {
|
||||
return Err(EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
repository_name.to_string(),
|
||||
self.name_with_id(),
|
||||
CommandError::new_from_safe_message(e),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// apply retention policy
|
||||
@@ -250,32 +319,30 @@ impl ECR {
|
||||
};
|
||||
|
||||
match block_on(self.ecr_client().put_lifecycle_policy(plp)) {
|
||||
Err(err) => {
|
||||
error!(
|
||||
"can't set lifecycle policy to ECR repository {} for {}: {}",
|
||||
image.name.as_str(),
|
||||
self.name_with_id(),
|
||||
err
|
||||
);
|
||||
|
||||
Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"can't set lifecycle policy to ECR repository {} for {}",
|
||||
image.name.as_str(),
|
||||
self.name_with_id()
|
||||
),
|
||||
))
|
||||
}
|
||||
Err(err) => Err(
|
||||
EngineError::new_container_registry_repository_set_lifecycle_policy_error(
|
||||
event_details.clone(),
|
||||
repository_name.to_string(),
|
||||
CommandError::new_from_safe_message(err.to_string()),
|
||||
),
|
||||
),
|
||||
_ => Ok(self.get_repository(image).expect("cannot get repository")),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_create_repository(&self, image: &Image) -> Result<Repository, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
// check if the repository already exists
|
||||
let repository = self.get_repository(image);
|
||||
if repository.is_some() {
|
||||
info!("ECR repository {} already exists", image.name.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("ECR repository {} already exists", image.name.as_str())),
|
||||
),
|
||||
);
|
||||
return Ok(repository.unwrap());
|
||||
}
|
||||
|
||||
@@ -283,6 +350,7 @@ impl ECR {
|
||||
}
|
||||
|
||||
fn get_credentials(&self) -> Result<ECRCredentials, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let r = block_on(
|
||||
self.ecr_client()
|
||||
.get_authorization_token(GetAuthorizationTokenRequest::default()),
|
||||
@@ -306,22 +374,16 @@ impl ECR {
|
||||
)
|
||||
}
|
||||
None => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to retrieve credentials and endpoint URL from ECR {}",
|
||||
self.name_with_id(),
|
||||
),
|
||||
return Err(EngineError::new_container_registry_get_credentials_error(
|
||||
event_details.clone(),
|
||||
self.name_with_id(),
|
||||
));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to retrieve credentials and endpoint URL from ECR {}",
|
||||
self.name_with_id(),
|
||||
),
|
||||
return Err(EngineError::new_container_registry_get_credentials_error(
|
||||
event_details.clone(),
|
||||
self.name_with_id(),
|
||||
));
|
||||
}
|
||||
};
|
||||
@@ -330,6 +392,7 @@ impl ECR {
|
||||
}
|
||||
|
||||
fn exec_docker_login(&self) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let credentials = self.get_credentials()?;
|
||||
|
||||
let mut cmd = QoveryCommand::new(
|
||||
@@ -346,12 +409,8 @@ impl ECR {
|
||||
);
|
||||
|
||||
if let Err(_) = cmd.exec() {
|
||||
return 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!("failed to login to ECR {}", self.name_with_id()),
|
||||
return Err(EngineError::new_client_invalid_cloud_provider_credentials(
|
||||
event_details.clone(),
|
||||
));
|
||||
};
|
||||
|
||||
@@ -382,30 +441,54 @@ impl ContainerRegistry for ECR {
|
||||
|
||||
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(EngineError::new_client_invalid_cloud_provider_credentials(
|
||||
self.get_event_details(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
fn on_create(&self) -> Result<(), EngineError> {
|
||||
info!("ECR.on_create() called");
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe("ECR.on_create() called".to_string()),
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_create_error(&self) -> Result<(), EngineError> {
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe("ECR.on_create_error() called".to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete(&self) -> Result<(), EngineError> {
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe("ECR.on_delete() called".to_string()),
|
||||
),
|
||||
);
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_delete_error(&self) -> Result<(), EngineError> {
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe("ECR.on_delete_error() called".to_string()),
|
||||
),
|
||||
);
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
@@ -414,11 +497,19 @@ impl ContainerRegistry for ECR {
|
||||
}
|
||||
|
||||
fn pull(&self, image: &Image) -> Result<PullResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
if !self.does_image_exists(image) {
|
||||
let info_message = format!("image {:?} does not exist in ECR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -433,7 +524,14 @@ impl ContainerRegistry for ECR {
|
||||
}
|
||||
|
||||
let info_message = format!("pull image {:?} from ECR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -446,19 +544,7 @@ impl ContainerRegistry for ECR {
|
||||
|
||||
let _ = self.exec_docker_login()?;
|
||||
|
||||
let repository = match self.get_or_create_repository(image) {
|
||||
Ok(r) => r,
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to create ECR repository for {} with image {:?}",
|
||||
self.name_with_id(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
let repository = self.get_or_create_repository(image)?;
|
||||
|
||||
let dest = format!("{}:{}", repository.repository_uri.unwrap(), image.tag.as_str());
|
||||
|
||||
@@ -469,23 +555,11 @@ impl ContainerRegistry for ECR {
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let _ = self.exec_docker_login()?;
|
||||
|
||||
let repository = match if force_push {
|
||||
let repository = if force_push {
|
||||
self.create_repository(image)
|
||||
} else {
|
||||
self.get_or_create_repository(image)
|
||||
} {
|
||||
Ok(r) => r,
|
||||
_ => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
format!(
|
||||
"failed to create ECR repository for {} with image {:?}",
|
||||
self.name_with_id(),
|
||||
image,
|
||||
),
|
||||
));
|
||||
}
|
||||
};
|
||||
}?;
|
||||
|
||||
let dest = format!("{}:{}", repository.repository_uri.unwrap(), image.tag.as_str());
|
||||
|
||||
@@ -499,7 +573,13 @@ impl ContainerRegistry for ECR {
|
||||
self.name()
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -522,7 +602,13 @@ impl ContainerRegistry for ECR {
|
||||
self.name()
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
|
||||
@@ -1,8 +1,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;
|
||||
use crate::events::{EnvironmentStep, EventDetails, Stage};
|
||||
use crate::models::{Context, Listen, QoveryIdentifier};
|
||||
|
||||
pub mod docker;
|
||||
pub mod docker_hub;
|
||||
@@ -27,15 +28,16 @@ pub trait ContainerRegistry: Listen {
|
||||
fn pull(&self, image: &Image) -> Result<PullResult, EngineError>;
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError>;
|
||||
fn push_error(&self, image: &Image) -> Result<PushResult, EngineError>;
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::ContainerRegistry(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 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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ use crate::container_registry::docker::{
|
||||
docker_login, docker_manifest_inspect, docker_pull_image, docker_tag_and_push_image,
|
||||
};
|
||||
use crate::container_registry::{ContainerRegistry, Kind, PullResult, PushResult};
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::errors::{CommandError, EngineError};
|
||||
use crate::events::{EngineEvent, EventMessage};
|
||||
use crate::logger::{LogLevel, Logger};
|
||||
use crate::models::{
|
||||
Context, Listen, Listener, Listeners, ListenersHelper, ProgressInfo, ProgressLevel, ProgressScope,
|
||||
};
|
||||
@@ -18,7 +20,7 @@ use retry::Error::Operation;
|
||||
use retry::OperationResult;
|
||||
use rusoto_core::param::ToParam;
|
||||
|
||||
pub struct ScalewayCR {
|
||||
pub struct ScalewayCR<'a> {
|
||||
context: Context,
|
||||
id: String,
|
||||
name: String,
|
||||
@@ -27,9 +29,10 @@ pub struct ScalewayCR {
|
||||
secret_token: String,
|
||||
zone: ScwZone,
|
||||
listeners: Listeners,
|
||||
logger: &'a dyn Logger,
|
||||
}
|
||||
|
||||
impl ScalewayCR {
|
||||
impl<'a> ScalewayCR<'a> {
|
||||
pub fn new(
|
||||
context: Context,
|
||||
id: &str,
|
||||
@@ -37,6 +40,7 @@ impl ScalewayCR {
|
||||
secret_token: &str,
|
||||
default_project_id: &str,
|
||||
zone: ScwZone,
|
||||
logger: &'a dyn Logger,
|
||||
) -> ScalewayCR {
|
||||
ScalewayCR {
|
||||
context,
|
||||
@@ -47,6 +51,7 @@ impl ScalewayCR {
|
||||
secret_token: secret_token.to_string(),
|
||||
zone,
|
||||
listeners: Vec::new(),
|
||||
logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +89,15 @@ impl ScalewayCR {
|
||||
)) {
|
||||
Ok(res) => res.namespaces,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Error while interacting with Scaleway API (list_namespaces), error: {}, image: {}",
|
||||
e, &image.name
|
||||
self.logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
self.get_event_details(),
|
||||
EventMessage::new(
|
||||
"Error while interacting with Scaleway API (list_namespaces).".to_string(),
|
||||
Some(format!("error: {}, image: {}", e, &image.name)),
|
||||
),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@@ -121,9 +132,15 @@ impl ScalewayCR {
|
||||
)) {
|
||||
Ok(res) => res.images,
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Error while interacting with Scaleway API (list_images), error: {}, image: {}",
|
||||
e, &image.name
|
||||
self.logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
self.get_event_details(),
|
||||
EventMessage::new(
|
||||
"Error while interacting with Scaleway API (list_namespaces).".to_string(),
|
||||
Some(format!("error: {}, image: {}", e, &image.name)),
|
||||
),
|
||||
),
|
||||
);
|
||||
return None;
|
||||
}
|
||||
@@ -143,13 +160,20 @@ impl ScalewayCR {
|
||||
}
|
||||
|
||||
pub fn delete_image(&self, image: &Image) -> Result<scaleway_api_rs::models::ScalewayRegistryV1Image, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
// https://developers.scaleway.com/en/products/registry/api/#delete-67dbf7
|
||||
let image_to_delete = self.get_image(image);
|
||||
if image_to_delete.is_none() {
|
||||
let message = format!("While tyring to delete image {}, image doesn't exist", &image.name,);
|
||||
error!("{}", message);
|
||||
let err = EngineError::new_container_registry_image_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
None,
|
||||
);
|
||||
|
||||
return Err(self.engine_error(EngineErrorCause::Internal, message));
|
||||
self.logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let image_to_delete = image_to_delete.unwrap();
|
||||
@@ -161,49 +185,62 @@ impl ScalewayCR {
|
||||
)) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while interacting with Scaleway API (delete_image), error: {}, image: {}",
|
||||
e, &image.name
|
||||
let err = EngineError::new_container_registry_delete_image_error(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
Some(CommandError::new(e.to_string(), None)),
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message))
|
||||
self.logger.log(LogLevel::Error, EngineEvent::Error(err.clone(), None));
|
||||
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_image(&self, dest: String, image: &Image) -> Result<PushResult, EngineError> {
|
||||
// https://www.scaleway.com/en/docs/deploy-an-image-from-registry-to-kubernetes-kapsule/
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_tag_and_push_image(
|
||||
self.kind(),
|
||||
self.get_docker_envs(),
|
||||
image.name.clone(),
|
||||
image.tag.clone(),
|
||||
dest,
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker push".to_string()),
|
||||
))
|
||||
}
|
||||
};
|
||||
Err(e) => Err(EngineError::new_docker_push_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
}
|
||||
|
||||
let result = retry::retry(Fibonacci::from_millis(10000).take(10), || {
|
||||
match self.does_image_exists(image) {
|
||||
true => OperationResult::Ok(&image),
|
||||
false => {
|
||||
warn!("image is not yet available on Scaleway Registry Namespace, retrying in a few seconds...");
|
||||
self.logger.log(
|
||||
LogLevel::Warning,
|
||||
EngineEvent::Warning(
|
||||
self.get_event_details(),
|
||||
EventMessage::new_from_safe(
|
||||
"Image is not yet available on Scaleway Registry Namespace, retrying in a few seconds...".to_string(),
|
||||
),
|
||||
),
|
||||
);
|
||||
OperationResult::Retry(())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let image_not_reachable = Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
"image has been pushed on Scaleway Registry Namespace but is not yet available after 4min. Please try to redeploy in a few minutes".to_string(),
|
||||
let image_not_reachable = Err(EngineError::new_container_registry_image_unreachable_after_push(
|
||||
event_details.clone(),
|
||||
image.name.to_string(),
|
||||
));
|
||||
|
||||
match result {
|
||||
@@ -214,15 +251,22 @@ impl ScalewayCR {
|
||||
}
|
||||
|
||||
fn pull_image(&self, dest: String, image: &Image) -> Result<PullResult, EngineError> {
|
||||
match docker_pull_image(self.kind(), self.get_docker_envs(), dest) {
|
||||
let event_details = self.get_event_details();
|
||||
|
||||
match docker_pull_image(
|
||||
self.kind(),
|
||||
self.get_docker_envs(),
|
||||
dest,
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::Internal,
|
||||
e.message
|
||||
.unwrap_or_else(|| "unknown error occurring during docker pull".to_string()),
|
||||
))
|
||||
}
|
||||
Err(e) => Err(EngineError::new_docker_pull_image_error(
|
||||
event_details,
|
||||
image.name.to_string(),
|
||||
dest.to_string(),
|
||||
e,
|
||||
)),
|
||||
};
|
||||
|
||||
Ok(PullResult::Some(image.clone()))
|
||||
@@ -246,13 +290,17 @@ impl ScalewayCR {
|
||||
)) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while interacting with Scaleway API (create_namespace), error: {}, image: {}",
|
||||
e, &image.name
|
||||
let error = EngineError::new_container_registry_namespace_creation_error(
|
||||
event_details.clone(),
|
||||
repository_name.to_string(),
|
||||
self.name_with_id(),
|
||||
CommandError::new(e.to_string(), Some("Can't create SCW repository".to_string())),
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message))
|
||||
self.logger
|
||||
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
|
||||
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -262,15 +310,19 @@ impl ScalewayCR {
|
||||
image: &Image,
|
||||
) -> Result<scaleway_api_rs::models::ScalewayRegistryV1Namespace, EngineError> {
|
||||
// https://developers.scaleway.com/en/products/registry/api/#delete-c1ac9b
|
||||
let event_details = self.get_event_details();
|
||||
let registry_to_delete = self.get_registry_namespace(image);
|
||||
if registry_to_delete.is_none() {
|
||||
let message = format!(
|
||||
"While tyring to delete registry namespace for image {}, registry namespace doesn't exist",
|
||||
&image.name,
|
||||
let error = EngineError::new_container_registry_repository_doesnt_exist(
|
||||
event_details.clone(),
|
||||
image.registry_name.unwrap_or("unknown".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 registry_to_delete = registry_to_delete.unwrap();
|
||||
@@ -282,13 +334,16 @@ impl ScalewayCR {
|
||||
)) {
|
||||
Ok(res) => Ok(res),
|
||||
Err(e) => {
|
||||
let message = format!(
|
||||
"Error while interacting with Scaleway API (delete_namespace), error: {}, image: {}",
|
||||
e, &image.name
|
||||
let error = EngineError::new_container_registry_delete_repository_error(
|
||||
event_details.clone(),
|
||||
image.registry_name.unwrap_or("unknown".to_string()),
|
||||
Some(CommandError::new(e.to_string(), None)),
|
||||
);
|
||||
|
||||
error!("{}", message);
|
||||
Err(self.engine_error(EngineErrorCause::Internal, message))
|
||||
self.logger
|
||||
.log(LogLevel::Error, EngineEvent::Error(error.clone(), None));
|
||||
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -298,9 +353,16 @@ impl ScalewayCR {
|
||||
image: &Image,
|
||||
) -> Result<scaleway_api_rs::models::ScalewayRegistryV1Namespace, EngineError> {
|
||||
// check if the repository already exists
|
||||
let event_details = self.get_event_details();
|
||||
let registry_namespace = self.get_registry_namespace(&image);
|
||||
if let Some(namespace) = registry_namespace {
|
||||
info!("Scaleway registry namespace {} already exists", image.name.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!("SCW repository {} already exists", image.name.as_str())),
|
||||
),
|
||||
);
|
||||
return Ok(namespace);
|
||||
}
|
||||
|
||||
@@ -319,21 +381,20 @@ impl ScalewayCR {
|
||||
}
|
||||
|
||||
fn exec_docker_login(&self, registry_url: &String) -> Result<(), EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
if docker_login(
|
||||
Kind::ScalewayCr,
|
||||
self.get_docker_envs(),
|
||||
self.login.clone(),
|
||||
self.secret_token.clone(),
|
||||
registry_url.clone(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return Err(self.engine_error(
|
||||
EngineErrorCause::User(
|
||||
"Your Scaleway 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 Scaleway {}", self.name_with_id()),
|
||||
return Err(EngineError::new_client_invalid_cloud_provider_credentials(
|
||||
event_details,
|
||||
));
|
||||
};
|
||||
|
||||
@@ -379,6 +440,7 @@ impl ContainerRegistry for ScalewayCR {
|
||||
}
|
||||
|
||||
fn does_image_exists(&self, image: &Image) -> bool {
|
||||
let event_details = self.get_event_details();
|
||||
let registry_url = image
|
||||
.registry_url
|
||||
.as_ref()
|
||||
@@ -391,6 +453,8 @@ impl ContainerRegistry for ScalewayCR {
|
||||
self.login.clone(),
|
||||
self.secret_token.clone(),
|
||||
registry_url.clone(),
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@@ -401,11 +465,14 @@ impl ContainerRegistry for ScalewayCR {
|
||||
image.name.clone(),
|
||||
image.tag.clone(),
|
||||
registry_url,
|
||||
event_details.clone(),
|
||||
self.logger(),
|
||||
)
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn pull(&self, image: &Image) -> Result<PullResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let listeners_helper = ListenersHelper::new(&self.listeners);
|
||||
|
||||
let mut image = image.clone();
|
||||
@@ -413,10 +480,17 @@ impl ContainerRegistry for ScalewayCR {
|
||||
|
||||
match self.get_or_create_registry_namespace(&image) {
|
||||
Ok(registry) => {
|
||||
info!(
|
||||
"Scaleway registry namespace for {} has been created",
|
||||
image.name.as_str()
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!(
|
||||
"Scaleway registry namespace for {} has been created",
|
||||
image.name.as_str()
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
image.registry_name = Some(image.name.clone()); // Note: Repository namespace should have the same name as the image name
|
||||
image.registry_url = registry.endpoint.clone();
|
||||
image.registry_secret = Some(self.secret_token.clone());
|
||||
@@ -424,18 +498,23 @@ impl ContainerRegistry for ScalewayCR {
|
||||
registry_url = registry.endpoint.unwrap_or_else(|| "undefined".to_string());
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Scaleway registry namespace for {} cannot be created, error: {:?}",
|
||||
image.name.as_str(),
|
||||
e
|
||||
);
|
||||
self.logger.log(LogLevel::Error, EngineEvent::Error(e.clone(), None));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
if !self.does_image_exists(&image) {
|
||||
let info_message = format!("image {:?} does not exist in SCR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(format!(
|
||||
"Image {:?} does not exist in SCR {} repository",
|
||||
image,
|
||||
self.name()
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -450,7 +529,14 @@ impl ContainerRegistry for ScalewayCR {
|
||||
}
|
||||
|
||||
let info_message = format!("pull image {:?} from SCR {} repository", image, self.name());
|
||||
info!("{}", info_message.as_str());
|
||||
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -470,16 +556,13 @@ impl ContainerRegistry for ScalewayCR {
|
||||
}
|
||||
|
||||
fn push(&self, image: &Image, force_push: bool) -> Result<PushResult, EngineError> {
|
||||
let event_details = self.get_event_details();
|
||||
let mut image = image.clone();
|
||||
let registry_url: String;
|
||||
let registry_name: String;
|
||||
|
||||
match self.get_or_create_registry_namespace(&image) {
|
||||
Ok(registry) => {
|
||||
info!(
|
||||
"Scaleway registry namespace for {} has been created",
|
||||
image.name.as_str()
|
||||
);
|
||||
image.registry_name = Some(image.name.clone()); // Note: Repository namespace should have the same name as the image name
|
||||
image.registry_url = registry.endpoint.clone();
|
||||
image.registry_secret = Some(self.secret_token.clone());
|
||||
@@ -488,11 +571,7 @@ impl ContainerRegistry for ScalewayCR {
|
||||
registry_name = registry.name.unwrap();
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Scaleway registry namespace for {} cannot be created, error: {:?}",
|
||||
image.name.as_str(),
|
||||
e
|
||||
);
|
||||
self.logger.log(LogLevel::Error, EngineEvent::Error(e.clone(), None));
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
@@ -510,7 +589,13 @@ impl ContainerRegistry for ScalewayCR {
|
||||
image, registry_name,
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
@@ -530,7 +615,13 @@ impl ContainerRegistry for ScalewayCR {
|
||||
self.name()
|
||||
);
|
||||
|
||||
info!("{}", info_message.as_str());
|
||||
self.logger.log(
|
||||
LogLevel::Info,
|
||||
EngineEvent::Info(
|
||||
event_details.clone(),
|
||||
EventMessage::new_from_safe(info_message.to_string()),
|
||||
),
|
||||
);
|
||||
|
||||
listeners_helper.deployment_in_progress(ProgressInfo::new(
|
||||
ProgressScope::Application {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::dns_provider::{DnsProvider, Kind};
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::errors::EngineError;
|
||||
use crate::models::{Context, Domain};
|
||||
|
||||
pub struct Cloudflare {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::errors::EngineError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::models::{Context, Domain};
|
||||
|
||||
pub mod cloudflare;
|
||||
@@ -21,17 +21,6 @@ pub trait DnsProvider {
|
||||
fn domain(&self) -> &Domain;
|
||||
fn resolvers(&self) -> Vec<Ipv4Addr>;
|
||||
fn is_valid(&self) -> Result<(), EngineError>;
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::DnsProvider(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -68,6 +68,7 @@ pub enum EngineErrorCause {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[deprecated(note = "errors.CommandError to be used instead")]
|
||||
pub struct SimpleError {
|
||||
pub kind: SimpleErrorKind,
|
||||
pub message: Option<String>,
|
||||
|
||||
@@ -204,6 +204,46 @@ pub enum Tag {
|
||||
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,
|
||||
/// 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,
|
||||
/// ContainerRegistryRepositoryCreationError: represents an error when trying to create a repository.
|
||||
ContainerRegistryRepositoryCreationError,
|
||||
/// ContainerRegistryRepositorySetLifecycleError: represents an error when trying to set repository lifecycle policy.
|
||||
ContainerRegistryRepositorySetLifecycleError,
|
||||
/// ContainerRegistryGetCredentialsError: represents an error when trying to get container registry credentials.
|
||||
ContainerRegistryGetCredentialsError,
|
||||
/// ContainerRegistryDeleteImageError: represents an error while trying to delete an image.
|
||||
ContainerRegistryDeleteImageError,
|
||||
/// ContainerRegistryImageDoesntExist: represents an error, image doesn't exist in the registry.
|
||||
ContainerRegistryImageDoesntExist,
|
||||
/// ContainerRegistryImageUnreachableAfterPush: represents an error when image has been pushed but is unreachable.
|
||||
ContainerRegistryImageUnreachableAfterPush,
|
||||
/// ContainerRegistryRepositoryDoesntExist: represents an error, repository doesn't exist.
|
||||
ContainerRegistryRepositoryDoesntExist,
|
||||
/// ContainerRegistryDeleteRepositoryError: represents an error while trying to delete a repository.
|
||||
ContainerRegistryDeleteRepositoryError,
|
||||
/// NotImplementedError: represents an error where feature / code has not been implemented yet.
|
||||
NotImplementedError,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -1852,4 +1892,512 @@ impl EngineError {
|
||||
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 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()),
|
||||
)
|
||||
}
|
||||
|
||||
/// 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 create a new container registry namespace.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `repository_name`: Container repository name.
|
||||
/// * `registry_name`: Registry to be created.
|
||||
/// * `raw_error`: Raw error message.
|
||||
pub fn new_container_registry_namespace_creation_error(
|
||||
event_details: EventDetails,
|
||||
repository_name: String,
|
||||
registry_name: String,
|
||||
raw_error: CommandError,
|
||||
) -> EngineError {
|
||||
let message = format!(
|
||||
"Error, trying to create registry `{}` in `{}`.",
|
||||
registry_name, repository_name
|
||||
);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryRepositoryCreationError,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
Some(raw_error),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to set container repository lifecycle policy.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `repository_name`: Repository name.
|
||||
/// * `raw_error`: Raw error message.
|
||||
pub fn new_container_registry_repository_set_lifecycle_policy_error(
|
||||
event_details: EventDetails,
|
||||
repository_name: String,
|
||||
raw_error: CommandError,
|
||||
) -> EngineError {
|
||||
let message = format!(
|
||||
"Error, trying to set lifecycle policy repository `{}`.",
|
||||
repository_name,
|
||||
);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryRepositorySetLifecycleError,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
Some(raw_error),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to get container registry credentials.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `repository_name`: Repository name.
|
||||
pub fn new_container_registry_get_credentials_error(
|
||||
event_details: EventDetails,
|
||||
repository_name: String,
|
||||
) -> EngineError {
|
||||
let message = format!(
|
||||
"Failed to retrieve credentials and endpoint URL from container registry `{}`.",
|
||||
repository_name,
|
||||
);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryGetCredentialsError,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to delete an image.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `image_name`: Image name.
|
||||
/// * `raw_error`: Raw error message.
|
||||
pub fn new_container_registry_delete_image_error(
|
||||
event_details: EventDetails,
|
||||
image_name: String,
|
||||
raw_error: Option<CommandError>,
|
||||
) -> EngineError {
|
||||
let message = format!("Failed to delete image `{}`.", image_name,);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryDeleteImageError,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
raw_error,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to get image from a registry.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `image_name`: Image name.
|
||||
pub fn new_container_registry_image_doesnt_exist(
|
||||
event_details: EventDetails,
|
||||
image_name: String,
|
||||
raw_error: Option<CommandError>,
|
||||
) -> EngineError {
|
||||
let message = format!("Image `{}` doesn't exists.", image_name,);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryImageDoesntExist,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
raw_error,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when image is unreachable after push.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `image_name`: Image name.
|
||||
/// * `raw_error`: Raw error message.
|
||||
pub fn new_container_registry_image_unreachable_after_push(
|
||||
event_details: EventDetails,
|
||||
image_name: String,
|
||||
) -> EngineError {
|
||||
let message = format!(
|
||||
"Image `{}` has been pushed on registry namespace but is not yet available after some time.",
|
||||
image_name,
|
||||
);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryImageUnreachableAfterPush,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
None,
|
||||
None,
|
||||
Some("Please try to redeploy in a few minutes.".to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to get image from a registry.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `repository_name`: Repository name.
|
||||
pub fn new_container_registry_repository_doesnt_exist(
|
||||
event_details: EventDetails,
|
||||
repository_name: String,
|
||||
raw_error: Option<CommandError>,
|
||||
) -> EngineError {
|
||||
let message = format!("Repository `{}` doesn't exists.", repository_name,);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryRepositoryDoesntExist,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
raw_error,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Creates new error when trying to delete repository.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `event_details`: Error linked event details.
|
||||
/// * `repository_name`: Repository name.
|
||||
/// * `raw_error`: Raw error message.
|
||||
pub fn new_container_registry_delete_repository_error(
|
||||
event_details: EventDetails,
|
||||
repository_name: String,
|
||||
raw_error: Option<CommandError>,
|
||||
) -> EngineError {
|
||||
let message = format!("Failed to delete repository `{}`.", repository_name,);
|
||||
|
||||
EngineError::new(
|
||||
event_details,
|
||||
Tag::ContainerRegistryDeleteRepositoryError,
|
||||
message.to_string(),
|
||||
message.to_string(),
|
||||
raw_error,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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.
|
||||
@@ -449,7 +455,8 @@ mod tests {
|
||||
use crate::cloud_provider::Kind::Aws;
|
||||
use crate::errors::{CommandError, EngineError};
|
||||
use crate::events::{
|
||||
EngineEvent, EnvironmentStep, EventDetails, EventMessage, InfrastructureStep, Stage, Transmitter,
|
||||
EngineEvent, EnvironmentStep, EventDetails, EventMessage, EventMessageVerbosity, InfrastructureStep, Stage,
|
||||
Transmitter,
|
||||
};
|
||||
use crate::models::QoveryIdentifier;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::error::{EngineError, EngineErrorCause, EngineErrorScope};
|
||||
use crate::errors::EngineError;
|
||||
use crate::models::{Context, StringPath};
|
||||
use std::fs::File;
|
||||
|
||||
@@ -21,17 +21,6 @@ pub trait ObjectStorage {
|
||||
fn delete_bucket(&self, bucket_name: &str) -> Result<(), EngineError>;
|
||||
fn get(&self, bucket_name: &str, object_key: &str, use_cache: bool) -> Result<(StringPath, File), EngineError>;
|
||||
fn put(&self, bucket_name: &str, object_key: &str, file_path: &str) -> Result<(), EngineError>;
|
||||
fn engine_error_scope(&self) -> EngineErrorScope {
|
||||
EngineErrorScope::ObjectStorage(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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::cloud_provider::aws::regions::AwsRegion;
|
||||
use crate::errors::EngineError;
|
||||
use rusoto_core::credential::StaticProvider;
|
||||
use rusoto_core::{Client, HttpClient, Region as RusotoRegion};
|
||||
use rusoto_s3::{
|
||||
@@ -14,7 +15,6 @@ use rusoto_s3::{
|
||||
};
|
||||
use tokio::io;
|
||||
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::models::{Context, StringPath};
|
||||
use crate::object_storage::{Kind, ObjectStorage};
|
||||
use crate::runtime::block_on;
|
||||
|
||||
@@ -3,10 +3,10 @@ use std::fs::File;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::cloud_provider::scaleway::application::ScwZone;
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::models::{Context, StringPath};
|
||||
use crate::object_storage::{Kind, ObjectStorage};
|
||||
|
||||
use crate::errors::EngineError;
|
||||
use crate::runtime::block_on;
|
||||
use rusoto_core::{Client, HttpClient, Region as RusotoRegion};
|
||||
use rusoto_credential::StaticProvider;
|
||||
|
||||
@@ -12,7 +12,7 @@ use rusoto_s3::{
|
||||
use tokio::io;
|
||||
|
||||
use crate::cloud_provider::digitalocean::application::DoRegion;
|
||||
use crate::error::{EngineError, EngineErrorCause};
|
||||
use crate::errors::EngineError;
|
||||
use crate::models::{Context, StringPath};
|
||||
use crate::object_storage::{Kind, ObjectStorage};
|
||||
use crate::runtime;
|
||||
|
||||
@@ -6,8 +6,7 @@ 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;
|
||||
use crate::models::{
|
||||
Action, Environment, EnvironmentAction, EnvironmentError, ListenersHelper, ProgressInfo, ProgressLevel,
|
||||
ProgressScope,
|
||||
@@ -28,7 +27,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 +37,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 +47,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));
|
||||
|
||||
Reference in New Issue
Block a user